Website : rimsha.abasa.com
backdoor
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
var
/
canvas
/
node_modules
/
mathlive
/
dist
/
Filename :
mathlive.js
back
Copy
/** MathLive 0.77.0 */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.MathLive = {})); })(this, (function (exports) { 'use strict'; function isArray(x) { return Array.isArray(x); } // This file is GENERATED by buildMetrics.sh. DO NOT MODIFY. const M6 = [0, 0.69444, 0, 0]; // 221 const M11 = [0, 0.61111, 0, 0]; // 76 const M15 = [0.25, 0.75, 0, 0]; // 53 const M7 = [0, 0.44444, 0, 0]; // 52 const M4 = [0, 0.68611, 0, 0]; //48 const M16 = [0.19444, 0.69444, 0, 0]; // 44 const M9 = [0, 0.68333, 0, 0]; // 42 const M1 = [0, 0.68889, 0, 0]; // 41 const M21 = [0, 0.69141, 0, 0]; // 36 const M0 = [0, 0, 0, 0]; // 37 const M5 = [0, 0.64444, 0, 0]; // 25 const M3 = [0, 0.69224, 0, 0]; // 21 const M8 = [0.19444, 0.44444, 0, 0]; // 21 const M19 = [0.65002, 1.15, 0, 0]; // 18 const M20 = [0, 0.55556, 0, 0]; // 17 const M10 = [0.35001, 0.85, 0, 0]; // 16 const M12 = [1.25003, 1.75, 0, 0]; // 16 const M13 = [0.95003, 1.45, 0, 0]; // 16 const M14 = [0, 0.75, 0, 0]; // 15 const M22 = [0, 0.47534, 0, 0]; // 15 const M17 = [0.25001, 0.75, 0, 0]; // 13 const M18 = [0.55001, 1.05, 0, 0]; // 13 const M2 = [0.16667, 0.68889, 0, 0]; // 4 const M23 = [0.08167, 0.58167, 0, 0]; // 12 var CHARACTER_METRICS_MAP = { 'AMS-Regular': { '32': M0, '65': M1, '66': M1, '67': M1, '68': M1, '69': M1, '70': M1, '71': M1, '72': M1, '73': M1, '74': M2, '75': M1, '76': M1, '77': M1, '78': M1, '79': M2, '80': M1, '81': M2, '82': M1, '83': M1, '84': M1, '85': M1, '86': M1, '87': M1, '88': M1, '89': M1, '90': M1, '107': M1, '160': M0, '165': [0, 0.675, 0.025, 0], '174': [0.15559, 0.69224, 0, 0], '240': M1, '295': M1, '710': [0, 0.825, 0, 0], '732': [0, 0.9, 0, 0], '770': [0, 0.825, 0, 0], '771': [0, 0.9, 0, 0], '989': M23, '1008': [0, 0.43056, 0.04028, 0], '8245': [0, 0.54986, 0, 0], '8463': M1, '8487': M1, '8498': M1, '8502': M1, '8503': M1, '8504': M1, '8513': M1, '8592': [-0.03598, 0.46402, 0, 0], '8594': [-0.03598, 0.46402, 0, 0], '8602': [-0.13313, 0.36687, 0, 0], '8603': [-0.13313, 0.36687, 0, 0], '8606': [0.01354, 0.52239, 0, 0], '8608': [0.01354, 0.52239, 0, 0], '8610': [0.01354, 0.52239, 0, 0], '8611': [0.01354, 0.52239, 0, 0], '8619': [0, 0.54986, 0, 0], '8620': [0, 0.54986, 0, 0], '8621': [-0.13313, 0.37788, 0, 0], '8622': [-0.13313, 0.36687, 0, 0], '8624': M3, '8625': M3, '8630': [0, 0.43056, 0, 0], '8631': [0, 0.43056, 0, 0], '8634': [0.08198, 0.58198, 0, 0], '8635': [0.08198, 0.58198, 0, 0], '8638': [0.19444, 0.69224, 0, 0], '8639': [0.19444, 0.69224, 0, 0], '8642': [0.19444, 0.69224, 0, 0], '8643': [0.19444, 0.69224, 0, 0], '8644': [0.1808, 0.675, 0, 0], '8646': [0.1808, 0.675, 0, 0], '8647': [0.1808, 0.675, 0, 0], '8648': [0.19444, 0.69224, 0, 0], '8649': [0.1808, 0.675, 0, 0], '8650': [0.19444, 0.69224, 0, 0], '8651': [0.01354, 0.52239, 0, 0], '8652': [0.01354, 0.52239, 0, 0], '8653': [-0.13313, 0.36687, 0, 0], '8654': [-0.13313, 0.36687, 0, 0], '8655': [-0.13313, 0.36687, 0, 0], '8666': [0.13667, 0.63667, 0, 0], '8667': [0.13667, 0.63667, 0, 0], '8669': [-0.13313, 0.37788, 0, 0], '8672': [-0.064, 0.437, 0, 0], '8674': [-0.064, 0.437, 0, 0], '8705': [0, 0.825, 0, 0], '8708': M1, '8709': M23, '8717': [0, 0.43056, 0, 0], '8722': [-0.03598, 0.46402, 0, 0], '8724': [0.08198, 0.69224, 0, 0], '8726': M23, '8733': M3, '8736': M3, '8737': M3, '8738': [0.03517, 0.52239, 0, 0], '8739': M23, '8740': [0.25142, 0.74111, 0, 0], '8741': M23, '8742': [0.25142, 0.74111, 0, 0], '8756': M3, '8757': M3, '8764': [-0.13313, 0.36687, 0, 0], '8765': [-0.13313, 0.37788, 0, 0], '8769': [-0.13313, 0.36687, 0, 0], '8770': [-0.03625, 0.46375, 0, 0], '8774': [0.30274, 0.79383, 0, 0], '8776': [-0.01688, 0.48312, 0, 0], '8778': M23, '8782': [0.06062, 0.54986, 0, 0], '8783': [0.06062, 0.54986, 0, 0], '8785': [0.08198, 0.58198, 0, 0], '8786': [0.08198, 0.58198, 0, 0], '8787': [0.08198, 0.58198, 0, 0], '8790': M3, '8791': [0.22958, 0.72958, 0, 0], '8796': [0.08198, 0.91667, 0, 0], '8806': [0.25583, 0.75583, 0, 0], '8807': [0.25583, 0.75583, 0, 0], '8808': [0.25142, 0.75726, 0, 0], '8809': [0.25142, 0.75726, 0, 0], '8812': [0.25583, 0.75583, 0, 0], '8814': [0.20576, 0.70576, 0, 0], '8815': [0.20576, 0.70576, 0, 0], '8816': [0.30274, 0.79383, 0, 0], '8817': [0.30274, 0.79383, 0, 0], '8818': [0.22958, 0.72958, 0, 0], '8819': [0.22958, 0.72958, 0, 0], '8822': [0.1808, 0.675, 0, 0], '8823': [0.1808, 0.675, 0, 0], '8828': [0.13667, 0.63667, 0, 0], '8829': [0.13667, 0.63667, 0, 0], '8830': [0.22958, 0.72958, 0, 0], '8831': [0.22958, 0.72958, 0, 0], '8832': [0.20576, 0.70576, 0, 0], '8833': [0.20576, 0.70576, 0, 0], '8840': [0.30274, 0.79383, 0, 0], '8841': [0.30274, 0.79383, 0, 0], '8842': [0.13597, 0.63597, 0, 0], '8843': [0.13597, 0.63597, 0, 0], '8847': [0.03517, 0.54986, 0, 0], '8848': [0.03517, 0.54986, 0, 0], '8858': [0.08198, 0.58198, 0, 0], '8859': [0.08198, 0.58198, 0, 0], '8861': [0.08198, 0.58198, 0, 0], '8862': [0, 0.675, 0, 0], '8863': [0, 0.675, 0, 0], '8864': [0, 0.675, 0, 0], '8865': [0, 0.675, 0, 0], '8872': M3, '8873': M3, '8874': M3, '8876': M1, '8877': M1, '8878': M1, '8879': M1, '8882': [0.03517, 0.54986, 0, 0], '8883': [0.03517, 0.54986, 0, 0], '8884': [0.13667, 0.63667, 0, 0], '8885': [0.13667, 0.63667, 0, 0], '8888': [0, 0.54986, 0, 0], '8890': [0.19444, 0.43056, 0, 0], '8891': [0.19444, 0.69224, 0, 0], '8892': [0.19444, 0.69224, 0, 0], '8901': [0, 0.54986, 0, 0], '8903': M23, '8905': M23, '8906': M23, '8907': M3, '8908': M3, '8909': [-0.03598, 0.46402, 0, 0], '8910': [0, 0.54986, 0, 0], '8911': [0, 0.54986, 0, 0], '8912': [0.03517, 0.54986, 0, 0], '8913': [0.03517, 0.54986, 0, 0], '8914': [0, 0.54986, 0, 0], '8915': [0, 0.54986, 0, 0], '8916': M3, '8918': [0.0391, 0.5391, 0, 0], '8919': [0.0391, 0.5391, 0, 0], '8920': [0.03517, 0.54986, 0, 0], '8921': [0.03517, 0.54986, 0, 0], '8922': [0.38569, 0.88569, 0, 0], '8923': [0.38569, 0.88569, 0, 0], '8926': [0.13667, 0.63667, 0, 0], '8927': [0.13667, 0.63667, 0, 0], '8928': [0.30274, 0.79383, 0, 0], '8929': [0.30274, 0.79383, 0, 0], '8934': [0.23222, 0.74111, 0, 0], '8935': [0.23222, 0.74111, 0, 0], '8936': [0.23222, 0.74111, 0, 0], '8937': [0.23222, 0.74111, 0, 0], '8938': [0.20576, 0.70576, 0, 0], '8939': [0.20576, 0.70576, 0, 0], '8940': [0.30274, 0.79383, 0, 0], '8941': [0.30274, 0.79383, 0, 0], '8994': [0.19444, 0.69224, 0, 0], '8995': [0.19444, 0.69224, 0, 0], '9416': [0.15559, 0.69224, 0, 0], '9484': M3, '9488': M3, '9492': [0, 0.37788, 0, 0], '9496': [0, 0.37788, 0, 0], '9585': [0.19444, 0.68889, 0, 0], '9586': [0.19444, 0.74111, 0, 0], '9632': [0, 0.675, 0, 0], '9633': [0, 0.675, 0, 0], '9650': [0, 0.54986, 0, 0], '9651': [0, 0.54986, 0, 0], '9654': [0.03517, 0.54986, 0, 0], '9660': [0, 0.54986, 0, 0], '9661': [0, 0.54986, 0, 0], '9664': [0.03517, 0.54986, 0, 0], '9674': [0.11111, 0.69224, 0, 0], '9733': [0.19444, 0.69224, 0, 0], '10003': M3, '10016': M3, '10731': [0.11111, 0.69224, 0, 0], '10846': [0.19444, 0.75583, 0, 0], '10877': [0.13667, 0.63667, 0, 0], '10878': [0.13667, 0.63667, 0, 0], '10885': [0.25583, 0.75583, 0, 0], '10886': [0.25583, 0.75583, 0, 0], '10887': [0.13597, 0.63597, 0, 0], '10888': [0.13597, 0.63597, 0, 0], '10889': [0.26167, 0.75726, 0, 0], '10890': [0.26167, 0.75726, 0, 0], '10891': [0.48256, 0.98256, 0, 0], '10892': [0.48256, 0.98256, 0, 0], '10901': [0.13667, 0.63667, 0, 0], '10902': [0.13667, 0.63667, 0, 0], '10933': [0.25142, 0.75726, 0, 0], '10934': [0.25142, 0.75726, 0, 0], '10935': [0.26167, 0.75726, 0, 0], '10936': [0.26167, 0.75726, 0, 0], '10937': [0.26167, 0.75726, 0, 0], '10938': [0.26167, 0.75726, 0, 0], '10949': [0.25583, 0.75583, 0, 0], '10950': [0.25583, 0.75583, 0, 0], '10955': [0.28481, 0.79383, 0, 0], '10956': [0.28481, 0.79383, 0, 0], '57350': M23, '57351': M23, '57352': M23, '57353': [0, 0.43056, 0.04028, 0], '57356': [0.25142, 0.75726, 0, 0], '57357': [0.25142, 0.75726, 0, 0], '57358': [0.41951, 0.91951, 0, 0], '57359': [0.30274, 0.79383, 0, 0], '57360': [0.30274, 0.79383, 0, 0], '57361': [0.41951, 0.91951, 0, 0], '57366': [0.25142, 0.75726, 0, 0], '57367': [0.25142, 0.75726, 0, 0], '57368': [0.25142, 0.75726, 0, 0], '57369': [0.25142, 0.75726, 0, 0], '57370': [0.13597, 0.63597, 0, 0], '57371': [0.13597, 0.63597, 0, 0], }, 'Caligraphic-Regular': { '32': M0, '65': [0, 0.68333, 0, 0.19445], '66': [0, 0.68333, 0.03041, 0.13889], '67': [0, 0.68333, 0.05834, 0.13889], '68': [0, 0.68333, 0.02778, 0.08334], '69': [0, 0.68333, 0.08944, 0.11111], '70': [0, 0.68333, 0.09931, 0.11111], '71': [0.09722, 0.68333, 0.0593, 0.11111], '72': [0, 0.68333, 0.00965, 0.11111], '73': [0, 0.68333, 0.07382, 0], '74': [0.09722, 0.68333, 0.18472, 0.16667], '75': [0, 0.68333, 0.01445, 0.05556], '76': [0, 0.68333, 0, 0.13889], '77': [0, 0.68333, 0, 0.13889], '78': [0, 0.68333, 0.14736, 0.08334], '79': [0, 0.68333, 0.02778, 0.11111], '80': [0, 0.68333, 0.08222, 0.08334], '81': [0.09722, 0.68333, 0, 0.11111], '82': [0, 0.68333, 0, 0.08334], '83': [0, 0.68333, 0.075, 0.13889], '84': [0, 0.68333, 0.25417, 0], '85': [0, 0.68333, 0.09931, 0.08334], '86': [0, 0.68333, 0.08222, 0], '87': [0, 0.68333, 0.08222, 0.08334], '88': [0, 0.68333, 0.14643, 0.13889], '89': [0.09722, 0.68333, 0.08222, 0.08334], '90': [0, 0.68333, 0.07944, 0.13889], '160': M0, }, 'Fraktur-Regular': { '32': M0, '33': M21, '34': M21, '38': M21, '39': M21, '40': [0.24982, 0.74947, 0, 0], '41': [0.24982, 0.74947, 0, 0], '42': [0, 0.62119, 0, 0], '43': [0.08319, 0.58283, 0, 0], '44': [0, 0.10803, 0, 0], '45': [0.08319, 0.58283, 0, 0], '46': [0, 0.10803, 0, 0], '47': [0.24982, 0.74947, 0, 0], '48': M22, '49': M22, '50': M22, '51': [0.18906, 0.47534, 0, 0], '52': [0.18906, 0.47534, 0, 0], '53': [0.18906, 0.47534, 0, 0], '54': M21, '55': [0.18906, 0.47534, 0, 0], '56': M21, '57': [0.18906, 0.47534, 0, 0], '58': M22, '59': [0.12604, 0.47534, 0, 0], '61': [-0.13099, 0.36866, 0, 0], '63': M21, '65': M21, '66': M21, '67': M21, '68': M21, '69': M21, '70': [0.12604, 0.69141, 0, 0], '71': M21, '72': [0.06302, 0.69141, 0, 0], '73': M21, '74': [0.12604, 0.69141, 0, 0], '75': M21, '76': M21, '77': M21, '78': M21, '79': M21, '80': [0.18906, 0.69141, 0, 0], '81': [0.03781, 0.69141, 0, 0], '82': M21, '83': M21, '84': M21, '85': M21, '86': M21, '87': M21, '88': M21, '89': [0.18906, 0.69141, 0, 0], '90': [0.12604, 0.69141, 0, 0], '91': [0.24982, 0.74947, 0, 0], '93': [0.24982, 0.74947, 0, 0], '94': M21, '97': M22, '98': M21, '99': M22, '100': [0, 0.62119, 0, 0], '101': M22, '102': [0.18906, 0.69141, 0, 0], '103': [0.18906, 0.47534, 0, 0], '104': [0.18906, 0.69141, 0, 0], '105': M21, '106': M21, '107': M21, '108': M21, '109': M22, '110': M22, '111': M22, '112': [0.18906, 0.52396, 0, 0], '113': [0.18906, 0.47534, 0, 0], '114': M22, '115': M22, '116': [0, 0.62119, 0, 0], '117': M22, '118': [0, 0.52396, 0, 0], '119': [0, 0.52396, 0, 0], '120': [0.18906, 0.47534, 0, 0], '121': [0.18906, 0.47534, 0, 0], '122': [0.18906, 0.47534, 0, 0], '160': M0, '8216': M21, '8217': M21, '58112': [0, 0.62119, 0, 0], '58113': [0, 0.62119, 0, 0], '58114': [0.18906, 0.69141, 0, 0], '58115': [0.18906, 0.69141, 0, 0], '58116': [0.18906, 0.47534, 0, 0], '58117': M21, '58118': [0, 0.62119, 0, 0], '58119': M22, }, 'Main-Bold': { '32': M0, '33': M6, '34': M6, '35': M16, '36': [0.05556, 0.75, 0, 0], '37': [0.05556, 0.75, 0, 0], '38': M6, '39': M6, '40': M15, '41': M15, '42': M14, '43': [0.13333, 0.63333, 0, 0], '44': [0.19444, 0.15556, 0, 0], '45': M7, '46': [0, 0.15556, 0, 0], '47': M15, '48': M5, '49': M5, '50': M5, '51': M5, '52': M5, '53': M5, '54': M5, '55': M5, '56': M5, '57': M5, '58': M7, '59': M8, '60': [0.08556, 0.58556, 0, 0], '61': [-0.10889, 0.39111, 0, 0], '62': [0.08556, 0.58556, 0, 0], '63': M6, '64': M6, '65': M4, '66': M4, '67': M4, '68': M4, '69': M4, '70': M4, '71': M4, '72': M4, '73': M4, '74': M4, '75': M4, '76': M4, '77': M4, '78': M4, '79': M4, '80': M4, '81': [0.19444, 0.68611, 0, 0], '82': M4, '83': M4, '84': M4, '85': M4, '86': [0, 0.68611, 0.01597, 0], '87': [0, 0.68611, 0.01597, 0], '88': M4, '89': [0, 0.68611, 0.02875, 0], '90': M4, '91': M15, '92': M15, '93': M15, '94': M6, '95': [0.31, 0.13444, 0.03194, 0], '97': M7, '98': M6, '99': M7, '100': M6, '101': M7, '102': [0, 0.69444, 0.10903, 0], '103': [0.19444, 0.44444, 0.01597, 0], '104': M6, '105': M6, '106': M16, '107': M6, '108': M6, '109': M7, '110': M7, '111': M7, '112': M8, '113': M8, '114': M7, '115': M7, '116': [0, 0.63492, 0, 0], '117': M7, '118': [0, 0.44444, 0.01597, 0], '119': [0, 0.44444, 0.01597, 0], '120': M7, '121': [0.19444, 0.44444, 0.01597, 0], '122': M7, '123': M15, '124': M15, '125': M15, '126': [0.35, 0.34444, 0, 0], '160': M0, '163': M6, '168': M6, '172': M7, '176': M6, '177': [0.13333, 0.63333, 0, 0], '184': [0.17014, 0, 0, 0], '198': M4, '215': [0.13333, 0.63333, 0, 0], '216': [0.04861, 0.73472, 0, 0], '223': M6, '230': M7, '247': [0.13333, 0.63333, 0, 0], '248': [0.09722, 0.54167, 0, 0], '305': M7, '338': M4, '339': M7, '567': M8, '710': M6, '711': [0, 0.63194, 0, 0], '713': [0, 0.59611, 0, 0], '714': M6, '715': M6, '728': M6, '729': M6, '730': M6, '732': M6, '733': M6, '915': M4, '916': M4, '920': M4, '923': M4, '926': M4, '928': M4, '931': M4, '933': M4, '934': M4, '936': M4, '937': M4, '8211': [0, 0.44444, 0.03194, 0], '8212': [0, 0.44444, 0.03194, 0], '8216': M6, '8217': M6, '8220': M6, '8221': M6, '8224': M16, '8225': M16, '8242': M20, '8407': [0, 0.72444, 0.15486, 0], '8463': M6, '8465': M6, '8467': M6, '8472': M8, '8476': M6, '8501': M6, '8592': [-0.10889, 0.39111, 0, 0], '8593': M16, '8594': [-0.10889, 0.39111, 0, 0], '8595': M16, '8596': [-0.10889, 0.39111, 0, 0], '8597': M15, '8598': M16, '8599': M16, '8600': M16, '8601': M16, '8636': [-0.10889, 0.39111, 0, 0], '8637': [-0.10889, 0.39111, 0, 0], '8640': [-0.10889, 0.39111, 0, 0], '8641': [-0.10889, 0.39111, 0, 0], '8656': [-0.10889, 0.39111, 0, 0], '8657': M16, '8658': [-0.10889, 0.39111, 0, 0], '8659': M16, '8660': [-0.10889, 0.39111, 0, 0], '8661': M15, '8704': M6, '8706': [0, 0.69444, 0.06389, 0], '8707': M6, '8709': [0.05556, 0.75, 0, 0], '8711': M4, '8712': [0.08556, 0.58556, 0, 0], '8715': [0.08556, 0.58556, 0, 0], '8722': [0.13333, 0.63333, 0, 0], '8723': [0.13333, 0.63333, 0, 0], '8725': M15, '8726': M15, '8727': [-0.02778, 0.47222, 0, 0], '8728': [-0.02639, 0.47361, 0, 0], '8729': [-0.02639, 0.47361, 0, 0], '8730': [0.18, 0.82, 0, 0], '8733': M7, '8734': M7, '8736': M3, '8739': M15, '8741': M15, '8743': M20, '8744': M20, '8745': M20, '8746': M20, '8747': [0.19444, 0.69444, 0.12778, 0], '8764': [-0.10889, 0.39111, 0, 0], '8768': M16, '8771': [0.00222, 0.50222, 0, 0], '8776': [0.02444, 0.52444, 0, 0], '8781': [0.00222, 0.50222, 0, 0], '8801': [0.00222, 0.50222, 0, 0], '8804': [0.19667, 0.69667, 0, 0], '8805': [0.19667, 0.69667, 0, 0], '8810': [0.08556, 0.58556, 0, 0], '8811': [0.08556, 0.58556, 0, 0], '8826': [0.08556, 0.58556, 0, 0], '8827': [0.08556, 0.58556, 0, 0], '8834': [0.08556, 0.58556, 0, 0], '8835': [0.08556, 0.58556, 0, 0], '8838': [0.19667, 0.69667, 0, 0], '8839': [0.19667, 0.69667, 0, 0], '8846': M20, '8849': [0.19667, 0.69667, 0, 0], '8850': [0.19667, 0.69667, 0, 0], '8851': M20, '8852': M20, '8853': [0.13333, 0.63333, 0, 0], '8854': [0.13333, 0.63333, 0, 0], '8855': [0.13333, 0.63333, 0, 0], '8856': [0.13333, 0.63333, 0, 0], '8857': [0.13333, 0.63333, 0, 0], '8866': M6, '8867': M6, '8868': M6, '8869': M6, '8900': [-0.02639, 0.47361, 0, 0], '8901': [-0.02639, 0.47361, 0, 0], '8902': [-0.02778, 0.47222, 0, 0], '8968': M15, '8969': M15, '8970': M15, '8971': M15, '8994': [-0.13889, 0.36111, 0, 0], '8995': [-0.13889, 0.36111, 0, 0], '9651': M16, '9657': [-0.02778, 0.47222, 0, 0], '9661': M16, '9667': [-0.02778, 0.47222, 0, 0], '9711': M16, '9824': [0.12963, 0.69444, 0, 0], '9825': [0.12963, 0.69444, 0, 0], '9826': [0.12963, 0.69444, 0, 0], '9827': [0.12963, 0.69444, 0, 0], '9837': M14, '9838': M16, '9839': M16, '10216': M15, '10217': M15, '10815': M4, '10927': [0.19667, 0.69667, 0, 0], '10928': [0.19667, 0.69667, 0, 0], '57376': M16, }, 'Main-BoldItalic': { '32': M0, '33': [0, 0.69444, 0.11417, 0], '34': [0, 0.69444, 0.07939, 0], '35': [0.19444, 0.69444, 0.06833, 0], '37': [0.05556, 0.75, 0.12861, 0], '38': [0, 0.69444, 0.08528, 0], '39': [0, 0.69444, 0.12945, 0], '40': [0.25, 0.75, 0.15806, 0], '41': [0.25, 0.75, 0.03306, 0], '42': [0, 0.75, 0.14333, 0], '43': [0.10333, 0.60333, 0.03306, 0], '44': [0.19444, 0.14722, 0, 0], '45': [0, 0.44444, 0.02611, 0], '46': [0, 0.14722, 0, 0], '47': [0.25, 0.75, 0.15806, 0], '48': [0, 0.64444, 0.13167, 0], '49': [0, 0.64444, 0.13167, 0], '50': [0, 0.64444, 0.13167, 0], '51': [0, 0.64444, 0.13167, 0], '52': [0.19444, 0.64444, 0.13167, 0], '53': [0, 0.64444, 0.13167, 0], '54': [0, 0.64444, 0.13167, 0], '55': [0.19444, 0.64444, 0.13167, 0], '56': [0, 0.64444, 0.13167, 0], '57': [0, 0.64444, 0.13167, 0], '58': [0, 0.44444, 0.06695, 0], '59': [0.19444, 0.44444, 0.06695, 0], '61': [-0.10889, 0.39111, 0.06833, 0], '63': [0, 0.69444, 0.11472, 0], '64': [0, 0.69444, 0.09208, 0], '65': M4, '66': [0, 0.68611, 0.0992, 0], '67': [0, 0.68611, 0.14208, 0], '68': [0, 0.68611, 0.09062, 0], '69': [0, 0.68611, 0.11431, 0], '70': [0, 0.68611, 0.12903, 0], '71': [0, 0.68611, 0.07347, 0], '72': [0, 0.68611, 0.17208, 0], '73': [0, 0.68611, 0.15681, 0], '74': [0, 0.68611, 0.145, 0], '75': [0, 0.68611, 0.14208, 0], '76': M4, '77': [0, 0.68611, 0.17208, 0], '78': [0, 0.68611, 0.17208, 0], '79': [0, 0.68611, 0.09062, 0], '80': [0, 0.68611, 0.0992, 0], '81': [0.19444, 0.68611, 0.09062, 0], '82': [0, 0.68611, 0.02559, 0], '83': [0, 0.68611, 0.11264, 0], '84': [0, 0.68611, 0.12903, 0], '85': [0, 0.68611, 0.17208, 0], '86': [0, 0.68611, 0.18625, 0], '87': [0, 0.68611, 0.18625, 0], '88': [0, 0.68611, 0.15681, 0], '89': [0, 0.68611, 0.19803, 0], '90': [0, 0.68611, 0.14208, 0], '91': [0.25, 0.75, 0.1875, 0], '93': [0.25, 0.75, 0.09972, 0], '94': [0, 0.69444, 0.06709, 0], '95': [0.31, 0.13444, 0.09811, 0], '97': [0, 0.44444, 0.09426, 0], '98': [0, 0.69444, 0.07861, 0], '99': [0, 0.44444, 0.05222, 0], '100': [0, 0.69444, 0.10861, 0], '101': [0, 0.44444, 0.085, 0], '102': [0.19444, 0.69444, 0.21778, 0], '103': [0.19444, 0.44444, 0.105, 0], '104': [0, 0.69444, 0.09426, 0], '105': [0, 0.69326, 0.11387, 0], '106': [0.19444, 0.69326, 0.1672, 0], '107': [0, 0.69444, 0.11111, 0], '108': [0, 0.69444, 0.10861, 0], '109': [0, 0.44444, 0.09426, 0], '110': [0, 0.44444, 0.09426, 0], '111': [0, 0.44444, 0.07861, 0], '112': [0.19444, 0.44444, 0.07861, 0], '113': [0.19444, 0.44444, 0.105, 0], '114': [0, 0.44444, 0.11111, 0], '115': [0, 0.44444, 0.08167, 0], '116': [0, 0.63492, 0.09639, 0], '117': [0, 0.44444, 0.09426, 0], '118': [0, 0.44444, 0.11111, 0], '119': [0, 0.44444, 0.11111, 0], '120': [0, 0.44444, 0.12583, 0], '121': [0.19444, 0.44444, 0.105, 0], '122': [0, 0.44444, 0.13889, 0], '126': [0.35, 0.34444, 0.11472, 0], '160': M0, '168': [0, 0.69444, 0.11473, 0], '176': M6, '184': [0.17014, 0, 0, 0], '198': [0, 0.68611, 0.11431, 0], '216': [0.04861, 0.73472, 0.09062, 0], '223': [0.19444, 0.69444, 0.09736, 0], '230': [0, 0.44444, 0.085, 0], '248': [0.09722, 0.54167, 0.09458, 0], '305': [0, 0.44444, 0.09426, 0], '338': [0, 0.68611, 0.11431, 0], '339': [0, 0.44444, 0.085, 0], '567': [0.19444, 0.44444, 0.04611, 0], '710': [0, 0.69444, 0.06709, 0], '711': [0, 0.63194, 0.08271, 0], '713': [0, 0.59444, 0.10444, 0], '714': [0, 0.69444, 0.08528, 0], '715': M6, '728': [0, 0.69444, 0.10333, 0], '729': [0, 0.69444, 0.12945, 0], '730': M6, '732': [0, 0.69444, 0.11472, 0], '733': [0, 0.69444, 0.11472, 0], '915': [0, 0.68611, 0.12903, 0], '916': M4, '920': [0, 0.68611, 0.09062, 0], '923': M4, '926': [0, 0.68611, 0.15092, 0], '928': [0, 0.68611, 0.17208, 0], '931': [0, 0.68611, 0.11431, 0], '933': [0, 0.68611, 0.10778, 0], '934': [0, 0.68611, 0.05632, 0], '936': [0, 0.68611, 0.10778, 0], '937': [0, 0.68611, 0.0992, 0], '8211': [0, 0.44444, 0.09811, 0], '8212': [0, 0.44444, 0.09811, 0], '8216': [0, 0.69444, 0.12945, 0], '8217': [0, 0.69444, 0.12945, 0], '8220': [0, 0.69444, 0.16772, 0], '8221': [0, 0.69444, 0.07939, 0], }, 'Main-Italic': { '32': M0, '33': [0, 0.69444, 0.12417, 0], '34': [0, 0.69444, 0.06961, 0], '35': [0.19444, 0.69444, 0.06616, 0], '37': [0.05556, 0.75, 0.13639, 0], '38': [0, 0.69444, 0.09694, 0], '39': [0, 0.69444, 0.12417, 0], '40': [0.25, 0.75, 0.16194, 0], '41': [0.25, 0.75, 0.03694, 0], '42': [0, 0.75, 0.14917, 0], '43': [0.05667, 0.56167, 0.03694, 0], '44': [0.19444, 0.10556, 0, 0], '45': [0, 0.43056, 0.02826, 0], '46': [0, 0.10556, 0, 0], '47': [0.25, 0.75, 0.16194, 0], '48': [0, 0.64444, 0.13556, 0], '49': [0, 0.64444, 0.13556, 0], '50': [0, 0.64444, 0.13556, 0], '51': [0, 0.64444, 0.13556, 0], '52': [0.19444, 0.64444, 0.13556, 0], '53': [0, 0.64444, 0.13556, 0], '54': [0, 0.64444, 0.13556, 0], '55': [0.19444, 0.64444, 0.13556, 0], '56': [0, 0.64444, 0.13556, 0], '57': [0, 0.64444, 0.13556, 0], '58': [0, 0.43056, 0.0582, 0], '59': [0.19444, 0.43056, 0.0582, 0], '61': [-0.13313, 0.36687, 0.06616, 0], '63': [0, 0.69444, 0.1225, 0], '64': [0, 0.69444, 0.09597, 0], '65': M9, '66': [0, 0.68333, 0.10257, 0], '67': [0, 0.68333, 0.14528, 0], '68': [0, 0.68333, 0.09403, 0], '69': [0, 0.68333, 0.12028, 0], '70': [0, 0.68333, 0.13305, 0], '71': [0, 0.68333, 0.08722, 0], '72': [0, 0.68333, 0.16389, 0], '73': [0, 0.68333, 0.15806, 0], '74': [0, 0.68333, 0.14028, 0], '75': [0, 0.68333, 0.14528, 0], '76': M9, '77': [0, 0.68333, 0.16389, 0], '78': [0, 0.68333, 0.16389, 0], '79': [0, 0.68333, 0.09403, 0], '80': [0, 0.68333, 0.10257, 0], '81': [0.19444, 0.68333, 0.09403, 0], '82': [0, 0.68333, 0.03868, 0], '83': [0, 0.68333, 0.11972, 0], '84': [0, 0.68333, 0.13305, 0], '85': [0, 0.68333, 0.16389, 0], '86': [0, 0.68333, 0.18361, 0], '87': [0, 0.68333, 0.18361, 0], '88': [0, 0.68333, 0.15806, 0], '89': [0, 0.68333, 0.19383, 0], '90': [0, 0.68333, 0.14528, 0], '91': [0.25, 0.75, 0.1875, 0], '93': [0.25, 0.75, 0.10528, 0], '94': [0, 0.69444, 0.06646, 0], '95': [0.31, 0.12056, 0.09208, 0], '97': [0, 0.43056, 0.07671, 0], '98': [0, 0.69444, 0.06312, 0], '99': [0, 0.43056, 0.05653, 0], '100': [0, 0.69444, 0.10333, 0], '101': [0, 0.43056, 0.07514, 0], '102': [0.19444, 0.69444, 0.21194, 0], '103': [0.19444, 0.43056, 0.08847, 0], '104': [0, 0.69444, 0.07671, 0], '105': [0, 0.65536, 0.1019, 0], '106': [0.19444, 0.65536, 0.14467, 0], '107': [0, 0.69444, 0.10764, 0], '108': [0, 0.69444, 0.10333, 0], '109': [0, 0.43056, 0.07671, 0], '110': [0, 0.43056, 0.07671, 0], '111': [0, 0.43056, 0.06312, 0], '112': [0.19444, 0.43056, 0.06312, 0], '113': [0.19444, 0.43056, 0.08847, 0], '114': [0, 0.43056, 0.10764, 0], '115': [0, 0.43056, 0.08208, 0], '116': [0, 0.61508, 0.09486, 0], '117': [0, 0.43056, 0.07671, 0], '118': [0, 0.43056, 0.10764, 0], '119': [0, 0.43056, 0.10764, 0], '120': [0, 0.43056, 0.12042, 0], '121': [0.19444, 0.43056, 0.08847, 0], '122': [0, 0.43056, 0.12292, 0], '126': [0.35, 0.31786, 0.11585, 0], '160': M0, '168': [0, 0.66786, 0.10474, 0], '176': M6, '184': [0.17014, 0, 0, 0], '198': [0, 0.68333, 0.12028, 0], '216': [0.04861, 0.73194, 0.09403, 0], '223': [0.19444, 0.69444, 0.10514, 0], '230': [0, 0.43056, 0.07514, 0], '248': [0.09722, 0.52778, 0.09194, 0], '338': [0, 0.68333, 0.12028, 0], '339': [0, 0.43056, 0.07514, 0], '710': [0, 0.69444, 0.06646, 0], '711': [0, 0.62847, 0.08295, 0], '713': [0, 0.56167, 0.10333, 0], '714': [0, 0.69444, 0.09694, 0], '715': M6, '728': [0, 0.69444, 0.10806, 0], '729': [0, 0.66786, 0.11752, 0], '730': M6, '732': [0, 0.66786, 0.11585, 0], '733': [0, 0.69444, 0.1225, 0], '915': [0, 0.68333, 0.13305, 0], '916': M9, '920': [0, 0.68333, 0.09403, 0], '923': M9, '926': [0, 0.68333, 0.15294, 0], '928': [0, 0.68333, 0.16389, 0], '931': [0, 0.68333, 0.12028, 0], '933': [0, 0.68333, 0.11111, 0], '934': [0, 0.68333, 0.05986, 0], '936': [0, 0.68333, 0.11111, 0], '937': [0, 0.68333, 0.10257, 0], '8211': [0, 0.43056, 0.09208, 0], '8212': [0, 0.43056, 0.09208, 0], '8216': [0, 0.69444, 0.12417, 0], '8217': [0, 0.69444, 0.12417, 0], '8220': [0, 0.69444, 0.1685, 0], '8221': [0, 0.69444, 0.06961, 0], '8463': M1, }, 'Main-Regular': { '32': M0, '33': M6, '34': M6, '35': M16, '36': [0.05556, 0.75, 0, 0], '37': [0.05556, 0.75, 0, 0], '38': M6, '39': M6, '40': M15, '41': M15, '42': M14, '43': [0.08333, 0.58333, 0, 0], '44': [0.19444, 0.10556, 0, 0], '45': [0, 0.43056, 0, 0], '46': [0, 0.10556, 0, 0], '47': M15, '48': M5, '49': M5, '50': M5, '51': M5, '52': M5, '53': M5, '54': M5, '55': M5, '56': M5, '57': M5, '58': [0, 0.43056, 0, 0], '59': [0.19444, 0.43056, 0, 0], '60': [0.0391, 0.5391, 0, 0], '61': [-0.13313, 0.36687, 0, 0], '62': [0.0391, 0.5391, 0, 0], '63': M6, '64': M6, '65': M9, '66': M9, '67': M9, '68': M9, '69': M9, '70': M9, '71': M9, '72': M9, '73': M9, '74': M9, '75': M9, '76': M9, '77': M9, '78': M9, '79': M9, '80': M9, '81': [0.19444, 0.68333, 0, 0], '82': M9, '83': M9, '84': M9, '85': M9, '86': [0, 0.68333, 0.01389, 0], '87': [0, 0.68333, 0.01389, 0], '88': M9, '89': [0, 0.68333, 0.025, 0], '90': M9, '91': M15, '92': M15, '93': M15, '94': M6, '95': [0.31, 0.12056, 0.02778, 0], '97': [0, 0.43056, 0, 0], '98': M6, '99': [0, 0.43056, 0, 0], '100': M6, '101': [0, 0.43056, 0, 0], '102': [0, 0.69444, 0.07778, 0], '103': [0.19444, 0.43056, 0.01389, 0], '104': M6, '105': [0, 0.66786, 0, 0], '106': [0.19444, 0.66786, 0, 0], '107': M6, '108': M6, '109': [0, 0.43056, 0, 0], '110': [0, 0.43056, 0, 0], '111': [0, 0.43056, 0, 0], '112': [0.19444, 0.43056, 0, 0], '113': [0.19444, 0.43056, 0, 0], '114': [0, 0.43056, 0, 0], '115': [0, 0.43056, 0, 0], '116': [0, 0.61508, 0, 0], '117': [0, 0.43056, 0, 0], '118': [0, 0.43056, 0.01389, 0], '119': [0, 0.43056, 0.01389, 0], '120': [0, 0.43056, 0, 0], '121': [0.19444, 0.43056, 0.01389, 0], '122': [0, 0.43056, 0, 0], '123': M15, '124': M15, '125': M15, '126': [0.35, 0.31786, 0, 0], '160': M0, '163': M6, '167': M16, '168': [0, 0.66786, 0, 0], '172': [0, 0.43056, 0, 0], '176': M6, '177': [0.08333, 0.58333, 0, 0], '182': M16, '184': [0.17014, 0, 0, 0], '198': M9, '215': [0.08333, 0.58333, 0, 0], '216': [0.04861, 0.73194, 0, 0], '223': M6, '230': [0, 0.43056, 0, 0], '247': [0.08333, 0.58333, 0, 0], '248': [0.09722, 0.52778, 0, 0], '305': [0, 0.43056, 0, 0], '338': M9, '339': [0, 0.43056, 0, 0], '567': [0.19444, 0.43056, 0, 0], '710': M6, '711': [0, 0.62847, 0, 0], '713': [0, 0.56778, 0, 0], '714': M6, '715': M6, '728': M6, '729': [0, 0.66786, 0, 0], '730': M6, '732': [0, 0.66786, 0, 0], '733': M6, '915': M9, '916': M9, '920': M9, '923': M9, '926': M9, '928': M9, '931': M9, '933': M9, '934': M9, '936': M9, '937': M9, '8211': [0, 0.43056, 0.02778, 0], '8212': [0, 0.43056, 0.02778, 0], '8216': M6, '8217': M6, '8220': M6, '8221': M6, '8224': M16, '8225': M16, '8230': [0, 0.12, 0, 0], '8242': M20, '8407': [0, 0.71444, 0.15382, 0], '8463': M1, '8465': M6, '8467': [0, 0.69444, 0, 0.11111], '8472': [0.19444, 0.43056, 0, 0.11111], '8476': M6, '8501': M6, '8592': [-0.13313, 0.36687, 0, 0], '8593': M16, '8594': [-0.13313, 0.36687, 0, 0], '8595': M16, '8596': [-0.13313, 0.36687, 0, 0], '8597': M15, '8598': M16, '8599': M16, '8600': M16, '8601': M16, '8614': [0.011, 0.511, 0, 0], '8617': [0.011, 0.511, 0, 0], '8618': [0.011, 0.511, 0, 0], '8636': [-0.13313, 0.36687, 0, 0], '8637': [-0.13313, 0.36687, 0, 0], '8640': [-0.13313, 0.36687, 0, 0], '8641': [-0.13313, 0.36687, 0, 0], '8652': [0.011, 0.671, 0, 0], '8656': [-0.13313, 0.36687, 0, 0], '8657': M16, '8658': [-0.13313, 0.36687, 0, 0], '8659': M16, '8660': [-0.13313, 0.36687, 0, 0], '8661': M15, '8704': M6, '8706': [0, 0.69444, 0.05556, 0.08334], '8707': M6, '8709': [0.05556, 0.75, 0, 0], '8711': M9, '8712': [0.0391, 0.5391, 0, 0], '8715': [0.0391, 0.5391, 0, 0], '8722': [0.08333, 0.58333, 0, 0], '8723': [0.08333, 0.58333, 0, 0], '8725': M15, '8726': M15, '8727': [-0.03472, 0.46528, 0, 0], '8728': [-0.05555, 0.44445, 0, 0], '8729': [-0.05555, 0.44445, 0, 0], '8730': [0.2, 0.8, 0, 0], '8733': [0, 0.43056, 0, 0], '8734': [0, 0.43056, 0, 0], '8736': M3, '8739': M15, '8741': M15, '8743': M20, '8744': M20, '8745': M20, '8746': M20, '8747': [0.19444, 0.69444, 0.11111, 0], '8764': [-0.13313, 0.36687, 0, 0], '8768': M16, '8771': [-0.03625, 0.46375, 0, 0], '8773': [-0.022, 0.589, 0, 0], '8776': [-0.01688, 0.48312, 0, 0], '8781': [-0.03625, 0.46375, 0, 0], '8784': [-0.133, 0.67, 0, 0], '8801': [-0.03625, 0.46375, 0, 0], '8804': [0.13597, 0.63597, 0, 0], '8805': [0.13597, 0.63597, 0, 0], '8810': [0.0391, 0.5391, 0, 0], '8811': [0.0391, 0.5391, 0, 0], '8826': [0.0391, 0.5391, 0, 0], '8827': [0.0391, 0.5391, 0, 0], '8834': [0.0391, 0.5391, 0, 0], '8835': [0.0391, 0.5391, 0, 0], '8838': [0.13597, 0.63597, 0, 0], '8839': [0.13597, 0.63597, 0, 0], '8846': M20, '8849': [0.13597, 0.63597, 0, 0], '8850': [0.13597, 0.63597, 0, 0], '8851': M20, '8852': M20, '8853': [0.08333, 0.58333, 0, 0], '8854': [0.08333, 0.58333, 0, 0], '8855': [0.08333, 0.58333, 0, 0], '8856': [0.08333, 0.58333, 0, 0], '8857': [0.08333, 0.58333, 0, 0], '8866': M6, '8867': M6, '8868': M6, '8869': M6, '8872': [0.249, 0.75, 0, 0], '8900': [-0.05555, 0.44445, 0, 0], '8901': [-0.05555, 0.44445, 0, 0], '8902': [-0.03472, 0.46528, 0, 0], '8904': [0.005, 0.505, 0, 0], '8942': [0.03, 0.9, 0, 0], '8943': [-0.19, 0.31, 0, 0], '8945': [-0.1, 0.82, 0, 0], '8968': M15, '8969': M15, '8970': M15, '8971': M15, '8994': [-0.14236, 0.35764, 0, 0], '8995': [-0.14236, 0.35764, 0, 0], '9136': [0.244, 0.744, 0, 0], '9137': [0.244, 0.744, 0, 0], '9651': M16, '9657': [-0.03472, 0.46528, 0, 0], '9661': M16, '9667': [-0.03472, 0.46528, 0, 0], '9711': M16, '9824': [0.12963, 0.69444, 0, 0], '9825': [0.12963, 0.69444, 0, 0], '9826': [0.12963, 0.69444, 0, 0], '9827': [0.12963, 0.69444, 0, 0], '9837': M14, '9838': M16, '9839': M16, '10216': M15, '10217': M15, '10222': [0.244, 0.744, 0, 0], '10223': [0.244, 0.744, 0, 0], '10229': [0.011, 0.511, 0, 0], '10230': [0.011, 0.511, 0, 0], '10231': [0.011, 0.511, 0, 0], '10232': [0.024, 0.525, 0, 0], '10233': [0.024, 0.525, 0, 0], '10234': [0.024, 0.525, 0, 0], '10236': [0.011, 0.511, 0, 0], '10815': M9, '10927': [0.13597, 0.63597, 0, 0], '10928': [0.13597, 0.63597, 0, 0], '57376': M16, }, 'Math-BoldItalic': { '32': M0, '48': M7, '49': M7, '50': M7, '51': M8, '52': M8, '53': M8, '54': M5, '55': M8, '56': M5, '57': M8, '65': M4, '66': [0, 0.68611, 0.04835, 0], '67': [0, 0.68611, 0.06979, 0], '68': [0, 0.68611, 0.03194, 0], '69': [0, 0.68611, 0.05451, 0], '70': [0, 0.68611, 0.15972, 0], '71': M4, '72': [0, 0.68611, 0.08229, 0], '73': [0, 0.68611, 0.07778, 0], '74': [0, 0.68611, 0.10069, 0], '75': [0, 0.68611, 0.06979, 0], '76': M4, '77': [0, 0.68611, 0.11424, 0], '78': [0, 0.68611, 0.11424, 0], '79': [0, 0.68611, 0.03194, 0], '80': [0, 0.68611, 0.15972, 0], '81': [0.19444, 0.68611, 0, 0], '82': [0, 0.68611, 0.00421, 0], '83': [0, 0.68611, 0.05382, 0], '84': [0, 0.68611, 0.15972, 0], '85': [0, 0.68611, 0.11424, 0], '86': [0, 0.68611, 0.25555, 0], '87': [0, 0.68611, 0.15972, 0], '88': [0, 0.68611, 0.07778, 0], '89': [0, 0.68611, 0.25555, 0], '90': [0, 0.68611, 0.06979, 0], '97': M7, '98': M6, '99': M7, '100': M6, '101': M7, '102': [0.19444, 0.69444, 0.11042, 0], '103': [0.19444, 0.44444, 0.03704, 0], '104': M6, '105': [0, 0.69326, 0, 0], '106': [0.19444, 0.69326, 0.0622, 0], '107': [0, 0.69444, 0.01852, 0], '108': [0, 0.69444, 0.0088, 0], '109': M7, '110': M7, '111': M7, '112': M8, '113': [0.19444, 0.44444, 0.03704, 0], '114': [0, 0.44444, 0.03194, 0], '115': M7, '116': [0, 0.63492, 0, 0], '117': M7, '118': [0, 0.44444, 0.03704, 0], '119': [0, 0.44444, 0.02778, 0], '120': M7, '121': [0.19444, 0.44444, 0.03704, 0], '122': [0, 0.44444, 0.04213, 0], '160': M0, '915': [0, 0.68611, 0.15972, 0], '916': M4, '920': [0, 0.68611, 0.03194, 0], '923': M4, '926': [0, 0.68611, 0.07458, 0], '928': [0, 0.68611, 0.08229, 0], '931': [0, 0.68611, 0.05451, 0], '933': [0, 0.68611, 0.15972, 0], '934': M4, '936': [0, 0.68611, 0.11653, 0], '937': [0, 0.68611, 0.04835, 0], '945': M7, '946': [0.19444, 0.69444, 0.03403, 0], '947': [0.19444, 0.44444, 0.06389, 0], '948': [0, 0.69444, 0.03819, 0], '949': M7, '950': [0.19444, 0.69444, 0.06215, 0], '951': [0.19444, 0.44444, 0.03704, 0], '952': [0, 0.69444, 0.03194, 0], '953': M7, '954': M7, '955': M6, '956': M8, '957': [0, 0.44444, 0.06898, 0], '958': [0.19444, 0.69444, 0.03021, 0], '959': M7, '960': [0, 0.44444, 0.03704, 0], '961': M8, '962': [0.09722, 0.44444, 0.07917, 0], '963': [0, 0.44444, 0.03704, 0], '964': [0, 0.44444, 0.13472, 0], '965': [0, 0.44444, 0.03704, 0], '966': M8, '967': M8, '968': [0.19444, 0.69444, 0.03704, 0], '969': [0, 0.44444, 0.03704, 0], '977': M6, '981': M16, '982': [0, 0.44444, 0.03194, 0], '1009': M8, '1013': M7, '57649': M7, '57911': M8, }, 'Math-Italic': { '32': M0, '48': [0, 0.43056, 0, 0], '49': [0, 0.43056, 0, 0], '50': [0, 0.43056, 0, 0], '51': [0.19444, 0.43056, 0, 0], '52': [0.19444, 0.43056, 0, 0], '53': [0.19444, 0.43056, 0, 0], '54': M5, '55': [0.19444, 0.43056, 0, 0], '56': M5, '57': [0.19444, 0.43056, 0, 0], '65': [0, 0.68333, 0, 0.13889], '66': [0, 0.68333, 0.05017, 0.08334], '67': [0, 0.68333, 0.07153, 0.08334], '68': [0, 0.68333, 0.02778, 0.05556], '69': [0, 0.68333, 0.05764, 0.08334], '70': [0, 0.68333, 0.13889, 0.08334], '71': [0, 0.68333, 0, 0.08334], '72': [0, 0.68333, 0.08125, 0.05556], '73': [0, 0.68333, 0.07847, 0.11111], '74': [0, 0.68333, 0.09618, 0.16667], '75': [0, 0.68333, 0.07153, 0.05556], '76': [0, 0.68333, 0, 0.02778], '77': [0, 0.68333, 0.10903, 0.08334], '78': [0, 0.68333, 0.10903, 0.08334], '79': [0, 0.68333, 0.02778, 0.08334], '80': [0, 0.68333, 0.13889, 0.08334], '81': [0.19444, 0.68333, 0, 0.08334], '82': [0, 0.68333, 0.00773, 0.08334], '83': [0, 0.68333, 0.05764, 0.08334], '84': [0, 0.68333, 0.13889, 0.08334], '85': [0, 0.68333, 0.10903, 0.02778], '86': [0, 0.68333, 0.22222, 0], '87': [0, 0.68333, 0.13889, 0], '88': [0, 0.68333, 0.07847, 0.08334], '89': [0, 0.68333, 0.22222, 0], '90': [0, 0.68333, 0.07153, 0.08334], '97': [0, 0.43056, 0, 0], '98': M6, '99': [0, 0.43056, 0, 0.05556], '100': [0, 0.69444, 0, 0.16667], '101': [0, 0.43056, 0, 0.05556], '102': [0.19444, 0.69444, 0.10764, 0.16667], '103': [0.19444, 0.43056, 0.03588, 0.02778], '104': M6, '105': [0, 0.65952, 0, 0], '106': [0.19444, 0.65952, 0.05724, 0], '107': [0, 0.69444, 0.03148, 0], '108': [0, 0.69444, 0.01968, 0.08334], '109': [0, 0.43056, 0, 0], '110': [0, 0.43056, 0, 0], '111': [0, 0.43056, 0, 0.05556], '112': [0.19444, 0.43056, 0, 0.08334], '113': [0.19444, 0.43056, 0.03588, 0.08334], '114': [0, 0.43056, 0.02778, 0.05556], '115': [0, 0.43056, 0, 0.05556], '116': [0, 0.61508, 0, 0.08334], '117': [0, 0.43056, 0, 0.02778], '118': [0, 0.43056, 0.03588, 0.02778], '119': [0, 0.43056, 0.02691, 0.08334], '120': [0, 0.43056, 0, 0.02778], '121': [0.19444, 0.43056, 0.03588, 0.05556], '122': [0, 0.43056, 0.04398, 0.05556], '160': M0, '915': [0, 0.68333, 0.13889, 0.08334], '916': [0, 0.68333, 0, 0.16667], '920': [0, 0.68333, 0.02778, 0.08334], '923': [0, 0.68333, 0, 0.16667], '926': [0, 0.68333, 0.07569, 0.08334], '928': [0, 0.68333, 0.08125, 0.05556], '931': [0, 0.68333, 0.05764, 0.08334], '933': [0, 0.68333, 0.13889, 0.05556], '934': [0, 0.68333, 0, 0.08334], '936': [0, 0.68333, 0.11, 0.05556], '937': [0, 0.68333, 0.05017, 0.08334], '945': [0, 0.43056, 0.0037, 0.02778], '946': [0.19444, 0.69444, 0.05278, 0.08334], '947': [0.19444, 0.43056, 0.05556, 0], '948': [0, 0.69444, 0.03785, 0.05556], '949': [0, 0.43056, 0, 0.08334], '950': [0.19444, 0.69444, 0.07378, 0.08334], '951': [0.19444, 0.43056, 0.03588, 0.05556], '952': [0, 0.69444, 0.02778, 0.08334], '953': [0, 0.43056, 0, 0.05556], '954': [0, 0.43056, 0, 0], '955': M6, '956': [0.19444, 0.43056, 0, 0.02778], '957': [0, 0.43056, 0.06366, 0.02778], '958': [0.19444, 0.69444, 0.04601, 0.11111], '959': [0, 0.43056, 0, 0.05556], '960': [0, 0.43056, 0.03588, 0], '961': [0.19444, 0.43056, 0, 0.08334], '962': [0.09722, 0.43056, 0.07986, 0.08334], '963': [0, 0.43056, 0.03588, 0], '964': [0, 0.43056, 0.1132, 0.02778], '965': [0, 0.43056, 0.03588, 0.02778], '966': [0.19444, 0.43056, 0, 0.08334], '967': [0.19444, 0.43056, 0, 0.05556], '968': [0.19444, 0.69444, 0.03588, 0.11111], '969': [0, 0.43056, 0.03588, 0], '977': [0, 0.69444, 0, 0.08334], '981': [0.19444, 0.69444, 0, 0.08334], '982': [0, 0.43056, 0.02778, 0], '1009': [0.19444, 0.43056, 0, 0.08334], '1013': [0, 0.43056, 0, 0.05556], '57649': [0, 0.43056, 0, 0.02778], '57911': [0.19444, 0.43056, 0, 0.08334], }, 'SansSerif-Bold': { '32': M0, '33': M6, '34': M6, '35': M16, '36': [0.05556, 0.75, 0, 0], '37': [0.05556, 0.75, 0, 0], '38': M6, '39': M6, '40': M15, '41': M15, '42': M14, '43': [0.11667, 0.61667, 0, 0], '44': [0.10556, 0.13056, 0, 0], '45': [0, 0.45833, 0, 0], '46': [0, 0.13056, 0, 0], '47': M15, '48': M6, '49': M6, '50': M6, '51': M6, '52': M6, '53': M6, '54': M6, '55': M6, '56': M6, '57': M6, '58': [0, 0.45833, 0, 0], '59': [0.10556, 0.45833, 0, 0], '61': [-0.09375, 0.40625, 0, 0], '63': M6, '64': M6, '65': M6, '66': M6, '67': M6, '68': M6, '69': M6, '70': M6, '71': M6, '72': M6, '73': M6, '74': M6, '75': M6, '76': M6, '77': M6, '78': M6, '79': M6, '80': M6, '81': [0.10556, 0.69444, 0, 0], '82': M6, '83': M6, '84': M6, '85': M6, '86': [0, 0.69444, 0.01528, 0], '87': [0, 0.69444, 0.01528, 0], '88': M6, '89': [0, 0.69444, 0.0275, 0], '90': M6, '91': M15, '93': M15, '94': M6, '95': [0.35, 0.10833, 0.03056, 0], '97': [0, 0.45833, 0, 0], '98': M6, '99': [0, 0.45833, 0, 0], '100': M6, '101': [0, 0.45833, 0, 0], '102': [0, 0.69444, 0.07639, 0], '103': [0.19444, 0.45833, 0.01528, 0], '104': M6, '105': M6, '106': M16, '107': M6, '108': M6, '109': [0, 0.45833, 0, 0], '110': [0, 0.45833, 0, 0], '111': [0, 0.45833, 0, 0], '112': [0.19444, 0.45833, 0, 0], '113': [0.19444, 0.45833, 0, 0], '114': [0, 0.45833, 0.01528, 0], '115': [0, 0.45833, 0, 0], '116': [0, 0.58929, 0, 0], '117': [0, 0.45833, 0, 0], '118': [0, 0.45833, 0.01528, 0], '119': [0, 0.45833, 0.01528, 0], '120': [0, 0.45833, 0, 0], '121': [0.19444, 0.45833, 0.01528, 0], '122': [0, 0.45833, 0, 0], '126': [0.35, 0.34444, 0, 0], '160': M0, '168': M6, '176': M6, '180': M6, '184': [0.17014, 0, 0, 0], '305': [0, 0.45833, 0, 0], '567': [0.19444, 0.45833, 0, 0], '710': M6, '711': [0, 0.63542, 0, 0], '713': [0, 0.63778, 0, 0], '728': M6, '729': M6, '730': M6, '732': M6, '733': M6, '915': M6, '916': M6, '920': M6, '923': M6, '926': M6, '928': M6, '931': M6, '933': M6, '934': M6, '936': M6, '937': M6, '8211': [0, 0.45833, 0.03056, 0], '8212': [0, 0.45833, 0.03056, 0], '8216': M6, '8217': M6, '8220': M6, '8221': M6, }, 'SansSerif-Italic': { '32': M0, '33': [0, 0.69444, 0.05733, 0], '34': [0, 0.69444, 0.00316, 0], '35': [0.19444, 0.69444, 0.05087, 0], '36': [0.05556, 0.75, 0.11156, 0], '37': [0.05556, 0.75, 0.03126, 0], '38': [0, 0.69444, 0.03058, 0], '39': [0, 0.69444, 0.07816, 0], '40': [0.25, 0.75, 0.13164, 0], '41': [0.25, 0.75, 0.02536, 0], '42': [0, 0.75, 0.11775, 0], '43': [0.08333, 0.58333, 0.02536, 0], '44': [0.125, 0.08333, 0, 0], '45': [0, 0.44444, 0.01946, 0], '46': [0, 0.08333, 0, 0], '47': [0.25, 0.75, 0.13164, 0], '48': [0, 0.65556, 0.11156, 0], '49': [0, 0.65556, 0.11156, 0], '50': [0, 0.65556, 0.11156, 0], '51': [0, 0.65556, 0.11156, 0], '52': [0, 0.65556, 0.11156, 0], '53': [0, 0.65556, 0.11156, 0], '54': [0, 0.65556, 0.11156, 0], '55': [0, 0.65556, 0.11156, 0], '56': [0, 0.65556, 0.11156, 0], '57': [0, 0.65556, 0.11156, 0], '58': [0, 0.44444, 0.02502, 0], '59': [0.125, 0.44444, 0.02502, 0], '61': [-0.13, 0.37, 0.05087, 0], '63': [0, 0.69444, 0.11809, 0], '64': [0, 0.69444, 0.07555, 0], '65': M6, '66': [0, 0.69444, 0.08293, 0], '67': [0, 0.69444, 0.11983, 0], '68': [0, 0.69444, 0.07555, 0], '69': [0, 0.69444, 0.11983, 0], '70': [0, 0.69444, 0.13372, 0], '71': [0, 0.69444, 0.11983, 0], '72': [0, 0.69444, 0.08094, 0], '73': [0, 0.69444, 0.13372, 0], '74': [0, 0.69444, 0.08094, 0], '75': [0, 0.69444, 0.11983, 0], '76': M6, '77': [0, 0.69444, 0.08094, 0], '78': [0, 0.69444, 0.08094, 0], '79': [0, 0.69444, 0.07555, 0], '80': [0, 0.69444, 0.08293, 0], '81': [0.125, 0.69444, 0.07555, 0], '82': [0, 0.69444, 0.08293, 0], '83': [0, 0.69444, 0.09205, 0], '84': [0, 0.69444, 0.13372, 0], '85': [0, 0.69444, 0.08094, 0], '86': [0, 0.69444, 0.1615, 0], '87': [0, 0.69444, 0.1615, 0], '88': [0, 0.69444, 0.13372, 0], '89': [0, 0.69444, 0.17261, 0], '90': [0, 0.69444, 0.11983, 0], '91': [0.25, 0.75, 0.15942, 0], '93': [0.25, 0.75, 0.08719, 0], '94': [0, 0.69444, 0.0799, 0], '95': [0.35, 0.09444, 0.08616, 0], '97': [0, 0.44444, 0.00981, 0], '98': [0, 0.69444, 0.03057, 0], '99': [0, 0.44444, 0.08336, 0], '100': [0, 0.69444, 0.09483, 0], '101': [0, 0.44444, 0.06778, 0], '102': [0, 0.69444, 0.21705, 0], '103': [0.19444, 0.44444, 0.10836, 0], '104': [0, 0.69444, 0.01778, 0], '105': [0, 0.67937, 0.09718, 0], '106': [0.19444, 0.67937, 0.09162, 0], '107': [0, 0.69444, 0.08336, 0], '108': [0, 0.69444, 0.09483, 0], '109': [0, 0.44444, 0.01778, 0], '110': [0, 0.44444, 0.01778, 0], '111': [0, 0.44444, 0.06613, 0], '112': [0.19444, 0.44444, 0.0389, 0], '113': [0.19444, 0.44444, 0.04169, 0], '114': [0, 0.44444, 0.10836, 0], '115': [0, 0.44444, 0.0778, 0], '116': [0, 0.57143, 0.07225, 0], '117': [0, 0.44444, 0.04169, 0], '118': [0, 0.44444, 0.10836, 0], '119': [0, 0.44444, 0.10836, 0], '120': [0, 0.44444, 0.09169, 0], '121': [0.19444, 0.44444, 0.10836, 0], '122': [0, 0.44444, 0.08752, 0], '126': [0.35, 0.32659, 0.08826, 0], '160': M0, '168': [0, 0.67937, 0.06385, 0], '176': M6, '184': [0.17014, 0, 0, 0], '305': [0, 0.44444, 0.04169, 0], '567': [0.19444, 0.44444, 0.04169, 0], '710': [0, 0.69444, 0.0799, 0], '711': [0, 0.63194, 0.08432, 0], '713': [0, 0.60889, 0.08776, 0], '714': [0, 0.69444, 0.09205, 0], '715': M6, '728': [0, 0.69444, 0.09483, 0], '729': [0, 0.67937, 0.07774, 0], '730': M6, '732': [0, 0.67659, 0.08826, 0], '733': [0, 0.69444, 0.09205, 0], '915': [0, 0.69444, 0.13372, 0], '916': M6, '920': [0, 0.69444, 0.07555, 0], '923': M6, '926': [0, 0.69444, 0.12816, 0], '928': [0, 0.69444, 0.08094, 0], '931': [0, 0.69444, 0.11983, 0], '933': [0, 0.69444, 0.09031, 0], '934': [0, 0.69444, 0.04603, 0], '936': [0, 0.69444, 0.09031, 0], '937': [0, 0.69444, 0.08293, 0], '8211': [0, 0.44444, 0.08616, 0], '8212': [0, 0.44444, 0.08616, 0], '8216': [0, 0.69444, 0.07816, 0], '8217': [0, 0.69444, 0.07816, 0], '8220': [0, 0.69444, 0.14205, 0], '8221': [0, 0.69444, 0.00316, 0], }, 'SansSerif-Regular': { '32': M0, '33': M6, '34': M6, '35': M16, '36': [0.05556, 0.75, 0, 0], '37': [0.05556, 0.75, 0, 0], '38': M6, '39': M6, '40': M15, '41': M15, '42': M14, '43': [0.08333, 0.58333, 0, 0], '44': [0.125, 0.08333, 0, 0], '45': M7, '46': [0, 0.08333, 0, 0], '47': M15, '48': [0, 0.65556, 0, 0], '49': [0, 0.65556, 0, 0], '50': [0, 0.65556, 0, 0], '51': [0, 0.65556, 0, 0], '52': [0, 0.65556, 0, 0], '53': [0, 0.65556, 0, 0], '54': [0, 0.65556, 0, 0], '55': [0, 0.65556, 0, 0], '56': [0, 0.65556, 0, 0], '57': [0, 0.65556, 0, 0], '58': M7, '59': [0.125, 0.44444, 0, 0], '61': [-0.13, 0.37, 0, 0], '63': M6, '64': M6, '65': M6, '66': M6, '67': M6, '68': M6, '69': M6, '70': M6, '71': M6, '72': M6, '73': M6, '74': M6, '75': M6, '76': M6, '77': M6, '78': M6, '79': M6, '80': M6, '81': [0.125, 0.69444, 0, 0], '82': M6, '83': M6, '84': M6, '85': M6, '86': [0, 0.69444, 0.01389, 0], '87': [0, 0.69444, 0.01389, 0], '88': M6, '89': [0, 0.69444, 0.025, 0], '90': M6, '91': M15, '93': M15, '94': M6, '95': [0.35, 0.09444, 0.02778, 0], '97': M7, '98': M6, '99': M7, '100': M6, '101': M7, '102': [0, 0.69444, 0.06944, 0], '103': [0.19444, 0.44444, 0.01389, 0], '104': M6, '105': [0, 0.67937, 0, 0], '106': [0.19444, 0.67937, 0, 0], '107': M6, '108': M6, '109': M7, '110': M7, '111': M7, '112': M8, '113': M8, '114': [0, 0.44444, 0.01389, 0], '115': M7, '116': [0, 0.57143, 0, 0], '117': M7, '118': [0, 0.44444, 0.01389, 0], '119': [0, 0.44444, 0.01389, 0], '120': M7, '121': [0.19444, 0.44444, 0.01389, 0], '122': M7, '126': [0.35, 0.32659, 0, 0], '160': M0, '168': [0, 0.67937, 0, 0], '176': M6, '184': [0.17014, 0, 0, 0], '305': M7, '567': M8, '710': M6, '711': [0, 0.63194, 0, 0], '713': [0, 0.60889, 0, 0], '714': M6, '715': M6, '728': M6, '729': [0, 0.67937, 0, 0], '730': M6, '732': [0, 0.67659, 0, 0], '733': M6, '915': M6, '916': M6, '920': M6, '923': M6, '926': M6, '928': M6, '931': M6, '933': M6, '934': M6, '936': M6, '937': M6, '8211': [0, 0.44444, 0.02778, 0], '8212': [0, 0.44444, 0.02778, 0], '8216': M6, '8217': M6, '8220': M6, '8221': M6, }, 'Script-Regular': { '32': M0, '65': [0, 0.7, 0.22925, 0], '66': [0, 0.7, 0.04087, 0], '67': [0, 0.7, 0.1689, 0], '68': [0, 0.7, 0.09371, 0], '69': [0, 0.7, 0.18583, 0], '70': [0, 0.7, 0.13634, 0], '71': [0, 0.7, 0.17322, 0], '72': [0, 0.7, 0.29694, 0], '73': [0, 0.7, 0.19189, 0], '74': [0.27778, 0.7, 0.19189, 0], '75': [0, 0.7, 0.31259, 0], '76': [0, 0.7, 0.19189, 0], '77': [0, 0.7, 0.15981, 0], '78': [0, 0.7, 0.3525, 0], '79': [0, 0.7, 0.08078, 0], '80': [0, 0.7, 0.08078, 0], '81': [0, 0.7, 0.03305, 0], '82': [0, 0.7, 0.06259, 0], '83': [0, 0.7, 0.19189, 0], '84': [0, 0.7, 0.29087, 0], '85': [0, 0.7, 0.25815, 0], '86': [0, 0.7, 0.27523, 0], '87': [0, 0.7, 0.27523, 0], '88': [0, 0.7, 0.26006, 0], '89': [0, 0.7, 0.2939, 0], '90': [0, 0.7, 0.24037, 0], '160': M0, }, 'Size1-Regular': { '32': M0, '40': M10, '41': M10, '47': M10, '91': M10, '92': M10, '93': M10, '123': M10, '125': M10, '160': M0, '710': [0, 0.72222, 0, 0], '732': [0, 0.72222, 0, 0], '770': [0, 0.72222, 0, 0], '771': [0, 0.72222, 0, 0], '8214': [-0.00099, 0.601, 0, 0], '8593': [1e-5, 0.6, 0, 0], '8595': [1e-5, 0.6, 0, 0], '8657': [1e-5, 0.6, 0, 0], '8659': [1e-5, 0.6, 0, 0], '8719': M17, '8720': M17, '8721': M17, '8730': M10, '8739': [-0.00599, 0.606, 0, 0], '8741': [-0.00599, 0.606, 0, 0], '8747': [0.30612, 0.805, 0.19445, 0], '8748': [0.306, 0.805, 0.19445, 0], '8749': [0.306, 0.805, 0.19445, 0], '8750': [0.30612, 0.805, 0.19445, 0], '8896': M17, '8897': M17, '8898': M17, '8899': M17, '8968': M10, '8969': M10, '8970': M10, '8971': M10, '9168': [-0.00099, 0.601, 0, 0], '10216': M10, '10217': M10, '10752': M17, '10753': M17, '10754': M17, '10756': M17, '10758': M17, }, 'Size2-Regular': { '32': M0, '40': M19, '41': M19, '47': M19, '91': M19, '92': M19, '93': M19, '123': M19, '125': M19, '160': M0, '710': M14, '732': M14, '770': M14, '771': M14, '8719': M18, '8720': M18, '8721': M18, '8730': M19, '8747': [0.86225, 1.36, 0.44445, 0], '8748': [0.862, 1.36, 0.44445, 0], '8749': [0.862, 1.36, 0.44445, 0], '8750': [0.86225, 1.36, 0.44445, 0], '8896': M18, '8897': M18, '8898': M18, '8899': M18, '8968': M19, '8969': M19, '8970': M19, '8971': M19, '10216': M19, '10217': M19, '10752': M18, '10753': M18, '10754': M18, '10756': M18, '10758': M18, }, 'Size3-Regular': { '32': M0, '40': M13, '41': M13, '47': M13, '91': M13, '92': M13, '93': M13, '123': M13, '125': M13, '160': M0, '710': M14, '732': M14, '770': M14, '771': M14, '8730': M13, '8968': M13, '8969': M13, '8970': M13, '8971': M13, '10216': M13, '10217': M13, }, 'Size4-Regular': { '32': M0, '40': M12, '41': M12, '47': M12, '91': M12, '92': M12, '93': M12, '123': M12, '125': M12, '160': M0, '710': [0, 0.825, 0, 0], '732': [0, 0.825, 0, 0], '770': [0, 0.825, 0, 0], '771': [0, 0.825, 0, 0], '8730': M12, '8968': M12, '8969': M12, '8970': M12, '8971': M12, '9115': [0.64502, 1.155, 0, 0], '9116': [1e-5, 0.6, 0, 0], '9117': [0.64502, 1.155, 0, 0], '9118': [0.64502, 1.155, 0, 0], '9119': [1e-5, 0.6, 0, 0], '9120': [0.64502, 1.155, 0, 0], '9121': [0.64502, 1.155, 0, 0], '9122': [-0.00099, 0.601, 0, 0], '9123': [0.64502, 1.155, 0, 0], '9124': [0.64502, 1.155, 0, 0], '9125': [-0.00099, 0.601, 0, 0], '9126': [0.64502, 1.155, 0, 0], '9127': [1e-5, 0.9, 0, 0], '9128': M19, '9129': [0.90001, 0, 0, 0], '9130': [0, 0.3, 0, 0], '9131': [1e-5, 0.9, 0, 0], '9132': M19, '9133': [0.90001, 0, 0, 0], '9143': [0.88502, 0.915, 0, 0], '10216': M12, '10217': M12, '57344': [-0.00499, 0.605, 0, 0], '57345': [-0.00499, 0.605, 0, 0], '57680': [0, 0.12, 0, 0], '57681': [0, 0.12, 0, 0], '57682': [0, 0.12, 0, 0], '57683': [0, 0.12, 0, 0], }, 'Typewriter-Regular': { '32': M0, '33': M11, '34': M11, '35': M11, '36': [0.08333, 0.69444, 0, 0], '37': [0.08333, 0.69444, 0, 0], '38': M11, '39': M11, '40': [0.08333, 0.69444, 0, 0], '41': [0.08333, 0.69444, 0, 0], '42': [0, 0.52083, 0, 0], '43': [-0.08056, 0.53055, 0, 0], '44': [0.13889, 0.125, 0, 0], '45': [-0.08056, 0.53055, 0, 0], '46': [0, 0.125, 0, 0], '47': [0.08333, 0.69444, 0, 0], '48': M11, '49': M11, '50': M11, '51': M11, '52': M11, '53': M11, '54': M11, '55': M11, '56': M11, '57': M11, '58': [0, 0.43056, 0, 0], '59': [0.13889, 0.43056, 0, 0], '60': [-0.05556, 0.55556, 0, 0], '61': [-0.19549, 0.41562, 0, 0], '62': [-0.05556, 0.55556, 0, 0], '63': M11, '64': M11, '65': M11, '66': M11, '67': M11, '68': M11, '69': M11, '70': M11, '71': M11, '72': M11, '73': M11, '74': M11, '75': M11, '76': M11, '77': M11, '78': M11, '79': M11, '80': M11, '81': [0.13889, 0.61111, 0, 0], '82': M11, '83': M11, '84': M11, '85': M11, '86': M11, '87': M11, '88': M11, '89': M11, '90': M11, '91': [0.08333, 0.69444, 0, 0], '92': [0.08333, 0.69444, 0, 0], '93': [0.08333, 0.69444, 0, 0], '94': M11, '95': [0.09514, 0, 0, 0], '96': M11, '97': [0, 0.43056, 0, 0], '98': M11, '99': [0, 0.43056, 0, 0], '100': M11, '101': [0, 0.43056, 0, 0], '102': M11, '103': [0.22222, 0.43056, 0, 0], '104': M11, '105': M11, '106': [0.22222, 0.61111, 0, 0], '107': M11, '108': M11, '109': [0, 0.43056, 0, 0], '110': [0, 0.43056, 0, 0], '111': [0, 0.43056, 0, 0], '112': [0.22222, 0.43056, 0, 0], '113': [0.22222, 0.43056, 0, 0], '114': [0, 0.43056, 0, 0], '115': [0, 0.43056, 0, 0], '116': [0, 0.55358, 0, 0], '117': [0, 0.43056, 0, 0], '118': [0, 0.43056, 0, 0], '119': [0, 0.43056, 0, 0], '120': [0, 0.43056, 0, 0], '121': [0.22222, 0.43056, 0, 0], '122': [0, 0.43056, 0, 0], '123': [0.08333, 0.69444, 0, 0], '124': [0.08333, 0.69444, 0, 0], '125': [0.08333, 0.69444, 0, 0], '126': M11, '127': M11, '160': M0, '176': M11, '184': [0.19445, 0, 0, 0], '305': [0, 0.43056, 0, 0], '567': [0.22222, 0.43056, 0, 0], '711': [0, 0.56597, 0, 0], '713': [0, 0.56555, 0, 0], '714': M11, '715': M11, '728': M11, '730': M11, '770': M11, '771': M11, '776': M11, '915': M11, '916': M11, '920': M11, '923': M11, '926': M11, '928': M11, '931': M11, '933': M11, '934': M11, '936': M11, '937': M11, '8216': M11, '8217': M11, '8242': M11, '9251': [0.11111, 0.21944, 0, 0], }, }; /** * This module contains metrics regarding fonts and individual symbols. The sigma * and xi variables, as well as the CHARACTER_METRICS_MAP map contain data extracted from * TeX, TeX font metrics, and the TTF files. These data are then exposed via the * `metrics` variable and the getCharacterMetrics function. */ // This regex combines // - Hiragana: [\u3040-\u309F] // - Katakana: [\u30A0-\u30FF] // - CJK ideograms: [\u4E00-\u9FAF] // - Hangul syllables: [\uAC00-\uD7AF] // Notably missing are half width Katakana and Romaji glyphs. const CJK_REGEX = /[\u3040-\u309F]|[\u30A0-\u30FF]|[\u4E00-\u9FAF]|[\uAC00-\uD7AF]/; // This value determines how large a pt is, for metrics which are defined // in terms of pts. // This value should equal `@ptperem` in core.less; if you change it make sure the // values match. const PT_PER_EM = 10.0; // In math typesetting, the term axis refers to a horizontal reference line // used for positioning elements in a formula. The math axis is similar to but // distinct from the baseline for regular text layout. For example, in a // simple equation, a minus symbol or fraction rule would be on the axis, but // a string for a variable name would be set on a baseline that is offset from // the axis. The axisHeight value determines the amount of that offset. // (from https://docs.microsoft.com/en-us/typography/opentype/spec/math) const AXIS_HEIGHT = 0.25; // The minimum space between the botton of two successive lines in a paragraph (in em) const BASELINE_SKIP = 1.2; const X_HEIGHT = 0.431; // sigma 5 /* * * In TeX, there are actually three sets of dimensions, one for each of * textstyle, scriptstyle, and scriptscriptstyle. These are provided in the * the arrays below, in that order. * * The font metrics are stored in fonts cmsy10, cmsy7, and cmsy5 respectively. * This was determined by running the following script: *``` bash latex -interaction=nonstopmode \ '\documentclass{article}\usepackage{amsmath}\begin{document}' \ '$a$ \expandafter\show\the\textfont2' \ '\expandafter\show\the\scriptfont2' \ '\expandafter\show\the\scriptscriptfont2' \ '\stop' ``` * The metrics themselves were retrieved using the following commands: * ``` bash tftopl cmsy10 # displaystyle and textstyle tftopl cmsy7 # scriptstyle tftopl cmsy5 # scriptscriptstyle ``` * * The output of each of these commands is quite lengthy. The only part we * care about is the FONTDIMEN section. Each value is measured in EMs. */ const FONT_METRICS = { slant: [0.25, 0.25, 0.25], space: [0.0, 0.0, 0.0], stretch: [0.0, 0.0, 0.0], shrink: [0.0, 0.0, 0.0], xHeight: [X_HEIGHT, X_HEIGHT, X_HEIGHT], quad: [1.0, 1.171, 1.472], extraSpace: [0.0, 0.0, 0.0], num1: [0.677, 0.732, 0.925], num2: [0.394, 0.384, 0.387], num3: [0.444, 0.471, 0.504], denom1: [0.686, 0.752, 1.025], denom2: [0.345, 0.344, 0.532], sup1: [0.413, 0.503, 0.504], sup2: [0.363, 0.431, 0.404], sup3: [0.289, 0.286, 0.294], sub1: [0.15, 0.143, 0.2], sub2: [0.247, 0.286, 0.4], supDrop: [0.386, 0.353, 0.494], subDrop: [0.05, 0.071, 0.1], delim1: [2.39, 1.7, 1.98], delim2: [1.01, 1.157, 1.42], axisHeight: [AXIS_HEIGHT, AXIS_HEIGHT, AXIS_HEIGHT], defaultRuleThickness: [0.04, 0.049, 0.049], bigOpSpacing1: [0.111, 0.111, 0.111], bigOpSpacing2: [0.166, 0.166, 0.166], bigOpSpacing3: [0.2, 0.2, 0.2], bigOpSpacing4: [0.6, 0.611, 0.611], bigOpSpacing5: [0.1, 0.143, 0.143], sqrtRuleThickness: [0.04, 0.04, 0.04], }; // Maps a scale index from 1..10 to a value expressed in `em` relative // to the fontsize of the mathfield. const FONT_SCALE = [ 0, 0.5, 0.7, 0.8, 0.9, 1.0, 1.2, 1.44, 1.728, 2.074, 2.488, //size 10 ]; const DEFAULT_FONT_SIZE = 5; // These are very rough approximations. We default to Times New Roman which // should have Latin-1 and Cyrillic characters, but may not depending on the // operating system. The metrics do not account for extra height from the // accents. In the case of Cyrillic characters which have both ascenders and // descenders we prefer approximations with ascenders, primarily to prevent // the fraction bar or root line from intersecting the glyph. // TODO(kevinb) allow union of multiple glyph metrics for better accuracy. const extraCharacterMap = { '\u00A0': '\u0020', '\u200B': '\u0020', // Latin-1 'Å': 'A', 'Ç': 'C', 'Ð': 'D', 'Þ': 'o', 'å': 'a', 'ç': 'c', 'ð': 'd', 'þ': 'o', // Cyrillic 'А': 'A', 'Б': 'B', 'В': 'B', 'Г': 'F', 'Д': 'A', 'Е': 'E', 'Ж': 'K', 'З': '3', 'И': 'N', 'Й': 'N', 'К': 'K', 'Л': 'N', 'М': 'M', 'Н': 'H', 'О': 'O', 'П': 'N', 'Р': 'P', 'С': 'C', 'Т': 'T', 'У': 'y', 'Ф': 'O', 'Х': 'X', 'Ц': 'U', 'Ч': 'h', 'Ш': 'W', 'Щ': 'W', 'Ъ': 'B', 'Ы': 'X', 'Ь': 'B', 'Э': '3', 'Ю': 'X', 'Я': 'R', 'а': 'a', 'б': 'b', 'в': 'a', 'г': 'r', 'д': 'y', 'е': 'e', 'ж': 'm', 'з': 'e', 'и': 'n', 'й': 'n', 'к': 'n', 'л': 'n', 'м': 'm', 'н': 'n', 'о': 'o', 'п': 'n', 'р': 'p', 'с': 'c', 'т': 'o', 'у': 'y', 'ф': 'b', 'х': 'x', 'ц': 'n', 'ч': 'n', 'ш': 'w', 'щ': 'w', 'ъ': 'a', 'ы': 'm', 'ь': 'a', 'э': 'e', 'ю': 'm', 'я': 'r', }; /** * This function is a convenience function for looking up information in the * CHARACTER_METRICS_MAP table. It takes a codepoint, and a font name. * * @param fontName e.g. 'Main-Regular', 'Typewriter-Regular', etc... */ function getCharacterMetrics(codepoint, fontName) { if (codepoint === undefined) codepoint = 77; // 'M' const metrics = CHARACTER_METRICS_MAP[fontName][codepoint]; if (metrics) { return { defaultMetrics: false, depth: metrics[0], height: metrics[1], italic: metrics[2], skew: metrics[3], }; } if (codepoint === 11034) { // Placeholder character return { defaultMetrics: true, depth: 0.2, height: 0.8, italic: 0, skew: 0, }; } const char = String.fromCodePoint(codepoint); if (char in extraCharacterMap) codepoint = extraCharacterMap[char].codePointAt(0); else if (CJK_REGEX.test(char)) { codepoint = 77; // 'M'.codepointAt(0); return { defaultMetrics: true, depth: 0.2, height: 0.9, italic: 0, skew: 0, }; } return { defaultMetrics: true, depth: 0.2, height: 0.7, italic: 0, skew: 0, }; } /** * This file contains information and classes for the 'math styles' used by TeX, * which are specific layout algorithms for math. * * They get progressively smaller and tighter: * - displaystyle is used for expressions laid out on their own (in a block) * - textstyle is for expressions displayed on a line (usually with some text * around) * - scriptstyle is for expressions displayed as a superscript for example * - scriptscriptstyle is for expressions displayed as a superscript of a superscript * - the 'cramped' variations are used in various places, for examples a subscript * is using the 'scriptstyle', but cramped (so it's a bit more tight than a * superscript which is just using the 'scriptstyle') * * See Texbook, p.441: * * > A math list is a sequence of items of the various kinds listed in Chapter 17, * > and TEX typesets a formula by converting a math list to a horizontal list. * > When such typesetting begins, TEX has two other pieces of information in * > addition to the math list itself. (a) The starting style tells what style * > should be used for the math list, unless another style is specified by a * > style item. For example, the starting style for a displayed formula is D, * > but for an equation in the text or an equation number it is T; and for a * > subformula it can be any one of the eight styles defined in Chapter 17. * > * > We shall use C to stand for the current style, and we shall say that the * > math list is being typeset in style C. (b) The typesetting is done either * > with or without penalties. Formulas in the text of a paragraph are converted * > to horizontal lists in which additional penalty items are inserted after * > binary operations and relations, in order to aid in line breaking. Such * > penalties are not inserted in other cases, because they would serve no * > useful function. * > * > The eight styles are considered to be D > D′ > T > T′ > S > S′ > SS > SS′, * > in decreasing order. Thus, C ≤ S means that the current style is S, S', SS, * > or SS'. Style C′ means the current style with a prime added if one isn’t * > there; for example, we have C = T if and only if C = T or C = T'. * > Style C↑ is the superscript style for C; this means style S if C is D or T, * > style S′ if C is D′ or T′, style SS if C is S or SS, * > and style SS if C is S or SS. * > Finally, style C↓ is the subscript style, which is (C↑) . */ // IDs of the different MATHSTYLES const D$1 = 7; // Displaystyle const Dc = 6; // Displaystyle, cramped const T$1 = 5; // Textstyle const Tc = 4; const S$1 = 3; // Scriptstyle const Sc = 2; const SS = 1; // Scriptscriptstyle const SSc = 0; /** * @property {number} id unique id for the style * @property {number} sizeDelta (which is the same for cramped and uncramped version * of a style) * @property {boolean} cramped flag */ class Mathstyle { constructor(id, sizeDelta, cramped) { this.id = id; this.sizeDelta = sizeDelta; this.cramped = cramped; const metricsIndex = { '-4': 2, '-3': 1, 0: 0 }[sizeDelta]; this.metrics = Object.keys(FONT_METRICS).reduce((acc, x) => { return { ...acc, [x]: FONT_METRICS[x][metricsIndex] }; }, {}); } getFontSize(size) { return Math.max(1, size + this.sizeDelta); } /** * Get the style of a superscript given a base in the current style. */ get sup() { return MATHSTYLES[[SSc, SS, SSc, SS, Sc, S$1, Sc, S$1][this.id]]; } /** * Get the style of a subscript given a base in the current style. */ get sub() { return MATHSTYLES[[SSc, SSc, SSc, SSc, Sc, Sc, Sc, Sc][this.id]]; } /** * Get the style of a fraction numerator given the fraction in the current * style. * See TeXBook p 141. */ get fracNum() { return MATHSTYLES[[SSc, SS, SSc, SS, Sc, S$1, Tc, T$1][this.id]]; } /** * Get the style of a fraction denominator given the fraction in the current * style. * See TeXBook p 141. */ get fracDen() { return MATHSTYLES[[SSc, SSc, SSc, SSc, Sc, Sc, Tc, Tc][this.id]]; } /** * Get the cramped version of a style (in particular, cramping a cramped style * doesn't change the style). */ get cramp() { return MATHSTYLES[[SSc, SSc, Sc, Sc, Tc, Tc, Dc, Dc][this.id]]; } /** * Return if this style is tightly spaced (scriptstyle/scriptscriptstyle) */ get isTight() { return this.sizeDelta < 0; } } const MATHSTYLES = { 7: new Mathstyle(D$1, 0, false), 6: new Mathstyle(Dc, 0, true), 5: new Mathstyle(T$1, 0, false), 4: new Mathstyle(Tc, 0, true), 3: new Mathstyle(S$1, -3, false), 2: new Mathstyle(Sc, -3, true), 1: new Mathstyle(SS, -4, false), 0: new Mathstyle(SSc, -4, true), }; // Aliases MATHSTYLES.displaystyle = MATHSTYLES[D$1]; MATHSTYLES.textstyle = MATHSTYLES[T$1]; MATHSTYLES.scriptstyle = MATHSTYLES[S$1]; MATHSTYLES.scriptscriptstyle = MATHSTYLES[SS]; function convertDimensionToPt(value, precision) { var _a; if (!value) return 0; // If the units are missing, TeX assumes 'pt' const f = { pt: 1, mm: 7227 / 2540, cm: 7227 / 254, ex: 35271 / 8192, px: 3 / 4, em: PT_PER_EM, bp: 803 / 800, dd: 1238 / 1157, pc: 12, in: 72.27, mu: 10 / 18, }[(_a = value.unit) !== null && _a !== void 0 ? _a : 'pt']; if (Number.isFinite(precision)) { const factor = 10 ** precision; return Math.round((value.dimension / PT_PER_EM) * f * factor) / factor; } return value.dimension * f; } function convertDimensionToEm(value, precision) { if (value === null) return 0; return convertDimensionToPt(value, precision) / PT_PER_EM; } function convertGlueToEm(value) { return convertDimensionToEm(value.glue); } function serializeDimension(value) { var _a; return `${value.dimension}${(_a = value.unit) !== null && _a !== void 0 ? _a : 'pt'}`; } /** * This structure contains the rendering context of the current parse level. * * It keeps a reference to the parent context which is necessary to calculate * the proper scaling/fontsize since all sizes are specified in em and the * absolute value of an em depends of the fontsize of the parent context. * * When a new context is entered, a clone of the context is created with * `new()` so that any further changes remain local to the scope. * * When a context is exited, a 'wrapper' box is created to adjust the * fontsize on entry, and adjust the height/depth of the box to account for * the new fontsize (if applicable). * * The effective font size is determined by: * - the 'size' property, which represent a size set by a sizing command * (e.g. `\Huge`, `\tiny`, etc...) * - a size delta from the mathstyle (-1 for scriptstyle for example) * * * A context is defined for example by: * - an explicit group enclosed in braces `{...}` * - a semi-simple group enclosed in `\bgroup...\endgroup` * - the cells of a tabular environment * - the numerator or denominator of a fraction * - the root and radix of a fraction * */ class Context { constructor(parent, style, inMathstyle) { var _a, _b, _c, _d, _e, _f, _g; // If we don't have a parent context, we must provide an initial // mathstyle and fontsize console.assert(parent instanceof Context || (style === null || style === void 0 ? void 0 : style.fontSize) !== undefined); console.assert(parent instanceof Context || inMathstyle !== undefined); if (parent instanceof Context) this.parent = parent; if (!(parent instanceof Context)) this.registers = (_a = parent.registers) !== null && _a !== void 0 ? _a : {}; this.isPhantom = (_d = (_b = style === null || style === void 0 ? void 0 : style.isPhantom) !== null && _b !== void 0 ? _b : (_c = this.parent) === null || _c === void 0 ? void 0 : _c.isPhantom) !== null && _d !== void 0 ? _d : false; const from = { ...parent, }; if (style) { if (style.letterShapeStyle && style.letterShapeStyle !== 'auto') from.letterShapeStyle = style.letterShapeStyle; if (style.color && style.color !== 'none') from.color = style.color; if (style.backgroundColor && style.backgroundColor !== 'none') from.backgroundColor = style.backgroundColor; if (style.fontSize && style.fontSize !== 'auto' && style.fontSize !== ((_e = this.parent) === null || _e === void 0 ? void 0 : _e._size)) this._size = style.fontSize; } this.letterShapeStyle = (_f = from.letterShapeStyle) !== null && _f !== void 0 ? _f : 'tex'; this.color = from.color; this.backgroundColor = from.backgroundColor; let mathstyle; if (typeof inMathstyle === 'string') { if (parent instanceof Context) { switch (inMathstyle) { case 'cramp': mathstyle = parent.mathstyle.cramp; break; case 'superscript': mathstyle = parent.mathstyle.sup; break; case 'subscript': mathstyle = parent.mathstyle.sub; break; case 'numerator': mathstyle = parent.mathstyle.fracNum; break; case 'denominator': mathstyle = parent.mathstyle.fracDen; break; } } switch (inMathstyle) { case 'textstyle': mathstyle = MATHSTYLES.textstyle; break; case 'displaystyle': mathstyle = MATHSTYLES.displaystyle; break; case 'scriptstyle': mathstyle = MATHSTYLES.scriptstyle; break; case 'scriptscriptstyle': mathstyle = MATHSTYLES.scriptscriptstyle; break; } } this._mathstyle = mathstyle; this.atomIdsSettings = parent.atomIdsSettings; this.smartFence = (_g = from.smartFence) !== null && _g !== void 0 ? _g : false; this.renderPlaceholder = from.renderPlaceholder; console.assert(!(parent instanceof Context) || this.atomIdsSettings === parent.atomIdsSettings); } get mathstyle() { let result = this._mathstyle; let parent = this.parent; while (!result) { result = parent._mathstyle; parent = parent.parent; } return result; } getRegister(name) { var _a; if ((_a = this.registers) === null || _a === void 0 ? void 0 : _a[name]) return this.registers[name]; if (this.parent) return this.parent.getRegister(name); return undefined; } getRegisterAsGlue(name) { var _a; if ((_a = this.registers) === null || _a === void 0 ? void 0 : _a[name]) { const value = this.registers[name]; if (typeof value === 'object' && 'glue' in value) return value; else if (typeof value === 'object' && 'dimension' in value) return { glue: { dimension: value.dimension } }; else if (typeof value === 'number') return { glue: { dimension: value } }; return undefined; } if (this.parent) return this.parent.getRegisterAsGlue(name); return undefined; } getRegisterAsEm(name) { return convertDimensionToEm(this.getRegisterAsDimension(name)); } getRegisterAsDimension(name) { var _a; if ((_a = this.registers) === null || _a === void 0 ? void 0 : _a[name]) { const value = this.registers[name]; if (typeof value === 'object' && 'glue' in value) return value.glue; else if (typeof value === 'object' && 'dimension' in value) return value; else if (typeof value === 'number') return { dimension: value }; return undefined; } if (this.parent) return this.parent.getRegisterAsDimension(name); return undefined; } setRegister(name, value) { if (value === undefined) { delete this.registers[name]; return; } this.registers[name] = value; } setGlobalRegister(name, value) { // eslint-disable-next-line @typescript-eslint/no-this-alias let root = this; while (root.parent) { root.setRegister(name, undefined); root = root.parent; } root.setRegister(name, value); } get size() { let result = this._size; let parent = this.parent; while (!result) { result = parent._size; parent = parent.parent; } return result; } makeID() { if (!this.atomIdsSettings) return undefined; if (this.atomIdsSettings.overrideID) return this.atomIdsSettings.overrideID; if (typeof this.atomIdsSettings.seed !== 'number') { return (Date.now().toString(36).slice(-2) + Math.floor(Math.random() * 0x186a0).toString(36)); } const result = this.atomIdsSettings.seed.toString(36); this.atomIdsSettings.seed += 1; return result; } // Scale a value, in em, to account for the fontsize and mathstyle // of this context scale(value) { return value * this.effectiveFontSize; } get scalingFactor() { if (!this.parent) return 1.0; return this.effectiveFontSize / this.parent.effectiveFontSize; } get isDisplayStyle() { return this.mathstyle.id === D$1 || this.mathstyle.id === Dc; } get isCramped() { return this.mathstyle.cramped; } get isTight() { return this.mathstyle.isTight; } // Return the font size, in em relative to the mathfield fontsize, // accounting both for the base font size and the mathstyle get effectiveFontSize() { return FONT_SCALE[Math.max(1, this.size + this.mathstyle.sizeDelta)]; } get computedColor() { let result = this.color; let parent = this.parent; if (!result && parent) { result = parent.color; parent = parent.parent; } return result !== null && result !== void 0 ? result : ''; } get computedBackgroundColor() { let result = this.backgroundColor; let parent = this.parent; if (!result && parent) { result = parent.backgroundColor; parent = parent.parent; } return result !== null && result !== void 0 ? result : ''; } get metrics() { return this.mathstyle.metrics; } } const SVG_BODY = { // Adapted from https://github.com/KaTeX/KaTeX/blob/master/src/stretchy.js overrightarrow: [['rightarrow'], 0.888, 522, 'xMaxYMin'], overleftarrow: [['leftarrow'], 0.888, 522, 'xMinYMin'], underrightarrow: [['rightarrow'], 0.888, 522, 'xMaxYMin'], underleftarrow: [['leftarrow'], 0.888, 522, 'xMinYMin'], xrightarrow: [['rightarrow'], 1.469, 522, 'xMaxYMin'], xleftarrow: [['leftarrow'], 1.469, 522, 'xMinYMin'], Overrightarrow: [['doublerightarrow'], 0.888, 560, 'xMaxYMin'], xRightarrow: [['doublerightarrow'], 1.526, 560, 'xMaxYMin'], xLeftarrow: [['doubleleftarrow'], 1.526, 560, 'xMinYMin'], overleftharpoon: [['leftharpoon'], 0.888, 522, 'xMinYMin'], xleftharpoonup: [['leftharpoon'], 0.888, 522, 'xMinYMin'], xleftharpoondown: [['leftharpoondown'], 0.888, 522, 'xMinYMin'], overrightharpoon: [['rightharpoon'], 0.888, 522, 'xMaxYMin'], xrightharpoonup: [['rightharpoon'], 0.888, 522, 'xMaxYMin'], xrightharpoondown: [['rightharpoondown'], 0.888, 522, 'xMaxYMin'], xlongequal: [['longequal'], 0.888, 334, 'xMinYMin'], xtwoheadleftarrow: [['twoheadleftarrow'], 0.888, 334, 'xMinYMin'], xtwoheadrightarrow: [['twoheadrightarrow'], 0.888, 334, 'xMaxYMin'], overleftrightarrow: [['leftarrow', 'rightarrow'], 0.888, 522], overbrace: [['leftbrace', 'midbrace', 'rightbrace'], 1.6, 548], underbrace: [ ['leftbraceunder', 'midbraceunder', 'rightbraceunder'], 1.6, 548, ], underleftrightarrow: [['leftarrow', 'rightarrow'], 0.888, 522], xleftrightarrow: [['leftarrow', 'rightarrow'], 1.75, 522], xLeftrightarrow: [['doubleleftarrow', 'doublerightarrow'], 1.75, 560], xrightleftharpoons: [['leftharpoondownplus', 'rightharpoonplus'], 1.75, 716], xleftrightharpoons: [['leftharpoonplus', 'rightharpoondownplus'], 1.75, 716], xhookleftarrow: [['leftarrow', 'righthook'], 1.08, 522], xhookrightarrow: [['lefthook', 'rightarrow'], 1.08, 522], overlinesegment: [['leftlinesegment', 'rightlinesegment'], 0.888, 522], underlinesegment: [['leftlinesegment', 'rightlinesegment'], 0.888, 522], overgroup: [['leftgroup', 'rightgroup'], 0.888, 342], undergroup: [['leftgroupunder', 'rightgroupunder'], 0.888, 342], xmapsto: [['leftmapsto', 'rightarrow'], 1.5, 522], xtofrom: [['leftToFrom', 'rightToFrom'], 1.75, 528], // The next three arrows are from the mhchem package. // In mhchem.sty, min-length is 2.0em. But these arrows might appear in the // document as \xrightarrow or \xrightleftharpoons. Those have // min-length = 1.75em, so we set min-length on these next three to match. xrightleftarrows: [['baraboveleftarrow', 'rightarrowabovebar'], 1.75, 901], xrightequilibrium: [ ['baraboveshortleftharpoon', 'rightharpoonaboveshortbar'], 1.75, 716, ], xleftequilibrium: [ ['shortbaraboveleftharpoon', 'shortrightharpoonabovebar'], 1.75, 716, ], }; // While the images above are sliced, "accents" are stretched // The variants (xxx1, xxx2, etc...) are used depending on the length // (in number of characters) of the base. const SVG_ACCENTS = { // ViewBoxWidth, viewBoxHeight, height widehat1: [1062, 239, 0.24], widehat2: [2364, 300, 0.3], widehat3: [2364, 360, 0.36], widehat4: [2364, 420, 0.42], widecheck1: [1062, 239, 0.24], widecheck2: [2364, 300, 0.3], widecheck3: [2364, 360, 0.36], widecheck4: [2364, 420, 0.42], widetilde1: [600, 260, 0.26], widetilde2: [1033, 286, 0.286], widetilde3: [2339, 306, 0.306], widetilde4: [2340, 312, 0.34], overarc: [1061, 159, 0.3], underarc: [1061, 159, 0.3], }; // From https://github.com/KaTeX/KaTeX/blob/master/src/svgGeometry.js const PATHS = { // The doubleleftarrow geometry is from glyph U+21D0 in the font KaTeX Main doubleleftarrow: `M262 157 l10-10c34-36 62.7-77 86-123 3.3-8 5-13.3 5-16 0-5.3-6.7-8-20-8-7.3 0-12.2.5-14.5 1.5-2.3 1-4.8 4.5-7.5 10.5-49.3 97.3-121.7 169.3-217 216-28 14-57.3 25-88 33-6.7 2-11 3.8-13 5.5-2 1.7-3 4.2-3 7.5s1 5.8 3 7.5 c2 1.7 6.3 3.5 13 5.5 68 17.3 128.2 47.8 180.5 91.5 52.3 43.7 93.8 96.2 124.5 157.5 9.3 8 15.3 12.3 18 13h6c12-.7 18-4 18-10 0-2-1.7-7-5-15-23.3-46-52-87 -86-123l-10-10h399738v-40H218c328 0 0 0 0 0l-10-8c-26.7-20-65.7-43-117-69 2.7 -2 6-3.7 10-5 36.7-16 72.3-37.3 107-64l10-8h399782v-40z m8 0v40h399730v-40zm0 194v40h399730v-40z`, // Doublerightarrow is from glyph U+21D2 in font KaTeX Main doublerightarrow: `M399738 392l -10 10c-34 36-62.7 77-86 123-3.3 8-5 13.3-5 16 0 5.3 6.7 8 20 8 7.3 0 12.2-.5 14.5-1.5 2.3-1 4.8-4.5 7.5-10.5 49.3-97.3 121.7-169.3 217-216 28-14 57.3-25 88 -33 6.7-2 11-3.8 13-5.5 2-1.7 3-4.2 3-7.5s-1-5.8-3-7.5c-2-1.7-6.3-3.5-13-5.5-68 -17.3-128.2-47.8-180.5-91.5-52.3-43.7-93.8-96.2-124.5-157.5-9.3-8-15.3-12.3-18 -13h-6c-12 .7-18 4-18 10 0 2 1.7 7 5 15 23.3 46 52 87 86 123l10 10H0v40h399782 c-328 0 0 0 0 0l10 8c26.7 20 65.7 43 117 69-2.7 2-6 3.7-10 5-36.7 16-72.3 37.3 -107 64l-10 8H0v40zM0 157v40h399730v-40zm0 194v40h399730v-40z`, // Leftarrow is from glyph U+2190 in font KaTeX Main leftarrow: `M400000 241H110l3-3c68.7-52.7 113.7-120 135-202 4-14.7 6-23 6-25 0-7.3-7-11-21-11-8 0-13.2.8-15.5 2.5-2.3 1.7-4.2 5.8 -5.5 12.5-1.3 4.7-2.7 10.3-4 17-12 48.7-34.8 92-68.5 130S65.3 228.3 18 247 c-10 4-16 7.7-18 11 0 8.7 6 14.3 18 17 47.3 18.7 87.8 47 121.5 85S196 441.3 208 490c.7 2 1.3 5 2 9s1.2 6.7 1.5 8c.3 1.3 1 3.3 2 6s2.2 4.5 3.5 5.5c1.3 1 3.3 1.8 6 2.5s6 1 10 1c14 0 21-3.7 21-11 0-2-2-10.3-6-25-20-79.3-65-146.7-135-202 l-3-3h399890zM100 241v40h399900v-40z`, // Overbrace is from glyphs U+23A9/23A8/23A7 in font KaTeX_Size4-Regular leftbrace: `M6 548l-6-6v-35l6-11c56-104 135.3-181.3 238-232 57.3-28.7 117 -45 179-50h399577v120H403c-43.3 7-81 15-113 26-100.7 33-179.7 91-237 174-2.7 5-6 9-10 13-.7 1-7.3 1-20 1H6z`, leftbraceunder: `M0 6l6-6h17c12.688 0 19.313.3 20 1 4 4 7.313 8.3 10 13 35.313 51.3 80.813 93.8 136.5 127.5 55.688 33.7 117.188 55.8 184.5 66.5.688 0 2 .3 4 1 18.688 2.7 76 4.3 172 5h399450v120H429l-6-1c-124.688-8-235-61.7 -331-161C60.687 138.7 32.312 99.3 7 54L0 41V6z`, overarc: `M529 0c179 0 524 115 524 115 5 1 9 5 9 10 0 1-1 2-1 3l-4 22c-1 5-5 9-11 9h-2s-338-93-512-92c-174 0-513 92-513 92h-2c-5 0-9-4-11-9l-5-22c-1-6 2-12 8-13 0 0 342-115 520-115z`, underarc: `m 529 160 c -179 0 -524 -115 -524 -115 c -5 -1 -9 -5 -9 -10 c 0 -1 1 -2 1 -3 l 4 -22 c 1 -5 5 -9 11 -9 h 2 s 338 93 512 92 c 174 0 513 -92 513 -92 h 2 c 5 0 9 4 11 9 l 5 22 c 1 6 -2 12 -8 13 c 0 0 -342 115 -520 115 z `, // Overgroup is from the MnSymbol package (public domain) leftgroup: `M400000 80 H435C64 80 168.3 229.4 21 260c-5.9 1.2-18 0-18 0-2 0-3-1-3-3v-38C76 61 257 0 435 0h399565z`, leftgroupunder: `M400000 262 H435C64 262 168.3 112.6 21 82c-5.9-1.2-18 0-18 0-2 0-3 1-3 3v38c76 158 257 219 435 219h399565z`, // Harpoons are from glyph U+21BD in font KaTeX Main leftharpoon: `M0 267c.7 5.3 3 10 7 14h399993v-40H93c3.3 -3.3 10.2-9.5 20.5-18.5s17.8-15.8 22.5-20.5c50.7-52 88-110.3 112-175 4-11.3 5 -18.3 3-21-1.3-4-7.3-6-18-6-8 0-13 .7-15 2s-4.7 6.7-8 16c-42 98.7-107.3 174.7 -196 228-6.7 4.7-10.7 8-12 10-1.3 2-2 5.7-2 11zm100-26v40h399900v-40z`, leftharpoonplus: `M0 267c.7 5.3 3 10 7 14h399993v-40H93c3.3-3.3 10.2-9.5 20.5-18.5s17.8-15.8 22.5-20.5c50.7-52 88-110.3 112-175 4-11.3 5-18.3 3-21-1.3 -4-7.3-6-18-6-8 0-13 .7-15 2s-4.7 6.7-8 16c-42 98.7-107.3 174.7-196 228-6.7 4.7 -10.7 8-12 10-1.3 2-2 5.7-2 11zm100-26v40h399900v-40zM0 435v40h400000v-40z m0 0v40h400000v-40z`, leftharpoondown: `M7 241c-4 4-6.333 8.667-7 14 0 5.333.667 9 2 11s5.333 5.333 12 10c90.667 54 156 130 196 228 3.333 10.667 6.333 16.333 9 17 2 .667 5 1 9 1h5c10.667 0 16.667-2 18-6 2-2.667 1-9.667-3-21-32-87.333-82.667-157.667 -152-211l-3-3h399907v-40zM93 281 H400000 v-40L7 241z`, leftharpoondownplus: `M7 435c-4 4-6.3 8.7-7 14 0 5.3.7 9 2 11s5.3 5.3 12 10c90.7 54 156 130 196 228 3.3 10.7 6.3 16.3 9 17 2 .7 5 1 9 1h5c10.7 0 16.7 -2 18-6 2-2.7 1-9.7-3-21-32-87.3-82.7-157.7-152-211l-3-3h399907v-40H7zm93 0 v40h399900v-40zM0 241v40h399900v-40zm0 0v40h399900v-40z`, // Hook is from glyph U+21A9 in font KaTeX Main lefthook: `M400000 281 H103s-33-11.2-61-33.5S0 197.3 0 164s14.2-61.2 42.5 -83.5C70.8 58.2 104 47 142 47 c16.7 0 25 6.7 25 20 0 12-8.7 18.7-26 20-40 3.3 -68.7 15.7-86 37-10 12-15 25.3-15 40 0 22.7 9.8 40.7 29.5 54 19.7 13.3 43.5 21 71.5 23h399859zM103 281v-40h399897v40z`, leftlinesegment: `M40 281 V428 H0 V94 H40 V241 H400000 v40z M40 281 V428 H0 V94 H40 V241 H400000 v40z`, leftmapsto: `M40 281 V448H0V74H40V241H400000v40z M40 281 V448H0V74H40V241H400000v40z`, // Tofrom is from glyph U+21C4 in font KaTeX AMS Regular leftToFrom: `M0 147h400000v40H0zm0 214c68 40 115.7 95.7 143 167h22c15.3 0 23 -.3 23-1 0-1.3-5.3-13.7-16-37-18-35.3-41.3-69-70-101l-7-8h399905v-40H95l7-8 c28.7-32 52-65.7 70-101 10.7-23.3 16-35.7 16-37 0-.7-7.7-1-23-1h-22C115.7 265.3 68 321 0 361zm0-174v-40h399900v40zm100 154v40h399900v-40z`, longequal: `M0 50 h400000 v40H0z m0 194h40000v40H0z M0 50 h400000 v40H0z m0 194h40000v40H0z`, midbrace: `M200428 334 c-100.7-8.3-195.3-44-280-108-55.3-42-101.7-93-139-153l-9-14c-2.7 4-5.7 8.7-9 14 -53.3 86.7-123.7 153-211 199-66.7 36-137.3 56.3-212 62H0V214h199568c178.3-11.7 311.7-78.3 403-201 6-8 9.7-12 11-12 .7-.7 6.7-1 18-1s17.3.3 18 1c1.3 0 5 4 11 12 44.7 59.3 101.3 106.3 170 141s145.3 54.3 229 60h199572v120z`, midbraceunder: `M199572 214 c100.7 8.3 195.3 44 280 108 55.3 42 101.7 93 139 153l9 14c2.7-4 5.7-8.7 9-14 53.3-86.7 123.7-153 211-199 66.7-36 137.3-56.3 212-62h199568v120H200432c-178.3 11.7-311.7 78.3-403 201-6 8-9.7 12-11 12-.7.7-6.7 1-18 1s-17.3-.3-18-1c-1.3 0 -5-4-11-12-44.7-59.3-101.3-106.3-170-141s-145.3-54.3-229-60H0V214z`, oiintSize1: `M512.6 71.6c272.6 0 320.3 106.8 320.3 178.2 0 70.8-47.7 177.6 -320.3 177.6S193.1 320.6 193.1 249.8c0-71.4 46.9-178.2 319.5-178.2z m368.1 178.2c0-86.4-60.9-215.4-368.1-215.4-306.4 0-367.3 129-367.3 215.4 0 85.8 60.9 214.8 367.3 214.8 307.2 0 368.1-129 368.1-214.8z`, oiintSize2: `M757.8 100.1c384.7 0 451.1 137.6 451.1 230 0 91.3-66.4 228.8 -451.1 228.8-386.3 0-452.7-137.5-452.7-228.8 0-92.4 66.4-230 452.7-230z m502.4 230c0-111.2-82.4-277.2-502.4-277.2s-504 166-504 277.2 c0 110 84 276 504 276s502.4-166 502.4-276z`, oiiintSize1: `M681.4 71.6c408.9 0 480.5 106.8 480.5 178.2 0 70.8-71.6 177.6 -480.5 177.6S202.1 320.6 202.1 249.8c0-71.4 70.5-178.2 479.3-178.2z m525.8 178.2c0-86.4-86.8-215.4-525.7-215.4-437.9 0-524.7 129-524.7 215.4 0 85.8 86.8 214.8 524.7 214.8 438.9 0 525.7-129 525.7-214.8z`, oiiintSize2: `M1021.2 53c603.6 0 707.8 165.8 707.8 277.2 0 110-104.2 275.8 -707.8 275.8-606 0-710.2-165.8-710.2-275.8C311 218.8 415.2 53 1021.2 53z m770.4 277.1c0-131.2-126.4-327.6-770.5-327.6S248.4 198.9 248.4 330.1 c0 130 128.8 326.4 772.7 326.4s770.5-196.4 770.5-326.4z`, rightarrow: `M0 241v40h399891c-47.3 35.3-84 78-110 128 -16.7 32-27.7 63.7-33 95 0 1.3-.2 2.7-.5 4-.3 1.3-.5 2.3-.5 3 0 7.3 6.7 11 20 11 8 0 13.2-.8 15.5-2.5 2.3-1.7 4.2-5.5 5.5-11.5 2-13.3 5.7-27 11-41 14.7-44.7 39-84.5 73-119.5s73.7-60.2 119-75.5c6-2 9-5.7 9-11s-3-9-9-11c-45.3-15.3-85 -40.5-119-75.5s-58.3-74.8-73-119.5c-4.7-14-8.3-27.3-11-40-1.3-6.7-3.2-10.8-5.5 -12.5-2.3-1.7-7.5-2.5-15.5-2.5-14 0-21 3.7-21 11 0 2 2 10.3 6 25 20.7 83.3 67 151.7 139 205zm0 0v40h399900v-40z`, rightbrace: `M400000 542l -6 6h-17c-12.7 0-19.3-.3-20-1-4-4-7.3-8.3-10-13-35.3-51.3-80.8-93.8-136.5-127.5 s-117.2-55.8-184.5-66.5c-.7 0-2-.3-4-1-18.7-2.7-76-4.3-172-5H0V214h399571l6 1 c124.7 8 235 61.7 331 161 31.3 33.3 59.7 72.7 85 118l7 13v35z`, rightbraceunder: `M399994 0l6 6v35l-6 11c-56 104-135.3 181.3-238 232-57.3 28.7-117 45-179 50H-300V214h399897c43.3-7 81-15 113-26 100.7-33 179.7-91 237 -174 2.7-5 6-9 10-13 .7-1 7.3-1 20-1h17z`, rightgroup: `M0 80h399565c371 0 266.7 149.4 414 180 5.9 1.2 18 0 18 0 2 0 3-1 3-3v-38c-76-158-257-219-435-219H0z`, rightgroupunder: `M0 262h399565c371 0 266.7-149.4 414-180 5.9-1.2 18 0 18 0 2 0 3 1 3 3v38c-76 158-257 219-435 219H0z`, rightharpoon: `M0 241v40h399993c4.7-4.7 7-9.3 7-14 0-9.3 -3.7-15.3-11-18-92.7-56.7-159-133.7-199-231-3.3-9.3-6-14.7-8-16-2-1.3-7-2-15-2 -10.7 0-16.7 2-18 6-2 2.7-1 9.7 3 21 15.3 42 36.7 81.8 64 119.5 27.3 37.7 58 69.2 92 94.5zm0 0v40h399900v-40z`, rightharpoonplus: `M0 241v40h399993c4.7-4.7 7-9.3 7-14 0-9.3-3.7-15.3-11 -18-92.7-56.7-159-133.7-199-231-3.3-9.3-6-14.7-8-16-2-1.3-7-2-15-2-10.7 0-16.7 2-18 6-2 2.7-1 9.7 3 21 15.3 42 36.7 81.8 64 119.5 27.3 37.7 58 69.2 92 94.5z m0 0v40h399900v-40z m100 194v40h399900v-40zm0 0v40h399900v-40z`, rightharpoondown: `M399747 511c0 7.3 6.7 11 20 11 8 0 13-.8 15-2.5s4.7-6.8 8-15.5c40-94 99.3-166.3 178-217 13.3-8 20.3-12.3 21-13 5.3-3.3 8.5-5.8 9.5 -7.5 1-1.7 1.5-5.2 1.5-10.5s-2.3-10.3-7-15H0v40h399908c-34 25.3-64.7 57-92 95 -27.3 38-48.7 77.7-64 119-3.3 8.7-5 14-5 16zM0 241v40h399900v-40z`, rightharpoondownplus: `M399747 705c0 7.3 6.7 11 20 11 8 0 13-.8 15-2.5s4.7-6.8 8-15.5c40-94 99.3-166.3 178-217 13.3-8 20.3-12.3 21-13 5.3-3.3 8.5-5.8 9.5-7.5 1-1.7 1.5-5.2 1.5-10.5s-2.3-10.3-7-15H0v40h399908c-34 25.3 -64.7 57-92 95-27.3 38-48.7 77.7-64 119-3.3 8.7-5 14-5 16zM0 435v40h399900v-40z m0-194v40h400000v-40zm0 0v40h400000v-40z`, righthook: `M399859 241c-764 0 0 0 0 0 40-3.3 68.7-15.7 86-37 10-12 15-25.3 15-40 0-22.7-9.8-40.7-29.5-54-19.7-13.3-43.5-21-71.5-23-17.3-1.3-26-8-26-20 0 -13.3 8.7-20 26-20 38 0 71 11.2 99 33.5 0 0 7 5.6 21 16.7 14 11.2 21 33.5 21 66.8s-14 61.2-42 83.5c-28 22.3-61 33.5-99 33.5L0 241z M0 281v-40h399859v40z`, rightlinesegment: `M399960 241 V94 h40 V428 h-40 V281 H0 v-40z M399960 241 V94 h40 V428 h-40 V281 H0 v-40z`, rightToFrom: `M400000 167c-70.7-42-118-97.7-142-167h-23c-15.3 0-23 .3-23 1 0 1.3 5.3 13.7 16 37 18 35.3 41.3 69 70 101l7 8H0v40h399905l-7 8c-28.7 32 -52 65.7-70 101-10.7 23.3-16 35.7-16 37 0 .7 7.7 1 23 1h23c24-69.3 71.3-125 142 -167z M100 147v40h399900v-40zM0 341v40h399900v-40z`, // Twoheadleftarrow is from glyph U+219E in font KaTeX AMS Regular twoheadleftarrow: `M0 167c68 40 115.7 95.7 143 167h22c15.3 0 23-.3 23-1 0-1.3-5.3-13.7-16-37-18-35.3-41.3-69 -70-101l-7-8h125l9 7c50.7 39.3 85 86 103 140h46c0-4.7-6.3-18.7-19-42-18-35.3 -40-67.3-66-96l-9-9h399716v-40H284l9-9c26-28.7 48-60.7 66-96 12.7-23.333 19 -37.333 19-42h-46c-18 54-52.3 100.7-103 140l-9 7H95l7-8c28.7-32 52-65.7 70-101 10.7-23.333 16-35.7 16-37 0-.7-7.7-1-23-1h-22C115.7 71.3 68 127 0 167z`, twoheadrightarrow: `M400000 167 c-68-40-115.7-95.7-143-167h-22c-15.3 0-23 .3-23 1 0 1.3 5.3 13.7 16 37 18 35.3 41.3 69 70 101l7 8h-125l-9-7c-50.7-39.3-85-86-103-140h-46c0 4.7 6.3 18.7 19 42 18 35.3 40 67.3 66 96l9 9H0v40h399716l-9 9c-26 28.7-48 60.7-66 96-12.7 23.333 -19 37.333-19 42h46c18-54 52.3-100.7 103-140l9-7h125l-7 8c-28.7 32-52 65.7-70 101-10.7 23.333-16 35.7-16 37 0 .7 7.7 1 23 1h22c27.3-71.3 75-127 143-167z`, // Tilde1 is a modified version of a glyph from the MnSymbol package widetilde1: `M200 55.538c-77 0-168 73.953-177 73.953-3 0-7 -2.175-9-5.437L2 97c-1-2-2-4-2-6 0-4 2-7 5-9l20-12C116 12 171 0 207 0c86 0 114 68 191 68 78 0 168-68 177-68 4 0 7 2 9 5l12 19c1 2.175 2 4.35 2 6.525 0 4.35-2 7.613-5 9.788l-19 13.05c-92 63.077-116.937 75.308-183 76.128 -68.267.847-113-73.952-191-73.952z`, // Ditto tilde2, tilde3, & tilde4 widetilde2: `M344 55.266c-142 0-300.638 81.316-311.5 86.418 -8.01 3.762-22.5 10.91-23.5 5.562L1 120c-1-2-1-3-1-4 0-5 3-9 8-10l18.4-9C160.9 31.9 283 0 358 0c148 0 188 122 331 122s314-97 326-97c4 0 8 2 10 7l7 21.114 c1 2.14 1 3.21 1 4.28 0 5.347-3 9.626-7 10.696l-22.3 12.622C852.6 158.372 751 181.476 676 181.476c-149 0-189-126.21-332-126.21z`, widetilde3: `M786 59C457 59 32 175.242 13 175.242c-6 0-10-3.457 -11-10.37L.15 138c-1-7 3-12 10-13l19.2-6.4C378.4 40.7 634.3 0 804.3 0c337 0 411.8 157 746.8 157 328 0 754-112 773-112 5 0 10 3 11 9l1 14.075c1 8.066-.697 16.595-6.697 17.492l-21.052 7.31c-367.9 98.146-609.15 122.696-778.15 122.696 -338 0-409-156.573-744-156.573z`, widetilde4: `M786 58C457 58 32 177.487 13 177.487c-6 0-10-3.345 -11-10.035L.15 143c-1-7 3-12 10-13l22-6.7C381.2 35 637.15 0 807.15 0c337 0 409 177 744 177 328 0 754-127 773-127 5 0 10 3 11 9l1 14.794c1 7.805-3 13.38-9 14.495l-20.7 5.574c-366.85 99.79-607.3 139.372-776.3 139.372-338 0-409 -175.236-744-175.236z`, // Vec is from glyph U+20D7 in font KaTeX Main vec: `M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5 3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11 10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63 -1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1 -7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59 H213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359 c-16-25.333-24-45-24-59z`, // Widehat1 is a modified version of a glyph from the MnSymbol package widehat1: `M529 0h5l519 115c5 1 9 5 9 10 0 1-1 2-1 3l-4 22 c-1 5-5 9-11 9h-2L532 67 19 159h-2c-5 0-9-4-11-9l-5-22c-1-6 2-12 8-13z`, // Ditto widehat2, widehat3, & widehat4 widehat2: `M1181 0h2l1171 176c6 0 10 5 10 11l-2 23c-1 6-5 10 -11 10h-1L1182 67 15 220h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z`, widehat3: `M1181 0h2l1171 236c6 0 10 5 10 11l-2 23c-1 6-5 10 -11 10h-1L1182 67 15 280h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z`, widehat4: `M1181 0h2l1171 296c6 0 10 5 10 11l-2 23c-1 6-5 10 -11 10h-1L1182 67 15 340h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z`, // Widecheck paths are all inverted versions of widehat widecheck1: `M529,159h5l519,-115c5,-1,9,-5,9,-10c0,-1,-1,-2,-1,-3l-4,-22c-1, -5,-5,-9,-11,-9h-2l-512,92l-513,-92h-2c-5,0,-9,4,-11,9l-5,22c-1,6,2,12,8,13z`, widecheck2: `M1181,220h2l1171,-176c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10, -11,-10h-1l-1168,153l-1167,-153h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z`, widecheck3: `M1181,280h2l1171,-236c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10, -11,-10h-1l-1168,213l-1167,-213h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z`, widecheck4: `M1181,340h2l1171,-296c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10, -11,-10h-1l-1168,273l-1167,-273h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z`, // The next ten paths support reaction arrows from the mhchem package. // Arrows for \ce{<-->} are offset from xAxis by 0.22ex, per mhchem in LaTeX // baraboveleftarrow is mostly from from glyph U+2190 in font KaTeX Main baraboveleftarrow: `M400000 620h-399890l3 -3c68.7 -52.7 113.7 -120 135 -202 c4 -14.7 6 -23 6 -25c0 -7.3 -7 -11 -21 -11c-8 0 -13.2 0.8 -15.5 2.5 c-2.3 1.7 -4.2 5.8 -5.5 12.5c-1.3 4.7 -2.7 10.3 -4 17c-12 48.7 -34.8 92 -68.5 130 s-74.2 66.3 -121.5 85c-10 4 -16 7.7 -18 11c0 8.7 6 14.3 18 17c47.3 18.7 87.8 47 121.5 85s56.5 81.3 68.5 130c0.7 2 1.3 5 2 9s1.2 6.7 1.5 8c0.3 1.3 1 3.3 2 6 s2.2 4.5 3.5 5.5c1.3 1 3.3 1.8 6 2.5s6 1 10 1c14 0 21 -3.7 21 -11 c0 -2 -2 -10.3 -6 -25c-20 -79.3 -65 -146.7 -135 -202l-3 -3h399890z M100 620v40h399900v-40z M0 241v40h399900v-40zM0 241v40h399900v-40z`, // Rightarrowabovebar is mostly from glyph U+2192, KaTeX Main rightarrowabovebar: `M0 241v40h399891c-47.3 35.3-84 78-110 128-16.7 32 -27.7 63.7-33 95 0 1.3-.2 2.7-.5 4-.3 1.3-.5 2.3-.5 3 0 7.3 6.7 11 20 11 8 0 13.2-.8 15.5-2.5 2.3-1.7 4.2-5.5 5.5-11.5 2-13.3 5.7-27 11-41 14.7-44.7 39 -84.5 73-119.5s73.7-60.2 119-75.5c6-2 9-5.7 9-11s-3-9-9-11c-45.3-15.3-85-40.5 -119-75.5s-58.3-74.8-73-119.5c-4.7-14-8.3-27.3-11-40-1.3-6.7-3.2-10.8-5.5 -12.5-2.3-1.7-7.5-2.5-15.5-2.5-14 0-21 3.7-21 11 0 2 2 10.3 6 25 20.7 83.3 67 151.7 139 205zm96 379h399894v40H0zm0 0h399904v40H0z`, // The short left harpoon has 0.5em (i.e. 500 units) kern on the left end. // Ref from mhchem.sty: \rlap{\raisebox{-.22ex}{$\kern0.5em baraboveshortleftharpoon: `M507,435c-4,4,-6.3,8.7,-7,14c0,5.3,0.7,9,2,11 c1.3,2,5.3,5.3,12,10c90.7,54,156,130,196,228c3.3,10.7,6.3,16.3,9,17 c2,0.7,5,1,9,1c0,0,5,0,5,0c10.7,0,16.7,-2,18,-6c2,-2.7,1,-9.7,-3,-21 c-32,-87.3,-82.7,-157.7,-152,-211c0,0,-3,-3,-3,-3l399351,0l0,-40 c-398570,0,-399437,0,-399437,0z M593 435 v40 H399500 v-40z M0 281 v-40 H399908 v40z M0 281 v-40 H399908 v40z`, rightharpoonaboveshortbar: `M0,241 l0,40c399126,0,399993,0,399993,0 c4.7,-4.7,7,-9.3,7,-14c0,-9.3,-3.7,-15.3,-11,-18c-92.7,-56.7,-159,-133.7,-199, -231c-3.3,-9.3,-6,-14.7,-8,-16c-2,-1.3,-7,-2,-15,-2c-10.7,0,-16.7,2,-18,6 c-2,2.7,-1,9.7,3,21c15.3,42,36.7,81.8,64,119.5c27.3,37.7,58,69.2,92,94.5z M0 241 v40 H399908 v-40z M0 475 v-40 H399500 v40z M0 475 v-40 H399500 v40z`, shortbaraboveleftharpoon: `M7,435c-4,4,-6.3,8.7,-7,14c0,5.3,0.7,9,2,11 c1.3,2,5.3,5.3,12,10c90.7,54,156,130,196,228c3.3,10.7,6.3,16.3,9,17c2,0.7,5,1,9, 1c0,0,5,0,5,0c10.7,0,16.7,-2,18,-6c2,-2.7,1,-9.7,-3,-21c-32,-87.3,-82.7,-157.7, -152,-211c0,0,-3,-3,-3,-3l399907,0l0,-40c-399126,0,-399993,0,-399993,0z M93 435 v40 H400000 v-40z M500 241 v40 H400000 v-40z M500 241 v40 H400000 v-40z`, shortrightharpoonabovebar: `M53,241l0,40c398570,0,399437,0,399437,0 c4.7,-4.7,7,-9.3,7,-14c0,-9.3,-3.7,-15.3,-11,-18c-92.7,-56.7,-159,-133.7,-199, -231c-3.3,-9.3,-6,-14.7,-8,-16c-2,-1.3,-7,-2,-15,-2c-10.7,0,-16.7,2,-18,6 c-2,2.7,-1,9.7,3,21c15.3,42,36.7,81.8,64,119.5c27.3,37.7,58,69.2,92,94.5z M500 241 v40 H399408 v-40z M500 435 v40 H400000 v-40z`, }; /** * Generate the HTML markup to represent a SVG box. * */ function svgBodyToMarkup(svgBodyName) { if (SVG_ACCENTS[svgBodyName]) { const [vbWidth, vbHeight, height] = SVG_ACCENTS[svgBodyName]; const result = `<span class="stretchy" style="height:${height}em">` + `<svg width="100%" height="${height}em" ` + `viewBox="0 0 ${vbWidth} ${vbHeight}" ` + `preserveAspectRatio="none" >` + `<path fill="currentcolor" d="${PATHS[svgBodyName]}"></path>` + `</svg></span>`; return `<span style="display:inline-block;height:${height / 2}em;min-width:0">${result}</span>`; } const [paths, minWidth, viewBoxHeight, align] = SVG_BODY[svgBodyName]; let widthClasses; let aligns; const height = viewBoxHeight / 1000; if (paths.length === 3) { widthClasses = ['slice-1-of-3', 'slice-2-of-3', 'slice-3-of-3']; aligns = ['xMinYMin', 'xMidYMin', 'xMaxYMin']; } else if (paths.length === 2) { widthClasses = ['slice-1-of-2', 'slice-2-of-2']; aligns = ['xMinYMin', 'xMaxYMin']; } else { widthClasses = ['slice-1-of-1']; aligns = [align]; } const body = paths .map((path, i) => `<span class="${widthClasses[i]}" style=height:${height}em>` + `<svg width=400em height=${height}em ` + `viewBox="0 0 400000 ${viewBoxHeight}" ` + `preserveAspectRatio="${aligns[i]} slice">` + `<path fill="currentcolor" d="${PATHS[path]}"></path>` + `</svg></span>`) .join(''); return `<span style="display:inline-block;height:${height}em;min-width:${minWidth}em;">${body}</span>`; } function svgBodyHeight(svgBodyName) { if (SVG_BODY[svgBodyName]) return SVG_BODY[svgBodyName][2] / 1000; return SVG_ACCENTS[svgBodyName][2]; } class Mode { constructor(name) { Mode._registry[name] = this; } static createAtom(mode, command, style) { return Mode._registry[mode].createAtom(command, style); } // `run` should be a run (sequence) of atoms all with the same // mode static serialize(run, options) { console.assert(run.length > 0); const mode = Mode._registry[run[0].mode]; return mode.serialize(run, options); } static applyStyle(mode, box, style) { return Mode._registry[mode].applyStyle(box, style); } } Mode._registry = {}; /* * Return an array of runs with the same mode */ function getModeRuns(atoms) { const result = []; let run = []; let currentMode = 'NONE'; for (const atom of atoms) { if (atom.type !== 'first') { if (atom.mode !== currentMode) { if (run.length > 0) result.push(run); run = [atom]; currentMode = atom.mode; } else run.push(atom); } } // Push whatever is left if (run.length > 0) result.push(run); return result; } /* * Return an array of runs (array of atoms with the same value * for the specified property) */ function getPropertyRuns(atoms, property) { const result = []; let run = []; let currentValue = undefined; for (const atom of atoms) { if (atom.type !== 'first') { let value; if (property === 'variant') { value = atom.style.variant; if (atom.style.variantStyle && atom.style.variantStyle !== 'up') value += '-' + atom.style.variantStyle; } else if (property === 'cssClass') { if (atom.type === 'group') value = atom.customClass; } else value = atom.style[property]; if (value === currentValue) { // Same value, add it to the current run run.push(atom); } else { // The value of property for this atom is different from the // current value, start a new run if (run.length > 0) result.push(run); run = [atom]; currentValue = value; } } } // Push whatever is left if (run.length > 0) result.push(run); return result; } /** * This module contains some color dictionaries and algorithms to * parse a string into a hex RGB color value.s */ /** * First 10 predefined colors used for plotting by Mathematica. * * Also known as _indexed color scheme #97_. */ const MATHEMATICA_COLORS = { m0: '#3F3D99', m1: '#993D71', m2: '#998B3D', m3: '#3D9956', m4: '#3D5A99', m5: '#993D90', m6: '#996D3D', m7: '#43993D', m8: '#3D7999', m9: '#843D99', // Mulberry }; // ColorData97 (Mathematica standard lines) // rgb(0.368417, 0.506779, 0.709798), #5e81b5 // rgb(0.880722, 0.611041, 0.142051), // rgb(0.560181, 0.691569, 0.194885), // rgb(0.922526, 0.385626, 0.209179), // rgb(0.528488, 0.470624, 0.701351), // rgb(0.772079, 0.431554, 0.102387), // rgb(0.363898, 0.618501, 0.782349), // rgb(1, 0.75, 0), // rgb(0.647624, 0.37816, 0.614037), // rgb(0.571589, 0.586483, 0.), // rgb(0.915, 0.3325, 0.2125), // rgb(0.40082222609352647, 0.5220066643438841, 0.85), // rgb(0.9728288904374106, 0.621644452187053, 0.07336199581899142), // rgb(0.736782672705901, 0.358, 0.5030266573755369), // rgb(0.28026441037696703, 0.715, 0.4292089322474965) /** * Matlab colors */ const MATLAB_COLORS = { blue: '#0072BD', orange: '#D95319', yellow: '#EDB120', purple: '#7E2F8E', green: '#77AC30', cyan: '#4DBEEE', red: '#A2142F', // [0.6350, 0.0780, 0.1840] dark red }; // Colors from Chromatic 100 design scale const BACKGROUND_COLORS = { 'red': '#fbbbb6', 'orange': '#ffe0c2', 'yellow': '#fff1c2', 'lime': '#d0e8b9', 'green': '#bceac4', 'teal': '#b9f1f1', 'blue': '#b6d9fb', 'indigo': '#d1c2f0', 'purple': '#e3baf8', 'magenta': '#f9c8e0', 'black': '#353535', 'dark-grey': '#8C8C8C', 'grey': '#D0D0D0', 'light-grey': '#F0F0F0', 'white': '#ffffff', }; // Colors from Chromatic 500 (and 600, 700) design scale const FOREGROUND_COLORS = { 'red': '#d7170b', 'orange': '#fe8a2b', 'yellow': '#ffc02b', 'lime': '#63b215', 'green': '#21ba3a', 'teal': '#17cfcf', 'blue': '#0d80f2', 'indigo': '#63c', 'purple': '#a219e6', 'magenta': '#eb4799', 'black': '#000', 'dark-grey': '#666', 'grey': '#A6A6A6', 'light-grey': '#d4d5d2', 'white': '#ffffff', }; // Map some of the DVIPS color names to Chromatic const DVIPS_TO_CHROMATIC = { Red: 'red', Orange: 'orange', Yellow: 'yellow', LimeGreen: 'lime', Green: 'green', TealBlue: 'teal', Blue: 'blue', Violet: 'indigo', Purple: 'purple', Magenta: 'magenta', Black: 'black', Gray: 'grey', White: 'white', }; /** * 68 colors (+ white) known to dvips used in LaTeX. * * The color names are based on the names of the _Crayola Crayon_ box of * 64 crayons. * * See: * - {@link https://ctan.org/pkg/colordvi | ColorDVI.tex} * - {@link https://en.wikibooks.org/w/index.php?title=LaTeX/Colors | Wikibooks:LaTeX/Colors} * * We use the Matlab colors for common colors by default. * */ const DVIPS_COLORS = { Apricot: '#FBB982', Aquamarine: '#00B5BE', Bittersweet: '#C04F17', Black: '#221E1F', Blue: '#2D2F92', BlueGreen: '#00B3B8', BlueViolet: '#473992', BrickRed: '#B6321C', Brown: '#792500', BurntOrange: '#F7921D', CadetBlue: '#74729A', CarnationPink: '#F282B4', Cerulean: '#00A2E3', CornflowerBlue: '#41B0E4', Cyan: '#00AEEF', Dandelion: '#FDBC42', DarkOrchid: '#A4538A', Emerald: '#00A99D', ForestGreen: '#009B55', Fuchsia: '#8C368C', Goldenrod: '#FFDF42', Gray: '#949698', Green: '#00A64F', GreenYellow: '#DFE674', JungleGreen: '#00A99A', Lavender: '#F49EC4', Limegreen: '#8DC73E', Magenta: '#EC008C', Mahogany: '#A9341F', Maroon: '#AF3235', Melon: '#F89E7B', MidnightBlue: '#006795', Mulberry: '#A93C93', NavyBlue: '#006EB8', OliveGreen: '#3C8031', Orange: '#F58137', OrangeRed: '#ED135A', Orchid: '#AF72B0', Peach: '#F7965A', Periwinkle: '#7977B8', PineGreen: '#008B72', Plum: '#92268F', ProcessBlue: '#00B0F0', Purple: '#99479B', RawSienna: '#974006', Red: '#ED1B23', RedOrange: '#F26035', RedViolet: '#A1246B', Rhodamine: '#EF559F', RoyalBlue: '#0071BC', RoyalPurple: '#613F99', RubineRed: '#ED017D', Salmon: '#F69289', SeaGreen: '#3FBC9D', Sepia: '#671800', SkyBlue: '#46C5DD', SpringGreen: '#C6DC67', Tan: '#DA9D76', TealBlue: '#00AEB3', Thistle: '#D883B7', Turquoise: '#00B4CE', Violet: '#58429B', VioletRed: '#EF58A0', White: '#FFFFFF', WildStrawberry: '#EE2967', Yellow: '#FFF200', YellowGreen: '#98CC70', YellowOrange: '#FAA21A', }; // Other color lists: SVG colors, x11 colors /* aliceblue rgb(240, 248, 255) antiquewhite rgb(250, 235, 215) aqua rgb( 0, 255, 255) aquamarine rgb(127, 255, 212) azure rgb(240, 255, 255) beige rgb(245, 245, 220) bisque rgb(255, 228, 196) black rgb( 0, 0, 0) blanchedalmond rgb(255, 235, 205) blue rgb( 0, 0, 255) blueviolet rgb(138, 43, 226) brown rgb(165, 42, 42) burlywood rgb(222, 184, 135) cadetblue rgb( 95, 158, 160) chartreuse rgb(127, 255, 0) chocolate rgb(210, 105, 30) coral rgb(255, 127, 80) cornflowerblue rgb(100, 149, 237) cornsilk rgb(255, 248, 220) crimson rgb(220, 20, 60) cyan rgb( 0, 255, 255) darkblue rgb( 0, 0, 139) darkcyan rgb( 0, 139, 139) darkgoldenrod rgb(184, 134, 11) darkgray rgb(169, 169, 169) darkgreen rgb( 0, 100, 0) darkgrey rgb(169, 169, 169) darkkhaki rgb(189, 183, 107) darkmagenta rgb(139, 0, 139) darkolivegreen rgb( 85, 107, 47) darkorange rgb(255, 140, 0) darkorchid rgb(153, 50, 204) darkred rgb(139, 0, 0) darksalmon rgb(233, 150, 122) darkseagreen rgb(143, 188, 143) darkslateblue rgb( 72, 61, 139) darkslategray rgb( 47, 79, 79) darkslategrey rgb( 47, 79, 79) darkturquoise rgb( 0, 206, 209) darkviolet rgb(148, 0, 211) deeppink rgb(255, 20, 147) deepskyblue rgb( 0, 191, 255) dimgray rgb(105, 105, 105) dimgrey rgb(105, 105, 105) dodgerblue rgb( 30, 144, 255) firebrick rgb(178, 34, 34) floralwhite rgb(255, 250, 240) forestgreen rgb( 34, 139, 34) fuchsia rgb(255, 0, 255) gainsboro rgb(220, 220, 220) ghostwhite rgb(248, 248, 255) gold rgb(255, 215, 0) goldenrod rgb(218, 165, 32) gray rgb(128, 128, 128) grey rgb(128, 128, 128) green rgb( 0, 128, 0) greenyellow rgb(173, 255, 47) honeydew rgb(240, 255, 240) hotpink rgb(255, 105, 180) indianred rgb(205, 92, 92) indigo rgb( 75, 0, 130) ivory rgb(255, 255, 240) khaki rgb(240, 230, 140) lavender rgb(230, 230, 250) lavenderblush rgb(255, 240, 245) lawngreen rgb(124, 252, 0) lemonchiffon rgb(255, 250, 205) lightblue rgb(173, 216, 230) lightcoral rgb(240, 128, 128) lightcyan rgb(224, 255, 255) lightgoldenrodyellow rgb(250, 250, 210) lightgray rgb(211, 211, 211) lightgreen rgb(144, 238, 144) lightgrey rgb(211, 211, 211) lightpink rgb(255, 182, 193) lightsalmon rgb(255, 160, 122) lightseagreen rgb( 32, 178, 170) lightskyblue rgb(135, 206, 250) lightslategray rgb(119, 136, 153) lightslategrey rgb(119, 136, 153) lightsteelblue rgb(176, 196, 222) lightyellow rgb(255, 255, 224) lime rgb( 0, 255, 0) limegreen rgb( 50, 205, 50) linen rgb(250, 240, 230) magenta rgb(255, 0, 255) maroon rgb(128, 0, 0) mediumaquamarine rgb(102, 205, 170) mediumblue rgb( 0, 0, 205) mediumorchid rgb(186, 85, 211) mediumpurple rgb(147, 112, 219) mediumseagreen rgb( 60, 179, 113) mediumslateblue rgb(123, 104, 238) mediumspringgreen rgb( 0, 250, 154) mediumturquoise rgb( 72, 209, 204) mediumvioletred rgb(199, 21, 133) midnightblue rgb( 25, 25, 112) mintcream rgb(245, 255, 250) mistyrose rgb(255, 228, 225) moccasin rgb(255, 228, 181) navajowhite rgb(255, 222, 173) navy rgb( 0, 0, 128) oldlace rgb(253, 245, 230) olive rgb(128, 128, 0) olivedrab rgb(107, 142, 35) orange rgb(255, 165, 0) orangered rgb(255, 69, 0) orchid rgb(218, 112, 214) palegoldenrod rgb(238, 232, 170) palegreen rgb(152, 251, 152) paleturquoise rgb(175, 238, 238) palevioletred rgb(219, 112, 147) papayawhip rgb(255, 239, 213) peachpuff rgb(255, 218, 185) peru rgb(205, 133, 63) pink rgb(255, 192, 203) plum rgb(221, 160, 221) powderblue rgb(176, 224, 230) purple rgb(128, 0, 128) red rgb(255, 0, 0) rosybrown rgb(188, 143, 143) royalblue rgb( 65, 105, 225) saddlebrown rgb(139, 69, 19) salmon rgb(250, 128, 114) sandybrown rgb(244, 164, 96) seagreen rgb( 46, 139, 87) seashell rgb(255, 245, 238) sienna rgb(160, 82, 45) silver rgb(192, 192, 192) skyblue rgb(135, 206, 235) slateblue rgb(106, 90, 205) slategray rgb(112, 128, 144) slategrey rgb(112, 128, 144) snow rgb(255, 250, 250) springgreen rgb( 0, 255, 127) steelblue rgb( 70, 130, 180) tan rgb(210, 180, 140) teal rgb( 0, 128, 128) thistle rgb(216, 191, 216) tomato rgb(255, 99, 71) turquoise rgb( 64, 224, 208) violet rgb(238, 130, 238) wheat rgb(245, 222, 179) white rgb(255, 255, 255) whitesmoke rgb(245, 245, 245) yellow rgb(255, 255, 0) yellowgreen rgb(154, 205, 50) */ /** * Return a CSS color (#rrggbb) from a string. * * Possible formats include: * - named colors from the DVI color set: 'Yellow', 'red'... Case sensitive. * - colors from the Mathematica set: 'M1'...'M9' * - 3-digit hex: `"#d50"` * - 6-digit hex: `"#dd5500"` * - RGB functional: `"rgb(240, 20, 10)"` * * In addition, colors can be mixed using the following syntax: * `<mix> = <color>![<value>][!<mix>]` * For example: * - `"Blue!20"` = 20% blue + 80% white * - `"Blue!20!Black"` = 20% + 80% black * - `"Blue!20!Black!30!Green"` = (20% + 80% black) * 30 % + 70% green * * If the input string is prefixed with a dash, the complementary color * of the expression is returned. * * This creative syntax is defined by the {@link http://mirror.jmu.edu/pub/CTAN/macros/latex/contrib/xcolor/xcolor.pdf | `xcolor` LaTeX package}. * * @param s - An expression representing a color value * @return An RGB color expressed as a hex-triplet preceded by `#` */ function defaultColorMap(s) { var _a, _b, _c, _d, _e, _f; const colorSpec = s.split('!'); let baseRed; let baseGreen; let baseBlue; let red = 255; let green = 255; let blue = 255; let mix = -1; // If the string is prefixed with a '-', use the complementary color const complementary = colorSpec.length > 0 && colorSpec[0].startsWith('-'); if (complementary) colorSpec[0] = colorSpec[0].slice(1); for (let i = 0; i < colorSpec.length; i++) { baseRed = red; baseGreen = green; baseBlue = blue; const colorName = (_a = colorSpec[i].trim().match(/^([A-Za-z\d]+)/)) === null || _a === void 0 ? void 0 : _a[1]; const lcColorName = colorName === null || colorName === void 0 ? void 0 : colorName.toLowerCase(); const color = !colorName ? colorSpec[i].trim() : (_f = (_e = (_d = (_c = (_b = FOREGROUND_COLORS[lcColorName]) !== null && _b !== void 0 ? _b : FOREGROUND_COLORS[DVIPS_TO_CHROMATIC[colorName]]) !== null && _c !== void 0 ? _c : MATLAB_COLORS[colorName]) !== null && _d !== void 0 ? _d : DVIPS_COLORS[colorName]) !== null && _e !== void 0 ? _e : MATHEMATICA_COLORS[colorName]) !== null && _f !== void 0 ? _f : colorSpec[i].trim(); let m = color.match(/^#([\da-f]{2})([\da-f]{2})([\da-f]{2})$/i); if ((m === null || m === void 0 ? void 0 : m[1]) && m[2] && m[3]) { // It's a six-digit hex number red = Math.max(0, Math.min(255, Number.parseInt(m[1], 16))); green = Math.max(0, Math.min(255, Number.parseInt(m[2], 16))); blue = Math.max(0, Math.min(255, Number.parseInt(m[3], 16))); } else { m = color.match(/^#([\da-f]{3})$/i); if (m === null || m === void 0 ? void 0 : m[1]) { // It's a three-digit hex number const r1 = Number.parseInt(m[1][0], 16); const g1 = Number.parseInt(m[1][1], 16); const b1 = Number.parseInt(m[1][2], 16); red = Math.max(0, Math.min(255, r1 * 16 + r1)); green = Math.max(0, Math.min(255, g1 * 16 + g1)); blue = Math.max(0, Math.min(255, b1 * 16 + b1)); } else { // It's a rgb functional m = color.match(/^rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i); if ((m === null || m === void 0 ? void 0 : m[1]) && m[2] && m[3]) { red = Math.max(0, Math.min(255, Number.parseInt(m[1]))); green = Math.max(0, Math.min(255, Number.parseInt(m[2]))); blue = Math.max(0, Math.min(255, Number.parseInt(m[3]))); } else return undefined; } } if (mix >= 0) { red = (1 - mix) * red + mix * baseRed; green = (1 - mix) * green + mix * baseGreen; blue = (1 - mix) * blue + mix * baseBlue; mix = -1; } if (i + 1 < colorSpec.length) mix = Math.max(0, Math.min(100, Number.parseInt(colorSpec[++i]))) / 100; } if (mix >= 0) { red = mix * red + (1 - mix) * baseRed; green = mix * green + (1 - mix) * baseGreen; blue = mix * blue + (1 - mix) * baseBlue; } if (complementary) { red = 255 - red; green = 255 - green; blue = 255 - blue; } return ('#' + ('00' + Math.round(red).toString(16)).slice(-2) + ('00' + Math.round(green).toString(16)).slice(-2) + ('00' + Math.round(blue).toString(16)).slice(-2)); } function defaultBackgroundColorMap(s) { var _a, _b; s = s.trim(); return ((_b = (_a = BACKGROUND_COLORS[s.toLowerCase()]) !== null && _a !== void 0 ? _a : BACKGROUND_COLORS[DVIPS_TO_CHROMATIC[s]]) !== null && _b !== void 0 ? _b : defaultColorMap(s)); } function parseHex(hex) { if (!hex) return undefined; if (hex[0] !== '#') return undefined; hex = hex.slice(1); let result; if (hex.length <= 4) { result = { r: parseInt(hex[0] + hex[0], 16), g: parseInt(hex[1] + hex[1], 16), b: parseInt(hex[2] + hex[2], 16), }; if (hex.length === 4) result.a = parseInt(hex[3] + hex[3], 16) / 255; } else { result = { r: parseInt(hex[0] + hex[1], 16), g: parseInt(hex[2] + hex[3], 16), b: parseInt(hex[4] + hex[5], 16), }; if (hex.length === 8) result.a = parseInt(hex[6] + hex[7], 16) / 255; } if (result && result.a === undefined) result.a = 1.0; return result; } function hueToRgbChannel(t1, t2, hue) { if (hue < 0) hue += 6; if (hue >= 6) hue -= 6; if (hue < 1) return (t2 - t1) * hue + t1; else if (hue < 3) return t2; else if (hue < 4) return (t2 - t1) * (4 - hue) + t1; return t1; } function hslToRgb(hsl) { let [hue, sat, light] = [hsl.h, hsl.s, hsl.l]; hue = ((hue + 360) % 360) / 60.0; light = Math.max(0, Math.min(light, 1.0)); sat = Math.max(0, Math.min(sat, 1.0)); const t2 = light <= 0.5 ? light * (sat + 1) : light + sat - light * sat; const t1 = light * 2 - t2; return { r: Math.round(255 * hueToRgbChannel(t1, t2, hue + 2)), g: Math.round(255 * hueToRgbChannel(t1, t2, hue)), b: Math.round(255 * hueToRgbChannel(t1, t2, hue - 2)), }; } function clampByte(v) { if (v < 0) return 0; if (v > 255) return 255; return Math.round(v); } function rgbToHexstring(rgb) { const { r, g, b } = rgb; let hexString = ((1 << 24) + (clampByte(r) << 16) + (clampByte(g) << 8) + clampByte(b)) .toString(16) .slice(1); if (hexString[0] === hexString[1] && hexString[2] === hexString[3] && hexString[4] === hexString[5] && hexString[6] === hexString[7]) hexString = hexString[0] + hexString[2] + hexString[4]; return '#' + hexString; } function rgbToHsl(rgb) { let { r, g, b } = rgb; r = r / 255; g = g / 255; b = b / 255; const min = Math.min(r, g, b); const max = Math.max(r, g, b); const delta = max - min; let h; let s; if (max === min) h = 0; else if (r === max) h = (g - b) / delta; else if (g === max) h = 2 + (b - r) / delta; else if (b === max) h = 4 + (r - g) / delta; h = Math.min(h * 60, 360); if (h < 0) h += 360; const l = (min + max) / 2; if (max === min) s = 0; else if (l <= 0.5) s = delta / (max + min); else s = delta / (2 - max - min); return { h: h, s: s, l: l }; } function highlight(color) { // eslint-disable-next-line prefer-const let rgb = parseHex(color); if (!rgb) return color; // eslint-disable-next-line prefer-const let { h, s, l } = rgbToHsl(rgb); s += 0.1; l -= 0.1; return rgbToHexstring(hslToRgb({ h, s, l })); } /* * See https://tex.stackexchange.com/questions/81752/ * for a thorough description of the TeX atom type and their relevance to * proper kerning. * * See TeXBook p. 158 for a list of the "atom types" * Note: we are not using the following types: 'over', 'under', 'acc', 'rad', * 'vcent' */ const BOX_TYPE = [ '', 'chem', 'mord', 'mbin', 'mop', 'mrel', 'mopen', 'mclose', 'mpunct', 'minner', 'spacing', 'first', 'latex', 'composition', 'error', 'placeholder', 'supsub', 'none', 'mathfield', ]; // The const assertion prevents widening to string[] function isBoxType(type) { return BOX_TYPE.includes(type); } /* * See http://www.tug.org/TUGboat/tb30-3/tb96vieth.pdf for * typesetting conventions for mathematical physics (units, etc...) */ /** * TeXBook, p. 170 * * > In fact, TEX’s rules for spacing in formulas are fairly simple. A formula is * > converted to a math list as described at the end of Chapter 17, and the math * > list consists chiefly of “atoms” of eight basic types: Ord (ordinary), * > Op (large operator), Bin (binary operation), Rel (relation), Open (opening), * > Close (closing), Punct (punctuation), and Inner (a delimited subformula). * > Other kinds of atoms, which arise from commands like \overline or * > \mathaccent or \vcenter, etc., are all treated as type Ord; fractions are * > treated as type Inner. * * > The following table is used to determine the spacing between pair of adjacent * > atoms. * * In this table * - "3" = `\thinmuskip` * - "4" = `\medmuskip` * - "5" = `\thickmuskip` * */ const INTER_ATOM_SPACING = { mord: { mop: 3, mbin: 4, mrel: 5, minner: 3 }, mop: { mord: 3, mop: 3, rel: 5, minner: 3 }, mbin: { mord: 4, mop: 4, mopen: 4, minner: 4 }, mrel: { mord: 5, mop: 5, mopen: 5, minner: 5 }, mclose: { mop: 3, mbin: 4, mrel: 5, minner: 3 }, mpunct: { mord: 3, mop: 3, mrel: 3, mopen: 3, mpunct: 3, minner: 3 }, minner: { mord: 3, mop: 3, mbin: 4, mrel: 5, mopen: 3, mpunct: 3, minner: 3 }, }; /** * This table is used when the mathstyle is 'tight' (scriptstyle or * scriptscriptstyle). */ const INTER_ATOM_TIGHT_SPACING = { mord: { mop: 3 }, mop: { mord: 3, mop: 3 }, mclose: { mop: 3 }, minner: { mop: 3 }, }; function toString$1(arg1, arg2) { if (typeof arg1 === 'string') return arg1; if (typeof arg1 === 'number') { console.assert(Number.isFinite(arg1)); const numValue = Math.ceil(1e2 * arg1) / 1e2; if (numValue === 0) return '0'; return numValue.toString() + (arg2 !== null && arg2 !== void 0 ? arg2 : ''); } return ''; } //---------------------------------------------------------------------------- // BOX //---------------------------------------------------------------------------- /** * A box is the most elementary element that can be rendered. * It is composed of an optional body of text and an optional list * of children (other boxes). Each box can be decorated with * CSS classes and style attributes. * * @param content the items 'contained' by this node * @param classes list of classes attributes associated with this node * @property type - For example, `"latex"`, `"mrel"`, etc... * @property classes - A string of space separated CSS classes * associated with this element * @property cssId - A CSS ID assigned to this box (optional) * @property htmlData - data fields assigned to this box (optional) * @property children - An array, potentially empty, of boxes which * this box encloses * @property cssProperties - A set of key/value pairs specifying CSS properties * associated with this element. * @property height - The measurement from baseline to top, in em. * @property depth - The measurement from baseline to bottom, in em. */ class Box { constructor(content, options) { var _a, _b, _c, _d, _e, _f; if (typeof content === 'number') this.value = String.fromCodePoint(content); else if (typeof content === 'string') this.value = content; else if (isArray(content)) this.children = content.filter((x) => x !== null); else if (content && content instanceof Box) this.children = [content]; this.type = (_a = options === null || options === void 0 ? void 0 : options.type) !== null && _a !== void 0 ? _a : ''; this.isSelected = false; this.isTight = (_b = options === null || options === void 0 ? void 0 : options.isTight) !== null && _b !== void 0 ? _b : false; this.newList = (_c = options === null || options === void 0 ? void 0 : options.newList) !== null && _c !== void 0 ? _c : false; // CSS style, as a set of key value pairs. // Use `Box.setStyle()` to modify it. if (options === null || options === void 0 ? void 0 : options.properties) { for (const prop of Object.keys(options.properties)) this.setStyle(prop, options.properties[prop]); } if (options === null || options === void 0 ? void 0 : options.attributes) this.attributes = options.attributes; // Set initial classes this.classes = (_d = options === null || options === void 0 ? void 0 : options.classes) !== null && _d !== void 0 ? _d : ''; // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing let fontName = (options === null || options === void 0 ? void 0 : options.fontFamily) || 'Main-Regular'; if ((options === null || options === void 0 ? void 0 : options.style) && this.value) { fontName = // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing Mode.applyStyle((_e = options.mode) !== null && _e !== void 0 ? _e : 'math', this, options.style) || 'Main-Regular'; } this.height = 0; this.depth = 0; this.skew = 0; this.italic = 0; this.maxFontSize = 0; // // Calculate the dimensions of this box // if (this.type === 'latex') { // // Fixed width (and height) characters from "latex mode" // this.height = 0.8; this.depth = 0.2; } else if (typeof content === 'number') { // // A codepoint, as used by delimiters // const metrics = getCharacterMetrics(content, fontName); this.height = metrics.height; this.depth = metrics.depth; this.skew = metrics.skew; this.italic = metrics.italic; } else if (this.value) { // // A regular symbol // // Get the metrics information this.height = -Infinity; this.depth = -Infinity; this.skew = -Infinity; this.italic = -Infinity; // @revisit: when this.value has more than one char it can be for // a string like "cos", but sometimes it can be a multi-code-point grapheme for (let i = 0; i < this.value.length; i++) { const metrics = getCharacterMetrics(this.value.codePointAt(i), fontName || 'Main-Regular'); this.height = Math.max(this.height, metrics.height); this.depth = Math.max(this.depth, metrics.depth); this.skew = metrics.skew; this.italic = metrics.italic; } } else if (this.children && this.children.length > 0) { // // A sequence of boxes // if (this.children.length === 1) { // // A single child: inherit its metrics // const child = this.children[0]; this.height = child.height; this.depth = child.depth; this.maxFontSize = child.maxFontSize; this.skew = child.skew; this.italic = child.italic; } else { // // More than one child, assume they are being laid out horizontally // (we'll override the height/depth later if that wasn't the case) // let height = -Infinity; let depth = -Infinity; let maxFontSize = 0; for (const child of this.children) { if (child.height > height) height = child.height; if (child.depth > depth) depth = child.depth; maxFontSize = Math.max(maxFontSize, (_f = child.maxFontSize) !== null && _f !== void 0 ? _f : 0); } this.height = height; this.depth = depth; this.maxFontSize = maxFontSize; } } // // If a height/depth override was provided, use it. // if ((options === null || options === void 0 ? void 0 : options.height) !== undefined) this.height = options.height; if ((options === null || options === void 0 ? void 0 : options.depth) !== undefined) this.depth = options.depth; if ((options === null || options === void 0 ? void 0 : options.maxFontSize) !== undefined) this.maxFontSize = options.maxFontSize; } set atomID(id) { if (id === undefined || id.length === 0) return; if (!this.attributes) this.attributes = {}; this.attributes['data-atom-id'] = id; } selected(isSelected) { if (this.isSelected === isSelected) return; this.isSelected = isSelected; if (this.children) for (const child of this.children) child.selected(isSelected); } setStyle(prop, value, unit) { // console.assert( // prop !== 'height' || typeof value !== 'number' || value >= 0 // ); if (value === undefined) return; const v = toString$1(value, unit); if (v.length > 0) { if (!this.cssProperties) this.cssProperties = {}; this.cssProperties[prop] = v; } } setTop(top) { if (Number.isFinite(top) && Math.abs(top) > 1e-2) { if (!this.cssProperties) this.cssProperties = {}; this.cssProperties.top = toString$1(top, 'em'); this.height -= top; this.depth += top; } } get left() { var _a; if ((_a = this.cssProperties) === null || _a === void 0 ? void 0 : _a['margin-left']) return Number.parseFloat(this.cssProperties['margin-left']); return 0; } set left(value) { if (!Number.isFinite(value)) return; if (value === 0) { if (this.cssProperties) delete this.cssProperties['margin-left']; } else { if (!this.cssProperties) this.cssProperties = {}; this.cssProperties['margin-left'] = toString$1(value, 'em'); } } set right(value) { if (!Number.isFinite(value)) return; if (value === 0) { if (this.cssProperties) delete this.cssProperties['margin-right']; } else { if (!this.cssProperties) this.cssProperties = {}; this.cssProperties['margin-right'] = toString$1(value, 'em'); } } set width(value) { if (!Number.isFinite(value)) return; if (value === 0) { if (this.cssProperties) delete this.cssProperties.width; } else { if (!this.cssProperties) this.cssProperties = {}; this.cssProperties.width = toString$1(value, 'em'); } } /** * If necessary wrap this box with another one that adjust the font-size * to account for a change in size between the context and its parent. * Also, apply color and background-color attributes. */ wrap(context, options) { const parent = context.parent; // If we're at the root, nothing to do if (!parent) return this; if (context.isPhantom) this.setStyle('opacity', 0); let newColor = context.computedColor; if (newColor === parent.computedColor) newColor = ''; // // Apply color changes to the box // this.setStyle('color', newColor); const newSize = context.effectiveFontSize === parent.effectiveFontSize ? undefined : context.effectiveFontSize; let newBackgroundColor = context.computedBackgroundColor; if (this.isSelected) newBackgroundColor = highlight(newBackgroundColor); if (newBackgroundColor === parent.computedBackgroundColor) newBackgroundColor = ''; // // Wrap the box if necessary. // // Note that when the size changes, the font-size should be applied to // the wrapper, not to the nucleus, otherwise the size of the element // (which is used to calculate the selection rectangle)is incorrect // if (!newSize && !newBackgroundColor && !(options && (options.classes || options.type))) return this; let result; if (newBackgroundColor) { result = makeStruts(this, options); result.selected(this.isSelected); result.setStyle('background-color', newBackgroundColor); result.setStyle('display', 'inline-block'); } else result = new Box(this, options); // // Adjust the dimensions to account for the size variations // const factor = context.scalingFactor; if (factor !== 1.0) { result.setStyle('font-size', factor * 100, '%'); result.height *= factor; result.depth *= factor; result.italic *= factor; result.skew *= factor; } return result; } /** If necessary, wrap this box in another that accounts for * selected backgroundColor */ wrapSelect(context) { if (!this.isSelected) return this; const parent = context.parent; // If we're at the root, nothing to do if (!parent) return this; const newBackgroundColor = highlight(context.computedBackgroundColor); const result = makeStruts(this); result.selected(true); result.setStyle('background-color', newBackgroundColor); result.setStyle('display', 'inline-block'); return result; } /** * Generate the HTML markup to represent this box. */ toMarkup() { var _a, _b, _c; let body = (_a = this.value) !== null && _a !== void 0 ? _a : ''; // // 1. Render the children // if (this.children) for (const box of this.children) body += box.toMarkup(); // // 2. Calculate the classes associated with this box // const classes = this.classes.split(' '); classes.push((_b = { latex: 'ML__latex', placeholder: 'ML__placeholder', error: 'ML__error', }[this.type]) !== null && _b !== void 0 ? _b : ''); if (this.caret === 'latex') classes.push('ML__latex-caret'); // Remove duplicate and empty classes const classList = classes.length === 1 ? classes[0] : classes .filter((x, e, a) => x.length > 0 && a.indexOf(x) === e) .join(' '); // // 3. Markup for props and SVG // let result = ''; if ((body.length > 0 && body !== '\u200B') || classList.length > 0 || this.cssId || this.htmlData || this.htmlStyle || this.attributes || this.cssProperties || this.svgBody || this.svgOverlay) { let props = ''; if (this.cssId) { // A (HTML5) CSS id may not contain a space props += ` id=${this.cssId.replace(/ /g, '-')} `; } if (this.htmlData) { const entries = this.htmlData.split(','); for (const entry of entries) { const matched = entry.match(/([^=]+)=(.+$)/); if (matched) { const key = matched[1].trim().replace(/ /g, '-'); if (key) props += ` data-${key}=${matched[2]} `; } else { const key = entry.trim().replace(/ /g, '-'); if (key) props += ` data-${key} `; } } } if (this.htmlStyle) { const entries = this.htmlStyle.split(';'); let styleString = ''; for (const entry of entries) { const matched = entry.match(/([^=]+):(.+$)/); if (matched) { const key = matched[1].trim().replace(/ /g, '-'); if (key) styleString += `${key}:${matched[2]};`; } } if (styleString) props += ` style="${styleString}"`; } if (this.attributes) { props += ' ' + Object.keys(this.attributes) .map((x) => `${x}="${this.attributes[x]}"`) .join(' '); } if (classList.length > 0) props += ` class="${classList}"`; if (this.cssProperties) { const styleString = Object.keys(this.cssProperties) .map((x) => `${x}:${this.cssProperties[x]}`) .join(';'); if (styleString.length > 0) props += ` style="${styleString}"`; } // // If there is some SVG markup associated with this box, // include it now // let svgMarkup = ''; if (this.svgBody) svgMarkup = svgBodyToMarkup(this.svgBody); else if (this.svgOverlay) { svgMarkup = '<span style="'; svgMarkup += 'display: inline-block;'; svgMarkup += `height:${this.height + this.depth}em;`; svgMarkup += `vertical-align:${this.depth}em;`; svgMarkup += '">'; svgMarkup += body; svgMarkup += '</span>'; svgMarkup += '<svg style="position:absolute;overflow:overlay;'; svgMarkup += `height:${this.height + this.depth}em;`; if ((_c = this.cssProperties) === null || _c === void 0 ? void 0 : _c.padding) { svgMarkup += `top:${this.cssProperties.padding}em;`; svgMarkup += `left:${this.cssProperties.padding}em;`; svgMarkup += `width:calc(100% - 2 * ${this.cssProperties.padding}em );`; } else svgMarkup += 'top:0;left:0;width:100%;'; svgMarkup += 'z-index:2;'; svgMarkup += '"'; if (this.svgStyle) svgMarkup += ` style="${this.svgStyle}"`; svgMarkup += `>${this.svgOverlay}</svg>`; } // Note: We can't omit the tag, even if it has no props, // as some layouts (vlist) depends on the presence of the tag to function result = `<span${props}>${body}${svgMarkup}</span>`; } // // 4. Add markup for the caret // if (this.caret === 'text') result += '<span class="ML__text-caret"></span>'; else if (this.caret === 'math') result += '<span class="ML__caret"></span>'; return result; } /** * Can this box be coalesced with 'box'? * This is used to 'coalesce' (i.e. group together) a series of boxes that are * identical except for their value, and to avoid generating redundant boxes. * That is: '12' -> * "<span class='crm'>12</span>" * rather than: * "<span class='crm'>1</span><span class='crm'>2</span>" */ tryCoalesceWith(box) { // Don't coalesce if the types are different if (this.type !== box.type) return false; // Only coalesce some types if (!/ML__text/.test(this.classes) && !['mord', 'mbin', 'mrel'].includes(this.type)) return false; // Don't coalesce if some of the content is SVG if (this.svgBody || !this.value) return false; if (box.svgBody || !box.value) return false; // If this box or the candidate box have children, we can't // coalesce them, but we'll try to coalesce their children const hasChildren = this.children && this.children.length > 0; const boxHasChildren = box.children && box.children.length > 0; if (hasChildren || boxHasChildren) return false; // If they have a different number of styles, can't coalesce const thisStyleCount = this.cssProperties ? Object.keys(this.cssProperties).length : 0; const boxStyleCount = box.cssProperties ? Object.keys(box.cssProperties).length : 0; if (thisStyleCount !== boxStyleCount) return false; // If the styles are different, can't coalesce if (thisStyleCount > 0) { for (const prop of Object.keys(this.cssProperties)) if (this.cssProperties[prop] !== box.cssProperties[prop]) return false; } // For the purpose of our comparison, // any 'empty' classes (whitespace) const classes = this.classes.trim().replace(/\s+/g, ' ').split(' '); const boxClasses = box.classes.trim().replace(/\s+/g, ' ').split(' '); // If they have a different number of classes, can't coalesce if (classes.length !== boxClasses.length) return false; // OK, let's do the more expensive comparison now. // If they have different classes, can't coalesce classes.sort(); boxClasses.sort(); for (const [i, class_] of classes.entries()) { // Don't coalesce vertical separators // (used in column formating with {l||r} for example if (class_ === 'vertical-separator') return false; if (class_ !== boxClasses[i]) return false; } // OK, the attributes of those boxes are compatible. // Merge box into this this.value += box.value; this.height = Math.max(this.height, box.height); this.depth = Math.max(this.depth, box.depth); this.maxFontSize = Math.max(this.maxFontSize, box.maxFontSize); // The italic correction for the coalesced boxes is the // italic correction of the last box. this.italic = box.italic; return true; } } /** * Attempts to coalesce (merge) boxes, for example consecutive text boxes. * Return a new tree with coalesced boxes. * */ function coalesceRecursive(boxes) { if (!boxes || boxes.length === 0) return []; boxes[0].children = coalesceRecursive(boxes[0].children); const result = [boxes[0]]; for (let i = 1; i < boxes.length; i++) { if (!result[result.length - 1].tryCoalesceWith(boxes[i])) { boxes[i].children = coalesceRecursive(boxes[i].children); result.push(boxes[i]); } } return result; } function coalesce(box) { if (box.children) box.children = coalesceRecursive(box.children); return box; } /** * Handle proper spacing of, e.g. "-4" vs "1-4", by adjusting some box type */ function adjustType(root) { forEachBox(root, (prevBox, box) => { // TexBook p. 442: // > 5. If the current item is a Bin atom, and if this was the first atom in the // > list, or if the most recent previous atom was Bin, Op, Rel, Open, or // > Punct, change the current Bin to Ord and continue with Rule 14. // > Otherwise continue with Rule 17. if (box.type === 'mbin' && (!prevBox || /first|none|mbin|mop|mrel|mopen|mpunct/.test(prevBox.type))) box.type = 'mord'; // > 6. If the current item is a Rel or Close or Punct atom, and if the most // > recent previous atom was Bin, change that previous Bin to Ord. Continue // > with Rule 17. if (prevBox && prevBox.type === 'mbin' && /mrel|mclose|mpunct|placeholder/.test(box.type)) prevBox.type = 'mord'; }); } // // Adjust the atom(/box) types according to the TeX rules // function applyInterAtomSpacing(root, scale) { forEachBox(root, (prevBox, box) => { var _a, _b, _c, _d; const prevType = (_a = prevBox === null || prevBox === void 0 ? void 0 : prevBox.type) !== null && _a !== void 0 ? _a : 'none'; const table = box.isTight ? (_b = INTER_ATOM_TIGHT_SPACING[prevType]) !== null && _b !== void 0 ? _b : null : (_c = INTER_ATOM_SPACING[prevType]) !== null && _c !== void 0 ? _c : null; const hskip = table ? (_d = table[box.type]) !== null && _d !== void 0 ? _d : 0 : 0; if (hskip) box.left += scale * (hskip / 18); }); } /* * Iterate over each box, mimicking the TeX atom list walking logic * used to demote bin atoms to ord. * * Our boxes don't map one to one with atoms, since we may include * "construction" boxes that should be ignored. This function takes care * of that. * */ function forEachBoxRecursive(prevBox, box, f) { // The TeX algorithms scan each elements, and consider them to be part // of the same list of atoms, until they reach some branch points (superscript, // numerator,etc..). The boxes that indicate the start of a new list have // the `newList` property set. if (box.newList) prevBox = null; const type = box.type; if (type === 'first') { console.assert(box.newList === true); return null; } // Skip over first and spacing atoms if (type === 'spacing') return prevBox; f(prevBox, box); if (box.children) { let childPrev = null; if (type === undefined || type.length === 0) childPrev = prevBox; for (const child of box.children) childPrev = forEachBoxRecursive(childPrev, child, f); if (type === undefined || type.length === 0) prevBox = childPrev; } if (type !== 'supsub' && type !== undefined && type.length > 0) prevBox = box; return prevBox; } function forEachBox(box, f) { if (!box) return; forEachBoxRecursive(null, box, f); } function adjustInterAtomSpacing(root, scale = 1.0) { adjustType(root); applyInterAtomSpacing(root, scale); return root; } // function spanToString(span: Span, indent = 0): string { // let result = '\n' + ' '.repeat(indent * 2); // if (span.value !== undefined) { // result += `"${span.svgBody ?? span.value}"`; // } // result += ` ${span.type ?? '????'} ${toString(span.height)} / ${toString( // span.depth // )} / ${span.maxFontSize}`; // if (span.children) { // for (const child of span.children) { // result += spanToString(child, indent + 1); // } // } // return result; // } //---------------------------------------------------------------------------- // UTILITY FUNCTIONS //---------------------------------------------------------------------------- function makeStruts(content, options) { if (!content) return new Box(null, options); const topStrut = new Box(null, { classes: 'ML__strut' }); topStrut.setStyle('height', Math.max(0, content.height), 'em'); const struts = [topStrut]; if (content.depth !== 0) { const bottomStrut = new Box(null, { classes: 'ML__strut--bottom' }); bottomStrut.setStyle('height', content.height + content.depth, 'em'); bottomStrut.setStyle('vertical-align', -content.depth, 'em'); struts.push(bottomStrut); } struts.push(content); return new Box(struts, options); } /** * Add some SVG markup to be overlaid on top of the box */ function addSVGOverlay(body, svgMarkup, svgStyle) { body.svgOverlay = svgMarkup; body.svgStyle = svgStyle; return body; } /** * Create a box that consist of a (stretchy) SVG element */ function makeSVGBox(svgBodyName) { const height = svgBodyHeight(svgBodyName) / 2; const box = new Box(null, { height: height + 0.166, depth: height - 0.166, maxFontSize: 0, }); box.svgBody = svgBodyName; return box; } // Computes the updated `children` list and the overall depth. function getVListChildrenAndDepth(params) { if ('individualShift' in params) { const oldChildren = params.individualShift; let prevChild = oldChildren[0]; const children = [prevChild]; // Add in kerns to the list of params.children to get each element to be // shifted to the correct specified shift const depth = -prevChild.shift - prevChild.box.depth; let currPos = depth; for (let i = 1; i < oldChildren.length; i++) { const child = oldChildren[i]; const diff = -child.shift - currPos - child.box.depth; const size = diff - (prevChild.box.height + prevChild.box.depth); currPos = currPos + diff; children.push(size); children.push(child); prevChild = child; } return [children, depth]; } if ('top' in params) { // We always start at the bottom, so calculate the bottom by adding up // all the sizes let bottom = params.top; for (const child of params.children) { bottom -= typeof child === 'number' ? child : child.box.height + child.box.depth; } return [params.children, bottom]; } else if ('bottom' in params) return [params.children, -params.bottom]; else if ('firstBaseline' in params) { const firstChild = params.firstBaseline[0]; if (typeof firstChild === 'number') throw new Error('First child must be an element.'); return [params.firstBaseline, -firstChild.box.depth]; } else if ('shift' in params) { const firstChild = params.children[0]; if (typeof firstChild === 'number') throw new Error('First child must be an element.'); return [params.children, -firstChild.box.depth - params.shift]; } return [null, 0]; } /** * Makes a vertical list by stacking elements and kerns on top of each other. * Allows for many different ways of specifying the positioning method. * * See VListParam documentation above. * * Return a single row if the stack is entirely above the baseline. * Otherwise return 2 rows, the second one representing depth below the baseline. * This is necessary to workaround a Safari... behavior (see vlist-s and vlist-t2) */ function makeRows(params) { var _a; const [children, depth] = getVListChildrenAndDepth(params); if (!children) return [[], 0, 0]; // Create a strut that is taller than any list item. The strut is added to // each item, where it will determine the item's baseline. Since it has // `overflow:hidden`, the strut's top edge will sit on the item's line box's // top edge and the strut's bottom edge will sit on the item's baseline, // with no additional line-height spacing. This allows the item baseline to // be positioned precisely without worrying about font ascent and // line-height. let pstrutSize = 0; for (const child of children) { if (typeof child !== 'number') { const box = child.box; pstrutSize = Math.max(pstrutSize, box.maxFontSize, box.height); } } pstrutSize += 2; const pstrut = new Box(null, { classes: 'pstrut' }); pstrut.setStyle('height', pstrutSize, 'em'); // Create a new list of actual children at the correct offsets const realChildren = []; let minPos = depth; let maxPos = depth; let currPos = depth; for (const child of children) { if (typeof child === 'number') currPos += child; else { const box = child.box; const classes = (_a = child.classes) !== null && _a !== void 0 ? _a : []; const childWrap = new Box([pstrut, box], { classes: classes.join(' '), style: child.style, }); childWrap.setStyle('top', -pstrutSize - currPos - box.depth, 'em'); if (child.marginLeft) childWrap.setStyle('margin-left', child.marginLeft, 'em'); if (child.marginRight) childWrap.setStyle('margin-right', child.marginRight, 'em'); realChildren.push(childWrap); currPos += box.height + box.depth; } minPos = Math.min(minPos, currPos); maxPos = Math.max(maxPos, currPos); } // The vlist contents go in a table-cell with `vertical-align:bottom`. // This cell's bottom edge will determine the containing table's baseline // without overly expanding the containing line-box. const vlist = new Box(realChildren, { classes: 'vlist' }); vlist.setStyle('height', maxPos, 'em'); // A second row is used if necessary to represent the vlist's depth. let rows; if (minPos < 0) { // We will define depth in an empty box with display: table-cell. // It should render with the height that we define. But Chrome, in // contenteditable mode only, treats that box as if it contains some // text content. And that min-height over-rides our desired height. // So we put another empty box inside the depth strut box. const depthStrut = new Box(new Box(null), { classes: 'vlist' }); depthStrut.setStyle('height', -minPos, 'em'); // Safari wants the first row to have inline content; otherwise it // puts the bottom of the *second* row on the baseline. const topStrut = new Box(0x200b, { classes: 'vlist-s', maxFontSize: 0, height: 0, depth: 0, }); rows = [ new Box([vlist, topStrut], { classes: 'vlist-r' }), new Box(depthStrut, { classes: 'vlist-r' }), ]; } else rows = [new Box(vlist, { classes: 'vlist-r' })]; return [rows, maxPos, -minPos]; } class VBox extends Box { constructor(content, options) { var _a; const [rows, height, depth] = makeRows(content); super(rows.length === 1 ? rows[0] : rows, { classes: ((_a = options === null || options === void 0 ? void 0 : options.classes) !== null && _a !== void 0 ? _a : '') + ' vlist-t' + (rows.length === 2 ? ' vlist-t2' : ''), height, depth, type: options === null || options === void 0 ? void 0 : options.type, }); } } /* Combine a nucleus with an atom above and an atom below. Used to form * limits. * * @param context * @param nucleus The base over and under which the atoms will * be placed. * @param nucleusShift The vertical shift of the nucleus from * the baseline. * @param slant For operators that have a slant, such as \int, * indicate by how much to horizontally offset the above and below atoms */ function makeLimitsStack(context, options) { var _a, _b, _c, _d, _e; // If nothing above and nothing below, nothing to do. // if (!options.above && !options.below) { // return new Span(options.base, { type: options.boxType ?? 'mop' }).wrap( // context // ); // // return options.base; // } const metrics = context.metrics; // IE8 clips \int if it is in a display: inline-block. We wrap it // in a new box so it is an inline, and works. // @todo: revisit const base = new Box(options.base); const baseShift = (_a = options.baseShift) !== null && _a !== void 0 ? _a : 0; const slant = (_b = options.slant) !== null && _b !== void 0 ? _b : 0; let aboveShift = 0; let belowShift = 0; if (options.above) { aboveShift = (_c = options.aboveShift) !== null && _c !== void 0 ? _c : Math.max(metrics.bigOpSpacing1, metrics.bigOpSpacing3 - options.above.depth); } if (options.below) { belowShift = (_d = options.belowShift) !== null && _d !== void 0 ? _d : Math.max(metrics.bigOpSpacing2, metrics.bigOpSpacing4 - options.below.height); } let result = null; if (options.below && options.above) { const bottom = metrics.bigOpSpacing5 + options.below.height + options.below.depth + belowShift + base.depth + baseShift; // Here, we shift the limits by the slant of the symbol. Note // that we are supposed to shift the limits by 1/2 of the slant, // but since we are centering the limits adding a full slant of // margin will shift by 1/2 that. result = new VBox({ bottom, children: [ metrics.bigOpSpacing5, { box: options.below, marginLeft: -slant, classes: ['ML__center'], }, belowShift, // We need to center the base to account for the case where the // above/below is wider { box: base, classes: ['ML__center'] }, aboveShift, { box: options.above, marginLeft: slant, classes: ['ML__center'], }, metrics.bigOpSpacing5, ], }).wrap(context); } else if (options.below && !options.above) { result = new VBox({ top: base.height - baseShift, children: [ metrics.bigOpSpacing5, { box: options.below, marginLeft: -slant, classes: ['ML__center'], }, belowShift, { box: base, classes: ['ML__center'] }, ], }).wrap(context); } else if (!options.below && options.above) { const bottom = base.depth + baseShift; result = new VBox({ bottom, children: [ { box: base, classes: ['ML__center'] }, aboveShift, { box: options.above, marginLeft: slant, classes: ['ML__center'], }, metrics.bigOpSpacing5, ], }).wrap(context); } else { const bottom = base.depth + baseShift; result = new VBox({ bottom, children: [{ box: base }, metrics.bigOpSpacing5], }).wrap(context); } console.assert(options.type !== undefined); return new Box(result, { type: (_e = options.type) !== null && _e !== void 0 ? _e : 'mop' }); } function stringToCodepoints(string) { const result = []; for (let i = 0; i < string.length; i++) { let code = string.charCodeAt(i); if (code === 0x0d && string.charCodeAt(i + 1) === 0x0a) { code = 0x0a; i++; } if (code === 0x0d || code === 0x0c) code = 0x0a; if (code === 0x00) code = 0xfffd; // Decode a surrogate pair into an astral codepoint. if (code >= 0xd800 && code <= 0xdbff) { const nextCode = string.charCodeAt(i + 1); if (nextCode >= 0xdc00 && nextCode <= 0xdfff) { const lead = code - 0xd800; const trail = nextCode - 0xdc00; code = 2 ** 16 + lead * 2 ** 10 + trail; // N = ((H - 0xD800) * 0x400) + (L - 0xDC00) + 0x10000; i++; } } result.push(code); } return result; } const ZWJ = 0x200d; // Zero-width joiner // const ZWSP = 0x200b; // Zero-width space /* The following codepoints should combine with the previous ones */ const EMOJI_COMBINATOR = [ [ZWJ, 1], [0xfe0e, 2], [0x1f3fb, 5], [0x1f9b0, 4], [0xe0020, 96], // EMOJI_TAG ]; let emojiCombinator; // Regional indicator: a pair of codepoints indicating some flags const REGIONAL_INDICATOR = [0x1f1e6, 0x1f1ff]; function isEmojiCombinator(code) { var _a; if (emojiCombinator === undefined) { emojiCombinator = {}; for (const x of EMOJI_COMBINATOR) for (let i = x[0]; i <= x[0] + x[1] - 1; i++) emojiCombinator[i] = true; } return (_a = emojiCombinator[code]) !== null && _a !== void 0 ? _a : false; } function isRegionalIndicator(code) { return code >= REGIONAL_INDICATOR[0] && code <= REGIONAL_INDICATOR[1]; } /** * Return a string or an array of graphemes. * This includes: * - emoji with skin and hair modifiers * - emoji combination (for example "female pilot") * - text emoji with an emoji presentation style modifier * - U+1F512 U+FE0E 🔒︎ * - U+1F512 U+FE0F 🔒️ * - flags represented as two regional indicator codepoints * - flags represented as a flag emoji + zwj + an emoji tag * - other combinations (for example, rainbow flag) */ function splitGraphemes(string) { // If it's all ASCII, short-circuit the grapheme splitting... if (/^[\u0020-\u00FF]*$/.test(string)) return string; const result = []; const codePoints = stringToCodepoints(string); let index = 0; while (index < codePoints.length) { const code = codePoints[index++]; const next = codePoints[index]; // Combine sequences if (next === ZWJ) { // Zero-width joiner sequences are: // ZWJ_SEQUENCE := (CHAR + ZWJ)+ const baseIndex = index - 1; index += 2; while (codePoints[index] === ZWJ) index += 2; result.push(String.fromCodePoint(...codePoints.slice(baseIndex, index - baseIndex + 1))); } else if (isEmojiCombinator(next)) { // Combine emoji sequences // See http://unicode.org/reports/tr51/#def_emoji_tag_sequence const baseIndex = index - 1; // The previous character is the 'base' while (isEmojiCombinator(codePoints[index])) index += codePoints[index] === ZWJ ? 2 : 1; result.push(String.fromCodePoint(...codePoints.slice(baseIndex, 2 * index - baseIndex - 1))); } else if (isRegionalIndicator(code)) { // Some (but not all) flags are represented by a sequence of two // "regional indicators" codepoints. index += 1; result.push(String.fromCodePoint(...codePoints.slice(index - 2, 2))); } else result.push(String.fromCodePoint(code)); } return result; } /** * ## Reference * TeX source code: * {@link http://tug.org/texlive/devsrc/Build/source/texk/web2c/tex.web | Tex.web} * */ /** * Given a LaTeX expression represented as a character string, * the Lexer class will scan and return Tokens for the lexical * units in the string. * * @param s A string of LaTeX */ class Tokenizer { constructor(s) { this.s = splitGraphemes(s); this.pos = 0; this.obeyspaces = false; } /** * @return True if we reached the end of the stream */ end() { return this.pos >= this.s.length; } /** * Return the next char and advance */ get() { return this.pos < this.s.length ? this.s[this.pos++] : ''; } /** * Return the next char, but do not advance */ peek() { return this.s[this.pos]; } /** * Return the next substring matching regEx and advance. */ match(regEx) { // This.s can either be a string, if it's made up only of ASCII chars // or an array of graphemes, if it's more complicated. const execResult = typeof this.s === 'string' ? regEx.exec(this.s.slice(this.pos)) : regEx.exec(this.s.slice(this.pos).join('')); if (execResult === null || execResult === void 0 ? void 0 : execResult[0]) { this.pos += execResult[0].length; return execResult[0]; } return ''; } /** * Return the next token, or null. */ next() { // If we've reached the end, exit if (this.end()) return null; // Handle white space // In text mode, spaces are significant, // however they are coalesced unless \obeyspaces if (!this.obeyspaces && this.match(/^[ \f\n\r\t\v\u00A0\u2028\u2029]+/)) { // Note that browsers are inconsistent in their definitions of the // `\s` metacharacter, so we use an explicit pattern instead. // - IE: `[ \f\n\r\t\v]` // - Chrome: `[ \f\n\r\t\v\u00A0]` // - Firefox: `[ \f\n\r\t\v\u00A0\u2028\u2029]` // - \f \u000C: form feed (FORM FEED) // - \n \u000A: linefeed (LINE FEED) // - \r \u000D: carriage return // - \t \u0009: tab (CHARACTER TABULATION) // - \v \u000B: vertical tab (LINE TABULATION) // - \u00A0: NON-BREAKING SPACE // - \u2028: LINE SEPARATOR // - \u2029: PARAGRAPH SEPARATOR return '<space>'; } if (this.obeyspaces && this.match(/^[ \f\n\r\t\v\u00A0\u2028\u2029]/)) { // Don't coalesce when this.obeyspaces is true (different regex from above) return '<space>'; } const next = this.get(); // Is it a command? if (next === '\\') { if (!this.end()) { // A command is either a string of letters and asterisks... let command = this.match(/^[a-zA-Z\*]+/); if (command) { // Spaces after a 'control word' are ignored // (but not after a 'control symbol' (single char) this.match(/^[ \f\n\r\t\v\u00A0\u2028\u2029]*/); } else { // ... or a single non-letter character command = this.get(); if (command === ' ') { // The `\ ` command is equivalent to a single space return '<space>'; } } return '\\' + command; } } else if (next === '{') { // This is a group start return '<{>'; } else if (next === '}') { // This is a group end return '<}>'; } else if (next === '^') { if (this.peek() === '^') { // It might be a ^^ command (inline hex character) this.get(); // There can be zero to six carets with the same number of hex digits const hex = this.match(/^(\^(\^(\^(\^[\da-f])?[\da-f])?[\da-f])?[\da-f])?[\da-f]{2}/); if (hex) { return String.fromCodePoint(Number.parseInt(hex.slice(hex.lastIndexOf('^') + 1), 16)); } } return next; } else if (next === '#') { // This could be either a param token, or a literal # (used for // colorspecs, for example). A param token is a '#' followed by // - a digit 0-9 followed by a non-alpha, non-digit // - or '?' (to indicate a placeholder) // - or '@' (to indicate an implicit, optional, argument) // Otherwise, it's a literal '#'. if (!this.end()) { let isParameter = false; if (/[\d?@]/.test(this.peek())) { // Could be a param isParameter = true; // Need to look ahead to the following char // (to exclude, e.g. '#1c1b2d': it's not a '#' token, it's a color) if (this.pos + 1 < this.s.length) { const after = this.s[this.pos + 1]; isParameter = /[^\dA-Za-z]/.test(after); } } if (isParameter) return '#' + this.get(); return '#'; } } else if (next === '$') { // Mode switch if (this.peek() === '$') { // $$ this.get(); return '<$$>'; } // $ return '<$>'; } return next; } } // Some primitive commands need to be handled in the expansion phase // (the 'gullet') function expand(lex, args) { var _a, _b, _c, _d; const result = []; let token = lex.next(); if (token) { if (token === '\\relax') ; else if (token === '\\noexpand') { // Do not expand the next token token = lex.next(); if (token) result.push(token); } else if (token === '\\obeyspaces') lex.obeyspaces = true; else if (token === '\\space' || token === '~') { // The `\space` command is equivalent to a single space // The ~ is an 'active character' (a single character macro) // that maps to <space> result.push('<space>'); } else if (token === '\\bgroup') { // Begin group, synonym for opening brace result.push('<{>'); } else if (token === '\\egroup') { // End group, synonym for closing brace result.push('<}>'); } else if (token === '\\string') { // Turn the next token into a string token = lex.next(); if (token) { if (token.startsWith('\\')) for (const x of token) result.push(x === '\\' ? '\\backslash' : x); else if (token === '<{>') result.push('\\{'); else if (token === '<space>') result.push('~'); else if (token === '<}>') result.push('\\}'); } } else if (token === '\\csname') { // Turn the next tokens, until `\endcsname`, into a command while (lex.peek() === '<space>') lex.next(); let command = ''; let done = false; let tokens = []; do { if (tokens.length === 0) { // We're out of tokens to look at, get some more if (/^#[\d?@]$/.test(lex.peek())) { // Expand parameters (but not commands) const parameter = lex.get().slice(1); tokens = tokenize((_b = (_a = args === null || args === void 0 ? void 0 : args(parameter)) !== null && _a !== void 0 ? _a : args === null || args === void 0 ? void 0 : args('?')) !== null && _b !== void 0 ? _b : '\\placeholder{}', args); token = tokens[0]; } else { token = lex.next(); tokens = token ? [token] : []; } } done = tokens.length === 0; if (!done && token === '\\endcsname') { done = true; tokens.shift(); } if (!done) { done = token === '<$>' || token === '<$$>' || token === '<{>' || token === '<}>' || (typeof token === 'string' && token.length > 1 && token.startsWith('\\')); } if (!done) command += tokens.shift(); } while (!done); if (command) result.push('\\' + command); result.push(...tokens); } else if (token === '\\endcsname') ; else if (token.length > 1 && token.startsWith('#')) { // It's a parameter to expand const parameter = token.slice(1); result.push(...tokenize((_d = (_c = args === null || args === void 0 ? void 0 : args(parameter)) !== null && _c !== void 0 ? _c : args === null || args === void 0 ? void 0 : args('?')) !== null && _d !== void 0 ? _d : '\\placeholder{}', args)); } else result.push(token); } return result; } /** * Create Tokens from a stream of LaTeX * * @param s - A string of LaTeX. It can include comments (with the `%` * marker) and multiple lines. */ function tokenize(s, args = null) { // Merge multiple lines into one, and remove comments const stream = []; let sep = ''; for (const line of s.toString().split(/\r?\n/)) { if (sep) stream.push(sep); sep = ' '; // Remove everything after a % (comment marker) // (but \% should be preserved...) const m = line.match(/((?:\\%)|[^%])*/); if (m !== null) stream.push(m[0]); } const tokenizer = new Tokenizer(stream.join('')); const result = []; do result.push(...expand(tokenizer, args)); while (!tokenizer.end()); return result; } function joinLatex(segments) { let sep = ''; const result = []; for (const segment of segments) { if (segment) { // If the segment begins with a char that *could* be in a command // name... insert a separator (if one was needed for the previous segment) if (/[a-zA-Z\*]/.test(segment[0])) result.push(sep); result.push(segment); if (/\\[a-zA-Z]+\*?[\"\'][^\ ]+$/.test(segment)) result.push(' '); // If the segment ends in a command... sep = /\\[a-zA-Z]+\*?$/.test(segment) ? ' ' : ''; } } return result.join(''); } function tokensToString(tokens) { return joinLatex(tokens.map((token) => { var _a; return ((_a = { '<space>': ' ', '<$$>': '$$', '<$>': '$', '<{>': '{', '<}>': '}', }[token]) !== null && _a !== void 0 ? _a : token); })); } // Return true if this is a browser environment, false if this is // a server side environment (node.js) or web worker. function isBrowser() { return 'window' in globalThis && 'document' in globalThis; } function throwIfNotInBrowser() { if (!isBrowser()) { throw new Error('<math-field> is an interactive component that needs to run in a browser environment\n' + 'If you are using nextjs, see https://nextjs.org/docs/advanced-features/dynamic-import#with-no-ssr'); } } function isTouchCapable() { var _a, _b; return ((_b = (isBrowser() && ((_a = window.matchMedia) === null || _a === void 0 ? void 0 : _a.call(window, '(any-pointer: coarse)').matches))) !== null && _b !== void 0 ? _b : false); } function canVibrate() { return isBrowser() && typeof navigator.vibrate === 'function'; } function osPlatform() { var _a, _b; if (!isBrowser()) return 'other'; const platform = (_b = (_a = navigator['userAgentData']) === null || _a === void 0 ? void 0 : _a.platform) !== null && _b !== void 0 ? _b : navigator.platform; if (/^mac/i.test(platform)) { // WebKit on iPad OS 14 looks like macOS. // Use the number of touch points to distinguish between macOS and iPad OS if (navigator.maxTouchPoints === 5) return 'ios'; return 'macos'; } if (/^win/i.test(platform)) return 'windows'; if (/android/i.test(navigator.userAgent)) return 'android'; if (/iphone|ipod|ipad/i.test(navigator.userAgent)) return 'ios'; if (/\bcros\b/i.test(navigator.userAgent)) return 'chromeos'; return 'other'; } function supportRegexPropertyEscape() { if (!isBrowser()) return true; if (/firefox/i.test(navigator.userAgent)) { const m = navigator.userAgent.match(/firefox\/(\d+)/i); if (!m) return false; const version = parseInt(m[1]); return version >= 78; // https://www.mozilla.org/en-US/firefox/78.0/releasenotes/ } if (/trident/i.test(navigator.userAgent)) return false; if (/edge/i.test(navigator.userAgent)) { const m = navigator.userAgent.match(/edg\/(\d+)/i); if (!m) return false; const version = parseInt(m[1]); return version >= 79; } return true; } const MATH_SYMBOLS = {}; // Map a character to some corresponding LaTeX. // // This is used for some characters such as ² SUPERSCRIPT TWO. // This is also an opportunity to specify the prefered form when // a unicode character is encountered that maps to multiple commands, // for example ≠ could map either to \ne or \neq. // The table will also be populated by any registered symbol from MATH_SYMBOLS, // so an explicit entry is only needed in case of ambiguous mappings. // // prettier-ignore const REVERSE_MATH_SYMBOLS = { 0x003C: '<', 0x003E: '>', 0x006F: 'o', 0x0026: '\\&', 0x007B: '\\lbrace', 0x007D: '\\rbrace', 0x005B: '\\lbrack', 0x005D: '\\rbrack', 0x003A: '\\colon', 0x00A0: '~', 0x00AC: '\\neg', 0x00B7: '\\cdot', 0x00BC: '\\frac{1}{4}', 0x00BD: '\\frac{1}{2}', 0x00BE: '\\frac{3}{4}', 0x2070: '^{0}', 0x2071: '^{i}', 0x00B9: '^{1}', 0x00B2: '^{2}', 0x00B3: '^{3}', 0x2020: '\\dagger', 0x2021: '\\ddagger', 0x2026: '\\ldots', 0x2074: '^{4}', 0x2075: '^{5}', 0x2076: '^{6}', 0x2077: '^{7}', 0x2078: '^{8}', 0x2079: '^{9}', 0x207A: '^{+}', 0x207B: '^{-}', 0x207C: '^{=}', 0x207F: '^{n}', 0x2080: '_{0}', 0x2081: '_{1}', 0x2082: '_{2}', 0x2083: '_{3}', 0x2084: '_{4}', 0x2085: '_{5}', 0x2086: '_{6}', 0x2087: '_{7}', 0x2088: '_{8}', 0x2089: '_{9}', 0x208A: '_{+}', 0x208B: '_{-}', 0x208C: '_{=}', 0x2090: '_{a}', 0x2091: '_{e}', 0x2092: '_{o}', 0x2093: '_{x}', 0x2032: '\\prime', 0x0027: '\\prime', 0x2190: '\\gets', 0x2192: '\\to', 0x25B3: '\\triangle', 0x25BD: '\\triangledown', 0x220B: '\\owns', 0x2217: '\\ast', 0x2223: '\\vert', 0x2225: '\\Vert', 0x2227: '\\land', 0x2228: '\\lor', 0x22C5: '\\cdot', 0x22C8: '\\bowtie', 0x2260: '\\ne', 0x2264: '\\le', 0x2265: '\\ge', 0x22A5: '\\bot', 0x27F7: '\\biconditional', 0x27F8: '\\impliedby', 0x27F9: '\\implies', 0x27fa: '\\iff', 0x2102: '\\mathbb{C}', 0x2115: '\\mathbb{N}', 0x2119: '\\mathbb{P}', 0x211A: '\\mathbb{Q}', 0x211D: '\\mathbb{R}', 0x2124: '\\mathbb{Z}', // Also \doubleStruckCapitalZ }; const LATEX_COMMANDS = {}; const ENVIRONMENTS = {}; const TEXVC_MACROS = { ////////////////////////////////////////////////////////////////////// // texvc.sty // The texvc package contains macros available in mediawiki pages. // We omit the functions deprecated at // https://en.wikipedia.org/wiki/Help:Displaying_a_formula#Deprecated_syntax // We also omit texvc's \O, which conflicts with \text{\O} darr: '\\downarrow', dArr: '\\Downarrow', Darr: '\\Downarrow', lang: '\\langle', rang: '\\rangle', uarr: '\\uparrow', uArr: '\\Uparrow', Uarr: '\\Uparrow', N: '\\mathbb{N}', R: '\\mathbb{R}', Z: '\\mathbb{Z}', alef: '\\aleph', alefsym: '\\aleph', Alpha: '\\mathrm{A}', Beta: '\\mathrm{B}', bull: '\\bullet', Chi: '\\mathrm{X}', clubs: '\\clubsuit', cnums: '\\mathbb{C}', Complex: '\\mathbb{C}', Dagger: '\\ddagger', diamonds: '\\diamondsuit', empty: '\\emptyset', Epsilon: '\\mathrm{E}', Eta: '\\mathrm{H}', exist: '\\exists', harr: '\\leftrightarrow', hArr: '\\Leftrightarrow', Harr: '\\Leftrightarrow', hearts: '\\heartsuit', image: '\\Im', infin: '\\infty', Iota: '\\mathrm{I}', isin: '\\in', Kappa: '\\mathrm{K}', larr: '\\leftarrow', lArr: '\\Leftarrow', Larr: '\\Leftarrow', lrarr: '\\leftrightarrow', lrArr: '\\Leftrightarrow', Lrarr: '\\Leftrightarrow', Mu: '\\mathrm{M}', natnums: '\\mathbb{N}', Nu: '\\mathrm{N}', Omicron: '\\mathrm{O}', plusmn: '\\pm', rarr: '\\rightarrow', rArr: '\\Rightarrow', Rarr: '\\Rightarrow', real: '\\Re', reals: '\\mathbb{R}', Reals: '\\mathbb{R}', Rho: '\\mathrm{P}', sdot: '\\cdot', sect: '\\S', spades: '\\spadesuit', sub: '\\subset', sube: '\\subseteq', supe: '\\supseteq', Tau: '\\mathrm{T}', thetasym: '\\vartheta', // TODO: varcoppa: { def: "\\\mbox{\\coppa}", expand: false }, weierp: '\\wp', Zeta: '\\mathrm{Z}', }; const AMSMATH_MACROS = { // amsmath.sty // http://mirrors.concertpass.com/tex-archive/macros/latex/required/amsmath/amsmath.pdf // Italic Greek capital letters. AMS defines these with \DeclareMathSymbol, // but they are equivalent to \mathit{\Letter}. varGamma: '\\mathit{\\Gamma}', varDelta: '\\mathit{\\Delta}', varTheta: '\\mathit{\\Theta}', varLambda: '\\mathit{\\Lambda}', varXi: '\\mathit{\\Xi}', varPi: '\\mathit{\\Pi}', varSigma: '\\mathit{\\Sigma}', varUpsilon: '\\mathit{\\Upsilon}', varPhi: '\\mathit{\\Phi}', varPsi: '\\mathit{\\Psi}', varOmega: '\\mathit{\\Omega}', // From http://tug.ctan.org/macros/latex/required/amsmath/amsmath.dtx // > \newcommand{\pod}[1]{ // > \allowbreak // > \if@display // > \mkern18mu // > \else // > \mkern8mu // > \fi // > (#1) // > } // 18mu = \quad // > \renewcommand{\pmod}[1]{ // > \pod{{\operator@font mod}\mkern6mu#1} // > } pmod: { def: '\\quad(\\operatorname{mod}\\ #1)', args: 1, expand: false, }, // > \newcommand{\mod}[1]{ // > \allowbreak // > \if@display // > \mkern18mu // > \else // > \mkern12mu // > \fi //> {\operator@font mod}\,\,#1} mod: { def: '\\quad\\operatorname{mod}\\,\\,#1', args: 1, expand: false, }, // > \renewcommand{\bmod}{ // > \nonscript\mskip-\medmuskip\mkern5mu // > \mathbin{\operator@font mod} // > \penalty900 \mkern5mu // > \nonscript\mskip-\medmuskip // > } // 5mu = \; bmod: { def: '\\;\\mathbin{\\operatorname{mod }}', expand: false, }, }; // From `braket.sty`, Dirac notation const BRAKET_MACROS = { bra: '\\mathinner{\\langle{#1}|}', ket: '\\mathinner{|{#1}\\rangle}', braket: '\\mathinner{\\langle{#1}\\rangle}', set: '\\mathinner{\\lbrace #1 \\rbrace}', Bra: '\\left\\langle #1\\right|', Ket: '\\left|#1\\right\\rangle', Braket: '\\left\\langle{#1}\\right\\rangle', Set: '\\left\\lbrace #1 \\right\\rbrace', }; const DEFAULT_MACROS = { 'iff': '\\;\u27FA\\;', 'nicefrac': '^{#1}\\!\\!/\\!_{#2}', // Proof Wiki 'rd': '\\mathrm{d}', 'rD': '\\mathrm{D}', // From Wolfram Alpha 'doubleStruckCapitalN': '\\mathbb{N}', 'doubleStruckCapitalR': '\\mathbb{R}', 'doubleStruckCapitalQ': '\\mathbb{Q}', 'doubleStruckCapitalZ': '\\mathbb{Z}', 'doubleStruckCapitalP': '\\mathbb{P}', 'scriptCapitalE': '\\mathscr{E}', 'scriptCapitalH': '\\mathscr{H}', 'scriptCapitalL': '\\mathscr{L}', 'gothicCapitalC': '\\mathfrak{C}', 'gothicCapitalH': '\\mathfrak{H}', 'gothicCapitalI': '\\mathfrak{I}', 'gothicCapitalR': '\\mathfrak{R}', 'imaginaryI': '\\mathrm{i}', 'imaginaryJ': '\\mathrm{j}', 'exponentialE': '\\mathrm{e}', 'differentialD': '\\mathrm{d}', 'capitalDifferentialD': '\\mathrm{D}', 'braket.sty': { package: BRAKET_MACROS }, 'amsmath.sty': { package: AMSMATH_MACROS, expand: false, }, 'texvc.sty': { package: TEXVC_MACROS, expand: false, }, }; // Body-text symbols // See http://ctan.mirrors.hoobly.com/info/symbols/comprehensive/symbols-a4.pdf, p14 const TEXT_SYMBOLS = { ' ': 0x0020, '\\#': 0x0023, '\\&': 0x0026, '\\$': 0x0024, '\\%': 0x0025, '\\_': 0x005f, '\\euro': 0x20ac, '\\maltese': 0x2720, '\\{': 0x007b, '\\}': 0x007d, '\\nobreakspace': 0x00a0, '\\ldots': 0x2026, '\\textellipsis': 0x2026, '\\backslash': 0x005c, '`': 0x2018, "'": 0x2019, '``': 0x201c, "''": 0x201d, '\\degree': 0x00b0, '\\textasciicircum': 0x005e, '\\textasciitilde': 0x007e, '\\textasteriskcentered': 0x002a, '\\textbackslash': 0x005c, '\\textbraceleft': 0x007b, '\\textbraceright': 0x007d, '\\textbullet': 0x2022, '\\textdollar': 0x0024, '\\textsterling': 0x00a3, '\\textdagger': 0x2020, '\\textdaggerdbl': 0x2021, '–': 0x2013, '—': 0x2014, '‘': 0x2018, '’': 0x2019, '“': 0x201c, '”': 0x201d, '"': 0x201d, '\\ss': 0x00df, '\\ae': 0x00e6, '\\oe': 0x0153, '\\AE': 0x00c6, '\\OE': 0x0152, '\\O': 0x00d8, '\\i': 0x0131, '\\j': 0x0237, '\\aa': 0x00e5, '\\AA': 0x00c5, // LATIN CAPITAL LETTER A WITH RING ABOVE }; const COMMAND_MODE_CHARACTERS = /[\w!@*()-=+{}[\]\\';:?/.,~<>`|$%#&^" ]/; const LETTER = supportRegexPropertyEscape() ? /* eslint-disable-next-line prefer-regex-literals */ new RegExp('\\p{Letter}', 'u') : /[a-zA-ZаАбБвВгГдДеЕёЁжЖзЗиИйЙкКлЛмМнНоОпПрРсСтТуУфФхХцЦчЧшШщЩъЪыЫьЬэЭюЮяĄąĆćĘꣳŃńÓóŚśŹźŻżàâäôéèëêïîçùûüÿæœÀÂÄÔÉÈËÊÏΟÇÙÛÜÆŒößÖẞìíòúÌÍÒÚáñÁÑ]/; const LETTER_AND_DIGITS = supportRegexPropertyEscape() ? /* eslint-disable-next-line prefer-regex-literals */ new RegExp('[0-9\\p{Letter}]', 'u') : /[\da-zA-ZаАбБвВгГдДеЕёЁжЖзЗиИйЙкКлЛмМнНоОпПрРсСтТуУфФхХцЦчЧшШщЩъЪыЫьЬэЭюЮяĄąĆćĘꣳŃńÓóŚśŹźŻżàâäôéèëêïîçùûüÿæœÀÂÄÔÉÈËÊÏΟÇÙÛÜÆŒößÖẞìíòúÌÍÒÚáñÁÑ]/; /** * @param symbol The LaTeX command for this symbol, for * example `\alpha` or `+` */ function newSymbol(symbol, value, type = 'mord', variant) { if (value === undefined) return; MATH_SYMBOLS[symbol] = { type, variant, codepoint: value, }; if (!REVERSE_MATH_SYMBOLS[value] && !variant) REVERSE_MATH_SYMBOLS[value] = symbol; // We accept all math symbols in text mode as well // which is a bit more permissive than TeX if (!TEXT_SYMBOLS[symbol]) TEXT_SYMBOLS[symbol] = value; } /** * Define a set of single-character symbols */ function newSymbols(value, inType, inVariant) { if (typeof value === 'string') { for (let i = 0; i < value.length; i++) { const ch = value.charAt(i); newSymbol(ch, ch.codePointAt(0)); } return; } for (const [symbol, val, type, variant] of value) newSymbol(symbol, val, type !== null && type !== void 0 ? type : inType, variant !== null && variant !== void 0 ? variant : inVariant); } /** * Define a set of single-character symbols as a range of Unicode codepoints * @param from First Unicode codepoint * @param to Last Unicode codepoint */ function newSymbolRange(from, to) { for (let i = from; i <= to; i++) newSymbol(String.fromCodePoint(i), i); } /** * Given a codepoint, return a matching LaTeX expression. * If there is a matching command (e.g. `\alpha`) it is returned. */ function charToLatex(parseMode, codepoint) { if (codepoint === undefined) return ''; if (parseMode === 'math' && REVERSE_MATH_SYMBOLS[codepoint]) return REVERSE_MATH_SYMBOLS[codepoint]; if (parseMode === 'text') { let textSymbol = Object.keys(TEXT_SYMBOLS).find((x) => TEXT_SYMBOLS[x] === codepoint); if (!textSymbol) { const hex = codepoint.toString(16); textSymbol = '^'.repeat(hex.length) + hex; } return textSymbol; } return String.fromCodePoint(codepoint); } /* Some symbols in the MATHEMATICAL ALPHANUMERICAL SYMBOLS block had been previously defined in other blocks. Remap them */ const MATH_LETTER_EXCEPTIONS = { 0x1d455: 0x0210e, 0x1d49d: 0x0212c, 0x1d4a0: 0x02130, 0x1d4a1: 0x02131, 0x1d4a3: 0x0210b, 0x1d4a4: 0x02110, 0x1d4a7: 0x02112, 0x1d4a8: 0x02133, 0x1d4ad: 0x0211b, 0x1d4ba: 0x0212f, 0x1d4bc: 0x0210a, 0x1d4c4: 0x02134, 0x1d506: 0x0212d, 0x1d50b: 0x0210c, 0x1d50c: 0x02111, 0x1d515: 0x0211c, 0x1d51d: 0x02128, 0x1d53a: 0x02102, 0x1d53f: 0x0210d, 0x1d545: 0x02115, 0x1d547: 0x02119, 0x1d548: 0x0211a, 0x1d549: 0x0211d, 0x1d551: 0x02124, }; const MATH_UNICODE_BLOCKS = [ { start: 0x1d400, len: 26, offset: 65, style: 'bold' }, { start: 0x1d41a, len: 26, offset: 97, style: 'bold' }, { start: 0x1d434, len: 26, offset: 65, style: 'italic' }, { start: 0x1d44e, len: 26, offset: 97, style: 'italic' }, { start: 0x1d468, len: 26, offset: 65, style: 'bolditalic' }, { start: 0x1d482, len: 26, offset: 97, style: 'bolditalic' }, { start: 0x1d49c, len: 26, offset: 65, variant: 'script' }, { start: 0x1d4b6, len: 26, offset: 97, variant: 'script' }, { start: 0x1d4d0, len: 26, offset: 65, variant: 'script', style: 'bold' }, { start: 0x1d4ea, len: 26, offset: 97, variant: 'script', style: 'bold' }, { start: 0x1d504, len: 26, offset: 65, variant: 'fraktur' }, { start: 0x1d51e, len: 26, offset: 97, variant: 'fraktur' }, { start: 0x1d56c, len: 26, offset: 65, variant: 'fraktur', style: 'bold' }, { start: 0x1d586, len: 26, offset: 97, variant: 'fraktur', style: 'bold' }, { start: 0x1d538, len: 26, offset: 65, variant: 'double-struck' }, { start: 0x1d552, len: 26, offset: 97, variant: 'double-struck' }, { start: 0x1d5a0, len: 26, offset: 65, variant: 'sans-serif' }, { start: 0x1d5ba, len: 26, offset: 97, variant: 'sans-serif' }, { start: 0x1d5d4, len: 26, offset: 65, variant: 'sans-serif', style: 'bold', }, { start: 0x1d5ee, len: 26, offset: 97, variant: 'sans-serif', style: 'bold', }, { start: 0x1d608, len: 26, offset: 65, variant: 'sans-serif', style: 'italic', }, { start: 0x1d622, len: 26, offset: 97, variant: 'sans-serif', style: 'italic', }, { start: 0x1d63c, len: 26, offset: 65, variant: 'sans-serif', style: 'bolditalic', }, { start: 0x1d656, len: 26, offset: 97, variant: 'sans-serif', style: 'bolditalic', }, { start: 0x1d670, len: 26, offset: 65, variant: 'monospace' }, { start: 0x1d68a, len: 26, offset: 97, variant: 'monospace' }, { start: 0x1d6a8, len: 25, offset: 0x391, style: 'bold' }, { start: 0x1d6c2, len: 25, offset: 0x3b1, style: 'bold' }, { start: 0x1d6e2, len: 25, offset: 0x391, style: 'italic' }, { start: 0x1d6fc, len: 25, offset: 0x3b1, style: 'italic' }, { start: 0x1d71c, len: 25, offset: 0x391, style: 'bolditalic' }, { start: 0x1d736, len: 25, offset: 0x3b1, style: 'bolditalic' }, { start: 0x1d756, len: 25, offset: 0x391, variant: 'sans-serif', style: 'bold', }, { start: 0x1d770, len: 25, offset: 0x3b1, variant: 'sans-serif', style: 'bold', }, { start: 0x1d790, len: 25, offset: 0x391, variant: 'sans-serif', style: 'bolditalic', }, { start: 0x1d7aa, len: 25, offset: 0x3b1, variant: 'sans-serif', style: 'bolditalic', }, { start: 0x1d7ce, len: 10, offset: 48, variant: 'main', style: 'bold' }, { start: 0x1d7d8, len: 10, offset: 48, variant: 'double-struck' }, { start: 0x1d7e3, len: 10, offset: 48, variant: 'sans-serif' }, { start: 0x1d7ec, len: 10, offset: 48, variant: 'sans-serif', style: 'bold', }, { start: 0x1d7f6, len: 10, offset: 48, variant: 'monospace' }, ]; function unicodeToMathVariant(codepoint) { var _a; if ((codepoint < 0x1d400 || codepoint > 0x1d7ff) && (codepoint < 0x2100 || codepoint > 0x214f)) return { char: String.fromCodePoint(codepoint) }; // Handle the 'gap' letters by converting them back into their logical range for (const c in MATH_LETTER_EXCEPTIONS) { if (MATH_LETTER_EXCEPTIONS[c] === codepoint) { codepoint = (_a = c.codePointAt(0)) !== null && _a !== void 0 ? _a : 0; break; } } for (const MATH_UNICODE_BLOCK of MATH_UNICODE_BLOCKS) { if (codepoint >= MATH_UNICODE_BLOCK.start && codepoint < MATH_UNICODE_BLOCK.start + MATH_UNICODE_BLOCK.len) { return { char: String.fromCodePoint(codepoint - MATH_UNICODE_BLOCK.start + MATH_UNICODE_BLOCK.offset), variant: MATH_UNICODE_BLOCK.variant, style: MATH_UNICODE_BLOCK.style, }; } } return { char: String.fromCodePoint(codepoint) }; } /** * Given a character and variant ('double-struck', 'fraktur', etc...) * return the corresponding unicode character (a string) */ function mathVariantToUnicode(char, variant, style) { if (!/[A-Za-z\d]/.test(char)) return char; if (!variant && !style) return char; const codepoint = char.codePointAt(0); if (codepoint === undefined) return char; for (const MATH_UNICODE_BLOCK of MATH_UNICODE_BLOCKS) { if (!variant || MATH_UNICODE_BLOCK.variant === variant) { if (!style || MATH_UNICODE_BLOCK.style === style) { if (codepoint >= MATH_UNICODE_BLOCK.offset && codepoint < MATH_UNICODE_BLOCK.offset + MATH_UNICODE_BLOCK.len) { const result = MATH_UNICODE_BLOCK.start + codepoint - MATH_UNICODE_BLOCK.offset; return String.fromCodePoint(MATH_LETTER_EXCEPTIONS[result] || result); } } } } return char; } function unicodeCharToLatex(parseMode, char) { var _a; if (parseMode === 'text') return (_a = charToLatex(parseMode, char.codePointAt(0))) !== null && _a !== void 0 ? _a : char; let result; // Codepoint shortcuts have priority over variants // That is, "\N" vs "\mathbb{N}" // if (CODEPOINT_SHORTCUTS[cp]) return CODEPOINT_SHORTCUTS[cp]; result = charToLatex(parseMode, char.codePointAt(0)); if (result) return result; const cp = char.codePointAt(0); const v = unicodeToMathVariant(cp); if (!v.style && !v.variant) return ''; result = v.char; if (v.variant) result = '\\' + v.variant + '{' + result + '}'; if (v.style === 'bold') result = '\\mathbf{' + result + '}'; else if (v.style === 'italic') result = '\\mathit{' + result + '}'; else if (v.style === 'bolditalic') result = '\\mathbfit{' + result + '}'; return '\\mathord{' + result + '}'; } /** * Gets the value of a symbol in math mode */ // export function getSymbolValue(symbol: string): string { // return MATH_SYMBOLS[symbol]?.value ?? symbol; // } function getEnvironmentDefinition(name) { var _a; return (_a = ENVIRONMENTS[name]) !== null && _a !== void 0 ? _a : null; } /** * @param symbol A command (e.g. '\alpha') or a character (e.g. 'a') * @param parseMode One of 'math' or 'text' * @param macros A macros dictionary * @return {object} An info structure about the symbol, or null */ function getInfo(symbol, parseMode, macros) { var _a; if (!symbol || symbol.length === 0) return null; let info = null; if (symbol.startsWith('\\')) { // This could be a function or a symbol info = LATEX_COMMANDS[symbol]; if (info) return info; // It wasn't a function, maybe it's a symbol? if (parseMode === 'math') info = MATH_SYMBOLS[symbol]; else if (TEXT_SYMBOLS[symbol]) info = { type: 'mord', codepoint: TEXT_SYMBOLS[symbol] }; if (!info) { // Maybe it's a macro const command = symbol.slice(1); if (macros === null || macros === void 0 ? void 0 : macros[command]) { let argCount = (_a = macros[command].args) !== null && _a !== void 0 ? _a : 0; info = { type: 'group', params: [], infix: false, }; while (argCount >= 1) { info.params.push({ isOptional: false, type: 'math', }); argCount -= 1; } } } } else if (parseMode === 'math') info = MATH_SYMBOLS[symbol]; else if (TEXT_SYMBOLS[symbol]) info = { codepoint: TEXT_SYMBOLS[symbol], type: 'mord' }; else if (parseMode === 'text') info = { codepoint: symbol.codePointAt(0), type: 'mord' }; // Special case `f`, `g` and `h` are recognized as functions. if (info && info.type === 'mord' && (info.codepoint === 0x66 || info.codepoint === 0x67 || info.codepoint === 0x68)) { // "f", "g" or "h" info.isFunction = true; } return info; } /** * Return an array of suggestion for completing string 's'. * For example, for '\si', it could return ['\sin', '\sinh', '\sim', 'simeq', '\sigma'] * Infix operators are excluded, since they are deprecated commands. */ function suggest(mf, s) { var _a, _b; if (s === '\\') return []; if (!s.startsWith('\\')) return []; const result = []; // Iterate over items in the dictionary for (const p in LATEX_COMMANDS) { // Don't recommend infix commands if (p.startsWith(s) && !LATEX_COMMANDS[p].infix) result.push({ match: p, frequency: (_a = LATEX_COMMANDS[p].frequency) !== null && _a !== void 0 ? _a : 0 }); } for (const p in MATH_SYMBOLS) { if (p.startsWith(s)) result.push({ match: p, frequency: (_b = MATH_SYMBOLS[p].frequency) !== null && _b !== void 0 ? _b : 0 }); } // Consider macros const command = s.substring(1); for (const p of Object.keys(mf.options.macros)) if (p.startsWith(command)) result.push({ match: '\\' + p, frequency: 0 }); result.sort((a, b) => { var _a, _b; if (a.frequency === b.frequency) { if (a.match.length === b.match.length) return a.match < b.match ? -1 : +1; return a.match.length - b.match.length; } return ((_a = b.frequency) !== null && _a !== void 0 ? _a : 0) - ((_b = a.frequency) !== null && _b !== void 0 ? _b : 0); }); return result.map((x) => x.match); } /** * An argument template has the following syntax: * * <placeholder>:<type> * * where * - <placeholder> is a string whose value is displayed when the argument * is missing * - <type> is one of 'string', 'color', 'dimen', 'auto', 'text', 'math' * */ function parseParameterTemplateArgument(argTemplate) { let type = 'auto'; // Parse the type (:type) const r = argTemplate.match(/:([^=]+)/); if (r) type = r[1].trim(); return type; } function parseParameterTemplate(parameterTemplate) { if (!parameterTemplate) return []; const result = []; let parameters = parameterTemplate.split(']'); if (parameters[0].startsWith('[')) { // We found at least one optional parameter. result.push({ isOptional: true, type: parseParameterTemplateArgument(parameters[0].slice(1)), }); // Parse the rest for (let i = 1; i <= parameters.length; i++) result.push(...parseParameterTemplate(parameters[i])); } else { parameters = parameterTemplate.split('}'); if (parameters[0].startsWith('{')) { // We found a required parameter result.push({ isOptional: false, type: parseParameterTemplateArgument(parameters[0].slice(1)), }); // Parse the rest for (let i = 1; i <= parameters.length; i++) result.push(...parseParameterTemplate(parameters[i])); } } return result; } /** * If possible, i.e. if they are all simple atoms, return a string made up of * their body */ function parseArgAsString(atoms) { if (!atoms) return ''; let result = ''; let success = true; for (const atom of atoms) { if (typeof atom.value === 'string') result += atom.value; else success = false; } return success ? result : ''; } /** * Define one or more environments to be used with * \begin{<env-name>}...\end{<env-name>}. * * @param params The number and type of required and optional parameters. */ function defineEnvironment(names, parameters, parser, isTabular = false) { if (typeof names === 'string') names = [names]; const parsedParameters = parseParameterTemplate(parameters); // Set default values of functions const data = { tabular: isTabular, // Params: the parameters for this function, an array of // {optional, type} params: parsedParameters, // Callback to create an atom createAtom: parser, }; for (const name of names) ENVIRONMENTS[name] = data; } /** * Like defineEnvironment, but for a tabular environment, i.e. * one whose content is in tabular mode, where '&' indicata a new column * and '\\' indicate a new row. */ function defineTabularEnvironment(names, parameters, parser) { defineEnvironment(names, parameters, parser, true); } /** * Define one of more functions. * * @param names * @param parameters The number and type of required and optional parameters. * For example: '{}' defines a single mandatory parameter * '[string]{auto}' defines two params, one optional, one required */ function defineFunction(names, parameters, options) { var _a, _b; if (!options) options = {}; // Set default values of functions const data = { // The parameters for this function, an array of // {optional, type} params: parseParameterTemplate(parameters), ifMode: options.ifMode, isFunction: (_a = options.isFunction) !== null && _a !== void 0 ? _a : false, applyMode: options.applyMode, infix: (_b = options.infix) !== null && _b !== void 0 ? _b : false, createAtom: options.createAtom, applyStyle: options.applyStyle, }; if (typeof names === 'string') LATEX_COMMANDS['\\' + names] = data; else for (const name of names) LATEX_COMMANDS['\\' + name] = data; } let _DEFAULT_MACROS; function getMacros(otherMacros) { if (!_DEFAULT_MACROS) _DEFAULT_MACROS = normalizeMacroDictionary(DEFAULT_MACROS); if (!otherMacros) return _DEFAULT_MACROS; return normalizeMacroDictionary({ ..._DEFAULT_MACROS, ...otherMacros }); } function normalizeMacroDefinition(def, options) { var _a, _b, _c, _d; if (typeof def === 'string') { // It's a shorthand definition, let's expand it let argCount = 0; const defString = def; // Let's see if there are arguments in the definition. if (/(^|[^\\])#1/.test(defString)) argCount = 1; if (/(^|[^\\])#2/.test(defString)) argCount = 2; if (/(^|[^\\])#3/.test(defString)) argCount = 3; if (/(^|[^\\])#4/.test(defString)) argCount = 4; if (/(^|[^\\])#5/.test(defString)) argCount = 5; if (/(^|[^\\])#6/.test(defString)) argCount = 6; if (/(^|[^\\])#7/.test(defString)) argCount = 7; if (/(^|[^\\])#8/.test(defString)) argCount = 8; if (/(^|[^\\])#9/.test(defString)) argCount = 9; return { expand: (_a = options === null || options === void 0 ? void 0 : options.expand) !== null && _a !== void 0 ? _a : true, captureSelection: (_b = options === null || options === void 0 ? void 0 : options.captureSelection) !== null && _b !== void 0 ? _b : true, args: argCount, def: defString, }; } return { expand: (_c = options === null || options === void 0 ? void 0 : options.expand) !== null && _c !== void 0 ? _c : true, captureSelection: (_d = options === null || options === void 0 ? void 0 : options.captureSelection) !== null && _d !== void 0 ? _d : true, args: 0, ...def, }; } function normalizeMacroDictionary(macros) { if (!macros) return {}; const result = {}; for (const macro of Object.keys(macros)) { const macroDef = macros[macro]; if (macroDef === undefined || macroDef === null) delete result[macro]; else if (typeof macroDef === 'object' && 'package' in macroDef) { for (const packageMacro of Object.keys(macroDef.package)) { result[packageMacro] = normalizeMacroDefinition(macroDef.package[packageMacro], { expand: macroDef.expand, captureSelection: macroDef.captureSelection, }); } } else result[macro] = normalizeMacroDefinition(macroDef); } return result; } class MathfieldBox extends Box { constructor(placeholderId, element, options) { super(null, options); this.placeholderId = placeholderId; this.element = element; this.htmlData = `placeholder-id=${placeholderId}`; this.height = element.style.fontSize === '' ? 1 : (element.clientHeight / parseInt(element.style.fontSize)) * 0.6; } toMarkup() { let props = ''; const classes = this.classes.split(' '); const classList = classes.length === 1 ? classes[0] : classes .filter((x, e, a) => x.length > 0 && a.indexOf(x) === e) .join(' '); if (this.cssId) { // A (HTML5) CSS id may not contain a space props += ` id=${this.cssId.replace(/ /g, '-')} `; } if (this.htmlData) { const entries = this.htmlData.split(','); for (const entry of entries) { const matched = entry.match(/([^=]+)=(.+$)/); if (matched) { const key = matched[1].trim().replace(/ /g, '-'); if (key) props += ` data-${key}=${matched[2]} `; } else { const key = entry.trim().replace(/ /g, '-'); if (key) props += ` data-${key} `; } } } if (this.htmlStyle) { const entries = this.htmlStyle.split(';'); let styleString = ''; for (const entry of entries) { const matched = entry.match(/([^=]+):(.+$)/); if (matched) { const key = matched[1].trim().replace(/ /g, '-'); if (key) styleString += `${key}:${matched[2]};`; } } if (styleString) props += ` style="${styleString}"`; } if (this.attributes) { props += ' ' + Object.keys(this.attributes) .map((x) => `${x}="${this.attributes[x]}"`) .join(' '); } if (classList.length > 0) props += ` class="${classList}"`; props += ` style="display: inline-block; width:${this.element.clientWidth}px; height:${this.element.clientHeight}px; "`; return `<span ${props}></span>`; } } /** * The order of these branches specify the keyboard navigation order */ const NAMED_BRANCHES = [ 'above', 'body', 'below', 'superscript', 'subscript', ]; /** * A _branch_ is a set of children of an atom. * * There are two kind of branches: * - **cell branches** are addressed with a column and row number and are * used by `ArrayAtom` * - **named branches** used with other kind of atoms. There is a fixed set of * possible named branches. */ function isNamedBranch(branch) { return typeof branch === 'string' && NAMED_BRANCHES.includes(branch); } function isCellBranch(branch) { return branch !== undefined && Array.isArray(branch) && branch.length === 2; } /** * An atom is an object encapsulating an elementary mathematical unit, * independent of its graphical representation. * * It keeps track of the content, while the dimensions, position and style * are tracked by Box objects which are created by the `createBox()` function. */ class Atom { constructor(type, options) { var _a, _b, _c, _d, _e; // Used to match a DOM element to an Atom // (the corresponding DOM element has a `data-atom-id` attribute) this.id = undefined; // Verbatim LaTeX of the command and its arguments // Note that the empty string is a valid verbatim LaTeX , so it's important // to distinguish between `verbatimLatex === undefined` and `typeof verbatimLatex === 'string'` this.verbatimLatex = undefined; // If true, some structural changes have been made to the atom // (insertion or removal of children) or one of its children is dirty /** @internal */ this._isDirty = false; // A monotonically increasing counter to detect structural changes /** @internal */ this._changeCounter = 0; // How to display "limits" (i.e. superscript/subscript) for example // with `\sum`: // - 'over-under': directly above and below the symbol // - 'adjacent': to the right, above and below the baseline (for example // for operators in `textstyle` style) // - 'auto': 'over-under' in \displaystyle, 'adjacent' otherwise // If `undefined`, the subsup should be placed on a separate `msubsup` atom. this.subsupPlacement = undefined; // True if the subsupPlacement was set by `\limits`, `\nolimits` or // `\displaylimits`. // Necessary so the proper LaTeX can be output. this.explicitSubsupPlacement = false; // If true, when the caret reaches the first position in this element's body, // (moving right to left) it automatically moves to the outside of the // element. // Conversely, when the caret reaches the last position inside // this element, (moving left to right) it automatically moves to the one // outside the element. this.skipBoundary = false; // If true, the children of this atom cannot be selected and should be handled // as a unit. Used by the `\enclose` annotations, for example. this.captureSelection = false; this.type = type; this.command = (_a = options === null || options === void 0 ? void 0 : options.command) !== null && _a !== void 0 ? _a : ''; if (!this.command && (options === null || options === void 0 ? void 0 : options.value)) this.command = options.value; this.mode = (_b = options === null || options === void 0 ? void 0 : options.mode) !== null && _b !== void 0 ? _b : 'math'; if (typeof (options === null || options === void 0 ? void 0 : options.value) === 'string') this.value = options.value; this.isFunction = (_c = options === null || options === void 0 ? void 0 : options.isFunction) !== null && _c !== void 0 ? _c : false; this.subsupPlacement = options === null || options === void 0 ? void 0 : options.limits; this.style = (_d = options === null || options === void 0 ? void 0 : options.style) !== null && _d !== void 0 ? _d : {}; this.displayContainsHighlight = (_e = options === null || options === void 0 ? void 0 : options.displayContainsHighlight) !== null && _e !== void 0 ? _e : false; if (options === null || options === void 0 ? void 0 : options.serialize) { console.assert(typeof options.command === 'string'); Atom.customSerializer[options.command] = options.serialize; } } /** * Return a list of boxes equivalent to atoms. * * While an atom represent an abstract element (for example 'genfrac'), * a box corresponds to something to draw on screen (a character, a line, * etc...). * * @param parentContext Font family, variant, size, color, and other info useful * to render an expression * @param options.newList - If true, for the purpose of calculating spacing * between atoms, this list of atoms should be considered a new atom list, * in the sense of TeX atom lists (i.e. don't consider preceding atoms * to calculate spacing) */ static createBox(parentContext, atoms, options) { var _a, _b, _c; if (!atoms) return null; const runs = getStyleRuns(atoms); // // Special case when there's a single run // if (runs.length === 1) { const run = runs[0]; if (run[0].style) { return renderStyleRun(parentContext, run, { ...options, style: { color: run[0].style.color, backgroundColor: run[0].style.backgroundColor, fontSize: run[0].style.fontSize, }, }); } return renderStyleRun(parentContext, run, options); } // // There are multiple runs to handle // const boxes = []; let newList = options === null || options === void 0 ? void 0 : options.newList; for (const run of runs) { const context = new Context(parentContext, { color: (_a = run[0].style) === null || _a === void 0 ? void 0 : _a.color, backgroundColor: (_b = run[0].style) === null || _b === void 0 ? void 0 : _b.backgroundColor, fontSize: (_c = run[0].style) === null || _c === void 0 ? void 0 : _c.fontSize, }); const box = renderStyleRun(context, run, { newList }); if (box) { newList = false; boxes.push(box); } } if (boxes.length === 0) return null; if (boxes.length === 1 && !(options === null || options === void 0 ? void 0 : options.classes) && !(options === null || options === void 0 ? void 0 : options.type)) return boxes[0].wrap(parentContext); return new Box(boxes, { classes: options === null || options === void 0 ? void 0 : options.classes, type: options === null || options === void 0 ? void 0 : options.type, newList: options === null || options === void 0 ? void 0 : options.newList, }).wrap(parentContext); } /** * Given an atom or an array of atoms, return a LaTeX string representation */ static serialize(value, options) { if (isArray(value)) return serializeAtoms(value, options); if (typeof value === 'number' || typeof value === 'boolean') return value.toString(); if (typeof value === 'string') return value.replace(/\s/g, '~'); if (value === undefined) return ''; // If we have some verbatim latex for this atom, use it. // This allow non-significant punctuation to be preserved when possible. if (!options.expandMacro && typeof value.verbatimLatex === 'string') return value.verbatimLatex; if (value.command && Atom.customSerializer[value.command]) return Atom.customSerializer[value.command](value, options); return value.serialize(options); } /** * The common ancestor between two atoms */ static commonAncestor(a, b) { if (a === b) return a.parent; // Short-circuit a common case if (a.parent === b.parent) return a.parent; // Accumulate all the parents of `a` const parents = new WeakSet(); let { parent } = a; while (parent) { parents.add(parent); parent = parent.parent; } // Walk up the parents of `b`. If a parent of `b` is also a parent of // `a`, it's the common ancestor parent = b.parent; while (parent) { if (parents.has(parent)) return parent; parent = parent.parent; } console.assert(Boolean(parent)); // Never reached return undefined; } static fromJson(json) { const result = new Atom(json.type, json); // Restore the branches for (const branch of NAMED_BRANCHES) if (json[branch]) result.setChildren(json[branch], branch); return result; } toJson() { const result = { type: this.type }; if (this.mode !== 'math') result.mode = this.mode; if (this.command && this.command !== this.value) result.command = this.command; if (this.value !== undefined) result.value = this.value; if (this.style && Object.keys(this.style).length > 0) result.style = { ...this.style }; if (this.verbatimLatex !== undefined) result.verbatimLatex = this.verbatimLatex; if (this.subsupPlacement) result.subsupPlacement = this.subsupPlacement; if (this.explicitSubsupPlacement) result.explicitSubsupPlacement = true; if (this.isFunction) result.isFunction = true; if (this.displayContainsHighlight) result.displayContainsHighlight = true; if (this.isExtensibleSymbol) result.isExtensibleSymbol = true; if (this.skipBoundary) result.skipBoundary = true; if (this.captureSelection) result.captureSelection = true; if (this._branches) { for (const branch of Object.keys(this._branches)) { if (this._branches[branch]) { result[branch] = this._branches[branch] .filter((x) => x.type !== 'first') .map((x) => x.toJson()); } } } return result; } get changeCounter() { return this._changeCounter; } get isDirty() { return this._isDirty; } set isDirty(dirty) { this._isDirty = dirty; if (dirty) { this._changeCounter++; this.verbatimLatex = undefined; this._children = undefined; let { parent } = this; while (parent) { parent._isDirty = true; parent._changeCounter++; parent.verbatimLatex = undefined; parent._children = undefined; parent = parent.parent; } } } /** * Serialize the atom to LaTeX */ serialize(options) { var _a; if (this.body && this.command) { // There's a command and body return joinLatex([ this.command, '{', this.bodyToLatex(options), '}', this.supsubToLatex(options), ]); } if (this.body) { // There's a body with no command return joinLatex([ this.bodyToLatex(options), this.supsubToLatex(options), ]); } if (this.value && this.value !== '\u200B') { // There's probably just a value (which is a unicode character) return (_a = this.command) !== null && _a !== void 0 ? _a : unicodeCharToLatex(this.mode, this.value); } return ''; } bodyToLatex(options) { return serializeAtoms(this.body, options); } aboveToLatex(options) { return serializeAtoms(this.above, options); } belowToLatex(options) { return serializeAtoms(this.below, options); } supsubToLatex(options) { let result = ''; if (this.branch('subscript') !== undefined) { const sub = serializeAtoms(this.subscript, options); if (sub.length === 0) result += '_{}'; else if (sub.length === 1) result += '_' + sub; else result += `_{${sub}}`; } if (this.branch('superscript') !== undefined) { const sup = serializeAtoms(this.superscript, options); if (sup.length === 0) result += '^{}'; else if (sup.length === 1) { if (sup === '\u2032') result += '^\\prime '; else if (sup === '\u2033') result += '^\\doubleprime '; else result += '^' + sup; } else result += `^{${sup}}`; } return result; } get treeDepth() { let result = 1; let atom = this.parent; while (atom) { atom = atom.parent; result += 1; } return result; } get inCaptureSelection() { let result = false; // eslint-disable-next-line @typescript-eslint/no-this-alias let atom = this; while (atom) { if (atom.captureSelection) { result = true; break; } atom = atom.parent; } return result; } /** * Return the atoms in the branch, if it exists, otherwise null */ branch(name) { if (!isNamedBranch(name)) return undefined; if (!this._branches) return undefined; return this._branches[name]; } /** * Return all the branches that exist. * Some of them may be empty. */ get branches() { if (!this._branches) return []; const result = []; for (const branch of NAMED_BRANCHES) if (this._branches[branch]) result.push(branch); return result; } /** * Return the atoms in the branch, if it exists, otherwise create it */ createBranch(name) { console.assert(isNamedBranch(name)); if (!isNamedBranch(name)) return []; if (!this._branches) { this._branches = { [name]: [this.makeFirstAtom(name)], }; } else if (!this._branches[name]) this._branches[name] = [this.makeFirstAtom(name)]; this.isDirty = true; return this._branches[name]; } get row() { if (!isCellBranch(this.treeBranch)) return -1; return this.treeBranch[0]; } get col() { if (!isCellBranch(this.treeBranch)) return -1; return this.treeBranch[1]; } get body() { var _a; return (_a = this._branches) === null || _a === void 0 ? void 0 : _a.body; } set body(atoms) { this.setChildren(atoms, 'body'); } get superscript() { var _a; return (_a = this._branches) === null || _a === void 0 ? void 0 : _a.superscript; } set superscript(atoms) { this.setChildren(atoms, 'superscript'); } get subscript() { var _a; return (_a = this._branches) === null || _a === void 0 ? void 0 : _a.subscript; } set subscript(atoms) { this.setChildren(atoms, 'subscript'); } get above() { var _a; return (_a = this._branches) === null || _a === void 0 ? void 0 : _a.above; } set above(atoms) { this.setChildren(atoms, 'above'); } get below() { var _a; return (_a = this._branches) === null || _a === void 0 ? void 0 : _a.below; } set below(atoms) { this.setChildren(atoms, 'below'); } get computedStyle() { var _a; if (!this.parent) return { ...((_a = this.style) !== null && _a !== void 0 ? _a : {}) }; const hadVerbatimColor = this.style.verbatimColor !== undefined; const hadVerbatimBackgroundColor = this.style.verbatimBackgroundColor !== undefined; const result = { ...this.parent.computedStyle, ...this.style }; // Variant are not included in the computed style (they're not inherited) delete result.variant; delete result.variantStyle; if (!hadVerbatimBackgroundColor) delete result.verbatimBackgroundColor; if (!hadVerbatimColor) delete result.verbatimColor; return result; } applyStyle(style) { this.isDirty = true; this.style = { ...this.style, ...style }; if (this.style.fontFamily === 'none') delete this.style.fontFamily; if (this.style.fontShape === 'auto') delete this.style.fontShape; if (this.style.fontSeries === 'auto') delete this.style.fontSeries; if (this.style.color === 'none') { delete this.style.color; delete this.style.verbatimColor; } if (this.style.backgroundColor === 'none') { delete this.style.backgroundColor; delete this.style.verbatimBackgroundColor; } if (this.style.fontSize === 'auto') delete this.style.fontSize; for (const child of this.children) child.applyStyle(style); } getInitialBaseElement() { var _a; let result = undefined; if (!this.hasEmptyBranch('body')) { console.assert(((_a = this.body) === null || _a === void 0 ? void 0 : _a[0].type) === 'first'); result = this.body[1].getInitialBaseElement(); } return result !== null && result !== void 0 ? result : this; } getFinalBaseElement() { if (!this.hasEmptyBranch('body')) return this.body[this.body.length - 1].getFinalBaseElement(); return this; } isCharacterBox() { const base = this.getInitialBaseElement(); return /mord/.test(base.type); } hasEmptyBranch(branch) { const atoms = this.branch(branch); if (!atoms) return true; console.assert(atoms.length > 0); console.assert(atoms[0].type === 'first'); return atoms.length === 1; } /* * Setting `null` does nothing * Setting `[]` adds an empty list (the branch is created) * The children should *not* start with a `"first"` atom: * the `first` atom will be added if necessary */ setChildren(children, branch) { var _a; if (!children) return; console.assert(isNamedBranch(branch)); if (!isNamedBranch(branch)) return; console.assert(((_a = children[0]) === null || _a === void 0 ? void 0 : _a.type) !== 'first'); // Update the parent const newBranch = [this.makeFirstAtom(branch), ...children]; if (this._branches) this._branches[branch] = newBranch; else this._branches = { [branch]: newBranch }; // Update the children for (const child of children) { child.parent = this; child.treeBranch = branch; } this.isDirty = true; } makeFirstAtom(branch) { const result = new Atom('first', { mode: this.mode }); result.parent = this; result.treeBranch = branch; return result; } addChild(child, branch) { console.assert(child.type !== 'first'); this.createBranch(branch).push(child); this.isDirty = true; // Update the child child.parent = this; child.treeBranch = branch; } addChildBefore(child, before) { console.assert(before.treeBranch !== undefined); const branch = this.createBranch(before.treeBranch); branch.splice(branch.indexOf(before), 0, child); this.isDirty = true; // Update the child child.parent = this; child.treeBranch = before.treeBranch; } addChildAfter(child, after) { console.assert(after.treeBranch !== undefined); const branch = this.createBranch(after.treeBranch); branch.splice(branch.indexOf(after) + 1, 0, child); this.isDirty = true; // Update the child child.parent = this; child.treeBranch = after.treeBranch; } addChildren(children, branch) { for (const child of children) this.addChild(child, branch); } /** * Return the last atom that was added */ addChildrenAfter(children, after) { console.assert(children.length === 0 || children[0].type !== 'first'); console.assert(after.treeBranch !== undefined); const branch = this.createBranch(after.treeBranch); branch.splice(branch.indexOf(after) + 1, 0, ...children); this.isDirty = true; // Update the children for (const child of children) { child.parent = this; child.treeBranch = after.treeBranch; } return children[children.length - 1]; } removeBranch(name) { const children = this.branch(name); if (isNamedBranch(name)) this._branches[name] = undefined; if (!children) return []; for (const child of children) { child.parent = undefined; child.treeBranch = undefined; } // Drop the 'first' element console.assert(children[0].type === 'first'); children.shift(); this.isDirty = true; return children; } removeChild(child) { console.assert(child.parent === this); // `first` atom cannot be deleted if (child.type === 'first') return; // Update the parent const branch = this.branch(child.treeBranch); const index = branch.indexOf(child); console.assert(index >= 0); branch.splice(index, 1); this.isDirty = true; // Update the child child.parent = undefined; child.treeBranch = undefined; } get siblings() { if (this.type === 'root') return []; return this.parent.branch(this.treeBranch); } get firstSibling() { return this.siblings[0]; } get lastSibling() { const { siblings } = this; return siblings[siblings.length - 1]; } get isFirstSibling() { return this === this.firstSibling; } get isLastSibling() { return this === this.lastSibling; } get hasNoSiblings() { // There is always at least one sibling, the 'first' // atom, but we don't count it. return this.siblings.length === 1; } get leftSibling() { console.assert(this.parent !== undefined); const siblings = this.parent.branch(this.treeBranch); return siblings[siblings.indexOf(this) - 1]; } get rightSibling() { console.assert(this.parent !== undefined); const siblings = this.parent.branch(this.treeBranch); return siblings[siblings.indexOf(this) + 1]; } get hasChildren() { return this._branches && this.children.length > 0; } get firstChild() { console.assert(this.hasChildren); return this.children[0]; } get lastChild() { console.assert(this.hasChildren); const { children } = this; return children[children.length - 1]; } /** * All the children of this atom. * * The order of the atoms is the order in which they * are navigated using the keyboard. */ get children() { if (this._children) return this._children; if (!this._branches) return []; const result = []; for (const branchName of NAMED_BRANCHES) { if (this._branches[branchName]) { for (const x of this._branches[branchName]) { result.push(...x.children); result.push(x); } } } this._children = result; return result; } /** * Render this atom as an array of boxes. * * The parent context (color, size...) will be applied * to the result. * */ render(parentContext, options) { if (this.type === 'first' && !parentContext.atomIdsSettings) return null; // // 1. Render the body or value // const context = new Context(parentContext, this.style); let result = this.createBox(context, { classes: this.type === 'root' ? ' ML__base' : '', newList: (options === null || options === void 0 ? void 0 : options.newList) === true || this.type === 'first', }); if (!result) return null; // // 2. Render any attached superscript, subscripts // if (!this.subsupPlacement && (this.superscript || this.subscript)) { // If there is a `subsupPlacement`, the attachment of sup/sub was handled // in the atom decomposition (e.g. `mop`, `accent`) result = this.attachSupsub(context, { base: result }); } return result.wrap(context); } attachSupsub(parentContext, options) { var _a, _b; const base = options.base; const superscript = this.superscript; const subscript = this.subscript; // If no superscript or subscript, nothing to do. if (!superscript && !subscript) return base; // Superscript and subscripts are discussed in the TeXbook // on page 445-446, rules 18(a-f). // TeX:14859-14945 let supBox = null; let subBox = null; const isCharacterBox = (_a = options.isCharacterBox) !== null && _a !== void 0 ? _a : this.isCharacterBox(); // Rule 18a, p445 let supShift = 0; if (superscript) { const context = new Context(parentContext, undefined, 'superscript'); supBox = Atom.createBox(context, superscript, { newList: true }); if (!isCharacterBox) { supShift = base.height - parentContext.metrics.supDrop * context.scalingFactor; } } let subShift = 0; if (subscript) { const context = new Context(parentContext, undefined, 'subscript'); subBox = Atom.createBox(context, subscript, { newList: true }); if (!isCharacterBox) { subShift = base.depth + parentContext.metrics.subDrop * context.scalingFactor; } } // Rule 18c, p445 let minSupShift; if (parentContext.isDisplayStyle) minSupShift = parentContext.metrics.sup1; // Sigma13 else if (parentContext.isCramped) minSupShift = parentContext.metrics.sup3; // Sigma15 else minSupShift = parentContext.metrics.sup2; // Sigma14 // Scriptspace is a font-size-independent size, so scale it // appropriately const scriptspace = 0.5 / PT_PER_EM / parentContext.scalingFactor; let supsub = null; if (subBox && supBox) { // Rule 18e supShift = Math.max(supShift, minSupShift, supBox.depth + 0.25 * parentContext.metrics.xHeight); subShift = Math.max(subShift, parentContext.metrics.sub2); const ruleWidth = parentContext.metrics.defaultRuleThickness; if (supShift - supBox.depth - (subBox.height - subShift) < 4 * ruleWidth) { subShift = 4 * ruleWidth - (supShift - supBox.depth) + subBox.height; const psi = 0.8 * parentContext.metrics.xHeight - (supShift - supBox.depth); if (psi > 0) { supShift += psi; subShift -= psi; } } // Subscripts shouldn't be shifted by the nucleus' italic correction. // Account for that by shifting the subscript back the appropriate // amount. Note we only do this when the nucleus is a single symbol. const slant = this.isExtensibleSymbol && base.italic ? -base.italic : 0; supsub = new VBox({ individualShift: [ { box: subBox, shift: subShift, marginLeft: slant }, { box: supBox, shift: -supShift }, ], }).wrap(parentContext); } else if (subBox && !supBox) { // Rule 18b subShift = Math.max(subShift, parentContext.metrics.sub1, subBox.height - 0.8 * X_HEIGHT); supsub = new VBox({ shift: subShift, children: [ { box: subBox, marginRight: scriptspace, marginLeft: this.isCharacterBox() ? -((_b = base.italic) !== null && _b !== void 0 ? _b : 0) : 0, }, ], }); } else if (!subBox && supBox) { // Rule 18c, d supShift = Math.max(supShift, minSupShift, supBox.depth + 0.25 * X_HEIGHT); supsub = new VBox({ shift: -supShift, children: [{ box: supBox, marginRight: scriptspace }], }); supsub.wrap(parentContext); } // Display the caret *following* the superscript and subscript, // so attach the caret to the 'msubsup' element. const supsubContainer = new Box(supsub, { classes: 'msubsup' }); if (this.caret) supsubContainer.caret = this.caret; return new Box([base, supsubContainer], { type: options.type }); } attachLimits(parentContext, options) { var _a; const above = this.superscript ? Atom.createBox(new Context(parentContext, this.style, 'superscript'), this.superscript, { newList: true }) : null; const below = this.subscript ? Atom.createBox(new Context(parentContext, this.style, 'subscript'), this.subscript, { newList: true }) : null; if (!above && !below) return options.base.wrap(parentContext); return makeLimitsStack(parentContext, { ...options, above, below, type: (_a = options === null || options === void 0 ? void 0 : options.type) !== null && _a !== void 0 ? _a : 'mop', }); } bind(context, box) { // Don't bind to phantom boxes or "empty" atoms (\u200b) // (they won't be interactive, so no need for the id) if (!box || context.isPhantom || this.value === '\u200B') return box; let captureSelection = false; let parent = this.parent; while (parent && !parent.captureSelection) parent = parent.parent; if (parent === null || parent === void 0 ? void 0 : parent.captureSelection) captureSelection = true; if (captureSelection) return box; if (!this.id) this.id = context.makeID(); box.atomID = this.id; return box; } /** * Create a box with the specified body. */ createMathfieldBox(context, options) { var _a; // Ensure that the atom type is a valid Box type const type = 'mathfield'; // The font family is determined by: // - the base font family associated with this atom (optional). For example, // some atoms such as some functions ('\sin', '\cos', etc...) or some // symbols ('\Z') have an explicit font family. This overrides any // other font family // - the user-specified font family that has been explicitly applied to // this atom // - the font family determined automatically in math mode, for example // which italicizes some characters, but which can be overridden const classes = (_a = options === null || options === void 0 ? void 0 : options.classes) !== null && _a !== void 0 ? _a : ''; const result = new MathfieldBox(options.placeholderId, options.element, { type, mode: this.mode, maxFontSize: context.scalingFactor, style: { variant: 'normal', ...this.style, letterShapeStyle: context.letterShapeStyle, fontSize: Math.max(1, context.size + context.mathstyle.sizeDelta), }, classes, newList: options === null || options === void 0 ? void 0 : options.newList, }); // Set other attributes if (context.isTight) result.isTight = true; // The italic correction applies only in math mode if (this.mode !== 'math' || this.style.variant === 'main') result.italic = 0; result.right = result.italic; // To retrieve the atom from a box, for example when the box is clicked // on, attach a unique ID to the box and associate it with the atom. this.bind(context, result); return result; } /** * Create a box with the specified body. */ createBox(context, options) { var _a, _b, _c, _d; const value = (_a = this.value) !== null && _a !== void 0 ? _a : this.body; // Ensure that the atom type is a valid Box type const type = isBoxType(this.type) ? this.type : undefined; // The font family is determined by: // - the base font family associated with this atom (optional). For example, // some atoms such as some functions ('\sin', '\cos', etc...) or some // symbols ('\Z') have an explicit font family. This overrides any // other font family // - the user-specified font family that has been explicitly applied to // this atom // - the font family determined automatically in math mode, for example // which italicizes some characters, but which can be overridden let classes = (_b = options === null || options === void 0 ? void 0 : options.classes) !== null && _b !== void 0 ? _b : ''; if (this.mode === 'text') classes += ' ML__text'; const result = typeof value === 'string' || value === undefined ? new Box((_c = value) !== null && _c !== void 0 ? _c : null, { type, mode: this.mode, maxFontSize: context.scalingFactor, style: { variant: 'normal', ...this.style, letterShapeStyle: context.letterShapeStyle, fontSize: Math.max(1, context.size + context.mathstyle.sizeDelta), }, classes, newList: options === null || options === void 0 ? void 0 : options.newList, }) : (_d = Atom.createBox(context, value, { type, mode: this.mode, style: this.style, classes, newList: options === null || options === void 0 ? void 0 : options.newList, })) !== null && _d !== void 0 ? _d : new Box(null); // Set other attributes if (context.isTight) result.isTight = true; // The italic correction applies only in math mode if (this.mode !== 'math' || this.style.variant === 'main') result.italic = 0; result.right = result.italic; // To retrieve the atom from a box, for example when the box is clicked // on, attach a unique ID to the box and associate it with the atom. this.bind(context, result); if (this.caret) { // If this has a super/subscript, the caret will be attached // to the 'msubsup' atom, so no need to have it here. if (!this.superscript && !this.subscript) result.caret = this.caret; } return result; } /** Return true if a digit, or a decimal point, or a french decimal `{,}` */ isDigit() { var _a; if (this.type === 'mord' && this.value) return /^[\d,.]$/.test(this.value); if (this.type === 'group' && ((_a = this.body) === null || _a === void 0 ? void 0 : _a.length) === 2) return this.body[0].type === 'first' && this.body[1].value === ','; return false; } asDigit() { var _a; if (this.type === 'mord' && this.value && /^[\d,.]$/.test(this.value)) return this.value; if (this.type === 'group' && ((_a = this.body) === null || _a === void 0 ? void 0 : _a.length) === 2) { if (this.body[0].type === 'first' && this.body[1].value === ',') return '.'; } return ''; } } Atom.customSerializer = {}; /** * * @param atoms the list of atoms to emit as LaTeX * @param options.expandMacro true if macros should be expanded * @result a LaTeX string */ function serializeAtoms(atoms, options) { if (!atoms || atoms.length === 0) return ''; if (atoms[0].type === 'first') { if (atoms.length === 1) return ''; // Remove the 'first' atom, if present atoms = atoms.slice(1); } if (atoms.length === 0) return ''; return joinLatex(getPropertyRuns(atoms, 'cssClass').map((x) => joinLatex(getPropertyRuns(x, 'color').map((x) => joinLatex(getModeRuns(x).map((x) => Mode.serialize(x, options))))))); } function getStyleRuns(atoms) { let style = undefined; const runs = []; let run = []; for (const atom of atoms) { const atomStyle = atom.computedStyle; if (!style && !atom.style) run.push(atom); else if (style && atomStyle.color === style.color && atomStyle.backgroundColor === style.backgroundColor && atomStyle.fontSize === style.fontSize) { // Atom matches the current run run.push(atom); } else { // Start a new run if (run.length > 0) runs.push(run); run = [atom]; style = atom.computedStyle; } } if (run.length > 0) runs.push(run); return runs; } /** * Render a list of atoms with the same style (color, backgroundColor, size) */ function renderStyleRun(parentContext, atoms, options) { var _a, _b, _c, _d; function isText(atom) { return atom.mode === 'text'; } if (!atoms || atoms.length === 0) return null; const context = new Context(parentContext, options === null || options === void 0 ? void 0 : options.style); // In most cases we want to display selection, // except if the `atomIdsSettings.groupNumbers` flag is set which is used for // read aloud. const displaySelection = !context.atomIdsSettings || !context.atomIdsSettings.groupNumbers; let boxes = []; let newList = (_a = options === null || options === void 0 ? void 0 : options.newList) !== null && _a !== void 0 ? _a : false; if (atoms.length === 1) { const atom = atoms[0]; const box = atom.render(context, { newList }); if (box) { if (displaySelection && atom.isSelected) box.selected(true); boxes = [box]; } } else { let digitOrTextStringID = ''; let lastWasDigit = true; for (const atom of atoms) { if (((_b = context.atomIdsSettings) === null || _b === void 0 ? void 0 : _b.groupNumbers) && digitOrTextStringID && ((lastWasDigit && atom.isDigit()) || (!lastWasDigit && isText(atom)))) context.atomIdsSettings.overrideID = digitOrTextStringID; const box = atom.render(context, { newList }); if (context.atomIdsSettings) context.atomIdsSettings.overrideID = undefined; if (box) { // Groups (i.e. `{}`) without a specific boxType restart a new list // (for spacing purposes) newList = atom.type === 'group' && !atom['boxType']; // If this is a digit or text run, keep track of it if ((_c = context.atomIdsSettings) === null || _c === void 0 ? void 0 : _c.groupNumbers) { if (atom.isDigit() || isText(atom)) { if (!digitOrTextStringID || lastWasDigit !== atom.isDigit()) { // Changed from text to digits or vice-versa lastWasDigit = atom.isDigit(); digitOrTextStringID = (_d = atom.id) !== null && _d !== void 0 ? _d : ''; } } if (digitOrTextStringID && (!(atom.isDigit() || isText(atom)) || !atom.hasEmptyBranch('superscript') || !atom.hasEmptyBranch('subscript'))) { // Done with digits/text digitOrTextStringID = ''; } } if (displaySelection && atom.isSelected) box.selected(true); boxes.push(box); } } } if (boxes.length === 0) return null; let result; if (options || context.isTight || boxes.length > 1) { result = new Box(boxes, { isTight: context.isTight, ...(options !== null && options !== void 0 ? options : {}), }); result.isSelected = boxes.every((x) => x.isSelected); } else result = boxes[0]; // Apply size correction return result.wrap(context).wrap(parentContext); } class GroupAtom extends Atom { constructor(arg, options) { var _a, _b, _c; super('group', { command: options === null || options === void 0 ? void 0 : options.command, mode: (_a = options === null || options === void 0 ? void 0 : options.mode) !== null && _a !== void 0 ? _a : 'math', serialize: options === null || options === void 0 ? void 0 : options.serialize, style: options === null || options === void 0 ? void 0 : options.style, displayContainsHighlight: true, }); this.body = arg; this.mathstyleName = options === null || options === void 0 ? void 0 : options.mathstyleName; this.latexOpen = options === null || options === void 0 ? void 0 : options.latexOpen; this.latexClose = options === null || options === void 0 ? void 0 : options.latexClose; this.cssId = options === null || options === void 0 ? void 0 : options.cssId; this.htmlData = options === null || options === void 0 ? void 0 : options.htmlData; this.htmlStyle = options === null || options === void 0 ? void 0 : options.htmlStyle; this.customClass = options === null || options === void 0 ? void 0 : options.customClass; this.boxType = options === null || options === void 0 ? void 0 : options.boxType; this.skipBoundary = true; this.captureSelection = (_b = options === null || options === void 0 ? void 0 : options.captureSelection) !== null && _b !== void 0 ? _b : false; this.changeMode = (_c = options === null || options === void 0 ? void 0 : options.changeMode) !== null && _c !== void 0 ? _c : false; this.displayContainsHighlight = false; if (arg && arg.length === 1 && arg[0].command === ',') { // French decimal point this.captureSelection = true; } } static fromJson(json) { return new GroupAtom(json.body, json); } toJson() { const options = {}; if (this.mathstyleName) options.mathstyleName = this.mathstyleName; if (this.latexOpen) options.latexOpen = this.latexOpen; if (this.latexClose) options.latexClose = this.latexClose; if (this.cssId) options.cssId = this.cssId; if (this.htmlData) options.htmlData = this.htmlData; if (this.htmlStyle) options.htmlStyle = this.htmlStyle; if (this.customClass) options.customClass = this.customClass; if (this.boxType) options.boxType = this.boxType; if (this.captureSelection) options.captureSelection = true; if (this.changeMode) options.changeMode = true; return { ...super.toJson(), ...options }; } render(context) { // The scope of the context is this group, so clone it // so that any changes to it will be discarded when finished // with this group. // Note that the mathstyle property is optional and could be undefined // If that's the case, clone() returns a clone of the // context with the same mathstyle. const localContext = new Context(context, this.style, this.mathstyleName); const box = Atom.createBox(localContext, this.body, { type: this.boxType, classes: this.customClass, mode: this.mode, style: { backgroundColor: this.style.backgroundColor }, newList: !this.boxType, }); if (!box) return box; if (this.cssId) box.cssId = this.cssId; if (this.htmlData) box.htmlData = this.htmlData; if (this.htmlStyle) box.htmlStyle = this.htmlStyle; if (this.caret) box.caret = this.caret; // Need to bind the group so that the DOM element can be matched // and the atom iterated recursively. Otherwise, it behaves // as if `captureSelection === true` return this.bind(context, box); } serialize(options) { let result = this.bodyToLatex(options); if (typeof this.latexOpen === 'string') result = this.latexOpen + result + this.latexClose; if (this.htmlData) result = `\\htmlData{${this.htmlData}}{${result}}`; if (this.htmlStyle) result = `\\htmlStyle{${this.htmlStyle}}{${result}}`; if (this.customClass) result = `\\class{${this.customClass}}{${result}}`; if (this.cssId) result = `\\cssId{${this.cssId}}{${result}}`; return result; } } class SpacingAtom extends Atom { constructor(command, style, width) { super('spacing', { command, style }); this.width = width; } static fromJson(json) { return new SpacingAtom(json.command, json.style, json.width); } toJson() { const options = {}; if (this.width) options.width = this.width; return { ...super.toJson(), ...options }; } render(_context) { var _a; let result; if (this.width) { result = new Box(null, { classes: 'mspace' }); result.left = convertGlueToEm(this.width); } else { const spacingCls = (_a = { '\\qquad': 'qquad', '\\quad': 'quad', '\\enspace': 'enspace', '\\;': 'thickspace', '\\:': 'mediumspace', '\\,': 'thinspace', '\\!': 'negativethinspace', }[this.command]) !== null && _a !== void 0 ? _a : 'mediumspace'; result = new Box(null, { classes: spacingCls }); } if (this.caret) result.caret = this.caret; return result; } serialize(_options) { var _a; // Three kinds of spacing commands: // \hskip and \kern which take one implicit parameter // \hspace and hspace* with take one *explicit* parameter // \quad, etc... which take no parameters. let result = (_a = this.command) !== null && _a !== void 0 ? _a : ''; if (this.command === '\\hspace' || this.command === '\\hspace*') { if (Number.isFinite(this.width)) result += `{${this.width}em'}`; else result += `{0pt}`; } else if (Number.isFinite(this.width)) result += ` ${this.width}em`; return result; } } /** * This module deals with creating delimiters of various sizes. The TeXbook * discusses these routines on page 441-442, in the "Another subroutine sets box * x to a specified variable delimiter" paragraph. * * There are three main routines here. `makeSmallDelim` makes a delimiter in the * normal font, but in either text, script, or scriptscript style. * `makeLargeDelim` makes a delimiter in textstyle, but in one of the Size1, * Size2, Size3, or Size4 fonts. `makeStackedDelim` makes a delimiter out of * smaller pieces that are stacked on top of one another. * * The functions take a parameter `center`, which determines if the delimiter * should be centered around the axis. * * Then, there are three exposed functions. `sizedDelim` makes a delimiter in * one of the given sizes. This is used for things like `\bigl`. * `customSizedDelim` makes a delimiter with a given total height+depth. It is * called in places like `\sqrt`. `leftRightDelim` makes an appropriate * delimiter which surrounds an expression of a given height an depth. It is * used in `\left` and `\right`. * @summary Handling of delimiters surrounds symbols. */ const RIGHT_DELIM = { '(': ')', '{': '}', '[': ']', '|': '|', '\\lbrace': '\\rbrace', '\\lparen': '\\rparen', '\\{': '\\}', '\\langle': '\\rangle', '\\lfloor': '\\rfloor', '\\lceil': '\\rceil', '\\vert': '\\vert', '\\lvert': '\\rvert', '\\Vert': '\\Vert', '\\lVert': '\\rVert', '\\lbrack': '\\rbrack', '\\ulcorner': '\\urcorner', '\\llcorner': '\\lrcorner', '\\lgroup': '\\rgroup', '\\lmoustache': '\\rmoustache', }; const LEFT_DELIM = Object.fromEntries(Object.entries(RIGHT_DELIM).map(([leftDelim, rightDelim]) => [ rightDelim, leftDelim, ])); function getSymbolValue(symbol) { var _a; return ((_a = { '[': 0x5b, ']': 0x5d, '(': 0x28, ')': 0x29, '\\mid': 0x2223, '|': 0x2223, '\u2223': 0x2223, '\u2225': 0x2225, '\\|': 0x2223, '\\{': 0x7b, '\\}': 0x7d, '\\lbrace': 0x7b, '\\rbrace': 0x7d, '\\lparen': 0x28, '\\rparen': 0x29, '\\lbrack': 0x5b, '\\rbrack': 0x5d, '\\vert': 0x2223, '\\lvert': 0x2223, '\\mvert': 0x2223, '\\rvert': 0x2223, '\\Vert': 0x2225, '\\lVert': 0x2225, '\\mVert': 0x2225, '\\rVert': 0x2225, '\\parallel': 0x2225, '\\shortparallel': 0x2225, '\\langle': 0x27e8, '\\rangle': 0x27e9, '\\lfloor': 0x230a, '\\rfloor': 0x230b, '\\lceil': 0x2308, '\\rceil': 0x2309, '\\ulcorner': 0x250c, '\\urcorner': 0x2510, '\\llcorner': 0x2514, '\\lrcorner': 0x2518, '\\lgroup': 0x27ee, '\\rgroup': 0x27ef, '\\lmoustache': 0x23b0, '\\rmoustache': 0x23b1, '\\surd': 0x221a, }[symbol]) !== null && _a !== void 0 ? _a : symbol.codePointAt(0)); } /** * Makes a small delimiter. This is a delimiter that comes in the Main-Regular * font, but is restyled to either be in textstyle, scriptstyle, or * scriptscriptstyle. */ function makeSmallDelim(delim, context, center, options) { const text = new Box(getSymbolValue(delim), { fontFamily: 'Main-Regular' }); const box = text.wrap(context, options); if (center) box.setTop((1 - context.scalingFactor) * AXIS_HEIGHT); return box; } /** * Makes a large delimiter. This is a delimiter that comes in the Size1, Size2, * Size3, or Size4 fonts. */ function makeLargeDelim(delim, size, center, parentContext, options) { // Delimiters ignore the mathstyle, so use a 'textstyle' context. const context = new Context(parentContext, options === null || options === void 0 ? void 0 : options.style, 'textstyle'); const result = new Box(getSymbolValue(delim), { fontFamily: 'Size' + size + '-Regular', classes: 'ML__delim-size' + size, }).wrap(context); if (center) result.setTop((1 - context.scalingFactor) * AXIS_HEIGHT); return result; } /** * Make a stacked delimiter out of a given delimiter, with the total height at * least `heightTotal`. This routine is mentioned on page 442 of the TeXbook. */ function makeStackedDelim(delim, heightTotal, center, context, options) { var _a; // There are four parts, the top, an optional middle, a repeated part, and a // bottom. let top; let middle; let repeat; let bottom; top = repeat = bottom = getSymbolValue(delim); middle = null; // Also keep track of what font the delimiters are in let fontFamily = 'Size1-Regular'; // We set the parts and font based on the symbol. Note that we use // 0x23d0 instead of '|' and 0x2016 instead of '\\|' for the // repeats of the arrows if (delim === '\\vert' || delim === '\\lvert' || delim === '\\rvert' || delim === '\\mvert' || delim === '\\mid') repeat = top = bottom = 0x2223; else if (delim === '\\Vert' || delim === '\\lVert' || delim === '\\rVert' || delim === '\\mVert' || delim === '\\|') repeat = top = bottom = 0x2225; else if (delim === '\\uparrow') repeat = bottom = 0x23d0; else if (delim === '\\Uparrow') repeat = bottom = 0x2016; else if (delim === '\\downarrow') top = repeat = 0x23d0; else if (delim === '\\Downarrow') top = repeat = 0x2016; else if (delim === '\\updownarrow') { top = 0x2191; repeat = 0x23d0; bottom = 0x2193; } else if (delim === '\\Updownarrow') { top = 0x21d1; repeat = 0x2016; bottom = 0x21d3; } else if (delim === '[' || delim === '\\lbrack') { top = 0x23a1; repeat = 0x23a2; bottom = 0x23a3; fontFamily = 'Size4-Regular'; } else if (delim === ']' || delim === '\\rbrack') { top = 0x23a4; repeat = 0x23a5; bottom = 0x23a6; fontFamily = 'Size4-Regular'; } else if (delim === '\\lfloor' || delim === '\u230a') { repeat = top = 0x23a2; bottom = 0x23a3; fontFamily = 'Size4-Regular'; } else if (delim === '\\lceil' || delim === '\u2308') { top = 0x23a1; repeat = bottom = 0x23a2; fontFamily = 'Size4-Regular'; } else if (delim === '\\rfloor' || delim === '\u230b') { repeat = top = 0x23a5; bottom = 0x23a6; fontFamily = 'Size4-Regular'; } else if (delim === '\\rceil' || delim === '\u2309') { top = 0x23a4; repeat = bottom = 0x23a5; fontFamily = 'Size4-Regular'; } else if (delim === '(' || delim === '\\lparen') { top = 0x239b; repeat = 0x239c; bottom = 0x239d; fontFamily = 'Size4-Regular'; } else if (delim === ')' || delim === '\\rparen') { top = 0x239e; repeat = 0x239f; bottom = 0x23a0; fontFamily = 'Size4-Regular'; } else if (delim === '\\{' || delim === '\\lbrace') { top = 0x23a7; middle = 0x23a8; bottom = 0x23a9; repeat = 0x23aa; fontFamily = 'Size4-Regular'; } else if (delim === '\\}' || delim === '\\rbrace') { top = 0x23ab; middle = 0x23ac; bottom = 0x23ad; repeat = 0x23aa; fontFamily = 'Size4-Regular'; } else if (delim === '\\lgroup' || delim === '\u27ee') { top = 0x23a7; bottom = 0x23a9; repeat = 0x23aa; fontFamily = 'Size4-Regular'; } else if (delim === '\\rgroup' || delim === '\u27ef') { top = 0x23ab; bottom = 0x23ad; repeat = 0x23aa; fontFamily = 'Size4-Regular'; } else if (delim === '\\lmoustache' || delim === '\u23b0') { top = 0x23a7; bottom = 0x23ad; repeat = 0x23aa; fontFamily = 'Size4-Regular'; } else if (delim === '\\rmoustache' || delim === '\u23b1') { top = 0x23ab; bottom = 0x23a9; repeat = 0x23aa; fontFamily = 'Size4-Regular'; } else if (delim === '\\surd') { top = 0xe001; bottom = 0x23b7; repeat = 0xe000; fontFamily = 'Size4-Regular'; } else if (delim === '\\ulcorner') { top = 0x250c; repeat = bottom = 0x20; } else if (delim === '\\urcorner') { top = 0x2510; repeat = bottom = 0x20; } else if (delim === '\\llcorner') { bottom = 0x2514; repeat = top = 0x20; } else if (delim === '\\lrcorner') { top = 0x2518; repeat = top = 0x20; } // Get the metrics of the four sections const topMetrics = getCharacterMetrics(top, fontFamily); const topHeightTotal = topMetrics.height + topMetrics.depth; const repeatMetrics = getCharacterMetrics(repeat, fontFamily); const repeatHeightTotal = repeatMetrics.height + repeatMetrics.depth; const bottomMetrics = getCharacterMetrics(bottom, fontFamily); const bottomHeightTotal = bottomMetrics.height + bottomMetrics.depth; let middleHeightTotal = 0; let middleFactor = 1; if (middle !== null) { const middleMetrics = getCharacterMetrics(middle, fontFamily); middleHeightTotal = middleMetrics.height + middleMetrics.depth; middleFactor = 2; // Repeat symmetrically above and below middle } // Calculate the minimal height that the delimiter can have. // It is at least the size of the top, bottom, and optional middle combined. const minHeight = topHeightTotal + bottomHeightTotal + middleHeightTotal; // Compute the number of copies of the repeat symbol we will need const repeatCount = Math.max(0, Math.ceil((heightTotal - minHeight) / (middleFactor * repeatHeightTotal))); // Compute the total height of the delimiter including all the symbols const realHeightTotal = minHeight + repeatCount * middleFactor * repeatHeightTotal; // The center of the delimiter is placed at the center of the axis. Note // that in this context, 'center' means that the delimiter should be // centered around the axis in the current style, while normally it is // centered around the axis in textstyle. let axisHeight = AXIS_HEIGHT; if (center) axisHeight = axisHeight * context.scalingFactor; // Calculate the depth const depth = realHeightTotal / 2 - axisHeight; // Now, we start building the pieces that will go into the vlist const OVERLAP = 0.008; // Overlap between segments, in em // Keep a list of the inner pieces const stack = []; // Add the bottom symbol stack.push({ box: new Box(bottom, { fontFamily }) }); stack.push(-OVERLAP); const repeatBox = new Box(repeat, { fontFamily }); if (middle === null) { // Add that many symbols for (let i = 0; i < repeatCount; i++) stack.push({ box: repeatBox }); } else { // When there is a middle bit, we need the middle part and two repeated // sections for (let i = 0; i < repeatCount; i++) stack.push({ box: repeatBox }); stack.push(-OVERLAP); stack.push({ box: new Box(middle, { fontFamily }) }); stack.push(-OVERLAP); for (let i = 0; i < repeatCount; i++) stack.push({ box: repeatBox }); } // Add the top symbol stack.push(-OVERLAP); stack.push({ box: new Box(top, { fontFamily }) }); // Finally, build the vlist let sizeClass = ''; // Apply the correct CSS class to choose the right font. if (fontFamily === 'Size1-Regular') sizeClass = ' delim-size1'; else if (fontFamily === 'Size4-Regular') sizeClass = ' delim-size4'; const inner = new VBox({ bottom: depth, children: stack, }, { classes: sizeClass }); const result = new Box(inner, { ...(options !== null && options !== void 0 ? options : {}), classes: ((_a = options === null || options === void 0 ? void 0 : options.classes) !== null && _a !== void 0 ? _a : '') + ' ML__delim-mult', }); // result.setStyle('vertical-align', (result.depth + axisHeight) / 2, 'em'); return result; } // There are three kinds of delimiters, delimiters that stack when they become // too large const stackLargeDelimiters = new Set([ '(', ')', '\\lparen', '\\rparen', '[', ']', '\\lbrack', '\\rbrack', '\\{', '\\}', '\\lbrace', '\\rbrace', '\\lfloor', '\\rfloor', '\\lceil', '\\rceil', '\\surd', '\u230a', '\u230b', '\u2308', '\u2309', ]); // Delimiters that always stack const stackAlwaysDelimiters = new Set([ '\\uparrow', '\\downarrow', '\\updownarrow', '\\Uparrow', '\\Downarrow', '\\Updownarrow', '|', '\\|', '\\vert', '\\Vert', '\\lvert', '\\rvert', '\\lVert', '\\rVert', '\\mvert', '\\mid', '\\lgroup', '\\rgroup', '\\lmoustache', '\\rmoustache', '\u27ee', '\u27ef', '\u23b0', '\u23b1', ]); // And delimiters that never stack const stackNeverDelimiters = new Set([ '<', '>', '\\langle', '\\rangle', '/', '\\backslash', '\\lt', '\\gt', ]); // Metrics of the different sizes. Found by looking at TeX's output of // $\bigl| // \Bigl| \biggl| \Biggl| \showlists$ // Used to create stacked delimiters of appropriate sizes in makeSizedDelim. const sizeToMaxHeight = [0, 1.2, 1.8, 2.4, 3]; /** * Used to create a delimiter of a specific size, where `size` is 1, 2, 3, or 4. */ function makeSizedDelim(delim, size, context, options) { var _a; if (delim === undefined || delim === '.') { // Empty delimiters still count as elements, even though they don't // show anything. return makeNullDelimiter(context, (_a = options.type) !== null && _a !== void 0 ? _a : 'minner', options.classes); } // < and > turn into \langle and \rangle in delimiters if (delim === '<' || delim === '\\lt' || delim === '\u27e8') delim = '\\langle'; else if (delim === '>' || delim === '\\gt' || delim === '\u27e9') delim = '\\rangle'; // Sized delimiters are never centered. if (stackLargeDelimiters.has(delim) || stackNeverDelimiters.has(delim)) return makeLargeDelim(delim, size, false, context, options); if (stackAlwaysDelimiters.has(delim)) { return makeStackedDelim(delim, sizeToMaxHeight[size], false, context, options); } console.assert(false, "Unknown delimiter '" + delim + "'"); return null; } // Delimiters that never stack try small delimiters and large delimiters only const stackNeverDelimiterSequence = [ { type: 'small', mathstyle: 'scriptscriptstyle' }, { type: 'small', mathstyle: 'scriptstyle' }, { type: 'small', mathstyle: 'textstyle' }, { type: 'large', size: 1 }, { type: 'large', size: 2 }, { type: 'large', size: 3 }, { type: 'large', size: 4 }, ]; // Delimiters that always stack try the small delimiters first, then stack const stackAlwaysDelimiterSequence = [ { type: 'small', mathstyle: 'scriptscriptstyle' }, { type: 'small', mathstyle: 'scriptscriptstyle' }, { type: 'small', mathstyle: 'textstyle' }, { type: 'stack' }, ]; // Delimiters that stack when large try the small and then large delimiters, and // stack afterwards const stackLargeDelimiterSequence = [ { type: 'small', mathstyle: 'scriptscriptstyle' }, { type: 'small', mathstyle: 'scriptstyle' }, { type: 'small', mathstyle: 'textstyle' }, { type: 'large', size: 1 }, { type: 'large', size: 2 }, { type: 'large', size: 3 }, { type: 'large', size: 4 }, { type: 'stack' }, ]; /* * Get the font used in a delimiter based on what kind of delimiter it is. */ function delimTypeToFont(info) { if (info.type === 'small') return 'Main-Regular'; if (info.type === 'large') return 'Size' + info.size + '-Regular'; console.assert(info.type === 'stack'); return 'Size4-Regular'; } /** * Traverse a sequence of types of delimiters to decide what kind of delimiter * should be used to create a delimiter of the given height+depth. * @param delim - a character value (not a command) */ function traverseSequence(delim, height, sequence, context) { // Here, we choose the index we should start at in the sequences. In smaller // sizes (which correspond to larger numbers in style.size) we start earlier // in the sequence. Thus: // - scriptscript starts at index 0, // - script starts at index 1, // - text and display start at 2 const start = { '-4': 0, '-3': 1, '0': 2 }[context.mathstyle.sizeDelta]; for (let i = start; i < sequence.length; i++) { if (sequence[i].type === 'stack') { // This is always the last delimiter, so we just break the loop now. break; } const metrics = getCharacterMetrics(delim, delimTypeToFont(sequence[i])); if (metrics.defaultMetrics) { // If we don't have metrics info for this character, // assume we'll construct as a small delimiter return { type: 'small', mathstyle: 'scriptstyle' }; } let heightDepth = metrics.height + metrics.depth; // Small delimiters are scaled down versions of the same font, so we // account for the style change size. if (sequence[i].type === 'small') { if (sequence[i].mathstyle === 'scriptscriptstyle') heightDepth *= FONT_SCALE[Math.max(1, context.size - 2)]; else if (sequence[i].mathstyle === 'scriptstyle') heightDepth *= FONT_SCALE[Math.max(1, context.size - 1)]; } // Check if the delimiter at this size works for the given height. if (heightDepth > height) return sequence[i]; } // If we reached the end of the sequence, return the last sequence element. return sequence[sequence.length - 1]; } /** * Make a delimiter of a given height+depth, with optional centering. Here, we * traverse the sequences, and create a delimiter that the sequence tells us to. */ function makeCustomSizedDelim(type, delim, height, center, context, options) { var _a; if (!delim || delim.length === 0 || delim === '.') return makeNullDelimiter(context, type, type); if (delim === '<' || delim === '\\lt') delim = '\\langle'; else if (delim === '>' || delim === '\\gt') delim = '\\rangle'; // Decide what sequence to use let sequence; if (stackNeverDelimiters.has(delim)) sequence = stackNeverDelimiterSequence; else if (stackLargeDelimiters.has(delim)) sequence = stackLargeDelimiterSequence; else sequence = stackAlwaysDelimiterSequence; // Look through the sequence const delimType = traverseSequence(getSymbolValue(delim), height, sequence, context); const delimContext = new Context(context, options === null || options === void 0 ? void 0 : options.style, delimType.mathstyle); // Depending on the sequence element we decided on, // call the appropriate function. if (delimType.type === 'small') { return makeSmallDelim(delim, delimContext, center, { type, classes: 'ML__small-delim ' + ((_a = options === null || options === void 0 ? void 0 : options.classes) !== null && _a !== void 0 ? _a : ''), }); } if (delimType.type === 'large') { return makeLargeDelim(delim, delimType.size, center, delimContext, { ...options, type, }); } console.assert(delimType.type === 'stack'); return makeStackedDelim(delim, height, center, delimContext, { ...options, type, }); } /** * Make a delimiter for use with `\left` and `\right`, given a height and depth * of an expression that the delimiters surround. * See tex.web:14994 */ function makeLeftRightDelim(type, delim, height, depth, context, options) { // If this is the empty delimiter, return a null fence if (delim === '.') return makeNullDelimiter(context, type, options === null || options === void 0 ? void 0 : options.classes); // We always center \left/\right delimiters, so the axis is always shifted const axisHeight = AXIS_HEIGHT * context.scalingFactor; // Taken from TeX source, tex.web, function make_left_right const delimiterFactor = 901; // Plain.tex:327, texboox:152 // @todo: use register `\delimitershortfall` const delimiterExtend = 5 / PT_PER_EM; // Plain.tex:345, texboox:152 const maxDistFromAxis = Math.max(height - axisHeight, depth + axisHeight); const totalHeight = Math.max((maxDistFromAxis / 500) * delimiterFactor, 2 * maxDistFromAxis - delimiterExtend); // Finally, we defer to `makeCustomSizedDelim` with our calculated total // height return makeCustomSizedDelim(type, delim, totalHeight, true, context, options); } function makeNullDelimiter(parentContext, type, classes) { // The null delimiter has a width, specified by class 'nulldelimiter' // The size of the null delimiter is independent of the current mathstyle const context = new Context(parentContext, undefined, 'textstyle'); return new Box(null, { classes: ' nulldelimiter ' + (classes !== null && classes !== void 0 ? classes : ''), type, }).wrap(context); } /** * \left....\right * * Note that we can encounter malformed \left...\right, for example * a \left without a matching \right or vice versa. In that case, the * leftDelim (resp. rightDelim) will be undefined. We still need to handle * those cases. * */ class LeftRightAtom extends Atom { constructor(variant, body, options) { super('leftright', { style: options.style, displayContainsHighlight: true, }); this.variant = variant; this.body = body; this.leftDelim = options.leftDelim; this.rightDelim = options.rightDelim; } static fromJson(json) { var _a; return new LeftRightAtom((_a = json.variant) !== null && _a !== void 0 ? _a : '', json.body, json); } toJson() { const result = super.toJson(); if (this.variant) result.variant = this.variant; if (this.leftDelim) result.leftDelim = this.leftDelim; if (this.rightDelim) result.rightDelim = this.rightDelim; return result; } serialize(options) { var _a, _b; const rightDelim = this.matchingRightDelim(); if (this.variant === 'left...right') { return joinLatex([ '\\left' + ((_a = this.leftDelim) !== null && _a !== void 0 ? _a : '.'), this.bodyToLatex(options), '\\right' + rightDelim, ]); } if (this.variant === 'mleft...mright') { return joinLatex([ '\\mleft' + ((_b = this.leftDelim) !== null && _b !== void 0 ? _b : '.'), this.bodyToLatex(options), '\\mright' + rightDelim, ]); } return joinLatex([ !this.leftDelim || this.leftDelim === '.' ? '' : this.leftDelim, this.bodyToLatex(options), rightDelim, ]); } matchingRightDelim() { var _a, _b; if (this.rightDelim && this.rightDelim !== '?') return this.rightDelim; const leftDelim = (_a = this.leftDelim) !== null && _a !== void 0 ? _a : '.'; return (_b = RIGHT_DELIM[leftDelim]) !== null && _b !== void 0 ? _b : leftDelim; } render(parentContext) { var _a, _b, _c; const context = new Context(parentContext, this.style); console.assert(this.body !== undefined); // Calculate its height and depth // The size of delimiters is the same, regardless of what mathstyle we are // in. Thus, to correctly calculate the size of delimiter we need around // a group, we scale down the inner size based on the size. const delimContext = new Context(parentContext, this.style, 'textstyle'); const inner = (_a = Atom.createBox(context, this.body, { newList: true })) !== null && _a !== void 0 ? _a : new Box(null, { newList: true }); const innerHeight = inner.height / delimContext.scalingFactor; const innerDepth = inner.depth / delimContext.scalingFactor; const boxes = []; // Add the left delimiter to the beginning of the expression // @revisit: we call bind() on three difference boxes. Each box should // have a different ID. We should have a Box.hitTest() method to properly // handle the different boxes. if (this.leftDelim) { boxes.push(this.bind(delimContext, makeLeftRightDelim('mopen', this.leftDelim, innerHeight, innerDepth, delimContext, { classes: 'ML__open' + (this.containsCaret ? ' ML__contains-caret' : ''), mode: this.mode, style: this.style, }))); } if (inner) { // Replace the delim (\middle) boxes with proper ones now that we know // the height/depth if (inner.children) { for (let i = 0; i < inner.children.length; i++) { const child = inner.children[i]; if (child.delim) { const savedCaret = child.caret; inner.children[i] = this.bind(context, makeLeftRightDelim('minner', child.delim, innerHeight, innerDepth, context)); inner.children[i].caret = savedCaret; } } } boxes.push(inner); } // Add the right delimiter to the end of the expression. if (this.rightDelim) { let classes = this.containsCaret ? ' ML__contains-caret' : ''; let delim = this.rightDelim; if (delim === '?') { if (context.smartFence) { // Use a placeholder delimiter matching the open delimiter delim = this.matchingRightDelim(); classes += ' ML__smart-fence__close'; } else delim = '.'; } boxes.push(this.bind(delimContext, makeLeftRightDelim('mclose', delim, innerHeight, innerDepth, delimContext, { classes: classes + ' ML__close', mode: this.mode, style: this.style, }))); } // If the left sibling is a function (e.g. `\sin`, `f`...) // or we use the `mleft...mright` variant, // use a tighter spacing const tightSpacing = (_c = (this.variant === 'mleft...mright' || ((_b = this.leftSibling) === null || _b === void 0 ? void 0 : _b.isFunction))) !== null && _c !== void 0 ? _c : false; const result = new Box(boxes, { type: tightSpacing ? 'mclose' : 'minner', classes: 'left-right', }); if (this.caret) result.caret = this.caret; return this.bind(context, result.wrap(context)); } } class SubsupAtom extends Atom { constructor(options) { super('msubsup', { style: options === null || options === void 0 ? void 0 : options.style }); } static fromJson(json) { const result = new SubsupAtom(json); for (const branch of NAMED_BRANCHES) if (json[branch]) result.setChildren(json[branch], branch); return result; } toJson() { return super.toJson(); } render(context) { var _a; // The caret for this atom type is handled by its elements console.assert(!this.subsupPlacement); // The box type of a `subsup` atom is 'supsub' as it doesn't // have any special INTER_ATOM_SPACING with its attached atom (previous box) const phantomContex = new Context(context, { isPhantom: true }); const base = (_a = this.leftSibling.render(phantomContex)) !== null && _a !== void 0 ? _a : new Box(null); const phantom = new Box(null, { height: base.height, depth: base.depth }); return this.attachSupsub(context, { base: phantom, isCharacterBox: this.leftSibling.isCharacterBox(), // Set to 'supsub' so that it is skipped when walking the // atom to adjust for spacing. type: 'supsub', }); } serialize(options) { return this.supsubToLatex(options); } } const PLACEHOLDER_STRING = '■'; //'■' U+25A0 BLACK SQUARE //'▢' U+25A2 WHITE SQUARE WITH ROUNDED CORNERS //'⬚' U+2B1A DOTTED SQUARE class PlaceholderAtom extends Atom { constructor(options) { var _a; // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const value = (options === null || options === void 0 ? void 0 : options.value) || PLACEHOLDER_STRING; super('placeholder', { mode: (_a = options === null || options === void 0 ? void 0 : options.mode) !== null && _a !== void 0 ? _a : 'math', style: options === null || options === void 0 ? void 0 : options.style, value: value, command: '\\placeholder', }); this.captureSelection = true; this.placeholderId = options === null || options === void 0 ? void 0 : options.placeholderId; this.defaultValue = options === null || options === void 0 ? void 0 : options.default; } static fromJson(json) { return new PlaceholderAtom(json); } toJson() { const result = super.toJson(); if (this.placeholderId) result.placeholderId = this.placeholderId; if (this.value === PLACEHOLDER_STRING) delete result.value; if (this.defaultValue) result.defaultValue = this.defaultValue.map((x) => x.toJson()); return result; } render(context) { if (typeof context.renderPlaceholder === 'function') return context.renderPlaceholder(context, this); return this.createBox(context, { classes: this.caret ? 'ML__placeholder-selected' : '', }); } serialize(options) { var _a; let value = (_a = this.value) !== null && _a !== void 0 ? _a : ''; if (value === PLACEHOLDER_STRING) value = ''; const id = this.placeholderId ? `[${this.placeholderId}]` : ''; const defaultValue = this.defaultValue ? `[${Atom.serialize(this.defaultValue, options)}]` : ''; return `\\placeholder${id}${defaultValue}{${value}}`; } } /* * An atom representing a syntactic error, such as an unknown command */ class ErrorAtom extends Atom { constructor(value) { super('error', { value, command: value, mode: 'math' }); this.verbatimLatex = value; } static fromJson(json) { return new ErrorAtom(json.command); } toJson() { return super.toJson(); } render(context) { const result = this.createBox(context, { classes: 'ML__error' }); if (this.caret) result.caret = this.caret; return result; } } class MacroAtom extends Atom { constructor(macro, options) { var _a, _b, _c; super('macro', { command: macro }); this.body = options.body; // Set the `captureSelection` attribute so that the atom is handled // as an unbreakable unit this.captureSelection = (_a = options.captureSelection) !== null && _a !== void 0 ? _a : true; // Don't use verbatimLatex to save the macro, as it can get wiped when // the atom is modified (adding super/subscript, for example). this.macroArgs = (_b = options.args) !== null && _b !== void 0 ? _b : ''; this.expand = (_c = options.expand) !== null && _c !== void 0 ? _c : false; } static fromJson(json) { return new MacroAtom(json.command, json); } toJson() { const options = super.toJson(); if (this.expand) options.expand = true; if (!this.captureSelection) options.captureSelection = false; if (this.macroArgs) options.args = this.macroArgs; return options; } serialize(options) { return options.expandMacro && this.expand ? this.bodyToLatex(options) : this.command + this.macroArgs; } render(context) { const result = Atom.createBox(context, this.body); if (!result) return null; if (this.caret) result.caret = this.caret; return this.bind(context, result); } } class TextAtom extends Atom { constructor(command, value, style) { super('text', { command, mode: 'text', displayContainsHighlight: true }); this.value = value; this.verbatimLatex = value; this.applyStyle(style); } static fromJson(json) { return new TextAtom(json.command, json.value, json.style); } toJson() { return super.toJson(); } render(context) { const result = this.createBox(context); if (this.caret) result.caret = this.caret; return result; } serialize(_options) { var _a; return (_a = this.verbatimLatex) !== null && _a !== void 0 ? _a : charToLatex('text', this.value.codePointAt(0)); } } // Performance to check first char of string: https://jsben.ch/QLjdZ function isLiteral(token) { return !/^<({|}|\$|\$\$|space)>$/.test(token); } /** * Transform a list of tokens into a list of atoms (a mathlist in TeX's parlance) * */ class Parser { /** * @param tokens - An array of tokens generated by the lexer. */ constructor(tokens, options) { var _a, _b, _c, _d, _e, _f; // The current token to be parsed: index in `this.tokens` this.index = 0; this.smartFence = false; // Counter to prevent deadlock. If `end()` is called too many times (1,000) // in a row for the same token, bail. this.endCount = 0; this.tokens = tokens; this.args = (_a = options.args) !== null && _a !== void 0 ? _a : null; this.macros = options.macros; this.colorMap = options.colorMap; this.backgroundColorMap = (_b = options.backgroundColorMap) !== null && _b !== void 0 ? _b : this.colorMap; this.smartFence = (_c = options.smartFence) !== null && _c !== void 0 ? _c : false; this.onError = options.onError ? (err) => options.onError({ before: tokensToString(this.tokens.slice(this.index, this.index + 10)), after: tokensToString(this.tokens.slice(Math.max(0, this.index - 10), this.index)), ...err, }) : () => { }; this._parsingContexts = [ { parseMode: (_d = options.parseMode) !== null && _d !== void 0 ? _d : 'math', mathstyle: (_e = options.mathstyle) !== null && _e !== void 0 ? _e : 'displaystyle', registers: options.registers, tabular: false, style: (_f = options.style) !== null && _f !== void 0 ? _f : {}, mathlist: [], }, ]; } // swapMathlist(atoms: Atom[]): Atom[] { // const result = this.mathlist; // this.mathlist = atoms; // return result; // } // swapParseMode(mode: ParseMode): ParseMode { // const result = this.parseMode; // this.parseMode = mode; // return result; // } get currentContext() { return this._parsingContexts[this._parsingContexts.length - 1]; } beginContext(options) { var _a, _b, _c; this._parsingContexts.push({ parseMode: (_a = options === null || options === void 0 ? void 0 : options.mode) !== null && _a !== void 0 ? _a : this.currentContext.parseMode, mathstyle: (_b = options === null || options === void 0 ? void 0 : options.mathstyle) !== null && _b !== void 0 ? _b : this.currentContext.mathstyle, registers: null, style: { ...this.currentContext.style }, tabular: (_c = options === null || options === void 0 ? void 0 : options.tabular) !== null && _c !== void 0 ? _c : false, mathlist: [], }); } endContext() { this._parsingContexts.pop(); } get mathlist() { return this._parsingContexts[this._parsingContexts.length - 1].mathlist; } set mathlist(value) { this._parsingContexts[this._parsingContexts.length - 1].mathlist = value; } get parseMode() { return this._parsingContexts[this._parsingContexts.length - 1].parseMode; } // @revisit set parseMode(value) { this._parsingContexts[this._parsingContexts.length - 1].parseMode = value; } get tabularMode() { return this._parsingContexts[this._parsingContexts.length - 1].tabular; } get style() { // Style is inherited let i = this._parsingContexts.length - 1; let result = this._parsingContexts[i].style; while (result === undefined && i > 0) { i -= 1; result = this._parsingContexts[i].style; } return result !== null && result !== void 0 ? result : {}; } // @revisit set style(value) { this._parsingContexts[this._parsingContexts.length - 1].style = value; } getRegister(name) { var _a, _b, _c, _d; console.assert(name[0] !== '\\'); if (name.startsWith('global ')) return (_b = (_a = this._parsingContexts[0].registers) === null || _a === void 0 ? void 0 : _a[name.slice(7)]) !== null && _b !== void 0 ? _b : 0; let i = this._parsingContexts.length - 1; let registers = this._parsingContexts[i].registers; while (i >= 0 && registers && registers[name] === undefined) { i -= 1; registers = (_c = this._parsingContexts[i]) === null || _c === void 0 ? void 0 : _c.registers; } return (_d = registers === null || registers === void 0 ? void 0 : registers[name]) !== null && _d !== void 0 ? _d : 0; } setRegister(name, value) { if (name.startsWith('global ')) { // Set the global register, clear all the local ones. name = name.slice(7); let i = this._parsingContexts.length - 1; while (i > 0) { if (this._parsingContexts[i].registers) delete this._parsingContexts[i].registers[name]; i -= 1; } this._parsingContexts[0].registers[name] = value; return; } // Set the local register if (!this._parsingContexts[this._parsingContexts.length - 1].registers) this._parsingContexts[this._parsingContexts.length - 1].registers = {}; this._parsingContexts[this._parsingContexts.length - 1].registers[name] = value; } /** * True if we've reached the end of the token stream */ end() { // To prevent a deadlock, count how many times end() is called without the // index advancing. If it happens more than 1,000 times in a row, // assume something is broken and pretend the stream is finished. this.endCount++; return this.index >= this.tokens.length || this.endCount > 1000; } get() { this.endCount = 0; return this.index < this.tokens.length ? this.tokens[this.index++] : ''; } peek() { return this.tokens[this.index]; } /** * @return True if the next token matches the input, and advance */ match(input) { if (this.tokens[this.index] === input) { this.index++; return true; } return false; } /** * Return the last atom that can have a subscript/superscript attached to it. * If there isn't one, insert a `SubsupAtom` and return it. */ lastSubsupAtom() { let atom; if (this.mathlist.length > 0) { atom = this.mathlist[this.mathlist.length - 1]; // If this is a `subsup` atom, it can have a `subsup` attached to it. if (atom.type === 'msubsup') return atom; // if (atom.type === 'mop' && atom?.limits && atom?.limits !== undefined) { // An atom can have superscript/subscript attached to it if it accepts // limits (`\sum`, `\vec`...) if (atom.subsupPlacement !== undefined) return atom; } // Create a new `subsup` atom and return it atom = new SubsupAtom({ style: this.style }); this.mathlist.push(atom); return atom; } /** * @return True if the next token matches the specified regular expression pattern. */ hasPattern(pattern) { return pattern.test(this.tokens[this.index]); } hasInfixCommand() { var _a; const { index } = this; if (index < this.tokens.length && this.tokens[index].startsWith('\\')) { const info = getInfo(this.tokens[index], this.parseMode, this.macros); if (!info) return false; if (info.ifMode && !info.ifMode.includes(this.parseMode)) return false; return (_a = info.infix) !== null && _a !== void 0 ? _a : false; } return false; } matchColumnSeparator() { const { index } = this; if (this.tabularMode && this.tokens[index] === '&') { this.index++; return true; } return false; } matchRowSeparator() { const { index } = this; if (this.tabularMode && (this.tokens[index] === '\\\\' || this.tokens[index] === '\\cr')) { this.index++; return true; } return false; } /** * Return the appropriate value for a placeholder, either a default * one, or if a value was provided for #? via args, that value. */ placeholder() { var _a; const placeHolderArg = (_a = this.args) === null || _a === void 0 ? void 0 : _a.call(this, '?'); if (!placeHolderArg) { return [ new PlaceholderAtom({ mode: this.parseMode, style: this.style, }), ]; } // If there is a specific value defined for the placeholder, // use it. return parseLatex(placeHolderArg, { parseMode: this.parseMode, onError: this.onError, registers: this.currentContext.registers, mathstyle: 'textstyle', colorMap: this.colorMap, backgroundColorMap: this.backgroundColorMap, }); } matchWhitespace() { let found = false; while (this.match('<space>')) found = true; return found; } skipUntilToken(input) { let token = this.tokens[this.index]; while (token && token !== input) token = this.tokens[++this.index]; if (token === input) this.index++; } skipFiller() { let done = false; do { const skippedSpace = this.matchWhitespace(); const skippedRelax = this.match('\\relax'); done = !skippedSpace && !skippedRelax; } while (!done); } /** * Keywords are used to specify dimensions, and for various other * syntactic constructs. * * Unlike commands, they are not case sensitive. * * There are 25 keywords: * * at by bp cc cm dd depth em ex fil fill filll height in minus * mm mu pc plus pt sp spread to true width * * TeX: 8212 * @return true if the expected keyword is present */ matchKeyword(keyword) { const savedIndex = this.index; let done = this.end(); let value = ''; while (!done) { const token = this.get(); if (isLiteral(token)) { value += token; done = this.end() || value.length >= keyword.length; } else done = true; } const hasKeyword = keyword.toUpperCase() === value.toUpperCase(); if (!hasKeyword) this.index = savedIndex; return hasKeyword; } /** * Return a sequence of characters as a string. * i.e. 'abcd' returns 'abcd'. * Terminates on the first non-literal token encountered * e.g. '<{>', '<}>' etc... * Will also terminate on character literal ']' */ scanString() { let result = ''; while (!this.end()) { if (this.match('<space>')) result += ' '; else { const token = this.peek(); if (token === ']') break; if (isLiteral(token)) result += this.get(); else if (token.startsWith('\\')) { // TeX will give a 'Missing \endcsname inserted' error // if it encounters any command when expecting a string. // We're a bit more lax. this.onError({ code: 'unbalanced-braces' }); result += this.get(); } else { // It's '<{>', '<}>', '<$>' or '<$$> break; } } } return result; } /** * Return a sequence of characters as a string. * Terminates on a balanced closing bracket * This is used by the `\ce` command */ scanBalancedString() { let result = ''; let done = this.end(); let level = 1; while (!done) { if (this.match('<space>')) result += ' '; else { const token = this.get(); if (token === '<{>') { result += '{'; level += 1; } else if (token === '<}>') { level -= 1; if (level > 0) result += '}'; else this.index -= 1; } else if (token === '<$>') result += '$'; else if (token === '<$$>') result += '$$'; else result += token; } done = level === 0 || this.end(); } return result; } /** * Return the literal tokens, as a string, until a matching closing "}" */ matchLiteralArg() { var _a; let result = ''; if (this.match('<{>')) { let depth = 1; while (depth > 0 && !this.end()) { const token = this.get(); if (token === '<}>') { depth -= 1; if (depth > 0) { // Don't include final '}' result += '}'; } } else if (token === '<{>') { depth += 1; result += '{'; } else { result += (_a = { '<space>': ' ', '<$$>': '$$', '<$>': '$', }[token]) !== null && _a !== void 0 ? _a : token; } } } return result; } /** * Return as a number a group of characters representing a * numerical quantity. * * From TeX:8695 (scan_int): * > An integer number can be preceded by any number of spaces and `+' or * > `-' signs. Then comes either a decimal constant (i.e., radix 10), an * > octal constant (i.e., radix 8, preceded by '), a hexadecimal constant * > (radix 16, preceded by "), an alphabetic constant (preceded by `), or * > an internal variable. */ scanNumber(isInteger = true) { var _a, _b; let negative = false; let token = this.peek(); while (token === '<space>' || token === '+' || token === '-') { this.get(); if (token === '-') negative = !negative; token = this.peek(); } isInteger = Boolean(isInteger); let radix = 10; let digits = /\d/; if (this.match("'")) { // Apostrophe indicates an octal value radix = 8; digits = /[0-7]/; isInteger = true; } else if (this.match('"') || this.match('x')) { // Double-quote indicates a hex value // The 'x' prefix notation for the hexadecimal numbers is a MathJax extension. // For example: 'x3a' radix = 16; // Hex digits have to be upper-case digits = /[\dA-F]/; isInteger = true; } else if (this.match('`')) { // A backtick indicates an alphabetic constant: a letter, or a single-letter command token = this.get(); if (token) { if (token.startsWith('\\') && token.length === 2) return (negative ? -1 : 1) * ((_a = token.codePointAt(1)) !== null && _a !== void 0 ? _a : 0); return (negative ? -1 : 1) * ((_b = token.codePointAt(0)) !== null && _b !== void 0 ? _b : 0); } return null; } let value = ''; while (this.hasPattern(digits)) value += this.get(); // Parse the fractional part, if applicable // Note: TeX does accept `,` as a decimal separator see TeX: `continental_point_token` if (!isInteger && (this.match('.') || this.match(','))) { value += '.'; while (this.hasPattern(digits)) value += this.get(); } const result = isInteger ? Number.parseInt(value, radix) : Number.parseFloat(value); if (Number.isNaN(result)) return null; return negative ? -result : result; } /** * Return a dimension * * See TeX:8831 */ scanDimen() { var _a, _b, _c; let value = this.scanNumber(false); if (value === null) { // This wasn't a number, but perhaps it's a register name? if ((_a = this.peek()) === null || _a === void 0 ? void 0 : _a.startsWith('\\')) value = 1; else return null; } this.matchWhitespace(); let result; if (this.matchKeyword('pt')) result = { dimension: value, unit: 'pt' }; else if (this.matchKeyword('mm')) result = { dimension: value, unit: 'mm' }; else if (this.matchKeyword('cm')) result = { dimension: value, unit: 'cm' }; else if (this.matchKeyword('ex')) result = { dimension: value, unit: 'ex' }; else if (this.matchKeyword('px')) result = { dimension: value, unit: 'px' }; else if (this.matchKeyword('em')) result = { dimension: value, unit: 'em' }; else if (this.matchKeyword('bp')) result = { dimension: value, unit: 'bp' }; else if (this.matchKeyword('dd')) result = { dimension: value, unit: 'dd' }; else if (this.matchKeyword('pc')) result = { dimension: value, unit: 'pc' }; else if (this.matchKeyword('in')) result = { dimension: value, unit: 'in' }; else if (this.matchKeyword('mu')) result = { dimension: value, unit: 'mu' }; else { if ((_b = this.peek()) === null || _b === void 0 ? void 0 : _b.startsWith('\\')) { result = (_c = convertToDimension(this.getRegister(this.get().slice(1)), this.currentContext.registers)) !== null && _c !== void 0 ? _c : { dimension: 0 }; result.dimension *= value; } else { if (!this.match('\\relax')) { // If the units are missing, TeX assumes 'pt' this.onError({ code: 'missing-unit' }); } result = { dimension: value, unit: 'pt' }; } } return result; } scanGlue() { const dimen = this.scanDimen(); if (dimen === null) return null; const result = { glue: dimen }; // We parse, but ignore, the optional 'plus' and 'minus' // arguments. this.matchWhitespace(); if (this.match('\\relax')) return result; // 'plus', optionally followed by 'minus' // ('minus' cannot come before 'plus') // dimen or 'hfill' if (this.matchKeyword('plus')) { // @todo there could also be a \hFilLlL command here this.scanDimen(); } this.matchWhitespace(); if (this.match('\\relax')) return result; this.matchWhitespace(); if (this.matchKeyword('minus')) { // @todo there could also be a \hFilLlL command here this.scanDimen(); } return result; } scanColspec() { this.matchWhitespace(); const result = []; while (!this.end() && !(this.peek() === '<}>' || this.peek() === ']')) { const literal = this.get(); if (literal === 'c' || literal === 'r' || literal === 'l') result.push({ align: literal }); else if (literal === '|') result.push({ separator: 'solid' }); else if (literal === ':') result.push({ separator: 'dashed' }); else if (literal === '@') { if (this.match('<{>')) { this.beginContext({ mode: 'math' }); result.push({ gap: this.parse((token) => token === '<}>'), }); this.endContext(); } if (!this.match('<}>')) this.onError({ code: 'unbalanced-braces' }); } } return result; } /** * Parse a `\(...\)` or `\[...\]` sequence * @return group for the sequence or null */ parseModeSet() { let final = ''; if (this.match('\\(')) final = '\\)'; if (!final && this.match('\\[')) final = '\\]'; if (!final) return null; this.beginContext({ mode: 'math', mathstyle: final === '\\)' ? 'textstyle' : 'displaystyle', }); const result = new GroupAtom(this.parse((token) => token === final), { mathstyleName: final === '\\)' ? 'textstyle' : 'displaystyle', latexOpen: final === '\\]' ? '\\[' : '\\(', latexClose: final, boxType: 'mord', }); if (!this.match(final)) this.onError({ code: 'unbalanced-mode-shift' }); this.endContext(); if (result.hasEmptyBranch('body')) return null; return result; } /** * Parse a `$...$` or `$$...$$` sequence */ parseModeShift() { let final = ''; if (this.match('<$>')) final = '<$>'; if (!final && this.match('<$$>')) final = '<$$>'; if (!final) return null; this.beginContext({ mode: 'math', mathstyle: 'textstyle' , }); const result = new GroupAtom(this.parse((token) => token === final), { mathstyleName: final === '<$>' ? 'textstyle' : 'displaystyle', latexOpen: final === '<$>' ? '$ ' : '$$ ', latexClose: final === '<$>' ? ' $' : ' $$', }); if (!this.match(final)) this.onError({ code: 'unbalanced-mode-shift' }); this.endContext(); if (result.hasEmptyBranch('body')) return null; return result; } /** * Parse a \begin{env}...\end{end} sequence */ parseEnvironment() { // An environment starts with a \begin command if (!this.match('\\begin')) return null; // The \begin command is immediately followed by the environment // name, as a string argument const envName = this.parseArgument('string'); if (!envName) return null; const def = getEnvironmentDefinition(envName); if (!def) { this.onError({ code: 'unknown-environment', arg: envName, }); return null; } // If the environment has some arguments, parse them const args = []; if (def.params) { for (const parameter of def.params) { // Parse an argument if (parameter.isOptional) { // If it's not present, parseOptionalArgument returns null, // but push it on the list of arguments anyway. // The null value will be interpreted as unspecified // optional value by the command parse function. args.push(this.parseOptionalArgument(parameter.type)); } else { const arg = this.parseArgument(parameter.type); if (!arg) { this.onError({ code: 'missing-argument', arg: envName, }); } args.push(arg); } } } this.beginContext({ tabular: def.tabular }); const array = []; const rowGaps = []; let row = []; let done = false; do { if (this.end()) { this.onError({ code: 'unbalanced-environment', arg: envName }); done = true; } if (!done && this.match('\\end')) { if (this.parseArgument('string') !== envName) { this.onError({ code: 'unbalanced-environment', arg: envName, }); } done = true; } if (!done) { if (this.matchColumnSeparator()) { row.push(this.mathlist); this.mathlist = []; } else if (this.matchRowSeparator()) { row.push(this.mathlist); this.mathlist = []; let gap = null; this.matchWhitespace(); if (this.match('[')) { gap = this.scanDimen(); this.matchWhitespace(); this.match(']'); } rowGaps.push(gap !== null && gap !== void 0 ? gap : { dimension: 0 }); array.push(row); row = []; } else { this.mathlist.push(...this.parse((token) => token === '<}>' || token === '&' || token === '\\end' || token === '\\cr' || token === '\\\\')); } } } while (!done); row.push(this.mathlist); if (row.length > 0) array.push(row); this.endContext(); return def.createAtom(envName, array, rowGaps, args); } /** * Parse a sequence until a group end marker, such as * `}`, `\end`, `&`, etc... * * Returns an array of atoms or an empty array if the sequence * terminates right away. * * @param done - A predicate indicating if a * token signals the end of an implicit group */ parse(done) { this.beginContext(); if (!done) { // Default group end marker done = (token) => token === '<}>'; } // To handle infix commands, we'll keep track of their prefix // (tokens coming before them) and their arguments let infix = ''; let infixInfo = null; let infixArgs = []; let prefix = null; const saveAtoms = this.mathlist; this.mathlist = []; while (!this.end() && !done(this.peek())) { if (this.hasInfixCommand() && !infix) { // The next token is an infix and we have not seen one yet // (there can be only one infix command per implicit group). infix = this.get(); // The current parseMode, this.parseMode, may no longer have the value // it had when we encountered the infix. However, since all infix are // only defined in 'math' mode, we can use the 'math' constant // for the parseMode infixInfo = getInfo(infix, 'math', this.macros); if (infixInfo) infixArgs = this.parseArguments(infixInfo)[1]; // Save the math list so far and start a new one prefix = this.mathlist; this.mathlist = []; } else this.parseToken(); } let result; if (infix) { console.assert(Boolean(infixInfo)); infixArgs.unshift(this.mathlist); // Suffix this.mathlist = saveAtoms; if (prefix) infixArgs.unshift(prefix); result = [ infixInfo.createAtom(infix, infixArgs, this.style, { colorMap: this.colorMap, backgroundColorMap: this.backgroundColorMap, }), ]; } else { result = this.mathlist; this.mathlist = saveAtoms; } this.endContext(); return result; } /** * Parse a group enclosed in a pair of braces: `{...}`. * * Return either a group Atom or null if not a group. * * Return a group Atom with an empty body if an empty * group (i.e. `{}`). */ parseGroup() { if (!this.match('<{>')) return null; const result = new GroupAtom(this.parse((token) => token === '<}>'), { mode: this.parseMode, latexOpen: '{', latexClose: '}', }); if (!this.match('<}>')) this.onError({ code: 'unbalanced-braces' }); return result; } scanSmartFence() { this.matchWhitespace(); if (!this.match('(')) return null; // We've found an open paren... Convert to a `\left...\right` this.beginContext(); let nestLevel = 1; while (!this.end() && nestLevel !== 0) { if (this.match('(')) nestLevel += 1; if (this.match(')')) nestLevel -= 1; if (nestLevel !== 0) this.parseToken(); } if (nestLevel === 0) this.match(')'); const result = new LeftRightAtom('', this.mathlist, { leftDelim: '(', rightDelim: nestLevel === 0 ? ')' : '?', }); this.endContext(); return result; } /** * Scan a delimiter, e.g. '(', '|', '\vert', '\ulcorner' * * @return The delimiter (as a character or command) or null */ scanDelim() { this.matchWhitespace(); const token = this.get(); if (!token) { this.onError({ code: 'unexpected-end-of-string' }); return null; } let delim = '.'; if (token.startsWith('\\') || isLiteral(token)) delim = token; const info = getInfo(delim, 'math', this.macros); if (!info) { this.onError({ code: 'unknown-command', arg: delim }); return null; } if (info.ifMode && !info.ifMode.includes(this.parseMode)) { this.onError({ code: 'unexpected-delimiter', arg: delim }); return null; } if (info.type === 'mopen' || info.type === 'mclose') return delim; // Some symbols are not of type mopen/mclose, but are still // valid delimiters... // '?' is a special delimiter used as a 'placeholder' // (when the closing delimiter is displayed greyed out) if (/^(\.|\?|\||<|>|\\vert|\\Vert|\\\||\\surd|\\uparrow|\\downarrow|\\Uparrow|\\Downarrow|\\updownarrow|\\Updownarrow|\\mid|\\mvert|\\mVert)$/.test(delim)) return delim; this.onError({ code: 'unexpected-delimiter', arg: delim }); return null; } /** * Parse a `/left.../right` sequence. * * Note: the `/middle` command can occur multiple times inside a * `/left.../right` sequence, and is handled separately. * * Return either an atom of type `"leftright"` or null */ parseLeftRight() { var _a; if (this.match('\\right') || this.match('\\mright')) { // We have an unbalanced left/right (there's a \right, but no \left) this.onError({ code: 'unbalanced-braces' }); return null; } let close = '\\right'; if (!this.match('\\left')) { if (!this.match('\\mleft')) return null; close = '\\mright'; } const leftDelim = this.scanDelim(); if (!leftDelim) return null; this.beginContext(); while (!this.end() && !this.match(close)) this.parseToken(); const body = this.mathlist; this.endContext(); // If we've reached the end and there was no `\right` or // there isn't a valid delimiter after `\right`, we'll // consider the `\right` missing and set the `rightDelim` to undefined const rightDelim = (_a = this.scanDelim()) !== null && _a !== void 0 ? _a : '.'; return new LeftRightAtom(close === '\\right' ? 'left...right' : 'mleft...mright', body, { leftDelim, rightDelim, style: this.style, }); } /** * Parse a subscript/superscript: `^` and `_`. * * Modify the last atom accordingly, or create a new 'msubsup' carrier. * */ parseSupSub() { // No sup/sub in text or command mode. if (this.parseMode !== 'math') return false; // Apply the subscript/superscript to the last rendered atom. // If none is present (beginning of the list, i.e. `{^2}`, // an empty atom will be created, equivalent to `{{}^2}` let token = this.peek(); if (token !== '^' && token !== '_' && token !== "'") return false; while (token === '^' || token === '_' || token === "'") { if (this.match("'")) { if (this.match("'")) { // A single quote, twice, is equivalent to '^{\doubleprime}' this.lastSubsupAtom().addChild(new Atom('mord', { command: '\\doubleprime', mode: 'math', value: '\u2032\u2032', // "\u2033" displays too high }), 'superscript'); } else { // A single quote (prime) is equivalent to '^{\prime}' this.lastSubsupAtom().addChild(new Atom('mord', { command: '\\prime', mode: 'math', value: '\u2032', }), 'superscript'); } } else if (this.match('^') || this.match('_')) { const arg = this.parseArgument('math'); if (arg) { this.lastSubsupAtom().addChildren(arg, token === '_' ? 'subscript' : 'superscript'); } else { this.lastSubsupAtom().createBranch(token === '_' ? 'subscript' : 'superscript'); } } token = this.peek(); } return true; } /** * Parse a `\limits` or `\nolimits` command. * * This will change the placement of limits to be either above or below * (if `\limits`) or in the superscript/subscript position (if `\nolimits`). * * This overrides the calculation made for the placement, which is usually * dependent on the displaystyle (`textstyle` prefers `\nolimits`, while * `displaystyle` prefers `\limits`). */ parseLimits() { // Note: `\limits`, `\nolimits` and `\displaylimits` are only applicable \ // after an operator. // We skip them and ignore them if they are after something other // than an operator (TeX throws an error) const isLimits = this.match('\\limits'); const isNoLimits = !isLimits && this.match('\\nolimits'); const isDisplayLimits = !isNoLimits && !isLimits && this.match('\\displaylimits'); if (!isLimits && !isNoLimits && !isDisplayLimits) return false; const opAtom = this.mathlist.length > 0 ? this.mathlist[this.mathlist.length - 1] : null; if (opAtom === null || opAtom.type !== 'mop') return false; if (isLimits) { opAtom.subsupPlacement = 'over-under'; // Record that the limits was set through an explicit command // so we can generate the appropriate LaTeX later opAtom.explicitSubsupPlacement = true; return true; } if (isNoLimits) { opAtom.subsupPlacement = 'adjacent'; opAtom.explicitSubsupPlacement = true; return true; } if (isDisplayLimits) { opAtom.subsupPlacement = 'auto'; opAtom.explicitSubsupPlacement = true; return true; } return false; } parseArguments(info) { if (!info || !info.params) return [undefined, []]; let explicitGroup = undefined; const args = []; let i = info.infix ? 2 : 0; while (i < info.params.length) { const parameter = info.params[i]; // Parse an argument if (parameter.type === 'rest') { args.push(this.parse((token) => token === '<}>' || token === '&' || token === '\\end' || token === '\\cr' || token === '\\\\')); } else if (parameter.isOptional) args.push(this.parseOptionalArgument(parameter.type)); else if (parameter.type.endsWith('*')) { // For example 'math*'. // In this case, indicate that a 'yet-to-be-parsed' // argument (and 'explicit group') is present explicitGroup = parameter.type.slice(0, -1); } else { const arg = this.parseArgument(parameter.type); if (arg !== null) args.push(arg); else { // Report an error this.onError({ code: 'missing-argument' }); switch (parameter.type) { case 'number': // case 'dimen': // case 'glue': args.push(0); break; case 'dimen': args.push({ dimension: 0, unit: 'pt' }); break; case 'glue': args.push({ glue: { dimension: 0, unit: 'pt' } }); break; case 'string': case 'balanced-string': args.push(''); break; case 'delim': args.push('.'); break; case 'colspec': args.push('llllllllll'); break; case 'auto': default: args.push(this.placeholder()); break; } } } i += 1; } return [explicitGroup, args]; } parseArgument(argType) { var _a, _b; this.skipFiller(); if (argType === 'auto') argType = this.parseMode; let result = null; // An argument (which is called a 'math field' in TeX) // could be a single character or symbol, as in `\frac12` // Note that ``\frac\sqrt{-1}\alpha\beta`` is equivalent to // ``\frac{\sqrt}{-1}{\beta}`` const hasBrace = this.peek() === '<{>'; if (!hasBrace) { if (argType === 'delim') return (_a = this.scanDelim()) !== null && _a !== void 0 ? _a : '.'; if (argType === 'text' || argType === 'math') { // Parse a single token. this.beginContext(); const atom = this.parseSimpleToken(); this.endContext(); return atom; } } if (hasBrace) this.get(); if (argType === 'text' || argType === 'math') { this.beginContext({ mode: argType }); do this.mathlist.push(...this.parse()); while (!this.match('<}>') && !this.end()); } else { this.beginContext(); if (argType === 'string') result = this.scanString(); else if (argType === 'balanced-string') result = this.scanBalancedString(); else if (argType === 'number') result = this.scanNumber(); else if (argType === 'colspec') result = this.scanColspec(); else if (argType === 'dimen') result = this.scanDimen(); else if (argType === 'glue') result = this.scanGlue(); else if (argType === 'delim') result = (_b = this.scanDelim()) !== null && _b !== void 0 ? _b : '.'; if (hasBrace) this.skipUntilToken('<}>'); if (result === null) { this.endContext(); return null; } } const atoms = this.mathlist; this.endContext(); return result !== null && result !== void 0 ? result : atoms; } parseOptionalArgument(argType) { var _a; argType = argType === 'auto' ? this.parseMode : argType; this.matchWhitespace(); if (!this.match('[')) return null; let result = null; while (!this.end() && !this.match(']')) { if (argType === 'string') result = this.scanString(); else if (argType === 'number') result = this.scanNumber(); else if (argType === 'dimen') result = this.scanDimen(); else if (argType === 'glue') result = this.scanGlue(); else if (argType === 'colspec') result = this.scanColspec(); else if (argType === 'bbox') { // The \bbox command takes a very particular argument: // a comma delimited list of up to three arguments: // a color, a dimension and a string. // Split the string by comma delimited sub-strings, ignoring commas // that may be inside (). For example"x, rgb(a, b, c)" would return // ['x', 'rgb(a, b, c)'] const list = this.scanString() .toLowerCase() .trim() .split(/,(?![^(]*\)(?:(?:[^(]*\)){2})*[^"]*$)/); const bboxParameter = {}; for (const element of list) { const color = (_a = this.backgroundColorMap) === null || _a === void 0 ? void 0 : _a.call(this, element); if (color) bboxParameter.backgroundcolor = color; else { const m = element.match(/^\s*([\d.]+)\s*([a-z]{2})/); if (m) bboxParameter.padding = m[0]; else { const m = element.match(/^\s*border\s*:\s*(.*)/); if (m) bboxParameter.border = m[1]; } } } result = bboxParameter; } else if (argType === 'math') { this.beginContext({ mode: 'math' }); result = this.mathlist.concat(this.parse((token) => token === ']')); this.endContext(); } } return result; } parseCommand(command) { var _a, _b, _c, _d, _e, _f; if (command === '\\placeholder') { const placeholder = new PlaceholderAtom({ mode: this.parseMode, placeholderId: this.parseOptionalArgument('string'), value: (_a = this.parseArgument('string')) !== null && _a !== void 0 ? _a : undefined, style: this.style, }); return [placeholder]; } let result = null; if (command === '\\char') { const initialIndex = this.index; let codepoint = Math.floor((_b = this.scanNumber(true)) !== null && _b !== void 0 ? _b : Number.NaN); if (!Number.isFinite(codepoint) || codepoint < 0 || codepoint > 0x10ffff) codepoint = 0x2753; // BLACK QUESTION MARK const verbatimLatex = '\\char' + tokensToString(this.tokens.slice(initialIndex, this.index)); result = new Atom(this.parseMode === 'math' ? 'mord' : 'text', { command: '\\char', mode: this.parseMode, value: String.fromCodePoint(codepoint), serialize: () => verbatimLatex, }); result.verbatimLatex = verbatimLatex; return [result]; } if (command === '\\hskip' || command === '\\kern') { const width = this.scanGlue(); if (!width) return null; return [new SpacingAtom(command, this.style, width)]; } // Is this a macro? result = this.scanMacro(command); if (result) return [result]; // This wasn't a macro, so let's see if it's a regular command const info = getInfo(command, this.parseMode, {}); if (!info) { // An unknown command this.onError({ code: 'unknown-command', arg: command, }); return [new ErrorAtom(command)]; } if (info.ifMode && !info.ifMode.includes(this.parseMode)) { // Command not applicable in this mode: ignore it (TeX behavior) // (for example `\Huge` in math mode return []; } // Parse the arguments. // // If `deferredArg` is not empty, the content after the command // will be parsed *after* the command has been initially processed // (atom creation or style application) and passed to // // This is used for commands such as \textcolor{color}{content} // that need to apply the color to the content *after* the // style has been changed. // // In definitions, this is indicated with a parameter type // thats ends with a '*' ('math*', 'auto*'). const savedMode = this.parseMode; if (info.applyMode) this.parseMode = info.applyMode; const initialIndex = this.index; const [deferredArg, args] = this.parseArguments(info); this.parseMode = savedMode; if (!args) return null; // Some required arguments were missing... if (info.applyMode && !info.applyStyle && !info.createAtom) return args[0]; if (info.infix) { // Infix commands should be handled in scanImplicitGroup // If we find an infix command here, it's a syntax error // (second infix command in a group) and should be ignored. this.onError({ code: 'too-many-infix-commands', arg: command, }); return null; } // Invoke the createAtom() function if present if (typeof info.createAtom === 'function') { result = info.createAtom(command, args, this.style, { colorMap: this.colorMap, backgroundColorMap: this.backgroundColorMap, }); if (deferredArg) result.body = (_c = this.parseArgument(deferredArg)) !== null && _c !== void 0 ? _c : undefined; } else if (typeof info.applyStyle === 'function') { const style = info.applyStyle(command, args, { colorMap: this.colorMap, backgroundColorMap: this.backgroundColorMap, }); // No type provided -> the parse function will modify // the current style rather than create a new Atom. const savedMode = this.parseMode; if (info.applyMode) { // Change to 'text' (or 'math') mode if necessary this.parseMode = info.applyMode; } // If a deferred arg is expected, process it now if (deferredArg) { // Create a temporary style const saveStyle = this.style; this.style = { ...this.style, ...style }; const atoms = this.parseArgument(deferredArg); this.style = saveStyle; this.parseMode = savedMode; return atoms; } // Merge the new style info with the current style this.style = { ...this.style, ...style }; this.parseMode = savedMode; } else { // The new atom will inherit the current style const style = { ...this.style }; // Override the variant if an explicit variant is provided if (info.variant) style.variant = info.variant; result = new Atom((_d = info.type) !== null && _d !== void 0 ? _d : 'mop', { command, style, value: info.codepoint ? String.fromCodePoint(info.codepoint) : command, mode: (_e = info.applyMode) !== null && _e !== void 0 ? _e : this.parseMode, }); } if (result instanceof Atom && result.verbatimLatex === undefined && !/^\\(llap|rlap|class|cssId|htmlData)$/.test(command)) { result.verbatimLatex = ((_f = result.command) !== null && _f !== void 0 ? _f : '') + tokensToString(this.tokens.slice(initialIndex, this.index)); if (result.verbatimLatex.length === 0) result.verbatimLatex = undefined; if (result.isFunction && this.smartFence) { // The command was a function that may be followed by // an argument, like `\sin(` const smartFence = this.scanSmartFence(); if (smartFence) return [result, smartFence]; } } if (!result) return null; return [result]; } parseLiteral(literal) { const result = Mode.createAtom(this.parseMode, literal, { ...this.style, }); if (!result) return null; if (result.isFunction && this.smartFence) { // The atom was a function that may be followed by // an argument, like `f(`. const smartFence = this.scanSmartFence(); if (smartFence) return [result, smartFence]; } return [result]; } parseSimpleToken() { const token = this.get(); if (!token) return null; if (token === '<space>') { if (this.parseMode === 'text') return [new TextAtom(' ', ' ', this.style)]; return null; } if (token.startsWith('\\')) return this.parseCommand(token); if (isLiteral(token)) return this.parseLiteral(token); if (token === '<}>') this.onError({ latex: '', code: 'unbalanced-braces' }); else { this.onError({ latex: '', code: 'unexpected-token', arg: token, }); } return null; } /** * Attempt to scan the macro name and return an atom list if successful. * Otherwise, it wasn't a macro. */ scanMacro(macro) { var _a, _b; const macroName = macro.slice(1); if (!this.macros || !this.macros[macroName]) return null; const initialIndex = this.index; const def = this.macros[macroName].def; const argCount = this.macros[macroName].args; const args = {}; for (let i = 1; i <= argCount; i++) { // Parse each argument as a string. We don't know yet // what the proper parse mode is, so defer parsing till later // when invoking `parseString` args[i] = this.matchLiteralArg(); } // Carry forward the placeholder argument, if any. args['?'] = (_a = this.args) === null || _a === void 0 ? void 0 : _a.call(this, '?'); // Group the result of the macro expansion return new MacroAtom(macro, { expand: this.macros[macroName].expand, captureSelection: this.macros[macroName].captureSelection, args: tokensToString(this.tokens.slice(initialIndex, this.index)), body: parseLatex(def, { parseMode: this.parseMode, args: (arg) => args[arg], macros: this.macros, registers: (_b = this.currentContext.registers) !== null && _b !== void 0 ? _b : null, mathstyle: this.currentContext.mathstyle, colorMap: this.colorMap, backgroundColorMap: this.backgroundColorMap, onError: this.onError, }), }); } /** * Make an atom for the current token or token group and * add it to the parser's mathlist */ parseToken() { var _a, _b, _c, _d; let result = (_d = (_c = (_b = (_a = this.parseEnvironment()) !== null && _a !== void 0 ? _a : this.parseModeShift()) !== null && _b !== void 0 ? _b : this.parseModeSet()) !== null && _c !== void 0 ? _c : this.parseGroup()) !== null && _d !== void 0 ? _d : this.parseLeftRight(); if (result === null) { if (this.parseSupSub()) return true; if (this.parseLimits()) return true; result = this.parseSimpleToken(); } // If we have an atom to add, push it at the end of the current math list // We could have no atom for tokens that were skipped, a ' ' in math mode // for example if (isArray(result)) this.mathlist.push(...result); else if (result) this.mathlist.push(result); return result !== null; } } /** * Given a string of LaTeX, return a corresponding array of atoms. * @param args - If there are any placeholder tokens, e.g. * `#0`, `#1`, etc... they will be replaced by the value provided by `args`. * @param smartFence - If true, promote plain fences, e.g. `(`, * as `\left...\right` or `\mleft...\mright` */ function parseLatex(s, options) { var _a, _b, _c, _d, _e, _f, _g, _h, _j; const parser = new Parser(tokenize(s, (_a = options === null || options === void 0 ? void 0 : options.args) !== null && _a !== void 0 ? _a : null), { args: (_b = options === null || options === void 0 ? void 0 : options.args) !== null && _b !== void 0 ? _b : null, macros: normalizeMacroDictionary((_c = options === null || options === void 0 ? void 0 : options.macros) !== null && _c !== void 0 ? _c : {}), registers: (_d = options === null || options === void 0 ? void 0 : options.registers) !== null && _d !== void 0 ? _d : null, mathstyle: (_e = options === null || options === void 0 ? void 0 : options.mathstyle) !== null && _e !== void 0 ? _e : 'displaystyle', colorMap: (_f = options === null || options === void 0 ? void 0 : options.colorMap) !== null && _f !== void 0 ? _f : defaultColorMap, parseMode: (_g = options === null || options === void 0 ? void 0 : options.parseMode) !== null && _g !== void 0 ? _g : 'math', smartFence: options === null || options === void 0 ? void 0 : options.smartFence, backgroundColorMap: (_j = (_h = options === null || options === void 0 ? void 0 : options.backgroundColorMap) !== null && _h !== void 0 ? _h : options === null || options === void 0 ? void 0 : options.colorMap) !== null && _j !== void 0 ? _j : defaultBackgroundColorMap, onError: (err) => { if (typeof (options === null || options === void 0 ? void 0 : options.onError) === 'function') options.onError({ ...err, latex: s }); }, }); const atoms = []; while (!parser.end()) { const more = parser.parse(); if (!more) break; atoms.push(...more); } return atoms; } function convertToGlue(value, registers) { // If it's already a Glue, return it. if (typeof value === 'object' && 'glue' in value) return value; if (typeof value === 'object' && 'dimension' in value) return { glue: value }; if (typeof value === 'number') return { glue: { dimension: value } }; // It's a string, attempt to parse it. const parser = new Parser(tokenize(value), { registers }); return parser.scanGlue(); } /** Return a dimension. */ function convertToDimension(value, registers) { if (typeof value === 'number') return { dimension: value, unit: 'pt' }; if (typeof value === 'object' && 'glue' in value) value = value.glue; if (typeof value === 'object' && 'dimension' in value) return value; // It's a string const parser = new Parser(tokenize(value), { registers }); return parser.scanDimen(); } class AccentAtom extends Atom { constructor(command, body, options) { super('accent', { command, style: options.style }); if (options.accentChar) this.accent = options.accentChar; else this.svgAccent = options === null || options === void 0 ? void 0 : options.svgAccent; this.body = body; this.skipBoundary = true; // this.limits = 'accent'; // This will suppress the regular // supsub attachment and will delegate // it to the decomposeAccent // (any non-null value would do) } static fromJson(json) { return new AccentAtom(json.command, json.body, { accentChar: json.accentChar, svgAccent: json.svgAccent, style: json.style, }); } toJson() { return { ...super.toJson(), accentChar: this.accent, svgAccent: this.svgAccent, }; } render(parentContext) { var _a; const context = new Context(parentContext, this.style, 'cramp'); // Accents are handled in the TeXbook pg. 443, rule 12. // // 1. Build the base atom // const base = (_a = Atom.createBox(context, this.body)) !== null && _a !== void 0 ? _a : new Box(null); // // 2. Skew // // Calculate the skew of the accent. // > If the nucleus is not a single character, let s = 0; otherwise set s // > to the kern amount for the nucleus followed by the \skewchar of its // > font. // Note that our skew metrics are just the kern between each character // and the skewchar. let skew = 0; if (!this.hasEmptyBranch('body') && this.body.length === 2 && this.body[1].isCharacterBox()) skew = base.skew; // // 3. Calculate the amount of space between the base and the accent // let clearance = Math.min(base.height, X_HEIGHT); // // 4. Build the accent // let accentBox; if (this.svgAccent) { accentBox = makeSVGBox(this.svgAccent); clearance = context.metrics.bigOpSpacing1 - clearance; } else if (this.accent) { // Build the accent const accent = new Box(this.accent, { fontFamily: 'Main-Regular' }); // Remove the italic correction of the accent, because it only serves to // shift the accent over to a place we don't want. accent.italic = 0; // The \vec character that the fonts use is a combining character, and // thus shows up much too far to the left. To account for this, we add a // specific class which shifts the accent over to where we want it. const vecClass = this.accent === 0x20d7 ? ' ML__accent-vec' : ''; accentBox = new Box(new Box(accent), { classes: 'ML__accent-body' + vecClass, }); } // // 5. Combine the base and the accent // // Shift the accent over by the skew. Note we shift by twice the skew // because we are centering the accent, so by adding 2*skew to the left, // we shift it to the right by 1*skew. accentBox = new VBox({ shift: 0, children: [ { box: new Box(base) }, -clearance, { box: accentBox, marginLeft: base.left + 2 * skew, classes: ['ML__center'], }, ], }); const result = new Box(accentBox, { newList: true, type: 'mord' }); if (this.caret) result.caret = this.caret; this.bind(context, result.wrap(context)); return this.attachSupsub(context, { base: result }); } } // An `overunder` atom has the following attributes: // - body: atoms[]: atoms displayed on the base line // - svgBody: string. A SVG graphic displayed on the base line (if present, the body is ignored) // - above: atoms[]: atoms displayed above the body // - svgAbove: string. A named SVG graphic above the element // - below: atoms[]: atoms displayed below the body // - svgBelow: string. A named SVG graphic below the element class OverunderAtom extends Atom { constructor(command, options) { var _a, _b, _c; super('overunder', { command, serialize: options.serialize, style: options.style, }); this.skipBoundary = (_a = options.skipBoundary) !== null && _a !== void 0 ? _a : true; this.subsupPlacement = options.supsubPlacement; this.body = options.body; this.svgAbove = options.svgAbove; this.svgBelow = options.svgBelow; this.svgBody = options.svgBody; this.above = options.above; this.below = options.below; this.boxType = (_b = options.boxType) !== null && _b !== void 0 ? _b : 'mord'; this.padded = (_c = options.padded) !== null && _c !== void 0 ? _c : false; } static fromJson(json) { return new OverunderAtom(json.command, json); } toJson() { const options = {}; if (!this.skipBoundary) options.skipBoundary = false; if (this.subsupPlacement) options.subsupPlacement = this.subsupPlacement; if (this.svgAbove) options.svgAbove = this.svgAbove; if (this.svgBelow) options.svgBelow = this.svgBelow; if (this.svgBody) options.svgBody = this.svgBody; if (this.boxType !== 'mord') options.boxType = this.boxType; if (this.padded) options.padded = true; return { ...super.toJson(), ...options }; } /** * Combine a base with an atom above and an atom below. * * See http://tug.ctan.org/macros/latex/required/amsmath/amsmath.dtx * * > \newcommand{\overset}[2]{\binrel@{#2}% * > \binrel@@{\mathop{\kern\z@#2}\limits^{#1}}} * */ render(parentContext) { let body = this.svgBody ? makeSVGBox(this.svgBody) : Atom.createBox(parentContext, this.body, { newList: true }); const annotationContext = new Context(parentContext, this.style, 'scriptstyle'); let above = null; // let aboveShift: number; if (this.svgAbove) above = makeSVGBox(this.svgAbove); // aboveShift = 0; // aboveShift = -above.depth; else if (this.above) above = Atom.createBox(annotationContext, this.above, { newList: true }); let below = null; // let belowShift: number; if (this.svgBelow) below = makeSVGBox(this.svgBelow); // belowShift = 0; // belowShift = below.height; else if (this.below) below = Atom.createBox(annotationContext, this.below, { newList: true }); if (this.padded) { // The base of \overset are padded, but \overbrace aren't body = new Box([ makeNullDelimiter(parentContext, 'mopen'), body, makeNullDelimiter(parentContext, 'mclose'), ], { newList: true }); } // this.bind(parentContext, base); let base = makeOverunderStack(parentContext, { base: body, above, // aboveShift, below, // belowShift, type: this.boxType === 'mbin' || this.boxType === 'mrel' ? this.boxType : 'mord', }); if (!base) return null; if (this.subsupPlacement === 'over-under') base = this.attachLimits(parentContext, { base, type: base.type }); else base = this.attachSupsub(parentContext, { base }); if (this.caret) base.caret = this.caret; // Bind the generated box so its components can be selected return this.bind(parentContext, base); } } /** * Combine a nucleus with an atom above and an atom below. Used to form * stacks for the 'overunder' atom type . * * @param nucleus The base over and under which the atoms will * be placed. * @param type The type ('mop', 'mrel', etc...) of the result */ function makeOverunderStack(context, options) { // If no base, nothing to do if (!options.base) return null; // If nothing above and nothing below, nothing to do. if (!options.above && !options.below) { const box = new Box(options.base, { type: options.type }); box.setStyle('position', 'relative'); return box; } let aboveShift = 0; if (options.above) aboveShift = -options.above.depth + context.metrics.bigOpSpacing2; // Empirical let result = null; const base = options.base; const baseShift = 0; // (wrappedNucleus.height - wrappedNucleus.depth) / 2 - // context.mathstyle.metrics.axisHeight; if (options.below && options.above) { const bottom = context.metrics.bigOpSpacing5 + options.below.height + options.below.depth + base.depth + baseShift; result = new VBox({ bottom, children: [ context.metrics.bigOpSpacing5, { box: options.below, classes: ['ML__center'] }, { box: base, classes: ['ML__center'] }, aboveShift, { box: options.above, classes: ['ML__center'] }, context.metrics.bigOpSpacing5, ], }); } else if (options.below) { result = new VBox({ top: base.height - baseShift, children: [ context.metrics.bigOpSpacing5, { box: options.below, classes: ['ML__center'] }, { box: base, classes: ['ML__center'] }, ], }); } else if (options.above) { result = new VBox({ bottom: base.depth + baseShift, children: [ // base.depth, { box: base, classes: ['ML__center'] }, aboveShift, { box: options.above, classes: ['ML__center'] }, context.metrics.bigOpSpacing5, ], }); } return new Box(result, { type: options.type }); } class BoxAtom extends Atom { constructor(command, body, options) { super('box', { command, serialize: options.serialize, style: options.style, }); this.body = body; this.framecolor = options.framecolor; this.verbatimFramecolor = options.verbatimBackgroundcolor; this.backgroundcolor = options.backgroundcolor; this.verbatimBackgroundcolor = options.verbatimBackgroundcolor; this.padding = options.padding; this.border = options.border; } static fromJson(json) { return new BoxAtom(json.command, json.body, json); } toJson() { return { ...super.toJson(), framecolor: this.framecolor, verbatimFramecolor: this.verbatimFramecolor, backgroundcolor: this.backgroundcolor, verbatimBackgroundcolor: this.verbatimBackgroundcolor, padding: this.padding, border: this.border, }; } render(parentContext) { const context = new Context(parentContext, this.style); const fboxsep = convertDimensionToEm(context.getRegisterAsDimension('fboxsep')); // The padding extends outside of the base const padding = this.padding === undefined ? fboxsep : convertDimensionToEm(convertToDimension(this.padding, parentContext.registers)); // Base is the main content "inside" the box const content = Atom.createBox(parentContext, this.body); if (!content) return null; content.setStyle('vertical-align', -content.height, 'em'); const base = new Box(content, { type: 'mord' }); // This box will represent the box (background and border). // It's positioned to overlap the base. // The 'ML__box' class is required to prevent the box from being omitted // during rendering (it looks like an empty, no-op box) const box = new Box(null, { classes: 'ML__box' }); box.height = base.height + padding; box.depth = base.depth + padding; box.setStyle('box-sizing', 'border-box'); box.setStyle('position', 'absolute'); box.setStyle('height', base.height + base.depth + 2 * padding, 'em'); if (padding === 0) box.setStyle('width', '100%'); else { box.setStyle('width', `calc(100% + ${2 * padding}em)`); box.setStyle('top', fboxsep, 'em'); // empirical box.setStyle('left', -padding, 'em'); } box.setStyle('z-index', '-1'); // Ensure the box is *behind* the base if (this.backgroundcolor) box.setStyle('background-color', this.backgroundcolor); if (this.framecolor) { box.setStyle('border', `${convertDimensionToEm(context.getRegisterAsDimension('fboxrule'))}em solid ${this.framecolor}`); } if (this.border) box.setStyle('border', this.border); // box.setStyle('top', /* width of the border */); base.setStyle('display', 'inline-block'); base.setStyle('height', content.height + content.depth, 'em'); base.setStyle('vertical-align', -padding, 'em'); // The result is a box that encloses the box and the base const result = new Box([box, base]); // Set its position as relative so that the box can be absolute positioned // over the base result.setStyle('position', 'relative'); result.setStyle('display', 'inline-block'); result.setStyle('line-height', 0); // The padding adds to the width and height of the pod result.height = base.height + padding; result.depth = base.depth + padding; result.left = padding; result.right = padding; result.setStyle('height', base.height + padding, 'em'); result.setStyle('top', base.depth - base.height, 'em'); result.setStyle('vertical-align', base.depth + padding, 'em'); if (this.caret) result.caret = this.caret; return this.attachSupsub(parentContext, { base: result }); } } class PhantomAtom extends Atom { constructor(command, body, options) { var _a, _b, _c, _d; super('phantom', { command, style: options.style }); this.captureSelection = true; this.body = body; this.isInvisible = (_a = options.isInvisible) !== null && _a !== void 0 ? _a : false; this.smashDepth = (_b = options.smashDepth) !== null && _b !== void 0 ? _b : false; this.smashHeight = (_c = options.smashHeight) !== null && _c !== void 0 ? _c : false; this.smashWidth = (_d = options.smashWidth) !== null && _d !== void 0 ? _d : false; } static fromJson(json) { return new PhantomAtom(json.command, json.body, json); } toJson() { const options = {}; if (this.isInvisible) options.isInvisible = true; if (this.smashDepth) options.smashDepth = true; if (this.smashHeight) options.smashHeight = true; if (this.smashWidth) options.smashWidth = true; return { ...super.toJson(), ...options }; } render(context) { const phantom = new Context(context, { isPhantom: true }); if (!this.smashDepth && !this.smashHeight && !this.smashWidth) { console.assert(this.isInvisible); return Atom.createBox(phantom, this.body, { classes: 'inner' }); } const content = Atom.createBox(this.isInvisible ? phantom : context, this.body); if (!content) return null; if (this.smashWidth) { const fix = new Box(null, { classes: 'fix' }); return new Box([content, fix], { classes: 'rlap' }).wrap(context); } if (!this.smashHeight && !this.smashDepth) return content; if (this.smashHeight) content.height = 0; if (this.smashDepth) content.depth = 0; if (content.children) { for (const box of content.children) { if (this.smashHeight) box.height = 0; if (this.smashDepth) box.depth = 0; } } // We create a stack to suppress the HTML line height by setting // the display to 'table-cell' which prevents the browser from // acting on that height. return new VBox({ firstBaseline: [{ box: content }] }, { type: 'mord' }).wrap(context); } } class DelimAtom extends Atom { constructor(command, delim, options) { super('delim', { command, style: options === null || options === void 0 ? void 0 : options.style }); this.value = delim; this.size = options === null || options === void 0 ? void 0 : options.size; } static fromJson(json) { return new DelimAtom(json.command, json.delim, json); } toJson() { return { ...super.toJson(), delim: this.value, size: this.size }; } render(_context) { const box = new Box(null); box.delim = this.value; return box; } serialize(_options) { if (this.value.length === 1) return this.command + this.value; return `${this.command}{${this.value}}`; } } class SizedDelimAtom extends Atom { constructor(command, delim, options) { super('sizeddelim', { command, style: options.style }); this.value = delim; this.delimClass = options.delimClass; this.size = options.size; } static fromJson(json) { return new SizedDelimAtom(json.command, json.delim, json); } toJson() { return { ...super.toJson(), delim: this.value, size: this.size, delimClass: this.delimClass, }; } render(context) { let result = makeSizedDelim(this.value, this.size, context, { classes: this.delimClass, }); if (!result) return null; result = this.bind(context, result); if (this.caret) result.caret = this.caret; return result; } serialize(_options) { if (this.value.length === 1) return this.command + this.value; return `${this.command}{${this.value}}`; } } class LineAtom extends Atom { constructor(command, body, options) { super('line', { command, style: options.style }); this.skipBoundary = true; this.body = body; this.position = options.position; } static fromJson(json) { return new LineAtom(json.command, json.body, json); } toJson() { return { ...super.toJson(), position: this.position }; } render(parentContext) { // TeXBook:443. Rule 9 and 10 const context = new Context(parentContext, this.style, 'cramp'); const inner = Atom.createBox(context, this.body); if (!inner) return null; const ruleWidth = context.metrics.defaultRuleThickness / context.scalingFactor; const line = new Box(null, { classes: this.position + '-line' }); line.height = ruleWidth; line.maxFontSize = ruleWidth * 1.125 * context.scalingFactor; let stack; if (this.position === 'overline') { stack = new VBox({ shift: 0, children: [{ box: inner }, 3 * ruleWidth, { box: line }, ruleWidth], }); } else { stack = new VBox({ top: inner.height, children: [ruleWidth, { box: line }, 3 * ruleWidth, { box: inner }], }); } if (this.caret) stack.caret = this.caret; return new Box(stack, { classes: this.position, type: 'mord', }); } } class OverlapAtom extends Atom { constructor(command, body, options) { var _a, _b; super('overlap', { command, style: options === null || options === void 0 ? void 0 : options.style }); this.skipBoundary = true; if (typeof body === 'string') this.body = [new Atom('mord', { value: body })]; else this.body = body; this.align = (_a = options === null || options === void 0 ? void 0 : options.align) !== null && _a !== void 0 ? _a : 'left'; this.boxType = (_b = options === null || options === void 0 ? void 0 : options.boxType) !== null && _b !== void 0 ? _b : 'mord'; } static fromJson(json) { return new OverlapAtom(json.command, json.body, json); } toJson() { const options = {}; if (this.align) options.align = this.align; if (this.boxType) options.boxType = this.boxType; return { ...super.toJson(), ...options }; } render(context) { // For llap (18), rlap (270), clap (0) // smash (common), mathllap (0), mathrlap (0), mathclap (0) // See https://www.tug.org/TUGboat/tb22-4/tb72perlS.pdf // and https://tex.stackexchange.com/questions/98785/what-are-the-different-kinds-of-vertical-spacing-and-horizontal-spacing-commands const inner = Atom.createBox(context, this.body, { classes: 'inner' }); // @revisit if (!inner) return null; if (this.caret) inner.caret = this.caret; return this.bind(context, new Box([inner, new Box(null, { classes: 'fix' })], { classes: this.align === 'left' ? 'llap' : 'rlap', type: this.boxType, })); } } /** * Genfrac -- Generalized Fraction * * Decompose fractions, binomials, and in general anything made * of a numerator and denominator, optionally separated by a fraction bar, * and optionally surrounded by delimiters (parentheses, brackets, etc...). * * Depending on the type of fraction the mathstyle is either * displaystyle or textstyle. This value can also be set to 'auto', * to indicate it should use the current mathstyle */ class GenfracAtom extends Atom { constructor(command, above, below, options) { var _a, _b; super('genfrac', { style: options.style, command, serialize: options.serialize, displayContainsHighlight: true, }); this.above = above; this.below = below; this.hasBarLine = (_a = options === null || options === void 0 ? void 0 : options.hasBarLine) !== null && _a !== void 0 ? _a : true; this.continuousFraction = (_b = options === null || options === void 0 ? void 0 : options.continuousFraction) !== null && _b !== void 0 ? _b : false; this.numerPrefix = options === null || options === void 0 ? void 0 : options.numerPrefix; this.denomPrefix = options === null || options === void 0 ? void 0 : options.denomPrefix; this.mathstyleName = options === null || options === void 0 ? void 0 : options.mathstyleName; this.leftDelim = options === null || options === void 0 ? void 0 : options.leftDelim; this.rightDelim = options === null || options === void 0 ? void 0 : options.rightDelim; } static fromJson(json) { return new GenfracAtom(json.command, json.above, json.below, json); } toJson() { const options = {}; if (this.continuousFraction) options.continuousFraction = true; if (this.numerPrefix) options.numerPrefix = this.numerPrefix; if (this.denomPrefix) options.denomPrefix = this.denomPrefix; if (this.leftDelim) options.leftDelim = this.leftDelim; if (this.rightDelim) options.rightDelim = this.rightDelim; if (!this.hasBarLine) options.hasBarLine = false; if (this.mathstyleName) options.mathstyleName = this.mathstyleName; return { ...super.toJson(), ...options }; } serialize(options) { return (this.command + `{${this.aboveToLatex(options)}}` + `{${this.belowToLatex(options)}}`); } render(context) { var _a, _b; const fracContext = new Context(context, this.style, this.mathstyleName); const metrics = fracContext.metrics; const numContext = new Context(fracContext, this.style, this.continuousFraction ? '' : 'numerator'); const numerBox = this.numerPrefix ? new Box([new Box(this.numerPrefix), Atom.createBox(numContext, this.above)], { isTight: numContext.isTight, newList: true }) : (_a = Atom.createBox(numContext, this.above, { newList: true })) !== null && _a !== void 0 ? _a : new Box(null, { newList: true }); const denomContext = new Context(fracContext, this.style, this.continuousFraction ? '' : 'denominator'); const denomBox = this.denomPrefix ? new Box([ new Box(this.denomPrefix), Atom.createBox(denomContext, this.below, { newList: true }), ]) : (_b = Atom.createBox(denomContext, this.below, { newList: true })) !== null && _b !== void 0 ? _b : new Box(null, { newList: true }); const ruleWidth = this.hasBarLine ? metrics.defaultRuleThickness : 0; // Rule 15b from TeXBook Appendix G, p.444 // // 15b. If C > T, set u ← σ8 and v ← σ11. Otherwise set u ← σ9 or σ10,according // as θ ̸= 0 or θ = 0, and set v ← σ12. (The fraction will be typeset with // its numerator shifted up by an amount u with respect to the current // baseline, and with the denominator shifted down by v, unless the boxes // are unusually large.) let numerShift; let clearance = 0; let denomShift; if (fracContext.isDisplayStyle) { numerShift = metrics.num1; // Set u ← σ8 clearance = ruleWidth > 0 ? 3 * ruleWidth : 7 * ruleWidth; denomShift = metrics.denom1; // V ← σ11 } else { if (ruleWidth > 0) { numerShift = metrics.num2; // U ← σ9 clearance = ruleWidth; // Φ ← θ } else { numerShift = metrics.num3; // U ← σ10 clearance = 3 * ruleWidth; // Φ ← 3 ξ8 } denomShift = metrics.denom2; // V ← σ12 } const numerDepth = numerBox.depth; const denomHeight = denomBox.height; let frac; if (ruleWidth <= 0) { // Rule 15c from Appendix G // No bar line between numerator and denominator const candidateClearance = numerShift - numerDepth - (denomHeight - denomShift); if (candidateClearance < clearance) { numerShift += (clearance - candidateClearance) / 2; denomShift += (clearance - candidateClearance) / 2; } frac = new VBox({ individualShift: [ { box: numerBox, shift: -numerShift, classes: ['ML__center'], }, { box: denomBox, shift: denomShift, classes: ['ML__center'], }, ], }).wrap(fracContext); } else { // Rule 15d from Appendix G of the TeXBook. // There is a bar line between the numerator and the denominator const numerLine = AXIS_HEIGHT + ruleWidth / 2; const denomLine = AXIS_HEIGHT - ruleWidth / 2; if (numerShift < clearance + numerDepth + numerLine) numerShift = clearance + numerDepth + numerLine; if (denomShift < clearance + denomHeight - denomLine) denomShift = clearance + denomHeight - denomLine; const fracLine = new Box(null, { classes: 'ML__frac-line', mode: this.mode, style: this.style, }); // Manually set the height of the frac line because its height is // created in CSS fracLine.height = ruleWidth / 2; fracLine.depth = ruleWidth / 2; frac = new VBox({ individualShift: [ { box: denomBox, shift: denomShift, classes: ['ML__center'], }, { box: fracLine, shift: -denomLine + ruleWidth / 2 }, { box: numerBox, shift: -numerShift, classes: ['ML__center'], }, ], }).wrap(fracContext); } // Rule 15e of Appendix G const delimSize = fracContext.isDisplayStyle ? metrics.delim1 : metrics.delim2; // Optional delimiters const leftDelim = this.leftDelim ? this.bind(context, makeCustomSizedDelim('mopen', this.leftDelim, delimSize, true, context, { style: this.style, mode: this.mode })) : makeNullDelimiter(fracContext, 'mopen'); let rightDelim = null; if (this.continuousFraction) { // Zero width for `\cfrac` rightDelim = new Box(null, { type: 'mclose' }); } else if (!this.rightDelim) rightDelim = makeNullDelimiter(fracContext, 'mclose'); else { rightDelim = this.bind(context, makeCustomSizedDelim('mclose', this.rightDelim, delimSize, true, context, { style: this.style, mode: this.mode })); } // TeXBook p. 170 "fractions are treated as type Inner." // However, we add the nullDelimiter above which effectively account for this. const result = this.bind(context, new Box([leftDelim, frac, rightDelim], { isTight: fracContext.isTight, type: 'mord', classes: 'mfrac', })); if (!result) return null; if (this.caret) result.caret = this.caret; return this.attachSupsub(context, { base: result }); } } class RuleAtom extends Atom { constructor(command, options) { var _a; super('rule', { command, style: options.style }); this.shift = (_a = options.shift) !== null && _a !== void 0 ? _a : { dimension: 0 }; this.height = options.height; this.width = options.width; } static fromJson(json) { return new RuleAtom(json.command, json); } toJson() { const options = { height: this.height, width: this.width, }; if (this.shift) options.shift = this.shift; return { ...super.toJson(), ...options }; } render(parentContext) { // The mathstyle sizing corrections (size delta) do not // apply to the dimensions of rules. Create a 'textstyle' // context to do the measurements without accounting for the mathstyle. const context = new Context(parentContext, this.style, 'textstyle'); const shift = convertDimensionToEm(this.shift); const width = convertDimensionToEm(this.width); const height = convertDimensionToEm(this.height); const result = new Box(null, { classes: 'rule', type: 'mord' }); result.setStyle('border-right-width', width, 'em'); result.setStyle('border-top-width', height, 'em'); result.setStyle('border-color', this.style.color); result.setStyle('vertical-align', shift, 'em'); if (this.isSelected) result.setStyle('opacity', '50%'); result.width = width; result.height = height + shift; result.depth = -shift; this.bind(parentContext, result); if (this.caret) result.caret = this.caret; return result.wrap(context); } serialize(_options) { var _a; let result = (_a = this.command) !== null && _a !== void 0 ? _a : ''; if (this.shift) result += `[${serializeDimension(this.shift)}]`; result += `{${serializeDimension(this.width)}}{${serializeDimension(this.height)}}`; return result; } } /** * Operators are handled in the TeXbook pg. 443-444, rule 13(a). */ class OperatorAtom extends Atom { constructor(command, symbol, options) { var _a, _b, _c, _d; super((_a = options.type) !== null && _a !== void 0 ? _a : 'mop', { command, style: options.style, isFunction: options === null || options === void 0 ? void 0 : options.isFunction, }); if (typeof symbol === 'string') this.value = symbol; else this.body = symbol; this.captureSelection = (_b = options.captureSelection) !== null && _b !== void 0 ? _b : false; this.hasArgument = (_c = options.hasArgument) !== null && _c !== void 0 ? _c : false; this.variant = options === null || options === void 0 ? void 0 : options.variant; this.variantStyle = options === null || options === void 0 ? void 0 : options.variantStyle; this.subsupPlacement = options === null || options === void 0 ? void 0 : options.limits; this.isExtensibleSymbol = (_d = options === null || options === void 0 ? void 0 : options.isExtensibleSymbol) !== null && _d !== void 0 ? _d : false; } static fromJson(json) { return new OperatorAtom(json.command, json.body ? json.body : json.value, json); } toJson() { const result = super.toJson(); if (this.hasArgument) result.hasArgument = true; if (this.variant) result.variant = this.variant; if (this.variantStyle) result.variantStyle = this.variantStyle; if (this.subsupPlacement) result.limits = this.subsupPlacement; if (this.isExtensibleSymbol) result.isExtensibleSymbol = true; if (this.value) result.symbol = this.value; return result; } render(context) { var _a; let base; let baseShift = 0; let slant = 0; if (this.isExtensibleSymbol) { // Most symbol operators get larger in displaystyle (rule 13) // except `\smallint` const large = context.isDisplayStyle && this.value !== '\\smallint'; base = new Box(this.value, { fontFamily: large ? 'Size2-Regular' : 'Size1-Regular', classes: 'op-symbol ' + (large ? 'large-op' : 'small-op'), type: 'mop', maxFontSize: context.scalingFactor, }); if (!base) return null; // Apply italic correction base.right = base.italic; // Shift the symbol so its center lies on the axis (rule 13). It // appears that our fonts have the centers of the symbols already // almost on the axis, so these numbers are very small. Note we // don't actually apply this here, but instead it is used either in // the vlist creation or separately when there are no limits. baseShift = (base.height - base.depth) / 2 - AXIS_HEIGHT * context.scalingFactor; // The slant of the symbol is just its italic correction. slant = base.italic; base.setStyle('color', this.style.color); base.setStyle('background-color', this.style.backgroundColor); } else if (this.body) { // If this is a list, decompose that list. base = Atom.createBox(context, this.body, { newList: true }); if (!base) return null; base.setStyle('color', this.style.color); base.setStyle('background-color', this.style.backgroundColor); } else { // Otherwise, this is a text operator. Build the text from the // operator's name. console.assert(this.type === 'mop'); // Not all styles are applied, since the operators have a distinct // appearance (for example, can't override their font family) base = new Box(this.value, { type: 'mop', mode: 'math', maxFontSize: context.scalingFactor, style: { color: this.style.color, backgroundColor: this.style.backgroundColor, letterShapeStyle: context.letterShapeStyle, variant: this.variant, variantStyle: this.variantStyle, }, }); } if (this.isExtensibleSymbol) base.setTop(baseShift); let result = base; if (this.superscript || this.subscript) { const limits = (_a = this.subsupPlacement) !== null && _a !== void 0 ? _a : 'auto'; result = limits === 'over-under' || (limits === 'auto' && context.isDisplayStyle) ? this.attachLimits(context, { base, baseShift, slant }) : this.attachSupsub(context, { base }); } if (this.caret) result.caret = this.caret; // Bind the generated box with its limits so they // can all be selected as one return new Box(this.bind(context, result), { type: 'mop', classes: 'op-group', }); } serialize(options) { // ZERO-WIDTH? if (this.value === '\u200B') return this.supsubToLatex(options); const result = []; result.push(this.command); if (this.hasArgument) result.push(`{${this.bodyToLatex(options)}}`); if (this.explicitSubsupPlacement) { if (this.subsupPlacement === 'over-under') result.push('\\limits'); if (this.subsupPlacement === 'adjacent') result.push('\\nolimits'); if (this.subsupPlacement === 'auto') result.push('\\displaylimits'); } result.push(this.supsubToLatex(options)); return joinLatex(result); } } defineFunction('ensuremath', '{:math}', { createAtom: (_name, args, style) => new GroupAtom(args[0], { mode: 'math', latexOpen: '\\ensuremath{', latexClose: '}', style, // mathstyleName: 'textstyle', }), }); defineFunction('color', '{:string}', { applyStyle: (_name, args, options) => { var _a, _b; const color = args[0]; return { verbatimColor: args[0], color: (_b = (_a = options.colorMap) === null || _a === void 0 ? void 0 : _a.call(options, color)) !== null && _b !== void 0 ? _b : color, }; }, }); // From the xcolor package. // Unlike what its name might suggest, this command does not set the mode to text // That is, it can equally be applied to math and text mode. defineFunction('textcolor', '{:string}{content:auto*}', { applyStyle: (_name, args, options) => { var _a, _b; const color = args[0]; return { verbatimColor: color, color: (_b = (_a = options.colorMap) === null || _a === void 0 ? void 0 : _a.call(options, color)) !== null && _b !== void 0 ? _b : color, }; }, }); // Can be preceded by e.g. '\fboxsep=4pt' (also \fboxrule) // Note: // - \boxed: sets content in displaystyle mode (@todo: should change type of argument) // equivalent to \fbox{$$<content>$$} // - \fbox: sets content in 'auto' mode (frequency 777) // - \framebox[<width>][<alignment>]{<content>} (<alignment> := 'c'|'t'|'b' (center, top, bottom) (frequency 28) // @todo defineFunction('boxed', '{content:math}', { createAtom: (name, args, style) => new BoxAtom(name, args[0], { framecolor: 'black', style, }), }); // Technically, using a BoxAtom is more correct (there is a small margin around it) // However, just changing the background color makes editing easier defineFunction('colorbox', '{:string}{content:auto*}', { applyMode: 'text', applyStyle: (_name, args, options) => { var _a, _b; const color = args[0]; return { verbatimBackgroundColor: args[0], backgroundColor: (_b = (_a = options.backgroundColorMap) === null || _a === void 0 ? void 0 : _a.call(options, color)) !== null && _b !== void 0 ? _b : color, }; }, }); defineFunction('fcolorbox', '{frame-color:string}{background-color:string}{content:auto}', { applyMode: 'text', createAtom: (name, args, style, options) => { var _a, _b, _c, _d; const color = args[0]; const bgColor = args[1]; return new BoxAtom(name, args[2], { verbatimFramecolor: color, framecolor: (_b = (_a = options.colorMap) === null || _a === void 0 ? void 0 : _a.call(options, color)) !== null && _b !== void 0 ? _b : color, verbatimBackgroundcolor: args[1], backgroundcolor: (_d = (_c = options.backgroundColorMap) === null || _c === void 0 ? void 0 : _c.call(options, bgColor)) !== null && _d !== void 0 ? _d : bgColor, style, serialize: (atom, options) => { var _a, _b; return `${atom.command}{${(_a = atom.verbatimFramecolor) !== null && _a !== void 0 ? _a : atom.framecolor}{${(_b = atom.verbatimBackgroundcolor) !== null && _b !== void 0 ? _b : atom.backgroundcolor}}{${atom.bodyToLatex(options)}}`; }, }); }, }); // \bbox, MathJax extension // The first argument is a CSS border property shorthand, e.g. // \bbox[red], \bbox[5px,border:2px solid red] // The MathJax syntax is // arglist ::= <arg>[,<arg>[,<arg>]] // arg ::= [<background:string>|<padding:dimen>|<style>] // style ::= 'border:' <string> defineFunction('bbox', '[:bbox]{body:auto}', { createAtom: (name, args, style) => { if (args[0]) { const arg = args[0]; return new BoxAtom(name, args[1], { padding: arg.padding, border: arg.border, backgroundcolor: arg.backgroundcolor, style, serialize: (atom, options) => { var _a; let result = name; if (Number.isFinite(atom.padding) || atom.border !== undefined || atom.backgroundcolor !== undefined) { const bboxParameters = []; if (atom.padding) bboxParameters.push(atom.padding); if (atom.border) bboxParameters.push(`border: ${atom.border}`); if (atom.verbatimBackgroundcolor || atom.backgroundcolor) { bboxParameters.push((_a = atom.verbatimBackgroundcolor) !== null && _a !== void 0 ? _a : atom.backgroundcolor); } result += `[${bboxParameters.join(',')}]`; } return result + `{${atom.bodyToLatex(options)}}`; }, }); } return new BoxAtom(name, args[1], { style }); }, }); // The `\displaystyle` and `\textstyle` commands do not change the current size // but they do change how some of the layout is done. // `\scriptstyle` reduces the size by one on the FontSizeScale, and // `\scriptscriptstyle` reduces it by two. defineFunction(['displaystyle', 'textstyle', 'scriptstyle', 'scriptscriptstyle'], '{:rest}', { createAtom: (name, args, style) => new GroupAtom(args[0], { latexOpen: `{${name} `, latexClose: '}', style, mathstyleName: name.slice(1), }), }); // Size // // These size function are absolute. That is applying `\Large \Large X` does // not make twice as "Large". // // This is unlike the `\scriptstyle` and `\scriptscriptstyle` commands, which // are relative, that is `\scriptstyle \scriptstyle x` is smaller than // `\scriptstyle x`. defineFunction([ 'tiny', 'scriptsize', 'footnotesize', 'small', 'normalsize', 'large', 'Large', 'LARGE', 'huge', 'Huge', ], '', { // TeX behaves very inconsistently when sizing commands are applied // to math mode. We allow sizing commands to be applied in both math and // text mode applyStyle: (name, _args) => { return { fontSize: { '\\tiny': 1, '\\scriptsize': 2, '\\footnotesize': 3, '\\small': 4, '\\normalsize': 5, '\\large': 6, '\\Large': 7, '\\LARGE': 8, '\\huge': 9, '\\Huge': 10, }[name], }; }, }); // \fontseries only works in text mode defineFunction('fontseries', '{:string}', { ifMode: 'text', applyStyle: (_name, args) => { return { fontSeries: args[0] }; }, }); // SHAPE: italic, small caps defineFunction('fontshape', '{:string}', { ifMode: 'text', applyStyle: (_name, args) => { return { fontShape: args[0] }; }, }); // FONT FAMILY: Fraktur, Calligraphic, ... defineFunction('fontfamily', '{:string}', { ifMode: 'text', applyStyle: (_name, args) => { return { fontFamily: args[0] }; }, }); // In LaTeX, the \fontseries, \fontshape, \fontfamily, \fontsize commands // do not take effect until \selectfont is encoded. In our implementation, // they take effect immediately, and \selectfont is a no-op defineFunction('selectfont', '', { ifMode: 'text', applyStyle: (_name, _args) => { return {}; }, }); // \bf works in any mode // As per the LaTeX 2.09 semantics, it overrides shape, family defineFunction('bf', '', { applyStyle: (_name, _args) => { return { fontSeries: 'b', fontShape: 'n', fontFamily: 'cmr' }; }, }); // Note: These function work a little bit differently than LaTex // In LaTeX, \bm{x\mathrm{y}} yield a bold x and an upright y. // This is not necesarily intentional, but a side effect of the (current) // implementation of \bm defineFunction(['boldsymbol', 'bm'], '{:math*}', { applyMode: 'math', createAtom: (name, args, style) => new GroupAtom(args[0], { latexOpen: `${name}{`, latexClose: '}', style, customClass: 'ML__boldsymbol', }), }); // Note: switches to math mode defineFunction('bold', '{:math*}', { applyMode: 'math', applyStyle: (_name, _args) => { return { variantStyle: 'bold' }; }, }); defineFunction('bfseries', '', { applyMode: 'text', applyStyle: (_name, _args) => { return { fontSeries: 'b' }; }, }); defineFunction('mdseries', '', { applyMode: 'text', applyStyle: (_name, _args) => { return { fontSeries: 'm' }; }, }); defineFunction('upshape', '', { applyMode: 'text', applyStyle: (_name, _args) => { return { fontShape: 'n' }; }, }); defineFunction('slshape', '', { applyMode: 'text', applyStyle: (_name, _args) => { return { fontShape: 'sl' }; }, }); // Small caps defineFunction('scshape', '', { applyMode: 'text', applyStyle: (_name, _args) => { return { fontShape: 'sc' }; }, }); defineFunction('textbf', '{:text*}', { applyMode: 'text', applyStyle: (_name, _args) => { return { fontSeries: 'b' }; }, }); defineFunction('textmd', '{:text*}', { applyMode: 'text', applyStyle: (_name, _args) => { return { fontSeries: 'm' }; }, }); defineFunction('textup', '{:text*}', { applyMode: 'text', applyStyle: (_name, _args) => { return { fontShape: 'n' }; }, }); // @todo: family could be 'none' or 'default' // "normal" font of the body text, not necessarily roman defineFunction('textnormal', '{:text*}', { applyMode: 'text', applyStyle: (_name, _args) => { return { fontShape: 'n', fontSeries: 'm' }; }, }); defineFunction('textsl', '{:text*}', { applyMode: 'text', applyStyle: (_name, _args) => { return { fontShape: 'sl' }; }, }); defineFunction('textit', '{:text*}', { applyMode: 'text', applyStyle: (_name, _args) => { return { fontShape: 'it' }; }, }); defineFunction('textsc', '{:text*}', { applyMode: 'text', applyStyle: (_name, _args) => { return { fontShape: 'sc' }; }, }); defineFunction('textrm', '{:text*}', { applyMode: 'text', applyStyle: (_name, _args) => { return { fontFamily: 'roman' }; }, }); defineFunction('textsf', '{:text*}', { applyMode: 'text', applyStyle: (_name, _args) => { return { fontFamily: 'sans-serif' }; }, }); defineFunction('texttt', '{:text*}', { applyMode: 'text', applyStyle: (_name, _args) => { return { fontFamily: 'monospace' }; }, }); // Note: \mathbf is a no-op in text mode defineFunction('mathbf', '{:math*}', { applyMode: 'math', applyStyle: (_name, _args) => { return { variant: 'normal', variantStyle: 'bold' }; }, }); // `\mathnormal` includes italic correction, `\mathit` doesn't defineFunction('mathit', '{:math*}', { applyMode: 'math', applyStyle: (_name, _args) => { return { variant: 'main', variantStyle: 'italic' }; }, }); // `\mathnormal` includes italic correction, `\mathit` doesn't defineFunction('mathnormal', '{:math*}', { applyMode: 'math', applyStyle: (_name, _args) => { return { variant: 'normal', variantStyle: 'italic' }; }, }); // From the ISOMath package defineFunction('mathbfit', '{:math*}', { applyMode: 'math', applyStyle: (_name, _args) => { return { variant: 'main', variantStyle: 'bolditalic' }; }, }); defineFunction('mathrm', '{:math*}', { applyMode: 'math', applyStyle: (_name, _args) => { return { variant: 'normal', variantStyle: 'up' }; }, }); defineFunction('mathsf', '{:math*}', { applyMode: 'math', applyStyle: (_name, _args) => { return { variant: 'sans-serif', variantStyle: 'up' }; }, }); defineFunction('mathtt', '{:math*}', { applyMode: 'math', applyStyle: (_name, _args) => { return { variant: 'monospace', variantStyle: 'up' }; }, }); defineFunction('it', '', { applyStyle: (_name, _args) => { return { fontSeries: 'm', fontShape: 'it', fontFamily: 'cmr', variantStyle: 'italic', // For math mode }; }, }); // In LaTeX, \rmfamily, \sffamily and \ttfamily are no-op in math mode. defineFunction('rmfamily', '', { applyStyle: (_name, _args) => { return { fontFamily: 'roman' }; }, }); defineFunction('sffamily', '', { applyStyle: (_name, _args) => { return { fontFamily: 'sans-serif' }; }, }); defineFunction('ttfamily', '', { applyStyle: (_name, _args) => { return { fontFamily: 'monospace' }; }, }); // In LaTeX, \Bbb and \mathbb are no-op in text mode. // They also map lowercase characters to different glyphs. // Note that \Bbb has been deprecated for over 20 years (as well as \rm, \it, \bf) defineFunction(['Bbb', 'mathbb'], '{:math*}', { applyStyle: (_name, _args) => { return { variant: 'double-struck', variantStyle: 'up' }; }, }); defineFunction(['frak', 'mathfrak'], '{:math*}', { applyStyle: (_name, _args) => { return { variant: 'fraktur', variantStyle: 'up' }; }, }); defineFunction('mathcal', '{:math*}', { applyStyle: (_name, _args) => { return { variant: 'calligraphic', variantStyle: 'up' }; }, }); defineFunction('mathscr', '{:math*}', { applyStyle: (_name, _args) => { return { variant: 'script', variantStyle: 'up' }; }, }); /* * Rough synomym for \text{} * An \mbox within math mode does not use the current math font; rather it uses * the typeface of the surrounding running text. */ defineFunction('mbox', '{:text}', { ifMode: 'math', createAtom: (command, args, style) => new GroupAtom(args[0], { changeMode: true, style, mode: 'text', command, serialize: (atom, options) => `\\mbox{${atom.bodyToLatex({ ...options, skipModeCommand: true, })}}`, }), }); defineFunction('text', '{:text}', { ifMode: 'math', applyMode: 'text', }); /* A MathJax extension: assign a class to the element */ defineFunction('class', '{name:string}{content:auto*}', { createAtom: (_command, args, style) => new GroupAtom(args[1], { customClass: args[0], style, }), }); /* A MathJax extension: assign an ID to the element */ defineFunction('cssId', '{id:string}{content:auto}', { createAtom: (command, args, style) => new GroupAtom(args[1], { cssId: args[0], style, }), }); /* assign an property to the element */ defineFunction('htmlData', '{data:string}{content:auto}', { createAtom: (command, args, style) => new GroupAtom(args[1], { htmlData: args[0], style, }), }); /* assign CSS styles to the element */ defineFunction('htmlStyle', '{data:string}{content:auto}', { createAtom: (command, args, style) => new GroupAtom(args[1], { htmlStyle: args[0], style, }), }); /* Note: in TeX, \em is restricted to text mode. We extend it to math * This is the 'switch' variant of \emph, i.e: * `\emph{important text}` * `{\em important text}` */ defineFunction('em', '{:auto*}', { createAtom: (command, args, style) => new GroupAtom(args[0], { latexOpen: '\\em', latexClose: '', customClass: 'ML__emph', style, }), }); /* Note: in TeX, \emph is restricted to text mode. We extend it to math */ defineFunction('emph', '{:auto}', { createAtom: (command, args, style) => new GroupAtom(args[0], { latexOpen: '\\emph{', latexClose: '}', customClass: 'ML__emph', style, }), }); // Extra data needed for the delimiter parse function down below const DELIMITER_SIZES = { '\\bigl': { mclass: 'mopen', size: 1 }, '\\Bigl': { mclass: 'mopen', size: 2 }, '\\biggl': { mclass: 'mopen', size: 3 }, '\\Biggl': { mclass: 'mopen', size: 4 }, '\\bigr': { mclass: 'mclose', size: 1 }, '\\Bigr': { mclass: 'mclose', size: 2 }, '\\biggr': { mclass: 'mclose', size: 3 }, '\\Biggr': { mclass: 'mclose', size: 4 }, '\\bigm': { mclass: 'mrel', size: 1 }, '\\Bigm': { mclass: 'mrel', size: 2 }, '\\biggm': { mclass: 'mrel', size: 3 }, '\\Biggm': { mclass: 'mrel', size: 4 }, '\\big': { mclass: 'mord', size: 1 }, '\\Big': { mclass: 'mord', size: 2 }, '\\bigg': { mclass: 'mord', size: 3 }, '\\Bigg': { mclass: 'mord', size: 4 }, }; defineFunction([ 'bigl', 'Bigl', 'biggl', 'Biggl', 'bigr', 'Bigr', 'biggr', 'Biggr', 'bigm', 'Bigm', 'biggm', 'Biggm', 'big', 'Big', 'bigg', 'Bigg', ], '{:delim}', { createAtom: (name, args, style) => new SizedDelimAtom(name, args[0], { size: DELIMITER_SIZES[name].size, delimClass: DELIMITER_SIZES[name].mclass, style, }), }); defineFunction([ 'hspace', 'hspace*', // \hspace* inserts a non-breakable space, but since we don't line break... // it's the same as \hspace. ], '{width:glue}', { createAtom: (name, args, style) => { var _a; return new SpacingAtom(name, style, (_a = args[0]) !== null && _a !== void 0 ? _a : { glue: { dimension: 0 } }); }, }); defineFunction('mathop', '{:auto}', { createAtom: (command, args, style) => new OperatorAtom(command, args[0], { type: 'mop', captureSelection: true, limits: 'over-under', isFunction: true, hasArgument: true, style, }), }); defineFunction([ 'mathbin', 'mathrel', 'mathopen', 'mathclose', 'mathpunct', 'mathord', 'mathinner', ], '{:auto}', { createAtom: (command, args, style) => new OperatorAtom(command, args[0], { type: { '\\mathbin': 'mbin', '\\mathrel': 'mrel', '\\mathopen': 'mopen', '\\mathclose': 'mclose', '\\mathpunct': 'mpunct', '\\mathord': 'mord', '\\mathinner': 'minner', }[command], captureSelection: true, hasArgument: true, style, }), }); // @todo see http://mirrors.ibiblio.org/CTAN/macros/latex/required/amsmath/amsopn.pdf // for list of additional operators defineFunction(['operatorname', 'operatorname*'], '{operator:math}', { createAtom: (name, args, style) => { const result = new OperatorAtom(name, args[0], { isFunction: true, hasArgument: true, limits: name === '\\operatorname' ? 'adjacent' : 'over-under', style, }); result.captureSelection = true; // Do not let children be selected /* The \operatorname commands is defined with: \gdef\newmcodes@{\mathcode`\'39\mathcode`\*42\mathcode`\."613A% \ifnum\mathcode`\-=45 \else \mathchardef\std@minus\mathcode`\-\relax \fi \mathcode`\-45\mathcode`\/47\mathcode`\:"603A\relax} \mathcode assigns to a character its category (2=mbin), its font family (0=cmr), and its character code. It basically temporarily reassigns to ":.'-/*" the values/properties these characters have in text mode (but importantly, not to " " (space)) */ if (result.body) { result.body.forEach((x) => { var _a; if (x.type !== 'first') { x.type = 'mord'; x.value = (_a = { '\u2217': '*', '\u2212': '-' }[x.value]) !== null && _a !== void 0 ? _a : x.value; x.isFunction = false; if (!x.style.variant && !x.style.variantStyle) { // No variant as been specified (as it could have been with // \operatorname{\mathit{lim}} for example) // Bypass the default auto styling by specifing an upright style x.style.variant = 'main'; x.style.variantStyle = 'up'; } } }); } return result; }, }); class UnicodeAtom extends Atom { constructor(arg, style) { let codepoint = Number.parseInt(arg); if (!Number.isFinite(codepoint)) codepoint = 0x2753; // BLACK QUESTION MARK super('mord', { value: String.fromCodePoint(codepoint), style, }); this.codepoint = codepoint; } serialize(_options) { return ('\\unicode"' + ('000000' + this.codepoint.toString(16)).toUpperCase().slice(-6)); } } defineFunction('unicode', '{charcode:number}', { createAtom: (name, args, style) => new UnicodeAtom(args[0], style), }); // A box of the width and height defineFunction('rule', '[raise:dimen]{width:dimen}{thickness:dimen}', { createAtom: (name, args, style) => new RuleAtom(name, { shift: args[0], width: args[1], height: args[2], style, }), }); // An overline defineFunction('overline', '{:auto}', { createAtom: (name, args, style) => new LineAtom(name, args[0], { position: 'overline', style, }), }); defineFunction('underline', '{:auto}', { createAtom: (name, args, style) => new LineAtom(name, args[0], { position: 'underline', style, }), }); function binRelType(atoms) { if (atoms.length === 1) { const atom = atoms[0]; if (atom.type === 'mbin') return 'mbin'; if (atom.type === 'mrel') return 'mrel'; } return 'mord'; } defineFunction('overset', '{above:auto}{base:auto}', { createAtom: (name, args, style) => new OverunderAtom(name, { above: args[0], body: args[1], skipBoundary: false, style, boxType: binRelType(args[1]), serialize: (atom, options) => `${atom.command}{${atom.aboveToLatex(options)}}` + `{${atom.bodyToLatex(options)}}`, }), }); defineFunction('underset', '{below:auto}{base:auto}', { createAtom: (name, args, style) => new OverunderAtom(name, { below: args[0], body: args[1], skipBoundary: false, style, boxType: binRelType(args[1]), serialize: (atom, options) => `${name}{${atom.belowToLatex(options)}}` + `{${atom.bodyToLatex(options)}}`, }), }); defineFunction('overunderset', '{above:auto}{below:auto}{base:auto}', { createAtom: (name, args, style) => new OverunderAtom(name, { above: args[0], below: args[1], body: args[2], skipBoundary: false, style, boxType: binRelType(args[2]), serialize: (atom, options) => `${atom.command}{${atom.aboveToLatex(options)}}` + `{${atom.bodyToLatex(options)}}`, }), }); // `\stackrel` and `\stackbin` stack an item and provide an explicit // atom type of the result. They are considered obsolete commands. // `\underset` and `\overset` are recommended instead, which automatically // calculate the resulting type. defineFunction(['stackrel', 'stackbin'], '[below:auto]{above:auto}{base:auto}', { createAtom: (name, args, style) => new OverunderAtom(name, { body: args[2], above: args[1], below: args[0], skipBoundary: false, style, boxType: name === '\\stackrel' ? 'mrel' : 'mbin', serialize: (atom, options) => `${atom.command}{${atom.aboveToLatex(options)}}` + `{${atom.bodyToLatex(options)}}`, }), }); defineFunction(['overwithdelims', 'atopwithdelims'], '{numer:auto}{denom:auto}{left-delim:delim}{right-delim:delim}', { infix: true, createAtom: (name, args, style) => new GenfracAtom(name, args[0], args[1], { leftDelim: args[2], rightDelim: args[3], hasBarLine: false, style, serialize: (atom, options) => `${atom.aboveToLatex(options)} ${atom.command}${atom.leftDelim}${atom.rightDelim}${atom.belowToLatex(options)}`, }), }); defineFunction('smash', '[:string]{:auto}', { createAtom: (name, args, style) => { if (!args[0]) { return new PhantomAtom(name, args[1], { smashHeight: true, smashDepth: true, style, }); } return new PhantomAtom(name, args[1], { smashHeight: args[0].includes('t'), smashDepth: args[0].includes('b'), style, }); }, }); defineFunction(['vphantom'], '{:auto*}', { createAtom: (name, args, style) => new PhantomAtom(name, args[1], { isInvisible: true, smashWidth: true, style, }), }); defineFunction(['hphantom'], '{:auto*}', { createAtom: (name, args, style) => new PhantomAtom(name, args[1], { isInvisible: true, smashHeight: true, smashDepth: true, style, }), }); defineFunction(['phantom'], '{:auto*}', { createAtom: (name, args, style) => new PhantomAtom(name, args[1], { isInvisible: true, style, }), }); defineFunction('not', '{:math}', { createAtom: (name, args, style) => { if (args.length < 1 || args[0] === null) return new Atom('mrel', { command: name, style, value: '\ue020' }); const arg = args[0]; return new GroupAtom([ new OverlapAtom(name, '\ue020', { align: 'right', style, boxType: 'mrel', }), ...arg, ], { boxType: 'mrel', captureSelection: true, command: '\\not', serialize: (_atom, options) => { const argLatex = Atom.serialize(arg, options); if (argLatex.length === 1 && !/[a-zA-Z]/.test(argLatex)) return '\\not' + argLatex; return `\\not{${argLatex}}`; }, }); }, }); defineFunction(['ne', 'neq'], '', { createAtom: (name, _args, style) => new GroupAtom([ new OverlapAtom(name, '\ue020', { align: 'right', style, boxType: 'mrel', }), new Atom('mrel', { style, value: '=' }), ], { boxType: 'mrel', captureSelection: true, serialize: () => name, command: name, }), }); defineFunction('rlap', '{:auto}', { createAtom: (name, args, style) => new OverlapAtom(name, args[0], { align: 'right', style }), }); defineFunction('llap', '{:auto}', { createAtom: (name, args, style) => new OverlapAtom(name, args[0], { style }), }); defineFunction('mathllap', '{:auto}', { createAtom: (name, args, style) => new OverlapAtom(name, args[0], { style }), }); defineFunction('mathrlap', '{:auto}', { createAtom: (name, args, style) => new OverlapAtom(name, args[0], { align: 'right', style }), }); const ACCENTS = { acute: 0x02ca, grave: 0x02cb, dot: 0x02d9, ddot: 0x00a8, mathring: 0x02da, tilde: 0x007e, bar: 0x02c9, breve: 0x02d8, check: 0x02c7, hat: 0x005e, vec: 0x20d7, }; defineFunction(Object.keys(ACCENTS), '{body:auto}', { createAtom: (command, args, style) => new AccentAtom(command, args[0], { accentChar: ACCENTS[command.slice(1)], style, }), }); defineFunction(['widehat', 'widecheck', 'widetilde'], '{body:auto}', { createAtom: (command, args, style) => { // Pick the correct SVG template based on the length of the body const baseString = parseArgAsString(args[0]); return new AccentAtom(command, args[0], { style, svgAccent: command.slice(1) + (baseString.length > 5 ? '4' : ['1', '1', '2', '2', '3', '3'][baseString.length]), }); }, }); defineFunction(['overarc', 'overparen', 'wideparen'], '{body:auto}', { createAtom: (command, args, style) => { return new AccentAtom(command, args[0], { style, svgAccent: 'overarc', }); }, }); defineFunction(['underarc', 'underparen'], '{body:auto}', { createAtom: (command, args, style) => { return new OverunderAtom(command, { body: args[0], style, svgBelow: 'underarc', }); }, }); defineFunction('utilde', '{body:auto}', { createAtom: (command, args, style) => { const baseString = parseArgAsString(args[0]); const accent = 'widetilde' + (baseString.length > 5 ? '4' : ['1', '1', '2', '2', '3', '3'][baseString.length]); return new OverunderAtom(command, { body: args[0], svgBelow: accent, style, boxType: binRelType(args[0]), }); }, }); /* * From plain.tex * */ defineFunction('^', '{:string}', { createAtom: (command, args, style) => { var _a; return new Atom('mord', { command, isFunction: false, limits: 'adjacent', style, value: args[0] ? (_a = { a: 'â', e: 'ê', i: 'î', o: 'ô', u: 'û', A: 'Â', E: 'Ê', I: 'Î', O: 'Ô', U: 'Û', }[args[0]]) !== null && _a !== void 0 ? _a : '^' : '^', }); }, }); defineFunction('`', '{:string}', { createAtom: (command, args, style) => { var _a; return new Atom('mord', { command, isFunction: false, limits: 'adjacent', style, value: args[0] ? (_a = { a: 'à', e: 'è', i: 'ì', o: 'ò', u: 'ù', A: 'À', E: 'È', I: 'Ì', O: 'Ò', U: 'Ù', }[args[0]]) !== null && _a !== void 0 ? _a : '`' : '`', }); }, }); defineFunction("'", '{:string}', { createAtom: (command, args, style) => { var _a; return new Atom('mord', { command, isFunction: false, limits: 'adjacent', style, value: args[0] ? (_a = { a: 'á', e: 'é', i: 'í', o: 'ó', u: 'ú', A: 'Á', E: 'É', I: 'Í', O: 'Ó', U: 'Ú', }[args[0]]) !== null && _a !== void 0 ? _a : '\u005E' : '\u005E', }); }, }); defineFunction('~', '{:string}', { createAtom: (command, args, style) => { var _a; return new Atom('mord', { command, isFunction: false, limits: 'adjacent', style, value: args[0] ? (_a = { n: 'ñ', N: 'Ñ', a: 'ã', o: 'õ', A: 'Ã', O: 'Õ' }[args[0]]) !== null && _a !== void 0 ? _a : '\u00B4' : '\u00B4', }); }, }); defineFunction('c', '{:string}', { createAtom: (command, args, style) => { var _a; return new Atom('mord', { command, isFunction: false, limits: 'adjacent', style, value: args[0] ? (_a = { c: 'ç', C: 'Ç' }[args[0]]) !== null && _a !== void 0 ? _a : '' : '', }); }, }); class EncloseAtom extends Atom { constructor(command, body, notation, options) { super('enclose', { command, style: options.style }); this.body = body; this.backgroundcolor = options.backgroundcolor; if (notation.updiagonalarrow) notation.updiagonalstrike = false; if (notation.box) { notation.left = false; notation.right = false; notation.bottom = false; notation.top = false; } this.notation = notation; this.shadow = options.shadow; this.strokeWidth = options.strokeWidth; this.strokeStyle = options.strokeStyle; this.svgStrokeStyle = options.svgStrokeStyle; this.strokeColor = options.strokeColor; this.borderStyle = options.borderStyle; this.padding = options.padding; this.captureSelection = true; // Do not let children be selected } static fromJson(json) { return new EncloseAtom(json.command, json.body, json.notation, json); } toJson() { const notation = {}; if (this.notation.downdiagonalstrike) notation.downdiagonalstrike = true; if (this.notation.updiagonalstrike) notation.downdiagonalstrike = true; if (this.notation.verticalstrike) notation.downdiagonalstrike = true; if (this.notation.horizontalstrike) notation.downdiagonalstrike = true; if (this.notation.updiagonalarrow) notation.downdiagonalstrike = true; if (this.notation.right) notation.downdiagonalstrike = true; if (this.notation.bottom) notation.downdiagonalstrike = true; if (this.notation.left) notation.downdiagonalstrike = true; if (this.notation.top) notation.downdiagonalstrike = true; if (this.notation.circle) notation.downdiagonalstrike = true; if (this.notation.roundedbox) notation.downdiagonalstrike = true; if (this.notation.madruwb) notation.downdiagonalstrike = true; if (this.notation.actuarial) notation.downdiagonalstrike = true; if (this.notation.box) notation.downdiagonalstrike = true; return { ...super.toJson(), notation: notation, shadow: this.shadow, strokeWidth: this.strokeWidth, strokeStyle: this.strokeStyle, svgStrokeStyle: this.svgStrokeStyle, strokeColor: this.strokeColor, borderStyle: this.borderStyle, padding: this.padding, }; } serialize(options) { var _a; let result = (_a = this.command) !== null && _a !== void 0 ? _a : ''; if (this.command === '\\enclose') { result += '{' + Object.keys(this.notation).join(' ') + '}'; // \enclose can have optional parameters... let style = ''; let sep = ''; if (this.backgroundcolor && this.backgroundcolor !== 'transparent') { style += sep + 'mathbackground="' + this.backgroundcolor + '"'; sep = ','; } if (this.shadow && this.shadow !== 'auto') { style += sep + 'shadow="' + this.shadow + '"'; sep = ','; } if (this.strokeWidth || this.strokeStyle !== 'solid') { style += sep + this.borderStyle; sep = ','; } else if (this.strokeColor && this.strokeColor !== 'currentColor') { style += sep + 'mathcolor="' + this.strokeColor + '"'; sep = ','; } if (style) result += `[${style}]`; } result += `{${this.bodyToLatex(options)}}`; return result; } render(parentContext) { var _a; const context = new Context(parentContext, this.style); const base = Atom.createBox(context, this.body); if (!base) return null; // Account for the padding const padding = (_a = convertDimensionToEm(this.padding && this.padding !== 'auto' ? convertToDimension(this.padding, parentContext.registers) : context.getRegisterAsDimension('fboxsep'))) !== null && _a !== void 0 ? _a : 0; // The 'ML__notation' class is required to prevent the box from being omitted // during rendering (it looks like an empty, no-op box) const notation = new Box(null, { classes: 'ML__notation' }); notation.setStyle('position', 'absolute'); notation.setStyle('height', base.height + base.depth + 2 * padding, 'em'); notation.height = base.height + padding; notation.depth = base.depth + padding; if (padding !== 0) notation.setStyle('width', `calc(100% + ${2 * padding}em)`); else notation.setStyle('width', '100%'); notation.setStyle('top', -base.height + 2 * padding, 'em'); notation.setStyle('left', -padding, 'em'); notation.setStyle('z-index', '-1'); // Ensure the box is *behind* the base notation.setStyle('box-sizing', 'border-box'); if (this.backgroundcolor) notation.setStyle('background-color', this.backgroundcolor); if (this.notation.box) notation.setStyle('border', this.borderStyle); if (this.notation.actuarial) { notation.setStyle('border-top', this.borderStyle); notation.setStyle('border-right', this.borderStyle); } if (this.notation.madruwb) { notation.setStyle('border-bottom', this.borderStyle); notation.setStyle('border-right', this.borderStyle); } if (this.notation.roundedbox) { notation.setStyle('border-radius', (base.height + base.depth) / 2, 'em'); notation.setStyle('border', this.borderStyle); } if (this.notation.circle) { notation.setStyle('border-radius', '50%'); notation.setStyle('border', this.borderStyle); } if (this.notation.top) notation.setStyle('border-top', this.borderStyle); if (this.notation.left) notation.setStyle('border-left', this.borderStyle); if (this.notation.right) notation.setStyle('border-right', this.borderStyle); if (this.notation.bottom) notation.setStyle('border-bottom', this.borderStyle); let svg = ''; if (this.notation.horizontalstrike) { svg += '<line x1="3%" y1="50%" x2="97%" y2="50%"'; svg += ` stroke-width="${this.strokeWidth}" stroke="${this.strokeColor}"`; svg += ' stroke-linecap="round"'; if (this.svgStrokeStyle) svg += ` stroke-dasharray="${this.svgStrokeStyle}"`; svg += '/>'; } if (this.notation.verticalstrike) { svg += '<line x1="50%" y1="3%" x2="50%" y2="97%"'; svg += ` stroke-width="${this.strokeWidth}" stroke="${this.strokeColor}"`; svg += ' stroke-linecap="round"'; if (this.svgStrokeStyle) svg += ` stroke-dasharray="${this.svgStrokeStyle}"`; svg += '/>'; } if (this.notation.updiagonalstrike) { svg += '<line x1="3%" y1="97%" x2="97%" y2="3%"'; svg += ` stroke-width="${this.strokeWidth}" stroke="${this.strokeColor}"`; svg += ' stroke-linecap="round"'; if (this.svgStrokeStyle) svg += ` stroke-dasharray="${this.svgStrokeStyle}"`; svg += '/>'; } if (this.notation.downdiagonalstrike) { svg += '<line x1="3%" y1="3%" x2="97%" y2="97%"'; svg += ` stroke-width="${this.strokeWidth}" stroke="${this.strokeColor}"`; svg += ' stroke-linecap="round"'; if (this.svgStrokeStyle) svg += ` stroke-dasharray="${this.svgStrokeStyle}"`; svg += '/>'; } // if (this.notation.updiagonalarrow) { // const t = 1; // const length = Math.sqrt(w * w + h * h); // const f = (1 / length / 0.075) * t; // const wf = w * f; // const hf = h * f; // const x = w - t / 2; // let y = t / 2; // if (y + hf - 0.4 * wf < 0) y = 0.4 * wf - hf; // svg += '<line '; // svg += `x1="1" y1="${h - 1}px" x2="${x - 0.7 * wf}px" y2="${ // y + 0.7 * hf // }px"`; // svg += ` stroke-width="${this.strokeWidth}" stroke="${this.strokeColor}"`; // svg += ' stroke-linecap="round"'; // if (this.svgStrokeStyle) { // svg += ` stroke-dasharray="${this.svgStrokeStyle}"`; // } // svg += '/>'; // svg += '<polygon points="'; // svg += `${x},${y} ${x - wf - 0.4 * hf},${y + hf - 0.4 * wf} `; // svg += `${x - 0.7 * wf},${y + 0.7 * hf} ${x - wf + 0.4 * hf},${ // y + hf + 0.4 * wf // } `; // svg += `${x},${y}`; // svg += `" stroke='none' fill="${this.strokeColor}"`; // svg += '/>'; // } // if (this.notation.phasorangle) { // svg += '<path d="'; // svg += `M ${h / 2},1 L1,${h} L${w},${h} "`; // svg += ` stroke-width="${this.strokeWidth}" stroke="${this.strokeColor}" fill="none"`; // if (this.svgStrokeStyle) { // svg += ' stroke-linecap="round"'; // svg += ` stroke-dasharray="${this.svgStrokeStyle}"`; // } // svg += '/>'; // } // if (this.notation.radical) { // svg += '<path d="'; // svg += `M 0,${0.6 * h} L1,${h} L${ // convertDimensionToPixel(padding) * 2 // },1 "`; // svg += ` stroke-width="${this.strokeWidth}" stroke="${this.strokeColor}" fill="none"`; // if (this.svgStrokeStyle) { // svg += ' stroke-linecap="round"'; // svg += ` stroke-dasharray="${this.svgStrokeStyle}"`; // } // svg += '/>'; // } // if (this.notation.longdiv) { // svg += '<path d="'; // svg += `M ${w} 1 L1 1 a${convertDimensionToPixel(padding)} ${ // h / 2 // }, 0, 0, 1, 1 ${h} "`; // svg += ` stroke-width="${this.strokeWidth}" stroke="${this.strokeColor}" fill="none"`; // if (this.svgStrokeStyle) { // svg += ' stroke-linecap="round"'; // svg += ` stroke-dasharray="${this.svgStrokeStyle}"`; // } // svg += '/>'; // } if (svg) { let svgStyle; if (this.shadow !== 'none') { svgStyle = this.shadow === 'auto' ? 'filter: drop-shadow(0 0 .5px rgba(255, 255, 255, .7)) drop-shadow(1px 1px 2px #333)' : 'filter: drop-shadow(' + this.shadow + ')'; } addSVGOverlay(notation, svg, svgStyle); } const result = new Box([notation, base]); // Set its position as relative so that the box can be absolute positioned // over the base result.setStyle('position', 'relative'); result.setStyle('display', 'inline'); // The padding adds to the width and height of the pod result.height = base.height + padding; result.depth = base.depth + padding; result.left = padding; result.right = padding; if (this.caret) result.caret = this.caret; return result.wrap(context); } } // \enclose, a MathJax extension mapping to the MathML `menclose` tag. // The first argument is a comma delimited list of notations, as defined // here: https://developer.mozilla.org/en-US/docs/Web/MathML/Element/menclose // The second, optional, specifies the style to use for the notations. defineFunction('enclose', '{notation:string}[style:string]{body:auto}', { createAtom: (command, args, style) => { var _a; const options = { strokeColor: 'currentColor', strokeWidth: '', strokeStyle: 'solid', backgroundcolor: 'transparent', padding: 'auto', shadow: 'auto', svgStrokeStyle: undefined, borderStyle: undefined, style, }; // Extract info from style string if (args[1]) { // Split the string by comma delimited sub-strings, ignoring commas // that may be inside (). For example"x, rgb(a, b, c)" would return // ['x', 'rgb(a, b, c)'] const styles = args[1].split(/,(?![^(]*\)(?:(?:[^(]*\)){2})*[^"]*$)/); for (const s of styles) { const shorthand = s.match(/\s*(\S+)\s+(\S+)\s+(.*)/); if (shorthand) { options.strokeWidth = shorthand[1]; options.strokeStyle = shorthand[2]; options.strokeColor = shorthand[3]; } else { const attribute = s.match(/\s*([a-z]*)\s*=\s*"(.*)"/); if (attribute) { if (attribute[1] === 'mathbackground') options.backgroundcolor = attribute[2]; else if (attribute[1] === 'mathcolor') options.strokeColor = attribute[2]; else if (attribute[1] === 'padding') options.padding = attribute[2]; else if (attribute[1] === 'shadow') options.shadow = attribute[2]; } } } if (options.strokeStyle === 'dashed') options.svgStrokeStyle = '5,5'; else if (options.strokeStyle === 'dotted') options.svgStrokeStyle = '1,5'; } options.borderStyle = `${options.strokeWidth} ${options.strokeStyle} ${options.strokeColor}`; // Normalize the list of notations. const notation = {}; ((_a = args[0]) !== null && _a !== void 0 ? _a : '') .split(/[, ]/) .filter((v) => v.length > 0) .forEach((x) => { notation[x.toLowerCase()] = true; }); return new EncloseAtom(command, args[2], notation, options); }, }); defineFunction('cancel', '{body:auto}', { createAtom: (name, args, style) => new EncloseAtom(name, args[0], { updiagonalstrike: true }, { strokeColor: 'currentColor', strokeWidth: '', strokeStyle: 'solid', borderStyle: '1px solid currentColor', backgroundcolor: 'transparent', padding: 'auto', shadow: 'auto', style, }), }); defineFunction('bcancel', '{body:auto}', { createAtom: (name, args, style) => new EncloseAtom(name, args[0], { downdiagonalstrike: true }, { strokeColor: 'currentColor', strokeWidth: '', strokeStyle: 'solid', borderStyle: '1px solid currentColor', backgroundcolor: 'transparent', padding: 'auto', shadow: 'auto', style, }), }); defineFunction('xcancel', '{body:auto}', { createAtom: (name, args, style) => new EncloseAtom(name, args[0], { updiagonalstrike: true, downdiagonalstrike: true }, { strokeColor: 'currentColor', strokeWidth: '', strokeStyle: 'solid', borderStyle: '1px solid currentColor', backgroundcolor: 'transparent', padding: 'auto', shadow: 'auto', style, }), }); // function arrayToString(array: Atom[][][]): string { // if (array || array.length === 0) return `0 ⨉ 0\n`; // let result = `${array.length}r ⨉ ${array[0].length ?? 0}c\n`; // for (const row of array) { // result += ' '; // for (const cell of row) { // if (!cell || cell.length === 0) { // result += '😱'; // } else if (cell[0].type === 'first') { // if (cell[1]) { // result += cell[1].command; // } else { // result += '∅'; // } // } else { // result += '👎' + cell[0].command; // } // result += ' '; // } // result += '\n'; // } // return result; // } /** * Normalize the array: * - ensure it is dense (not sparse) * - fold rows that overflow (longer than maximum number of columns) * - ensure each cell begins with a `first` atom * - remove last row if empty */ function normalizeArray(atom, array, colFormat) { // // 1/ // - Fold the array so that there are no more columns of content than // there are columns prescribed by the column format. // - Fill rows that have fewer cells than expected with empty cells // - Ensure that all the cells have a `first` atom. // // The number of column is determined by the colFormat let maxColCount = 0; for (const colSpec of colFormat) if ('align' in colSpec) maxColCount += 1; // Actual number of columns (at most `maxColCount`) let colCount = 0; const rows = []; for (const row of array) { let colIndex = 0; colCount = Math.max(colCount, Math.min(row.length, maxColCount)); while (colIndex < row.length) { const newRow = []; const lastCol = Math.min(row.length, colIndex + maxColCount); while (colIndex < lastCol) { if (row[colIndex].length === 0) newRow.push([new Atom('first', { mode: atom.mode })]); else if (row[colIndex][0].type !== 'first') { newRow.push([ new Atom('first', { mode: atom.mode }), ...row[colIndex], ]); } else newRow.push(row[colIndex]); colIndex += 1; } rows.push(newRow); } } // // 2/ If the last row is empty, ignore it (TeX behavior) // if (rows[rows.length - 1].length === 1 && rows[rows.length - 1][0].length === 0) rows.pop(); // // 3/ Fill out any missing cells // const result = []; for (const row of rows) { if (row.length !== colCount) { for (let i = row.length; i < colCount; i++) { row.push([ new Atom('first', { mode: atom.mode }), new PlaceholderAtom(), ]); } } result.push(row); } // // 4/ Set the `parent` and `treeBranch` for each cell // let rowIndex = 0; let colIndex = 0; for (const row of result) { colIndex = 0; for (const cell of row) { for (const element of cell) { element.parent = atom; element.treeBranch = [rowIndex, colIndex]; } colIndex += 1; } rowIndex += 1; } atom.isDirty = true; return result; } // See http://ctan.math.utah.edu/ctan/tex-archive/macros/latex/base/lttab.dtx class ArrayAtom extends Atom { constructor(envName, array, rowGaps, options = {}) { var _a; super('array'); this.environmentName = envName; this.rowGaps = rowGaps; if (options.mathstyleName) this.mathstyleName = options.mathstyleName; if (options.columns) { if (options.columns.length === 0) this.colFormat = [{ align: 'l' }]; else this.colFormat = options.columns; } // The TeX definition is that arrays by default have a maximum // of 10, left-aligned, columns. if (!this.colFormat) { this.colFormat = [ { align: 'l' }, { align: 'l' }, { align: 'l' }, { align: 'l' }, { align: 'l' }, { align: 'l' }, { align: 'l' }, { align: 'l' }, { align: 'l' }, { align: 'l' }, ]; } this.array = normalizeArray(this, array, this.colFormat); // console.log(arrayToString(this.array)); if (options.leftDelim) this.leftDelim = options.leftDelim; if (options.rightDelim) this.rightDelim = options.rightDelim; if (options.jot !== undefined) this.jot = options.jot; if (options.arraycolsep) this.arraycolsep = options.arraycolsep; this.colSeparationType = options.colSeparationType; // Default \arraystretch from lttab.dtx this.arraystretch = (_a = options.arraystretch) !== null && _a !== void 0 ? _a : 1.0; } static fromJson(json) { return new ArrayAtom(json.environmentName, json.array, json.rowGaps, json); } toJson() { const result = { ...super.toJson(), environmentName: this.environmentName, array: this.array.map((row) => row.map((col) => col.map((x) => x.toJson()))), rowGaps: this.rowGaps, columns: this.colFormat, colSeparationType: this.colSeparationType, }; if (this.arraystretch !== 1.0) result.arraystretch = this.arraystretch; if (this.arraycolsep) result.arraycolsep = this.arraycolsep; if (this.leftDelim) result.leftDelim = this.leftDelim; if (this.rightDelim) result.rightDelim = this.rightDelim; if (this.jot !== undefined) result.jot = this.jot; return result; } branch(cell) { var _a; if (!isCellBranch(cell)) return undefined; return (_a = this.array[cell[0]][cell[1]]) !== null && _a !== void 0 ? _a : undefined; } get branches() { const result = super.branches; this.array.forEach((_, col) => { this.array[col].forEach((_, row) => { if (this.array[col][row]) result.push([col, row]); }); }); return result; } createBranch(cell) { var _a; if (!isCellBranch(cell)) return []; this.isDirty = true; return (_a = this.branch(cell)) !== null && _a !== void 0 ? _a : []; } get rowCount() { return this.array.length; } get colCount() { return this.array[0].length; } removeBranch(name) { if (isNamedBranch(name)) return super.removeBranch(name); const children = this.branch(name); this.array[name[0]][name[1]] = undefined; children.forEach((x) => { x.parent = undefined; x.treeBranch = undefined; }); // Drop the 'first' element console.assert(children[0].type === 'first'); children.shift(); this.isDirty = true; return children; } get hasChildren() { return this.children.length > 0; } get children() { const result = []; for (const row of this.array) { for (const cell of row) { if (cell) { for (const atom of cell) { result.push(...atom.children); result.push(atom); } } } } return [...result, ...super.children]; } render(context) { // See http://tug.ctan.org/macros/latex/base/ltfsstrc.dtx // and http://tug.ctan.org/macros/latex/base/lttab.dtx var _a, _b, _c, _d, _e; const innerContext = new Context(context, this.style, this.mathstyleName); const arrayRuleWidth = innerContext.getRegisterAsEm('arrayrulewidth'); const arrayColSep = innerContext.getRegisterAsEm('arraycolsep'); const doubleRuleSep = innerContext.getRegisterAsEm('doublerulesep'); // Row spacing const arraystretch = (_a = this.arraystretch) !== null && _a !== void 0 ? _a : 1.0; let arraycolsep = typeof this.arraycolsep === 'number' ? this.arraycolsep : arrayColSep; if (this.colSeparationType === 'small') { // We're in a {smallmatrix}. Default column space is \thickspace, // i.e. 5/18em = 0.2778em, per amsmath.dtx for {smallmatrix}. // But that needs adjustment because LaTeX applies \scriptstyle to the // entire array, including the colspace, but this function applies // \scriptstyle only inside each element. const localMultiplier = new Context(context, undefined, 'scriptstyle') .scalingFactor; arraycolsep = 0.2778 * (localMultiplier / context.scalingFactor); } const arrayskip = arraystretch * BASELINE_SKIP; const arstrutHeight = 0.7 * arrayskip; const arstrutDepth = 0.3 * arrayskip; // \@arstrutbox in lttab.dtx let totalHeight = 0; const body = []; let nc = 0; const nr = this.array.length; for (let r = 0; r < nr; ++r) { const inrow = this.array[r]; nc = Math.max(nc, inrow.length); // The "inner" is in mathstyleName. Create a **new** context for the // cells, with the same mathstyleName, but this will prevent the // style correction from being applied twice const cellContext = new Context(innerContext, this.style, this.mathstyleName); let height = arstrutHeight / cellContext.scalingFactor; // \@array adds an \@arstrut let depth = arstrutDepth / cellContext.scalingFactor; // To each row (via the template) const outrow = { cells: [], height: 0, depth: 0, pos: 0 }; for (const element of inrow) { const elt = (_b = Atom.createBox(cellContext, element, { newList: true })) !== null && _b !== void 0 ? _b : new Box(null, { newList: true }); depth = Math.max(depth, elt.depth); height = Math.max(height, elt.height); outrow.cells.push(elt); } let gap = (_c = convertDimensionToEm(this.rowGaps[r])) !== null && _c !== void 0 ? _c : 0; if (gap > 0) { // \@argarraycr gap += arstrutDepth; depth = Math.max(depth, gap); // \@xargarraycr gap = 0; } if (this.jot !== undefined) depth += this.jot; outrow.height = height; outrow.depth = depth; totalHeight += height; outrow.pos = totalHeight; totalHeight += depth + gap; // \@yargarraycr body.push(outrow); } const offset = totalHeight / 2 + AXIS_HEIGHT; const contentCols = []; for (let colIndex = 0; colIndex < nc; colIndex++) { const stack = []; for (const row of body) { const element = row.cells[colIndex]; element.depth = row.depth; element.height = row.height; stack.push({ box: element, shift: row.pos - offset }); } if (stack.length > 0) contentCols.push(new VBox({ individualShift: stack })); } // Iterate over each column description. // Each `colDesc` will indicate whether to insert a gap, a rule or // a column from 'contentCols' const cols = []; let previousColContent = false; let previousColRule = false; let currentContentCol = 0; let firstColumn = !this.leftDelim; const { colFormat } = this; for (const colDesc of colFormat) { if ('align' in colDesc && currentContentCol >= contentCols.length) { // If there are more column format than content, we're done break; } if ('align' in colDesc) { // If an alignment is specified, insert a column of content if (previousColContent) { // If no gap was provided, insert a default gap between // consecutive columns of content cols.push(makeColGap(2 * arraycolsep)); } else if (previousColRule || firstColumn) { // If the previous column was a rule or this is the first column // add a smaller gap cols.push(makeColGap(arraycolsep)); } cols.push(new Box(contentCols[currentContentCol], { classes: 'col-align-' + colDesc.align, })); currentContentCol++; previousColContent = true; previousColRule = false; firstColumn = false; } else if ('gap' in colDesc) { // // Something to insert in between columns of content // if (typeof colDesc.gap === 'number') { // It's a number, indicating how much space, in em, // to leave in between columns cols.push(makeColGap(colDesc.gap)); } else { // It's a list of atoms. // Create a column made up of the mathlist // as many times as there are rows. const col = makeColOfRepeatingElements(context, body, offset, colDesc.gap); if (col) cols.push(col); } previousColContent = false; previousColRule = false; firstColumn = false; } else if ('separator' in colDesc) { // // It's a column separator. // const separator = new Box(null, { classes: 'vertical-separator' }); separator.setStyle('height', totalHeight, 'em'); separator.setStyle('border-right', `${arrayRuleWidth}em ${colDesc.separator} currentColor`); // We have box-sizing border-box, no need to correct the margin // separator.setStyle( // 'margin', // `0 -${context.metrics.arrayRuleWidth / 2}em` // ); separator.setStyle('vertical-align', -(totalHeight - offset), 'em'); let gap = 0; if (previousColRule) gap = doubleRuleSep - arrayRuleWidth; else if (previousColContent) gap = arraycolsep - arrayRuleWidth; separator.left = gap; cols.push(separator); previousColContent = false; previousColRule = true; firstColumn = false; } } if (previousColContent && !this.rightDelim) { // If the last column was content, add a small gap cols.push(makeColGap(arraycolsep)); } const inner = new Box(cols, { classes: 'mtable' }); if ((!this.leftDelim || this.leftDelim === '.') && (!this.rightDelim || this.rightDelim === '.')) { // There are no delimiters around the array, just return what // we've built so far. return inner; } // There is at least one delimiter. Wrap the inner of the array with // appropriate left and right delimiters const innerHeight = inner.height; const innerDepth = inner.depth; const result = this.bind(context, new Box([ this.bind(context, makeLeftRightDelim('mopen', (_d = this.leftDelim) !== null && _d !== void 0 ? _d : '.', innerHeight, innerDepth, innerContext)), inner, this.bind(context, makeLeftRightDelim('mclose', (_e = this.rightDelim) !== null && _e !== void 0 ? _e : '.', innerHeight, innerDepth, innerContext)), ], { type: 'mord' })); if (!result) return null; if (this.caret) result.caret = this.caret; return this.attachSupsub(context, { base: result }); } serialize(options) { let result = '\\begin{' + this.environmentName + '}'; if (this.environmentName === 'array') { result += '{'; if (this.colFormat !== undefined) { for (const format of this.colFormat) { if ('align' in format) result += format.align; else if ('separator' in format && format.separator === 'solid') result += '|'; else if ('separator' in format && format.separator === 'dashed') result += ':'; } } result += '}'; } for (let row = 0; row < this.array.length; row++) { for (let col = 0; col < this.array[row].length; col++) { if (col > 0) result += ' & '; result = joinLatex([ result, Atom.serialize(this.array[row][col], options), ]); } // Adds a separator between rows (but not after the last row) if (row < this.array.length - 1) result += ' \\\\ '; } result += '\\end{' + this.environmentName + '}'; return result; } getCell(row, col) { return this.array[row][col]; } setCell(_row, _column, _value) { // @todo array console.assert(this.type === 'array' && Array.isArray(this.array)); this.isDirty = true; } addRowBefore(_row) { console.assert(this.type === 'array' && Array.isArray(this.array)); // @todo array this.isDirty = true; } addRowAfter(_row) { console.assert(this.type === 'array' && Array.isArray(this.array)); // @todo array this.isDirty = true; } addColumnBefore(_col) { console.assert(this.type === 'array' && Array.isArray(this.array)); this.isDirty = true; } addColumnAfter(_col) { console.assert(this.type === 'array' && Array.isArray(this.array)); // @todo array this.isDirty = true; } get cells() { const result = []; for (const row of this.array) for (const cell of row) if (cell) result.push(cell); return result; } } /** * Create a column separator box. * */ function makeColGap(width) { const separator = new Box(null, { classes: 'arraycolsep' }); separator.width = width; return separator; } /** * Create a column of repeating elements. */ function makeColOfRepeatingElements(context, rows, offset, element) { if (!element) return null; const col = []; for (const row of rows) { const cell = Atom.createBox(context, element, { newList: true }); if (cell) { cell.depth = row.depth; cell.height = row.height; col.push({ box: cell, shift: row.pos - offset }); } } return new VBox({ individualShift: col }).wrap(context); } /* See http://texdoc.net/texmf-dist/doc/latex/amsmath/amsldoc.pdf <columns> ::= <column>*<line> <column> ::= <line>('l'|'c'|'r') <line> ::= '|' | '||' | '' 'math', frequency 0 'displaymath', frequency 8 'equation' centered, numbered frequency 8 'subequations' with an 'equation' environment, appends a letter to eq no frequency 1 'array', {columns:text} cells are textstyle math no fence 'eqnarray' DEPRECATED see http://www.tug.org/pracjourn/2006-4/madsen/madsen.pdf {rcl} first and last cell in each row is displaystyle math each cell has a margin of \arraycolsep Each line has a eqno frequency 7 'theorem' text mode. Prepends in bold 'Theorem <counter>', then body in italics. 'multline' single column first row left aligned, last right aligned, others centered last line has an eqn. counter. multline* will omit the counter no output if inside an equation 'gather' at most two columns first column centered, second column right aligned frequency 1 'gathered' must be in equation environment single column, centered frequency: COMMON optional argument: [b], [t] to vertical align 'align' multiple columns, alternating rl there is some 'space' (additional column?) between each pair each line is numbered (except when inside an equation environment) there is an implicit {} at the beginning of left columns 'aligned' must be in equation environment frequency: COMMON @{}r@{}l@{\quad}@{}r@{}l@{} 'split' must be in an equation environment, two columns, additional columns are interpreted as line breaks first column is right aligned, second column is left aligned entire construct is numbered (as opposed to 'align' where each line is numbered) frequency: 0 'alignedat' From AMSMath: ---The alignedat environment was changed to take two arguments rather than one: a mandatory argument (as formerly) specifying the number of align structures, and a new optional one specifying the placement of the environment (parallel to the optional argument of aligned). However, aligned is simpler to use, allowing any number of aligned structures automatically, and therefore the use of alignedat is deprecated. 'alignat' {pairs:number} {rl} alternating as many times as indicated by <pairs> arg no space between column pairs (unlike align) there is an implicit {} at the beginning of left columns frequency: 0 'flalign' multiple columns alternate rl third column further away than align...? frequency: 0 'matrix' at most 10 columns cells centered no fence no colsep at beginning or end (mathtools package add an optional arg for the cell alignment) frequency: COMMON 'pmatrix' fence: () frequency: COMMON 'bmatrix' fence: [] frequency: COMMON 'Bmatrix' fence: {} frequency: 237 'vmatrix' fence: \vert frequency: 368 'Vmatrix' fence: \Vert frequency: 41 'smallmatrix' displaystyle: scriptstyle (?) frequency: 279 'cases' frequency: COMMON l@{2}l 'center' text mode only? frequency: ? */ // See https://en.wikibooks.org/wiki/LaTeX/Mathematics // and http://www.ele.uri.edu/faculty/vetter/Other-stuff/latex/Mathmode.pdf /* The star at the end of the name of a displayed math environment causes that the formula lines won't be numbered. Otherwise they would automatically get a number. \notag will also turn off the numbering. \shoveright and \shoveleft will force alignment of a line The only difference between align and equation is the spacing of the formulas. You should attempt to use equation when possible, and align when you have multi-line formulas. Equation will have space before/after < 1em if line before/after is short enough. Also: equation throws an error when you have an & inside the environment, so look out for that when converting between the two. Whereas align produces a structure whose width is the full line width, aligned gives a width that is the actual width of the contents, thus it can be used as a component in a containing expression, e.g. for putting the entire alignment in a parenthesis */ defineEnvironment('math', '', (name, array, rowGaps) => new ArrayAtom(name, array, rowGaps, { mathstyleName: 'textstyle' })); defineEnvironment('displaymath', '', (name, array, rowGaps) => new ArrayAtom(name, array, rowGaps, { mathstyleName: 'textstyle' })); defineTabularEnvironment('array', '{columns:colspec}', (name, array, rowGaps, args) => new ArrayAtom(name, array, rowGaps, { columns: args[0], mathstyleName: 'textstyle', })); defineTabularEnvironment(['equation', 'equation*', 'subequations'], '', (name, array, rowGaps) => new ArrayAtom(name, array, rowGaps, { columns: [{ align: 'c' }], })); // Note spelling: MULTLINE, not multiline. defineTabularEnvironment('multline', '', (name, array, rowGaps) => new ArrayAtom(name, array, rowGaps, { columns: [{ align: 'm' }], })); // An AMS-Math environment // See amsmath.dtx:3565 // Note that some versions of AMS-Math have a gap on the left. // More recent version suppresses that gap, but have an option to turn it back on // for backward compatibility. // Note that technically, 'eqnarray' behaves (slightly) differently. However, // is is generally recommended to avoid using eqnarray and use align isntead. // https://texblog.net/latex-archive/maths/eqnarray-align-environment/ defineTabularEnvironment(['align', 'align*', 'aligned', 'eqnarray'], '', (name, array, rowGaps) => { let colCount = 0; for (const row of array) colCount = Math.max(colCount, row.length); const colFormat = [ { gap: 0 }, { align: 'r' }, { gap: 0.25 }, { align: 'l' }, ]; let i = 2; while (i < colCount) { colFormat.push({ gap: 1 }); colFormat.push({ align: 'r' }); colFormat.push({ gap: 0.25 }); colFormat.push({ align: 'l' }); i += 2; } colFormat.push({ gap: 0 }); return new ArrayAtom(name, array, rowGaps, { arraycolsep: 0, columns: colFormat, colSeparationType: 'align', jot: 0.3, }); }); // DefineEnvironment('alignat', '', function(name, args) { // return { // }; // }); // defineEnvironment('flalign', '', function(name, args) { // return { // }; // }); defineTabularEnvironment('split', '', (name, array, rowGaps) => new ArrayAtom(name, array, rowGaps, { columns: [{ align: 'r' }, { align: 'l' }], })); defineTabularEnvironment(['gather', 'gathered'], '', (name, array, rowGaps) => // An AMS-Math environment // % The \env{gathered} environment is for several lines that are // % centered independently. // From amstex.sty // \newenvironment{gathered}[1][c]{% // \relax\ifmmode\else\nonmatherr@{\begin{gathered}}\fi // \null\,% // \if #1t\vtop \else \if#1b\vbox \else \vcenter \fi\fi // \bgroup\Let@\restore@math@cr // \ifinany@\else\openup\jot\fi\ialign // \bgroup\hfil\strut@$\m@th\displaystyle##$\hfil\crcr new ArrayAtom(name, array, rowGaps, { columns: [{ gap: 0.25 }, { align: 'c' }, { gap: 0 }], colSeparationType: 'gather', })); // DefineEnvironment('cardinality', '', function() { // const result = {}; // result.mathstyle = 'textstyle'; // result.lFence = '|'; // result.rFence = '|'; // return result; // }); defineTabularEnvironment([ 'matrix', 'pmatrix', 'bmatrix', 'Bmatrix', 'vmatrix', 'Vmatrix', 'matrix*', 'pmatrix*', 'bmatrix*', 'Bmatrix*', 'vmatrix*', 'Vmatrix*', ], '[columns:colspec]', (name, array, rowGaps, args) => { var _a; // From amstex.sty: // \def\matrix{\hskip -\arraycolsep\array{*\c@MaxMatrixCols c}} // \def\endmatrix{\endarray \hskip -\arraycolsep} let leftDelim = '.'; let rightDelim = '.'; switch (name) { case 'pmatrix': case 'pmatrix*': leftDelim = '('; rightDelim = ')'; break; case 'bmatrix': case 'bmatrix*': leftDelim = '['; rightDelim = ']'; break; case 'Bmatrix': case 'Bmatrix*': leftDelim = '\\lbrace'; rightDelim = '\\rbrace'; break; case 'vmatrix': case 'vmatrix*': leftDelim = '\\vert'; rightDelim = '\\vert'; break; case 'Vmatrix': case 'Vmatrix*': leftDelim = '\\Vert'; rightDelim = '\\Vert'; break; case 'matrix': case 'matrix*': // Specifying a fence, even a null fence, // will prevent the insertion of an initial and final gap leftDelim = '.'; rightDelim = '.'; break; } return new ArrayAtom(name, array, rowGaps, { mathstyleName: 'textstyle', leftDelim, rightDelim, columns: (_a = args[0]) !== null && _a !== void 0 ? _a : [ { align: 'c' }, { align: 'c' }, { align: 'c' }, { align: 'c' }, { align: 'c' }, { align: 'c' }, { align: 'c' }, { align: 'c' }, { align: 'c' }, { align: 'c' }, ], }); }); defineTabularEnvironment(['smallmatrix', 'smallmatrix*'], '[columns:colspec]', (name, array, rowGaps, args) => { var _a; return new ArrayAtom(name, array, rowGaps, { mathstyleName: 'scriptstyle', columns: (_a = args[0]) !== null && _a !== void 0 ? _a : [ { align: 'c' }, { align: 'c' }, { align: 'c' }, { align: 'c' }, { align: 'c' }, { align: 'c' }, { align: 'c' }, { align: 'c' }, { align: 'c' }, { align: 'c' }, ], colSeparationType: 'small', arraystretch: 0.5, }); }); // \cases is standard LaTeX // \dcases is from the mathtools package defineTabularEnvironment(['cases', 'dcases'], '', (name, array, rowGaps) => { // From amstex.sty: // \def\cases{\left\{\def\arraystretch{1.2}\hskip-\arraycolsep // \array{l@{\quad}l}} // \def\endcases{\endarray\hskip-\arraycolsep\right.} // From amsmath.dtx // \def\env@cases{% // \let\@ifnextchar\new@ifnextchar // \left\lbrace // \def\arraystretch{1.2}% // \array{@{}l@{\quad}l@{}}% return new ArrayAtom(name, array, rowGaps, { mathstyleName: name === 'dcases' ? 'displaystyle' : 'textstyle', arraystretch: 1.2, leftDelim: '\\lbrace', rightDelim: '.', columns: [{ align: 'l' }, { gap: 1 }, { align: 'l' }], }); }); // \rcases is from the mathtools package defineTabularEnvironment('rcases', '', (name, array, rowGaps) => { return new ArrayAtom(name, array, rowGaps, { arraystretch: 1.2, leftDelim: '.', rightDelim: '\\rbrace', columns: [{ align: 'l' }, { gap: 1 }, { align: 'l' }], }); }); // This is a text mode environment /* \begin{theorem} Let $f$ be a function whose derivative exists in every point, then $f$ is a continuous function. \end{theorem} */ // defineEnvironment('theorem', '', function () { // return {}; // }); defineEnvironment('center', '', (name, array, rowGaps) => new ArrayAtom(name, array, rowGaps, { columns: [{ align: 'c' }] })); // Extensible (horitontally stretchy) symbols defineFunction([ 'overrightarrow', 'overleftarrow', 'Overrightarrow', 'overleftharpoon', 'overrightharpoon', 'overleftrightarrow', 'overlinesegment', 'overgroup', ], '{:auto}', { createAtom: (command, args, style) => new OverunderAtom(command, { body: args[0], skipBoundary: false, supsubPlacement: 'over-under', padded: true, boxType: 'mrel', style, // Set the "svgAbove" to the name of a SVG object (which is the same // as the command name) svgAbove: command.slice(1), }), }); defineFunction('overbrace', '{:auto}', { createAtom: (command, args, style) => new OverunderAtom(command, { body: args[0], skipBoundary: false, supsubPlacement: 'over-under', padded: true, boxType: 'mord', style, svgAbove: command.slice(1), }), }); defineFunction([ 'underrightarrow', 'underleftarrow', 'underleftrightarrow', 'underlinesegment', 'undergroup', ], '{:auto}', { createAtom: (command, args, style) => new OverunderAtom(command, { body: args[0], skipBoundary: false, supsubPlacement: 'over-under', padded: true, boxType: 'mrel', style, // Set the "svgBelow" to the name of a SVG object (which is the same // as the command name) svgBelow: command.slice(1), }), }); defineFunction(['underbrace'], '{:auto}', { createAtom: (command, args, style) => new OverunderAtom(command, { body: args[0], skipBoundary: false, supsubPlacement: 'over-under', padded: true, boxType: 'mord', style, svgBelow: command.slice(1), }), }); defineFunction([ 'xrightarrow', 'xleftarrow', 'xRightarrow', 'xLeftarrow', 'xleftharpoonup', 'xleftharpoondown', 'xrightharpoonup', 'xrightharpoondown', 'xlongequal', 'xtwoheadleftarrow', 'xtwoheadrightarrow', 'xleftrightarrow', 'xLeftrightarrow', 'xrightleftharpoons', 'xleftrightharpoons', 'xhookleftarrow', 'xhookrightarrow', 'xmapsto', 'xtofrom', 'xrightleftarrows', 'xrightequilibrium', 'xleftequilibrium', // From mhchem.sty package ], '[:auto]{:auto}', { createAtom: (command, args, style) => { var _a, _b; return new OverunderAtom(command, { style, // Set the "svgBody" to the name of a SVG object (which is the same // as the command name) svgBody: command.slice(1), // The overscript is optional, i.e. `\xtofrom` is valid above: ((_a = args[1]) === null || _a === void 0 ? void 0 : _a.length) === 0 ? undefined : args[1], below: (_b = args[0]) !== null && _b !== void 0 ? _b : null, skipBoundary: false, supsubPlacement: 'over-under', padded: true, boxType: 'mrel', serialize: (atom, options) => command + (!atom.hasEmptyBranch('below') ? `[${atom.belowToLatex(options)}]` : '') + `{${atom.aboveToLatex(options)}}`, }); }, }); class SurdAtom extends Atom { constructor(command, options) { var _a; super('surd', { command, mode: (_a = options.mode) !== null && _a !== void 0 ? _a : 'math', style: options.style, displayContainsHighlight: true, }); this.body = options.body; this.above = options.index; } static fromJson(json) { return new SurdAtom(json.command, { ...json, index: json.above }); } toJson() { return super.toJson(); } serialize(options) { let args = ''; if (this.above) args += `[${this.aboveToLatex(options)}]`; args += `{${this.bodyToLatex(options)}}`; return this.command + args; } render(parentContext) { // See the TeXbook pg. 443, Rule 11. // http://www.ctex.org/documents/shredder/src/texbook.pdf var _a; // // 1. Render the inner box // // > 11. If the current item is a Rad atom (from \radical, e.g., \sqrt), // > set box x to the nucleus in style C′ // TeXBook p.443 const innerContext = new Context(parentContext, this.style, 'cramp'); const innerBox = (_a = Atom.createBox(innerContext, this.body, { style: this.style, newList: true, })) !== null && _a !== void 0 ? _a : new Box(null); // // 2. Render the radical line // const factor = innerContext.scalingFactor; const ruleWidth = innerContext.metrics.defaultRuleThickness / factor; // > let φ=σ5 if C>T (TeXBook p. 443) const phi = parentContext.isDisplayStyle ? X_HEIGHT : ruleWidth; const line = new Box(null, { classes: 'ML__sqrt-line', style: this.style, height: ruleWidth, }); // // 3. Create a radical delimiter of the required minimum size // // Calculate the clearance between the body and line // > Set ψ = θ + 1/4 |φ| let lineClearance = factor * (ruleWidth + phi / 4); const innerTotalHeight = Math.max(factor * 2 * phi, innerBox.height + innerBox.depth); const minDelimiterHeight = innerTotalHeight + lineClearance + ruleWidth; const delimContext = new Context(parentContext, this.style); const delimBox = this.bind(delimContext, new Box(makeCustomSizedDelim('', '\\surd', minDelimiterHeight, false, delimContext), { classes: 'ML__sqrt-sign', style: this.style })); if (!delimBox) return null; const delimDepth = delimBox.height + delimBox.depth - ruleWidth; // Adjust the clearance based on the delimiter size if (delimDepth > innerBox.height + innerBox.depth + lineClearance) { lineClearance = (lineClearance + delimDepth - (innerBox.height + innerBox.depth)) / 2; } // Shift the delimiter so that its top lines up with the top of the line delimBox.setTop(delimBox.height - innerBox.height - lineClearance); // // 4. Render the body (inner + line) // const bodyBox = this.bind(parentContext, new VBox({ firstBaseline: [ { box: new Box(innerBox) }, lineClearance - 2 * ruleWidth, { box: line }, ruleWidth, ], }).wrap(parentContext)); // // 5. Assemble the body and the delimiter // // // 5.1. Handle the optional root index // // The index is always in scriptscript style // TeXBook p. 360: // > \def\root#1\of{\setbox\rootbox= // > \hbox{$\m@th \scriptscriptstyle{#1}$}\mathpalette\r@@t} const indexBox = Atom.createBox(new Context(parentContext, this.style, 'scriptscriptstyle'), this.above, { style: this.style, newList: true, }); if (!indexBox) { // // 5.2. There's no root index (sqrt) // const result = new Box([delimBox, bodyBox], { classes: this.containsCaret ? 'ML__contains-caret' : '', type: 'mord', }); if (this.caret) result.caret = this.caret; return this.bind(parentContext, result.wrap(parentContext)); } // Build a stack with the index shifted up correctly. // The amount the index is shifted by is taken from the TeX // source, in the definition of `\r@@t`. const indexStack = new VBox({ shift: -0.6 * (Math.max(delimBox.height, bodyBox.height) - Math.max(delimBox.depth, bodyBox.depth)), children: [{ box: indexBox }], }); // Add a class surrounding it so we can add on the appropriate // kerning const result = new Box([new Box(indexStack, { classes: 'ML__sqrt-index' }), delimBox, bodyBox], { type: 'mord', classes: this.containsCaret ? 'ML__contains-caret' : '' }); result.height = delimBox.height; result.depth = delimBox.depth; if (this.caret) result.caret = this.caret; return this.bind(parentContext, result.wrap(parentContext)); } } defineFunction([ 'arccos', 'arcsin', 'arctan', 'arctg', 'arcctg', 'arg', 'ch', 'cos', 'cosh', 'cot', 'coth', 'ctg', 'cth', 'cotg', 'csc', 'cosec', 'deg', 'dim', 'exp', 'hom', 'inf', 'ker', 'lg', 'lb', 'lg', // Sometimes used as the log2 'ln', 'log', 'Pr', 'sec', 'sh', 'sin', 'sinh', 'sup', 'tan', 'tanh', 'tg', 'th', // Not LaTeX standard. \tanh ], '', { isFunction: true, createAtom: (command, _args, style) => new OperatorAtom(command, command.slice(1), { limits: 'adjacent', isFunction: true, variant: 'main', variantStyle: 'up', style, }), }); defineFunction(['liminf', 'limsup'], '', { createAtom: (command, _args, style) => new OperatorAtom(command, { '\\liminf': 'lim inf', '\\limsup': 'lim sup' }[command], { limits: 'over-under', variant: 'main', style, }), }); defineFunction(['lim', 'mod'], '', { createAtom: (command, _args, style) => new OperatorAtom(command, command.slice(1), { limits: 'over-under', variant: 'main', style, }), }); // With Limits defineFunction(['det', 'max', 'min'], '', { isFunction: true, createAtom: (command, _args, style) => new OperatorAtom(command, command.slice(1), { limits: 'over-under', isFunction: true, variant: 'main', style, }), }); // Root defineFunction('sqrt', '[index:auto]{radicand:auto}', { createAtom: (command, args, style) => new SurdAtom(command, { body: args[1], index: args[0], style, }), }); // Fractions defineFunction(['frac', 'dfrac', 'tfrac', 'cfrac', 'binom', 'dbinom', 'tbinom'], '{numerator}{denominator}', { createAtom: (command, args, style) => { const options = { style, }; switch (command) { case '\\dfrac': case '\\frac': case '\\tfrac': options.hasBarLine = true; break; case '\\atopfrac': options.hasBarLine = false; break; case '\\dbinom': case '\\binom': case '\\tbinom': options.hasBarLine = false; options.leftDelim = '('; options.rightDelim = ')'; break; } switch (command) { case '\\dfrac': case '\\dbinom': options.mathstyleName = 'displaystyle'; break; case '\\tfrac': case '\\tbinom': options.mathstyleName = 'textstyle'; break; case '\\cfrac': options.hasBarLine = true; options.continuousFraction = true; break; } return new GenfracAtom(command, args[0], args[1], options); }, }); defineFunction(['over', 'atop', 'choose'], '', { infix: true, createAtom: (command, args, style) => { let leftDelim = undefined; let rightDelim = undefined; if (command === '\\choose') { leftDelim = '('; rightDelim = ')'; } return new GenfracAtom(command, args[0], args[1], { hasBarLine: command === '\\over', leftDelim, rightDelim, style, serialize: (atom, options) => `{${atom.aboveToLatex(options)}${atom.command} ${atom.belowToLatex(options)}}`, }); }, }); // Slashed package /* defineFunction('\\slashed' */ defineFunction('pdiff', '{numerator}{denominator}', { createAtom: (command, args, style) => new GenfracAtom(command, args[0], args[1], { hasBarLine: true, numerPrefix: '\u2202', denomPrefix: '\u2202', style, }), }); // Limits, symbols defineFunction([ 'sum', 'prod', 'bigcup', 'bigcap', 'coprod', 'bigvee', 'bigwedge', 'biguplus', 'bigotimes', 'bigoplus', 'bigodot', 'bigsqcup', 'smallint', 'intop', ], '', { createAtom: (command, args, style) => new OperatorAtom(command, { coprod: '\u2210', bigvee: '\u22C1', bigwedge: '\u22C0', biguplus: '\u2A04', bigcap: '\u22C2', bigcup: '\u22C3', intop: '\u222B', prod: '\u220F', sum: '\u2211', bigotimes: '\u2A02', bigoplus: '\u2A01', bigodot: '\u2A00', bigsqcup: '\u2A06', smallint: '\u222B', }[command.slice(1)], { isExtensibleSymbol: true, limits: 'auto', variant: 'main', style, }), }); // No limits, symbols (i.e. display larger in 'display' mode, and // centered on the baseline) const EXTENSIBLE_SYMBOLS = { int: '\u222B', iint: '\u222C', iiint: '\u222D', oint: '\u222E', oiint: '\u222F', oiiint: '\u2230', intclockwise: '\u2231', varointclockwise: '\u2232', ointctrclockwise: '\u2233', intctrclockwise: '\u2A11', sqcup: '\u2294', sqcap: '\u2293', uplus: '\u228E', wr: '\u2240', amalg: '\u2A3F', Cap: '\u22D2', Cup: '\u22D3', doublecap: '\u22D2', doublecup: '\u22D3', }; defineFunction(Object.keys(EXTENSIBLE_SYMBOLS), '', { createAtom: (command, _args, style) => new OperatorAtom(command, EXTENSIBLE_SYMBOLS[command.slice(1)], { limits: 'adjacent', isExtensibleSymbol: true, style, variant: { '\u22D2': 'ams', '\u22D3': 'ams' }[EXTENSIBLE_SYMBOLS[command.slice(1)]], }), }); defineFunction(['Re', 'Im'], '', { createAtom: (command, _args, style) => new OperatorAtom(command, { '\\Re': '\u211C', '\\Im': '\u2111' }[command], { limits: 'adjacent', style, isFunction: true, variant: 'fraktur', }), }); defineFunction('middle', '{:delim}', { createAtom: (command, args, style) => new DelimAtom(command, args[0], { size: 1, style }), }); // TODO // Some missing greek letters, but see https://reference.wolfram.com/language/tutorial/LettersAndLetterLikeForms.html // koppa, stigma, Sampi // See https://tex.stackexchange.com/questions/231878/accessing-archaic-greek-koppa-in-the-birkmult-document-class // Capital Alpha, etc... // Colon (ratio) (2236) // Review: // https://en.wikipedia.org/wiki/Help:Displaying_a_formula // https://reference.wolfram.com/language/tutorial/LettersAndLetterLikeForms.html // ftp://ftp.dante.de/tex-archive/info/symbols/comprehensive/symbols-a4.pdf // Media Wiki Reference // https://en.wikipedia.org/wiki/Help:Displaying_a_formula // MathJax Reference // http://docs.mathjax.org/en/latest/tex.html#supported-latex-commands // http://www.onemathematicalcat.org/MathJaxDocumentation/TeXSyntax.htm // LaTeX Reference // http://ctan.sharelatex.com/tex-archive/info/latex2e-help-texinfo/latex2e.html // iBooks Author/Pages // https://support.apple.com/en-au/HT202501 // Mathematica Reference // https://reference.wolfram.com/language/tutorial/NamesOfSymbolsAndMathematicalObjects.html // https://reference.wolfram.com/language/guide/MathematicalTypesetting.html /* * @todo \sb (equivalent to _) $\mathfrak{sl}\sb 2$ frequency 184 * @todo \sp (equivalent to ^) $\mathfrak{sl}\sp 2$ frequency 274 * \intertext frequency 0 See http://mirrors.ibiblio.org/CTAN/macros/latex/contrib/mathtools/mathtools.pdf */ /* eslint-disable */ class ChemAtom extends Atom { constructor(command, arg) { super('chem', { command, mode: 'math' }); const tex = texify.go(mhchemParser.go(arg, command === '\\pu' ? 'pu' : 'ce'), false); this.body = parseLatex(tex); this.verbatimLatex = command + '{' + arg + '}'; this.arg = arg; this.captureSelection = true; } static fromJson(json) { return new ChemAtom(json.command, json.arg); } toJson() { return { ...super.toJson(), arg: this.arg }; } render(context) { const box = Atom.createBox(context, this.body, { type: 'chem', newList: true, }); if (this.caret) box.caret = this.caret; // Need to bind the group so that the DOM element can be matched // and the atom iterated recursively. Otherwise, it behaves // as if `captureSelection === true` return this.bind(context, box); } serialize(_options) { return this.verbatimLatex; } } defineFunction(['ce', 'pu'], '{chemformula:balanced-string}', { createAtom: (command, args, _style) => new ChemAtom(command, args[0]), }); /************************************************************* * * MathJax/extensions/TeX/mhchem.js * * Implements the \ce command for handling chemical formulas * from the mhchem LaTeX package. * * --------------------------------------------------------------------- * * Copyright (c) 2011-2015 The MathJax Consortium * Copyright (c) 2015-2018 Martin Hensel * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // // Core parser for mhchem syntax (recursive) // /** @type {MhchemParser} */ var mhchemParser = { // // Parses mchem \ce syntax // // Call like // go("H2O"); // go: function (input, stateMachine) { if (!input) { return []; } if (stateMachine === undefined) { stateMachine = 'ce'; } var state = '0'; // // String buffers for parsing: // // buffer.a == amount // buffer.o == element // buffer.b == left-side superscript // buffer.p == left-side subscript // buffer.q == right-side subscript // buffer.d == right-side superscript // // buffer.r == arrow // buffer.rdt == arrow, script above, type // buffer.rd == arrow, script above, content // buffer.rqt == arrow, script below, type // buffer.rq == arrow, script below, content // // buffer.text_ // buffer.rm // etc. // // buffer.parenthesisLevel == int, starting at 0 // buffer.sb == bool, space before // buffer.beginsWithBond == bool // // These letters are also used as state names. // // Other states: // 0 == begin of main part (arrow/operator unlikely) // 1 == next entity // 2 == next entity (arrow/operator unlikely) // 3 == next atom // c == macro // /** @type {Buffer} */ var buffer = {}; buffer['parenthesisLevel'] = 0; input = input.replace(/\n/g, ' '); input = input.replace(/[\u2212\u2013\u2014\u2010]/g, '-'); input = input.replace(/[\u2026]/g, '...'); // // Looks through mhchemParser.transitions, to execute a matching action // (recursive) // var lastInput; var watchdog = 10; /** @type {ParserOutput[]} */ var output = []; while (true) { if (lastInput !== input) { watchdog = 10; lastInput = input; } else { watchdog--; } // // Find actions in transition table // var machine = mhchemParser.stateMachines[stateMachine]; var t = machine.transitions[state] || machine.transitions['*']; iterateTransitions: for (var i = 0; i < t.length; i++) { var matches = mhchemParser.patterns.match_(t[i].pattern, input); if (matches) { // // Execute actions // var task = t[i].task; for (var iA = 0; iA < task.action_.length; iA++) { var o; // // Find and execute action // if (machine.actions[task.action_[iA].type_]) { o = machine.actions[task.action_[iA].type_](buffer, matches.match_, task.action_[iA].option); } else if (mhchemParser.actions[task.action_[iA].type_]) { o = mhchemParser.actions[task.action_[iA].type_](buffer, matches.match_, task.action_[iA].option); } else { throw [ 'MhchemBugA', 'mhchem bug A. Please report. (' + task.action_[iA].type_ + ')', ]; // Trying to use non-existing action } // // Add output // mhchemParser.concatArray(output, o); } // // Set next state, // Shorten input, // Continue with next character // (= apply only one transition per position) // state = task.nextState || state; if (input.length > 0) { if (!task.revisit) { input = matches.remainder; } if (!task.toContinue) { break iterateTransitions; } } else { return output; } } } // // Prevent infinite loop // if (watchdog <= 0) { throw ['MhchemBugU', 'mhchem bug U. Please report.']; // Unexpected character } } }, concatArray: function (a, b) { if (b) { if (Array.isArray(b)) { for (var iB = 0; iB < b.length; iB++) { a.push(b[iB]); } } else { a.push(b); } } }, patterns: { // // Matching patterns // either regexps or function that return null or {match_:"a", remainder:"bc"} // patterns: { // property names must not look like integers ("2") for correct property traversal order, later on 'empty': /^$/, 'else': /^./, 'else2': /^./, 'space': /^\s/, 'space A': /^\s(?=[A-Z\\$])/, 'space$': /^\s$/, 'a-z': /^[a-z]/, 'x': /^x/, 'x$': /^x$/, 'i$': /^i$/, 'letters': /^(?:[a-zA-Z\u03B1-\u03C9\u0391-\u03A9?@]|(?:\\(?:alpha|beta|gamma|delta|epsilon|zeta|eta|theta|iota|kappa|lambda|mu|nu|xi|omicron|pi|rho|sigma|tau|upsilon|phi|chi|psi|omega|Gamma|Delta|Theta|Lambda|Xi|Pi|Sigma|Upsilon|Phi|Psi|Omega)(?:\s+|\{\}|(?![a-zA-Z]))))+/, '\\greek': /^\\(?:alpha|beta|gamma|delta|epsilon|zeta|eta|theta|iota|kappa|lambda|mu|nu|xi|omicron|pi|rho|sigma|tau|upsilon|phi|chi|psi|omega|Gamma|Delta|Theta|Lambda|Xi|Pi|Sigma|Upsilon|Phi|Psi|Omega)(?:\s+|\{\}|(?![a-zA-Z]))/, 'one lowercase latin letter $': /^(?:([a-z])(?:$|[^a-zA-Z]))$/, '$one lowercase latin letter$ $': /^\$(?:([a-z])(?:$|[^a-zA-Z]))\$$/, 'one lowercase greek letter $': /^(?:\$?[\u03B1-\u03C9]\$?|\$?\\(?:alpha|beta|gamma|delta|epsilon|zeta|eta|theta|iota|kappa|lambda|mu|nu|xi|omicron|pi|rho|sigma|tau|upsilon|phi|chi|psi|omega)\s*\$?)(?:\s+|\{\}|(?![a-zA-Z]))$/, 'digits': /^[0-9]+/, '-9.,9': /^[+\-]?(?:[0-9]+(?:[,.][0-9]+)?|[0-9]*(?:\.[0-9]+))/, '-9.,9 no missing 0': /^[+\-]?[0-9]+(?:[.,][0-9]+)?/, '(-)(9.,9)(e)(99)': function (input) { var m = input.match(/^(\+\-|\+\/\-|\+|\-|\\pm\s?)?([0-9]+(?:[,.][0-9]+)?|[0-9]*(?:\.[0-9]+))?(\((?:[0-9]+(?:[,.][0-9]+)?|[0-9]*(?:\.[0-9]+))\))?(?:([eE]|\s*(\*|x|\\times|\u00D7)\s*10\^)([+\-]?[0-9]+|\{[+\-]?[0-9]+\}))?/); if (m && m[0]) { return { match_: m.splice(1), remainder: input.substr(m[0].length), }; } return null; }, '(-)(9)^(-9)': function (input) { var m = input.match(/^(\+\-|\+\/\-|\+|\-|\\pm\s?)?([0-9]+(?:[,.][0-9]+)?|[0-9]*(?:\.[0-9]+)?)\^([+\-]?[0-9]+|\{[+\-]?[0-9]+\})/); if (m && m[0]) { return { match_: m.splice(1), remainder: input.substr(m[0].length), }; } return null; }, 'state of aggregation $': function (input) { // ... or crystal system var a = mhchemParser.patterns.findObserveGroups(input, '', /^\([a-z]{1,3}(?=[\),])/, ')', ''); // (aq), (aq,$\infty$), (aq, sat) if (a && a.remainder.match(/^($|[\s,;\)\]\}])/)) { return a; } // AND end of 'phrase' var m = input.match(/^(?:\((?:\\ca\s?)?\$[amothc]\$\))/); // OR crystal system ($o$) (\ca$c$) if (m) { return { match_: m[0], remainder: input.substr(m[0].length), }; } return null; }, '_{(state of aggregation)}$': /^_\{(\([a-z]{1,3}\))\}/, '{[(': /^(?:\\\{|\[|\()/, ')]}': /^(?:\)|\]|\\\})/, ', ': /^[,;]\s*/, ',': /^[,;]/, '.': /^[.]/, '. ': /^([.\u22C5\u00B7\u2022])\s*/, '...': /^\.\.\.(?=$|[^.])/, '* ': /^([*])\s*/, '^{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, '^{', '', '', '}'); }, '^($...$)': function (input) { return mhchemParser.patterns.findObserveGroups(input, '^', '$', '$', ''); }, '^a': /^\^([0-9]+|[^\\_])/, '^\\x{}{}': function (input) { return mhchemParser.patterns.findObserveGroups(input, '^', /^\\[a-zA-Z]+\{/, '}', '', '', '{', '}', '', true); }, '^\\x{}': function (input) { return mhchemParser.patterns.findObserveGroups(input, '^', /^\\[a-zA-Z]+\{/, '}', ''); }, '^\\x': /^\^(\\[a-zA-Z]+)\s*/, '^(-1)': /^\^(-?\d+)/, "'": /^'/, '_{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, '_{', '', '', '}'); }, '_($...$)': function (input) { return mhchemParser.patterns.findObserveGroups(input, '_', '$', '$', ''); }, '_9': /^_([+\-]?[0-9]+|[^\\])/, '_\\x{}{}': function (input) { return mhchemParser.patterns.findObserveGroups(input, '_', /^\\[a-zA-Z]+\{/, '}', '', '', '{', '}', '', true); }, '_\\x{}': function (input) { return mhchemParser.patterns.findObserveGroups(input, '_', /^\\[a-zA-Z]+\{/, '}', ''); }, '_\\x': /^_(\\[a-zA-Z]+)\s*/, '^_': /^(?:\^(?=_)|\_(?=\^)|[\^_]$)/, '{}': /^\{\}/, '{...}': function (input) { return mhchemParser.patterns.findObserveGroups(input, '', '{', '}', ''); }, '{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, '{', '', '', '}'); }, '$...$': function (input) { return mhchemParser.patterns.findObserveGroups(input, '', '$', '$', ''); }, '${(...)}$': function (input) { return mhchemParser.patterns.findObserveGroups(input, '${', '', '', '}$'); }, '$(...)$': function (input) { return mhchemParser.patterns.findObserveGroups(input, '$', '', '', '$'); }, '=<>': /^[=<>]/, '#': /^[#\u2261]/, '+': /^\+/, '-$': /^-(?=[\s_},;\]/]|$|\([a-z]+\))/, '-9': /^-(?=[0-9])/, '- orbital overlap': /^-(?=(?:[spd]|sp)(?:$|[\s,;\)\]\}]))/, '-': /^-/, 'pm-operator': /^(?:\\pm|\$\\pm\$|\+-|\+\/-)/, 'operator': /^(?:\+|(?:[\-=<>]|<<|>>|\\approx|\$\\approx\$)(?=\s|$|-?[0-9]))/, 'arrowUpDown': /^(?:v|\(v\)|\^|\(\^\))(?=$|[\s,;\)\]\}])/, '\\bond{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, '\\bond{', '', '', '}'); }, '->': /^(?:<->|<-->|->|<-|<=>>|<<=>|<=>|[\u2192\u27F6\u21CC])/, 'CMT': /^[CMT](?=\[)/, '[(...)]': function (input) { return mhchemParser.patterns.findObserveGroups(input, '[', '', '', ']'); }, '1st-level escape': /^(&|\\\\|\\hline)\s*/, '\\,': /^(?:\\[,\ ;:])/, '\\x{}{}': function (input) { return mhchemParser.patterns.findObserveGroups(input, '', /^\\[a-zA-Z]+\{/, '}', '', '', '{', '}', '', true); }, '\\x{}': function (input) { return mhchemParser.patterns.findObserveGroups(input, '', /^\\[a-zA-Z]+\{/, '}', ''); }, '\\ca': /^\\ca(?:\s+|(?![a-zA-Z]))/, '\\x': /^(?:\\[a-zA-Z]+\s*|\\[_&{}%])/, 'orbital': /^(?:[0-9]{1,2}[spdfgh]|[0-9]{0,2}sp)(?=$|[^a-zA-Z])/, 'others': /^[\/~|]/, '\\frac{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, '\\frac{', '', '', '}', '{', '', '', '}'); }, '\\overset{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, '\\overset{', '', '', '}', '{', '', '', '}'); }, '\\underset{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, '\\underset{', '', '', '}', '{', '', '', '}'); }, '\\underbrace{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, '\\underbrace{', '', '', '}_', '{', '', '', '}'); }, '\\color{(...)}0': function (input) { return mhchemParser.patterns.findObserveGroups(input, '\\color{', '', '', '}'); }, '\\color{(...)}{(...)}1': function (input) { return mhchemParser.patterns.findObserveGroups(input, '\\color{', '', '', '}', '{', '', '', '}'); }, '\\color(...){(...)}2': function (input) { return mhchemParser.patterns.findObserveGroups(input, '\\color', '\\', '', /^(?=\{)/, '{', '', '', '}'); }, '\\ce{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, '\\ce{', '', '', '}'); }, 'oxidation$': /^(?:[+-][IVX]+|\\pm\s*0|\$\\pm\$\s*0)$/, 'd-oxidation$': /^(?:[+-]?\s?[IVX]+|\\pm\s*0|\$\\pm\$\s*0)$/, 'roman numeral': /^[IVX]+/, '1/2$': /^[+\-]?(?:[0-9]+|\$[a-z]\$|[a-z])\/[0-9]+(?:\$[a-z]\$|[a-z])?$/, 'amount': function (input) { var match; // e.g. 2, 0.5, 1/2, -2, n/2, +; $a$ could be added later in parsing match = input.match(/^(?:(?:(?:\([+\-]?[0-9]+\/[0-9]+\)|[+\-]?(?:[0-9]+|\$[a-z]\$|[a-z])\/[0-9]+|[+\-]?[0-9]+[.,][0-9]+|[+\-]?\.[0-9]+|[+\-]?[0-9]+)(?:[a-z](?=\s*[A-Z]))?)|[+\-]?[a-z](?=\s*[A-Z])|\+(?!\s))/); if (match) { return { match_: match[0], remainder: input.substr(match[0].length), }; } var a = mhchemParser.patterns.findObserveGroups(input, '', '$', '$', ''); if (a) { // e.g. $2n-1$, $-$ match = a.match_.match(/^\$(?:\(?[+\-]?(?:[0-9]*[a-z]?[+\-])?[0-9]*[a-z](?:[+\-][0-9]*[a-z]?)?\)?|\+|-)\$$/); if (match) { return { match_: match[0], remainder: input.substr(match[0].length), }; } } return null; }, 'amount2': function (input) { return this['amount'](input); }, '(KV letters),': /^(?:[A-Z][a-z]{0,2}|i)(?=,)/, 'formula$': function (input) { if (input.match(/^\([a-z]+\)$/)) { return null; } // state of aggregation = no formula var match = input.match(/^(?:[a-z]|(?:[0-9\ \+\-\,\.\(\)]+[a-z])+[0-9\ \+\-\,\.\(\)]*|(?:[a-z][0-9\ \+\-\,\.\(\)]+)+[a-z]?)$/); if (match) { return { match_: match[0], remainder: input.substr(match[0].length), }; } return null; }, 'uprightEntities': /^(?:pH|pOH|pC|pK|iPr|iBu)(?=$|[^a-zA-Z])/, '/': /^\s*(\/)\s*/, '//': /^\s*(\/\/)\s*/, '*': /^\s*[*.]\s*/, }, findObserveGroups: function (input, begExcl, begIncl, endIncl, endExcl, beg2Excl, beg2Incl, end2Incl, end2Excl, combine) { /** @type {{(input: string, pattern: string | RegExp): string | string[] | null;}} */ var _match = function (input, pattern) { if (typeof pattern === 'string') { if (input.indexOf(pattern) !== 0) { return null; } return pattern; } else { var match = input.match(pattern); if (!match) { return null; } return match[0]; } }; /** @type {{(input: string, i: number, endChars: string | RegExp): {endMatchBegin: number, endMatchEnd: number} | null;}} */ var _findObserveGroups = function (input, i, endChars) { var braces = 0; while (i < input.length) { var a = input.charAt(i); var match = _match(input.substr(i), endChars); if (match !== null && braces === 0) { return { endMatchBegin: i, endMatchEnd: i + match.length, }; } else if (a === '{') { braces++; } else if (a === '}') { if (braces === 0) { throw [ 'ExtraCloseMissingOpen', 'Extra close brace or missing open brace', ]; } else { braces--; } } i++; } if (braces > 0) { return null; } return null; }; var match = _match(input, begExcl); if (match === null) { return null; } input = input.substr(match.length); match = _match(input, begIncl); if (match === null) { return null; } var e = _findObserveGroups(input, match.length, endIncl || endExcl); if (e === null) { return null; } var match1 = input.substring(0, endIncl ? e.endMatchEnd : e.endMatchBegin); if (!(beg2Excl || beg2Incl)) { return { match_: match1, remainder: input.substr(e.endMatchEnd), }; } else { var group2 = this.findObserveGroups(input.substr(e.endMatchEnd), beg2Excl, beg2Incl, end2Incl, end2Excl); if (group2 === null) { return null; } /** @type {string[]} */ var matchRet = [match1, group2.match_]; return { match_: combine ? matchRet.join('') : matchRet, remainder: group2.remainder, }; } }, // // Matching function // e.g. match("a", input) will look for the regexp called "a" and see if it matches // returns null or {match_:"a", remainder:"bc"} // match_: function (m, input) { var pattern = mhchemParser.patterns.patterns[m]; if (pattern === undefined) { throw ['MhchemBugP', 'mhchem bug P. Please report. (' + m + ')']; // Trying to use non-existing pattern } else if (typeof pattern === 'function') { return mhchemParser.patterns.patterns[m](input); // cannot use cached var pattern here, because some pattern functions need this===mhchemParser } else { // RegExp var match = input.match(pattern); if (match) { var mm; if (match[2]) { mm = [match[1], match[2]]; } else if (match[1]) { mm = match[1]; } else { mm = match[0]; } return { match_: mm, remainder: input.substr(match[0].length), }; } return null; } }, }, // // Generic state machine actions // actions: { 'a=': function (buffer, m) { buffer.a = (buffer.a || '') + m; }, 'b=': function (buffer, m) { buffer.b = (buffer.b || '') + m; }, 'p=': function (buffer, m) { buffer.p = (buffer.p || '') + m; }, 'o=': function (buffer, m) { buffer.o = (buffer.o || '') + m; }, 'q=': function (buffer, m) { buffer.q = (buffer.q || '') + m; }, 'd=': function (buffer, m) { buffer.d = (buffer.d || '') + m; }, 'rm=': function (buffer, m) { buffer.rm = (buffer.rm || '') + m; }, 'text=': function (buffer, m) { buffer.text_ = (buffer.text_ || '') + m; }, 'insert': function (buffer, m, a) { return { type_: a }; }, 'insert+p1': function (buffer, m, a) { return { type_: a, p1: m }; }, 'insert+p1+p2': function (buffer, m, a) { return { type_: a, p1: m[0], p2: m[1] }; }, 'copy': function (buffer, m) { return m; }, 'rm': function (buffer, m) { return { type_: 'rm', p1: m || '' }; }, 'text': function (buffer, m) { return mhchemParser.go(m, 'text'); }, '{text}': function (buffer, m) { var ret = ['{']; mhchemParser.concatArray(ret, mhchemParser.go(m, 'text')); ret.push('}'); return ret; }, 'tex-math': function (buffer, m) { return mhchemParser.go(m, 'tex-math'); }, 'tex-math tight': function (buffer, m) { return mhchemParser.go(m, 'tex-math tight'); }, 'bond': function (buffer, m, k) { return { type_: 'bond', kind_: k || m }; }, 'color0-output': function (buffer, m) { return { type_: 'color0', color: m[0] }; }, 'ce': function (buffer, m) { return mhchemParser.go(m); }, '1/2': function (buffer, m) { /** @type {ParserOutput[]} */ var ret = []; if (m.match(/^[+\-]/)) { ret.push(m.substr(0, 1)); m = m.substr(1); } var n = m.match(/^([0-9]+|\$[a-z]\$|[a-z])\/([0-9]+)(\$[a-z]\$|[a-z])?$/); n[1] = n[1].replace(/\$/g, ''); ret.push({ type_: 'frac', p1: n[1], p2: n[2] }); if (n[3]) { n[3] = n[3].replace(/\$/g, ''); ret.push({ type_: 'tex-math', p1: n[3] }); } return ret; }, '9,9': function (buffer, m) { return mhchemParser.go(m, '9,9'); }, }, // // createTransitions // convert { 'letter': { 'state': { action_: 'output' } } } to { 'state' => [ { pattern: 'letter', task: { action_: [{type_: 'output'}] } } ] } // with expansion of 'a|b' to 'a' and 'b' (at 2 places) // createTransitions: function (o) { var pattern, state; /** @type {string[]} */ var stateArray; var i; // // 1. Collect all states // /** @type {Transitions} */ var transitions = {}; for (pattern in o) { for (state in o[pattern]) { stateArray = state.split('|'); o[pattern][state].stateArray = stateArray; for (i = 0; i < stateArray.length; i++) { transitions[stateArray[i]] = []; } } } // // 2. Fill states // for (pattern in o) { for (state in o[pattern]) { stateArray = o[pattern][state].stateArray || []; for (i = 0; i < stateArray.length; i++) { // // 2a. Normalize actions into array: 'text=' ==> [{type_:'text='}] // (Note to myself: Resolving the function here would be problematic. It would need .bind (for *this*) and currying (for *option*).) // /** @type {any} */ var p = o[pattern][state]; if (p.action_) { p.action_ = [].concat(p.action_); for (var k = 0; k < p.action_.length; k++) { if (typeof p.action_[k] === 'string') { p.action_[k] = { type_: p.action_[k] }; } } } else { p.action_ = []; } // // 2.b Multi-insert // var patternArray = pattern.split('|'); for (var j = 0; j < patternArray.length; j++) { if (stateArray[i] === '*') { // insert into all for (var t in transitions) { transitions[t].push({ pattern: patternArray[j], task: p, }); } } else { transitions[stateArray[i]].push({ pattern: patternArray[j], task: p, }); } } } } } return transitions; }, stateMachines: {}, }; // // Definition of state machines // mhchemParser.stateMachines = { // // \ce state machines // //#region ce 'ce': { // main parser transitions: mhchemParser.createTransitions({ 'empty': { '*': { action_: 'output' }, }, 'else': { '0|1|2': { action_: 'beginsWithBond=false', revisit: true, toContinue: true, }, }, 'oxidation$': { '0': { action_: 'oxidation-output' }, }, 'CMT': { r: { action_: 'rdt=', nextState: 'rt' }, rd: { action_: 'rqt=', nextState: 'rdt' }, }, 'arrowUpDown': { '0|1|2|as': { action_: ['sb=false', 'output', 'operator'], nextState: '1', }, }, 'uprightEntities': { '0|1|2': { action_: ['o=', 'output'], nextState: '1' }, }, 'orbital': { '0|1|2|3': { action_: 'o=', nextState: 'o' }, }, '->': { '0|1|2|3': { action_: 'r=', nextState: 'r' }, 'a|as': { action_: ['output', 'r='], nextState: 'r' }, '*': { action_: ['output', 'r='], nextState: 'r' }, }, '+': { 'o': { action_: 'd= kv', nextState: 'd' }, 'd|D': { action_: 'd=', nextState: 'd' }, 'q': { action_: 'd=', nextState: 'qd' }, 'qd|qD': { action_: 'd=', nextState: 'qd' }, 'dq': { action_: ['output', 'd='], nextState: 'd' }, '3': { action_: ['sb=false', 'output', 'operator'], nextState: '0', }, }, 'amount': { '0|2': { action_: 'a=', nextState: 'a' }, }, 'pm-operator': { '0|1|2|a|as': { action_: [ 'sb=false', 'output', { type_: 'operator', option: '\\pm' }, ], nextState: '0', }, }, 'operator': { '0|1|2|a|as': { action_: ['sb=false', 'output', 'operator'], nextState: '0', }, }, '-$': { 'o|q': { action_: ['charge or bond', 'output'], nextState: 'qd', }, 'd': { action_: 'd=', nextState: 'd' }, 'D': { action_: ['output', { type_: 'bond', option: '-' }], nextState: '3', }, 'q': { action_: 'd=', nextState: 'qd' }, 'qd': { action_: 'd=', nextState: 'qd' }, 'qD|dq': { action_: ['output', { type_: 'bond', option: '-' }], nextState: '3', }, }, '-9': { '3|o': { action_: ['output', { type_: 'insert', option: 'hyphen' }], nextState: '3', }, }, '- orbital overlap': { o: { action_: ['output', { type_: 'insert', option: 'hyphen' }], nextState: '2', }, d: { action_: ['output', { type_: 'insert', option: 'hyphen' }], nextState: '2', }, }, '-': { '0|1|2': { action_: [ { type_: 'output', option: 1 }, 'beginsWithBond=true', { type_: 'bond', option: '-' }, ], nextState: '3', }, '3': { action_: { type_: 'bond', option: '-' } }, 'a': { action_: ['output', { type_: 'insert', option: 'hyphen' }], nextState: '2', }, 'as': { action_: [ { type_: 'output', option: 2 }, { type_: 'bond', option: '-' }, ], nextState: '3', }, 'b': { action_: 'b=' }, 'o': { action_: { type_: '- after o/d', option: false }, nextState: '2', }, 'q': { action_: { type_: '- after o/d', option: false }, nextState: '2', }, 'd|qd|dq': { action_: { type_: '- after o/d', option: true }, nextState: '2', }, 'D|qD|p': { action_: ['output', { type_: 'bond', option: '-' }], nextState: '3', }, }, 'amount2': { '1|3': { action_: 'a=', nextState: 'a' }, }, 'letters': { '0|1|2|3|a|as|b|p|bp|o': { action_: 'o=', nextState: 'o' }, 'q|dq': { action_: ['output', 'o='], nextState: 'o' }, 'd|D|qd|qD': { action_: 'o after d', nextState: 'o' }, }, 'digits': { 'o': { action_: 'q=', nextState: 'q' }, 'd|D': { action_: 'q=', nextState: 'dq' }, 'q': { action_: ['output', 'o='], nextState: 'o' }, 'a': { action_: 'o=', nextState: 'o' }, }, 'space A': { 'b|p|bp': {}, }, 'space': { 'a': { nextState: 'as' }, '0': { action_: 'sb=false' }, '1|2': { action_: 'sb=true' }, 'r|rt|rd|rdt|rdq': { action_: 'output', nextState: '0' }, '*': { action_: ['output', 'sb=true'], nextState: '1' }, }, '1st-level escape': { '1|2': { action_: [ 'output', { type_: 'insert+p1', option: '1st-level escape' }, ], }, '*': { action_: [ 'output', { type_: 'insert+p1', option: '1st-level escape' }, ], nextState: '0', }, }, '[(...)]': { 'r|rt': { action_: 'rd=', nextState: 'rd' }, 'rd|rdt': { action_: 'rq=', nextState: 'rdq' }, }, '...': { 'o|d|D|dq|qd|qD': { action_: ['output', { type_: 'bond', option: '...' }], nextState: '3', }, '*': { action_: [ { type_: 'output', option: 1 }, { type_: 'insert', option: 'ellipsis' }, ], nextState: '1', }, }, '. |* ': { '*': { action_: ['output', { type_: 'insert', option: 'addition compound' }], nextState: '1', }, }, 'state of aggregation $': { '*': { action_: ['output', 'state of aggregation'], nextState: '1', }, }, '{[(': { 'a|as|o': { action_: ['o=', 'output', 'parenthesisLevel++'], nextState: '2', }, '0|1|2|3': { action_: ['o=', 'output', 'parenthesisLevel++'], nextState: '2', }, '*': { action_: ['output', 'o=', 'output', 'parenthesisLevel++'], nextState: '2', }, }, ')]}': { '0|1|2|3|b|p|bp|o': { action_: ['o=', 'parenthesisLevel--'], nextState: 'o', }, 'a|as|d|D|q|qd|qD|dq': { action_: ['output', 'o=', 'parenthesisLevel--'], nextState: 'o', }, }, ', ': { '*': { action_: ['output', 'comma'], nextState: '0' }, }, '^_': { // ^ and _ without a sensible argument '*': {}, }, '^{(...)}|^($...$)': { '0|1|2|as': { action_: 'b=', nextState: 'b' }, 'p': { action_: 'b=', nextState: 'bp' }, '3|o': { action_: 'd= kv', nextState: 'D' }, 'q': { action_: 'd=', nextState: 'qD' }, 'd|D|qd|qD|dq': { action_: ['output', 'd='], nextState: 'D' }, }, "^a|^\\x{}{}|^\\x{}|^\\x|'": { '0|1|2|as': { action_: 'b=', nextState: 'b' }, 'p': { action_: 'b=', nextState: 'bp' }, '3|o': { action_: 'd= kv', nextState: 'd' }, 'q': { action_: 'd=', nextState: 'qd' }, 'd|qd|D|qD': { action_: 'd=' }, 'dq': { action_: ['output', 'd='], nextState: 'd' }, }, '_{(state of aggregation)}$': { 'd|D|q|qd|qD|dq': { action_: ['output', 'q='], nextState: 'q' }, }, '_{(...)}|_($...$)|_9|_\\x{}{}|_\\x{}|_\\x': { '0|1|2|as': { action_: 'p=', nextState: 'p' }, 'b': { action_: 'p=', nextState: 'bp' }, '3|o': { action_: 'q=', nextState: 'q' }, 'd|D': { action_: 'q=', nextState: 'dq' }, 'q|qd|qD|dq': { action_: ['output', 'q='], nextState: 'q' }, }, '=<>': { '0|1|2|3|a|as|o|q|d|D|qd|qD|dq': { action_: [{ type_: 'output', option: 2 }, 'bond'], nextState: '3', }, }, '#': { '0|1|2|3|a|as|o': { action_: [ { type_: 'output', option: 2 }, { type_: 'bond', option: '#' }, ], nextState: '3', }, }, '{}': { '*': { action_: { type_: 'output', option: 1 }, nextState: '1', }, }, '{...}': { '0|1|2|3|a|as|b|p|bp': { action_: 'o=', nextState: 'o' }, 'o|d|D|q|qd|qD|dq': { action_: ['output', 'o='], nextState: 'o', }, }, '$...$': { 'a': { action_: 'a=' }, '0|1|2|3|as|b|p|bp|o': { action_: 'o=', nextState: 'o' }, 'as|o': { action_: 'o=' }, 'q|d|D|qd|qD|dq': { action_: ['output', 'o='], nextState: 'o' }, }, '\\bond{(...)}': { '*': { action_: [{ type_: 'output', option: 2 }, 'bond'], nextState: '3', }, }, '\\frac{(...)}': { '*': { action_: [{ type_: 'output', option: 1 }, 'frac-output'], nextState: '3', }, }, '\\overset{(...)}': { '*': { action_: [{ type_: 'output', option: 2 }, 'overset-output'], nextState: '3', }, }, '\\underset{(...)}': { '*': { action_: [{ type_: 'output', option: 2 }, 'underset-output'], nextState: '3', }, }, '\\underbrace{(...)}': { '*': { action_: [{ type_: 'output', option: 2 }, 'underbrace-output'], nextState: '3', }, }, '\\color{(...)}{(...)}1|\\color(...){(...)}2': { '*': { action_: [{ type_: 'output', option: 2 }, 'color-output'], nextState: '3', }, }, '\\color{(...)}0': { '*': { action_: [{ type_: 'output', option: 2 }, 'color0-output'], }, }, '\\ce{(...)}': { '*': { action_: [{ type_: 'output', option: 2 }, 'ce'], nextState: '3', }, }, '\\,': { '*': { action_: [{ type_: 'output', option: 1 }, 'copy'], nextState: '1', }, }, '\\x{}{}|\\x{}|\\x': { '0|1|2|3|a|as|b|p|bp|o|c0': { action_: ['o=', 'output'], nextState: '3', }, '*': { action_: ['output', 'o=', 'output'], nextState: '3' }, }, 'others': { '*': { action_: [{ type_: 'output', option: 1 }, 'copy'], nextState: '3', }, }, 'else2': { 'a': { action_: 'a to o', nextState: 'o', revisit: true }, 'as': { action_: ['output', 'sb=true'], nextState: '1', revisit: true, }, 'r|rt|rd|rdt|rdq': { action_: ['output'], nextState: '0', revisit: true, }, '*': { action_: ['output', 'copy'], nextState: '3' }, }, }), actions: { 'o after d': function (buffer, m) { var ret; if ((buffer.d || '').match(/^[0-9]+$/)) { var tmp = buffer.d; buffer.d = undefined; ret = this['output'](buffer); buffer.b = tmp; } else { ret = this['output'](buffer); } mhchemParser.actions['o='](buffer, m); return ret; }, 'd= kv': function (buffer, m) { buffer.d = m; buffer.dType = 'kv'; }, 'charge or bond': function (buffer, m) { if (buffer['beginsWithBond']) { /** @type {ParserOutput[]} */ var ret = []; mhchemParser.concatArray(ret, this['output'](buffer)); mhchemParser.concatArray(ret, mhchemParser.actions['bond'](buffer, m, '-')); return ret; } else { buffer.d = m; } }, '- after o/d': function (buffer, m, isAfterD) { var c1 = mhchemParser.patterns.match_('orbital', buffer.o || ''); var c2 = mhchemParser.patterns.match_('one lowercase greek letter $', buffer.o || ''); var c3 = mhchemParser.patterns.match_('one lowercase latin letter $', buffer.o || ''); var c4 = mhchemParser.patterns.match_('$one lowercase latin letter$ $', buffer.o || ''); var hyphenFollows = m === '-' && ((c1 && c1.remainder === '') || c2 || c3 || c4); if (hyphenFollows && !buffer.a && !buffer.b && !buffer.p && !buffer.d && !buffer.q && !c1 && c3) { buffer.o = '$' + buffer.o + '$'; } /** @type {ParserOutput[]} */ var ret = []; if (hyphenFollows) { mhchemParser.concatArray(ret, this['output'](buffer)); ret.push({ type_: 'hyphen' }); } else { c1 = mhchemParser.patterns.match_('digits', buffer.d || ''); if (isAfterD && c1 && c1.remainder === '') { mhchemParser.concatArray(ret, mhchemParser.actions['d='](buffer, m)); mhchemParser.concatArray(ret, this['output'](buffer)); } else { mhchemParser.concatArray(ret, this['output'](buffer)); mhchemParser.concatArray(ret, mhchemParser.actions['bond'](buffer, m, '-')); } } return ret; }, 'a to o': function (buffer) { buffer.o = buffer.a; buffer.a = undefined; }, 'sb=true': function (buffer) { buffer.sb = true; }, 'sb=false': function (buffer) { buffer.sb = false; }, 'beginsWithBond=true': function (buffer) { buffer['beginsWithBond'] = true; }, 'beginsWithBond=false': function (buffer) { buffer['beginsWithBond'] = false; }, 'parenthesisLevel++': function (buffer) { buffer['parenthesisLevel']++; }, 'parenthesisLevel--': function (buffer) { buffer['parenthesisLevel']--; }, 'state of aggregation': function (buffer, m) { return { type_: 'state of aggregation', p1: mhchemParser.go(m, 'o'), }; }, 'comma': function (buffer, m) { var a = m.replace(/\s*$/, ''); var withSpace = a !== m; if (withSpace && buffer['parenthesisLevel'] === 0) { return { type_: 'comma enumeration L', p1: a }; } else { return { type_: 'comma enumeration M', p1: a }; } }, 'output': function (buffer, m, entityFollows) { // entityFollows: // undefined = if we have nothing else to output, also ignore the just read space (buffer.sb) // 1 = an entity follows, never omit the space if there was one just read before (can only apply to state 1) // 2 = 1 + the entity can have an amount, so output a\, instead of converting it to o (can only apply to states a|as) /** @type {ParserOutput | ParserOutput[]} */ var ret; if (!buffer.r) { ret = []; if (!buffer.a && !buffer.b && !buffer.p && !buffer.o && !buffer.q && !buffer.d && !entityFollows) ; else { if (buffer.sb) { ret.push({ type_: 'entitySkip' }); } if (!buffer.o && !buffer.q && !buffer.d && !buffer.b && !buffer.p && entityFollows !== 2) { buffer.o = buffer.a; buffer.a = undefined; } else if (!buffer.o && !buffer.q && !buffer.d && (buffer.b || buffer.p)) { buffer.o = buffer.a; buffer.d = buffer.b; buffer.q = buffer.p; buffer.a = buffer.b = buffer.p = undefined; } else { if (buffer.o && buffer.dType === 'kv' && mhchemParser.patterns.match_('d-oxidation$', buffer.d || '')) { buffer.dType = 'oxidation'; } else if (buffer.o && buffer.dType === 'kv' && !buffer.q) { buffer.dType = undefined; } } ret.push({ type_: 'chemfive', a: mhchemParser.go(buffer.a, 'a'), b: mhchemParser.go(buffer.b, 'bd'), p: mhchemParser.go(buffer.p, 'pq'), o: mhchemParser.go(buffer.o, 'o'), q: mhchemParser.go(buffer.q, 'pq'), d: mhchemParser.go(buffer.d, buffer.dType === 'oxidation' ? 'oxidation' : 'bd'), dType: buffer.dType, }); } } else { // r /** @type {ParserOutput[]} */ var rd; if (buffer.rdt === 'M') { rd = mhchemParser.go(buffer.rd, 'tex-math'); } else if (buffer.rdt === 'T') { rd = [{ type_: 'text', p1: buffer.rd || '' }]; } else { rd = mhchemParser.go(buffer.rd); } /** @type {ParserOutput[]} */ var rq; if (buffer.rqt === 'M') { rq = mhchemParser.go(buffer.rq, 'tex-math'); } else if (buffer.rqt === 'T') { rq = [{ type_: 'text', p1: buffer.rq || '' }]; } else { rq = mhchemParser.go(buffer.rq); } ret = { type_: 'arrow', r: buffer.r, rd: rd, rq: rq, }; } for (var p in buffer) { if (p !== 'parenthesisLevel' && p !== 'beginsWithBond') { delete buffer[p]; } } return ret; }, 'oxidation-output': function (buffer, m) { var ret = ['{']; mhchemParser.concatArray(ret, mhchemParser.go(m, 'oxidation')); ret.push('}'); return ret; }, 'frac-output': function (buffer, m) { return { type_: 'frac-ce', p1: mhchemParser.go(m[0]), p2: mhchemParser.go(m[1]), }; }, 'overset-output': function (buffer, m) { return { type_: 'overset', p1: mhchemParser.go(m[0]), p2: mhchemParser.go(m[1]), }; }, 'underset-output': function (buffer, m) { return { type_: 'underset', p1: mhchemParser.go(m[0]), p2: mhchemParser.go(m[1]), }; }, 'underbrace-output': function (buffer, m) { return { type_: 'underbrace', p1: mhchemParser.go(m[0]), p2: mhchemParser.go(m[1]), }; }, 'color-output': function (buffer, m) { return { type_: 'color', color1: m[0], color2: mhchemParser.go(m[1]), }; }, 'r=': function (buffer, m) { buffer.r = m; }, 'rdt=': function (buffer, m) { buffer.rdt = m; }, 'rd=': function (buffer, m) { buffer.rd = m; }, 'rqt=': function (buffer, m) { buffer.rqt = m; }, 'rq=': function (buffer, m) { buffer.rq = m; }, 'operator': function (buffer, m, p1) { return { type_: 'operator', kind_: p1 || m }; }, }, }, 'a': { transitions: mhchemParser.createTransitions({ 'empty': { '*': {}, }, '1/2$': { '0': { action_: '1/2' }, }, 'else': { '0': { nextState: '1', revisit: true }, }, '$(...)$': { '*': { action_: 'tex-math tight', nextState: '1' }, }, ',': { '*': { action_: { type_: 'insert', option: 'commaDecimal' } }, }, 'else2': { '*': { action_: 'copy' }, }, }), actions: {}, }, 'o': { transitions: mhchemParser.createTransitions({ 'empty': { '*': {}, }, '1/2$': { '0': { action_: '1/2' }, }, 'else': { '0': { nextState: '1', revisit: true }, }, 'letters': { '*': { action_: 'rm' }, }, '\\ca': { '*': { action_: { type_: 'insert', option: 'circa' } }, }, '\\x{}{}|\\x{}|\\x': { '*': { action_: 'copy' }, }, '${(...)}$|$(...)$': { '*': { action_: 'tex-math' }, }, '{(...)}': { '*': { action_: '{text}' }, }, 'else2': { '*': { action_: 'copy' }, }, }), actions: {}, }, 'text': { transitions: mhchemParser.createTransitions({ 'empty': { '*': { action_: 'output' }, }, '{...}': { '*': { action_: 'text=' }, }, '${(...)}$|$(...)$': { '*': { action_: 'tex-math' }, }, '\\greek': { '*': { action_: ['output', 'rm'] }, }, '\\,|\\x{}{}|\\x{}|\\x': { '*': { action_: ['output', 'copy'] }, }, 'else': { '*': { action_: 'text=' }, }, }), actions: { output: function (buffer) { if (buffer.text_) { /** @type {ParserOutput} */ var ret = { type_: 'text', p1: buffer.text_ }; for (var p in buffer) { delete buffer[p]; } return ret; } }, }, }, 'pq': { transitions: mhchemParser.createTransitions({ 'empty': { '*': {}, }, 'state of aggregation $': { '*': { action_: 'state of aggregation' }, }, 'i$': { '0': { nextState: '!f', revisit: true }, }, '(KV letters),': { '0': { action_: 'rm', nextState: '0' }, }, 'formula$': { '0': { nextState: 'f', revisit: true }, }, '1/2$': { '0': { action_: '1/2' }, }, 'else': { '0': { nextState: '!f', revisit: true }, }, '${(...)}$|$(...)$': { '*': { action_: 'tex-math' }, }, '{(...)}': { '*': { action_: 'text' }, }, 'a-z': { f: { action_: 'tex-math' }, }, 'letters': { '*': { action_: 'rm' }, }, '-9.,9': { '*': { action_: '9,9' }, }, ',': { '*': { action_: { type_: 'insert+p1', option: 'comma enumeration S', }, }, }, '\\color{(...)}{(...)}1|\\color(...){(...)}2': { '*': { action_: 'color-output' }, }, '\\color{(...)}0': { '*': { action_: 'color0-output' }, }, '\\ce{(...)}': { '*': { action_: 'ce' }, }, '\\,|\\x{}{}|\\x{}|\\x': { '*': { action_: 'copy' }, }, 'else2': { '*': { action_: 'copy' }, }, }), actions: { 'state of aggregation': function (buffer, m) { return { type_: 'state of aggregation subscript', p1: mhchemParser.go(m, 'o'), }; }, 'color-output': function (buffer, m) { return { type_: 'color', color1: m[0], color2: mhchemParser.go(m[1], 'pq'), }; }, }, }, 'bd': { transitions: mhchemParser.createTransitions({ 'empty': { '*': {}, }, 'x$': { '0': { nextState: '!f', revisit: true }, }, 'formula$': { '0': { nextState: 'f', revisit: true }, }, 'else': { '0': { nextState: '!f', revisit: true }, }, '-9.,9 no missing 0': { '*': { action_: '9,9' }, }, '.': { '*': { action_: { type_: 'insert', option: 'electron dot' } }, }, 'a-z': { f: { action_: 'tex-math' }, }, 'x': { '*': { action_: { type_: 'insert', option: 'KV x' } }, }, 'letters': { '*': { action_: 'rm' }, }, "'": { '*': { action_: { type_: 'insert', option: 'prime' } }, }, '${(...)}$|$(...)$': { '*': { action_: 'tex-math' }, }, '{(...)}': { '*': { action_: 'text' }, }, '\\color{(...)}{(...)}1|\\color(...){(...)}2': { '*': { action_: 'color-output' }, }, '\\color{(...)}0': { '*': { action_: 'color0-output' }, }, '\\ce{(...)}': { '*': { action_: 'ce' }, }, '\\,|\\x{}{}|\\x{}|\\x': { '*': { action_: 'copy' }, }, 'else2': { '*': { action_: 'copy' }, }, }), actions: { 'color-output': function (buffer, m) { return { type_: 'color', color1: m[0], color2: mhchemParser.go(m[1], 'bd'), }; }, }, }, 'oxidation': { transitions: mhchemParser.createTransitions({ 'empty': { '*': {}, }, 'roman numeral': { '*': { action_: 'roman-numeral' }, }, '${(...)}$|$(...)$': { '*': { action_: 'tex-math' }, }, 'else': { '*': { action_: 'copy' }, }, }), actions: { 'roman-numeral': function (buffer, m) { return { type_: 'roman numeral', p1: m || '' }; }, }, }, 'tex-math': { transitions: mhchemParser.createTransitions({ 'empty': { '*': { action_: 'output' }, }, '\\ce{(...)}': { '*': { action_: ['output', 'ce'] }, }, '{...}|\\,|\\x{}{}|\\x{}|\\x': { '*': { action_: 'o=' }, }, 'else': { '*': { action_: 'o=' }, }, }), actions: { output: function (buffer) { if (buffer.o) { /** @type {ParserOutput} */ var ret = { type_: 'tex-math', p1: buffer.o }; for (var p in buffer) { delete buffer[p]; } return ret; } }, }, }, 'tex-math tight': { transitions: mhchemParser.createTransitions({ 'empty': { '*': { action_: 'output' }, }, '\\ce{(...)}': { '*': { action_: ['output', 'ce'] }, }, '{...}|\\,|\\x{}{}|\\x{}|\\x': { '*': { action_: 'o=' }, }, '-|+': { '*': { action_: 'tight operator' }, }, 'else': { '*': { action_: 'o=' }, }, }), actions: { 'tight operator': function (buffer, m) { buffer.o = (buffer.o || '') + '{' + m + '}'; }, 'output': function (buffer) { if (buffer.o) { /** @type {ParserOutput} */ var ret = { type_: 'tex-math', p1: buffer.o }; for (var p in buffer) { delete buffer[p]; } return ret; } }, }, }, '9,9': { transitions: mhchemParser.createTransitions({ 'empty': { '*': {}, }, ',': { '*': { action_: 'comma' }, }, 'else': { '*': { action_: 'copy' }, }, }), actions: { comma: function () { return { type_: 'commaDecimal' }; }, }, }, //#endregion // // \pu state machines // //#region pu 'pu': { transitions: mhchemParser.createTransitions({ 'empty': { '*': { action_: 'output' }, }, 'space$': { '*': { action_: ['output', 'space'] }, }, '{[(|)]}': { '0|a': { action_: 'copy' }, }, '(-)(9)^(-9)': { '0': { action_: 'number^', nextState: 'a' }, }, '(-)(9.,9)(e)(99)': { '0': { action_: 'enumber', nextState: 'a' }, }, 'space': { '0|a': {}, }, 'pm-operator': { '0|a': { action_: { type_: 'operator', option: '\\pm' }, nextState: '0', }, }, 'operator': { '0|a': { action_: 'copy', nextState: '0' }, }, '//': { d: { action_: 'o=', nextState: '/' }, }, '/': { d: { action_: 'o=', nextState: '/' }, }, '{...}|else': { '0|d': { action_: 'd=', nextState: 'd' }, 'a': { action_: ['space', 'd='], nextState: 'd' }, '/|q': { action_: 'q=', nextState: 'q' }, }, }), actions: { 'enumber': function (buffer, m) { /** @type {ParserOutput[]} */ var ret = []; if (m[0] === '+-' || m[0] === '+/-') { ret.push('\\pm '); } else if (m[0]) { ret.push(m[0]); } if (m[1]) { mhchemParser.concatArray(ret, mhchemParser.go(m[1], 'pu-9,9')); if (m[2]) { if (m[2].match(/[,.]/)) { mhchemParser.concatArray(ret, mhchemParser.go(m[2], 'pu-9,9')); } else { ret.push(m[2]); } } m[3] = m[4] || m[3]; if (m[3]) { m[3] = m[3].trim(); if (m[3] === 'e' || m[3].substr(0, 1) === '*') { ret.push({ type_: 'cdot' }); } else { ret.push({ type_: 'times' }); } } } if (m[3]) { ret.push('10^{' + m[5] + '}'); } return ret; }, 'number^': function (buffer, m) { /** @type {ParserOutput[]} */ var ret = []; if (m[0] === '+-' || m[0] === '+/-') { ret.push('\\pm '); } else if (m[0]) { ret.push(m[0]); } mhchemParser.concatArray(ret, mhchemParser.go(m[1], 'pu-9,9')); ret.push('^{' + m[2] + '}'); return ret; }, 'operator': function (buffer, m, p1) { return { type_: 'operator', kind_: p1 || m }; }, 'space': function () { return { type_: 'pu-space-1' }; }, 'output': function (buffer) { /** @type {ParserOutput | ParserOutput[]} */ var ret; var md = mhchemParser.patterns.match_('{(...)}', buffer.d || ''); if (md && md.remainder === '') { buffer.d = md.match_; } var mq = mhchemParser.patterns.match_('{(...)}', buffer.q || ''); if (mq && mq.remainder === '') { buffer.q = mq.match_; } if (buffer.d) { buffer.d = buffer.d.replace(/\u00B0C|\^oC|\^{o}C/g, '{}^{\\circ}C'); buffer.d = buffer.d.replace(/\u00B0F|\^oF|\^{o}F/g, '{}^{\\circ}F'); } if (buffer.q) { // fraction buffer.q = buffer.q.replace(/\u00B0C|\^oC|\^{o}C/g, '{}^{\\circ}C'); buffer.q = buffer.q.replace(/\u00B0F|\^oF|\^{o}F/g, '{}^{\\circ}F'); var b5 = { d: mhchemParser.go(buffer.d, 'pu'), q: mhchemParser.go(buffer.q, 'pu'), }; if (buffer.o === '//') { ret = { type_: 'pu-frac', p1: b5.d, p2: b5.q }; } else { ret = b5.d; if (b5.d.length > 1 || b5.q.length > 1) { ret.push({ type_: ' / ' }); } else { ret.push({ type_: '/' }); } mhchemParser.concatArray(ret, b5.q); } } else { // no fraction ret = mhchemParser.go(buffer.d, 'pu-2'); } for (var p in buffer) { delete buffer[p]; } return ret; }, }, }, 'pu-2': { transitions: mhchemParser.createTransitions({ 'empty': { '*': { action_: 'output' }, }, '*': { '*': { action_: ['output', 'cdot'], nextState: '0' }, }, '\\x': { '*': { action_: 'rm=' }, }, 'space': { '*': { action_: ['output', 'space'], nextState: '0' }, }, '^{(...)}|^(-1)': { '1': { action_: '^(-1)' }, }, '-9.,9': { '0': { action_: 'rm=', nextState: '0' }, '1': { action_: '^(-1)', nextState: '0' }, }, '{...}|else': { '*': { action_: 'rm=', nextState: '1' }, }, }), actions: { 'cdot': function () { return { type_: 'tight cdot' }; }, '^(-1)': function (buffer, m) { buffer.rm += '^{' + m + '}'; }, 'space': function () { return { type_: 'pu-space-2' }; }, 'output': function (buffer) { /** @type {ParserOutput | ParserOutput[]} */ var ret = []; if (buffer.rm) { var mrm = mhchemParser.patterns.match_('{(...)}', buffer.rm || ''); if (mrm && mrm.remainder === '') { ret = mhchemParser.go(mrm.match_, 'pu'); } else { ret = { type_: 'rm', p1: buffer.rm }; } } for (var p in buffer) { delete buffer[p]; } return ret; }, }, }, 'pu-9,9': { transitions: mhchemParser.createTransitions({ 'empty': { '0': { action_: 'output-0' }, 'o': { action_: 'output-o' }, }, ',': { '0': { action_: ['output-0', 'comma'], nextState: 'o' }, }, '.': { '0': { action_: ['output-0', 'copy'], nextState: 'o' }, }, 'else': { '*': { action_: 'text=' }, }, }), actions: { 'comma': function () { return { type_: 'commaDecimal' }; }, 'output-0': function (buffer) { /** @type {ParserOutput[]} */ var ret = []; buffer.text_ = buffer.text_ || ''; if (buffer.text_.length > 4) { var a = buffer.text_.length % 3; if (a === 0) { a = 3; } for (var i = buffer.text_.length - 3; i > 0; i -= 3) { ret.push(buffer.text_.substr(i, 3)); ret.push({ type_: '1000 separator' }); } ret.push(buffer.text_.substr(0, a)); ret.reverse(); } else { ret.push(buffer.text_); } for (var p in buffer) { delete buffer[p]; } return ret; }, 'output-o': function (buffer) { /** @type {ParserOutput[]} */ var ret = []; buffer.text_ = buffer.text_ || ''; if (buffer.text_.length > 4) { var a = buffer.text_.length - 3; for (var i = 0; i < a; i += 3) { ret.push(buffer.text_.substr(i, 3)); ret.push({ type_: '1000 separator' }); } ret.push(buffer.text_.substr(i)); } else { ret.push(buffer.text_); } for (var p in buffer) { delete buffer[p]; } return ret; }, }, }, //#endregion }; // // texify: Take MhchemParser output and convert it to TeX // /** @type {Texify} */ var texify = { go: function (input, isInner) { // (recursive, max 4 levels) if (!input) { return ''; } var res = ''; var cee = false; for (var i = 0; i < input.length; i++) { var inputi = input[i]; if (typeof inputi === 'string') { res += inputi; } else { res += texify._go2(inputi); if (inputi.type_ === '1st-level escape') { cee = true; } } } if (!isInner && !cee && res) { res = '{' + res + '}'; } return res; }, _goInner: function (input) { if (!input) { return input; } return texify.go(input, true); }, _go2: function (buf) { /** @type {undefined | string} */ var res; switch (buf.type_) { case 'chemfive': res = ''; var b5 = { a: texify._goInner(buf.a), b: texify._goInner(buf.b), p: texify._goInner(buf.p), o: texify._goInner(buf.o), q: texify._goInner(buf.q), d: texify._goInner(buf.d), }; // // a // if (b5.a) { if (b5.a.match(/^[+\-]/)) { b5.a = '{' + b5.a + '}'; } res += b5.a + '\\,'; } // // b and p // if (b5.b || b5.p) { res += '{\\vphantom{X}}'; res += '^{\\hphantom{' + (b5.b || '') + '}}_{\\hphantom{' + (b5.p || '') + '}}'; res += '{\\vphantom{X}}'; res += '^{\\smash[t]{\\vphantom{2}}\\mathllap{' + (b5.b || '') + '}}'; res += '_{\\vphantom{2}\\mathllap{\\smash[t]{' + (b5.p || '') + '}}}'; } // // o // if (b5.o) { if (b5.o.match(/^[+\-]/)) { b5.o = '{' + b5.o + '}'; } res += b5.o; } // // q and d // if (buf.dType === 'kv') { if (b5.d || b5.q) { res += '{\\vphantom{X}}'; } if (b5.d) { res += '^{' + b5.d + '}'; } if (b5.q) { res += '_{\\smash[t]{' + b5.q + '}}'; } } else if (buf.dType === 'oxidation') { if (b5.d) { res += '{\\vphantom{X}}'; res += '^{' + b5.d + '}'; } if (b5.q) { res += '{\\vphantom{X}}'; res += '_{\\smash[t]{' + b5.q + '}}'; } } else { if (b5.q) { res += '{\\vphantom{X}}'; res += '_{\\smash[t]{' + b5.q + '}}'; } if (b5.d) { res += '{\\vphantom{X}}'; res += '^{' + b5.d + '}'; } } break; case 'rm': res = '\\mathrm{' + buf.p1 + '}'; break; case 'text': if (buf.p1.match(/[\^_]/)) { buf.p1 = buf.p1.replace(' ', '~').replace('-', '\\text{-}'); res = '\\mathrm{' + buf.p1 + '}'; } else { res = '\\text{' + buf.p1 + '}'; } break; case 'roman numeral': res = '\\mathrm{' + buf.p1 + '}'; break; case 'state of aggregation': res = '\\mskip2mu ' + texify._goInner(buf.p1); break; case 'state of aggregation subscript': res = '\\mskip1mu ' + texify._goInner(buf.p1); break; case 'bond': res = texify._getBond(buf.kind_); if (!res) { throw [ 'MhchemErrorBond', 'mhchem Error. Unknown bond type (' + buf.kind_ + ')', ]; } break; case 'frac': var c = '\\frac{' + buf.p1 + '}{' + buf.p2 + '}'; res = '\\mathchoice{\\textstyle' + c + '}{' + c + '}{' + c + '}{' + c + '}'; break; case 'pu-frac': var d = '\\frac{' + texify._goInner(buf.p1) + '}{' + texify._goInner(buf.p2) + '}'; res = '\\mathchoice{\\textstyle' + d + '}{' + d + '}{' + d + '}{' + d + '}'; break; case 'tex-math': res = buf.p1 + ' '; break; case 'frac-ce': res = '\\frac{' + texify._goInner(buf.p1) + '}{' + texify._goInner(buf.p2) + '}'; break; case 'overset': res = '\\overset{' + texify._goInner(buf.p1) + '}{' + texify._goInner(buf.p2) + '}'; break; case 'underset': res = '\\underset{' + texify._goInner(buf.p1) + '}{' + texify._goInner(buf.p2) + '}'; break; case 'underbrace': res = '\\underbrace{' + texify._goInner(buf.p1) + '}_{' + texify._goInner(buf.p2) + '}'; break; case 'color': res = '{\\color{' + buf.color1 + '}{' + texify._goInner(buf.color2) + '}}'; break; case 'color0': res = '\\color{' + buf.color + '}'; break; case 'arrow': var b6 = { rd: texify._goInner(buf.rd), rq: texify._goInner(buf.rq), }; var arrow = '\\x' + texify._getArrow(buf.r); if (b6.rq) { arrow += '[{' + b6.rq + '}]'; } if (b6.rd) { arrow += '{' + b6.rd + '}'; } else { arrow += '{}'; } res = arrow; break; case 'operator': res = texify._getOperator(buf.kind_); break; case '1st-level escape': res = buf.p1 + ' '; // &, \\\\, \\hlin break; case 'space': res = ' '; break; case 'entitySkip': res = '~'; break; case 'pu-space-1': res = '~'; break; case 'pu-space-2': res = '\\mkern3mu '; break; case '1000 separator': res = '\\mkern2mu '; break; case 'commaDecimal': res = '{,}'; break; case 'comma enumeration L': res = '{' + buf.p1 + '}\\mkern6mu '; break; case 'comma enumeration M': res = '{' + buf.p1 + '}\\mkern3mu '; break; case 'comma enumeration S': res = '{' + buf.p1 + '}\\mkern1mu '; break; case 'hyphen': res = '\\text{-}'; break; case 'addition compound': res = '\\,{\\cdot}\\,'; break; case 'electron dot': res = '\\mkern1mu \\bullet\\mkern1mu '; break; case 'KV x': res = '{\\times}'; break; case 'prime': res = '\\prime '; break; case 'cdot': res = '\\cdot '; break; case 'tight cdot': res = '\\mkern1mu{\\cdot}\\mkern1mu '; break; case 'times': res = '\\times '; break; case 'circa': res = '{\\sim}'; break; case '^': res = 'uparrow'; break; case 'v': res = 'downarrow'; break; case 'ellipsis': res = '\\ldots '; break; case '/': res = '/'; break; case ' / ': res = '\\,/\\,'; break; default: throw ['MhchemBugT', 'mhchem bug T. Please report.']; // Missing texify rule or unknown MhchemParser output } return res; }, _getArrow: function (a) { switch (a) { case '->': return 'rightarrow'; case '\u2192': return 'rightarrow'; case '\u27F6': return 'rightarrow'; case '<-': return 'leftarrow'; case '<->': return 'leftrightarrow'; case '<-->': return 'rightleftarrows'; case '<=>': return 'rightleftharpoons'; case '\u21CC': return 'rightleftharpoons'; case '<=>>': return 'rightequilibrium'; case '<<=>': return 'leftequilibrium'; default: throw ['MhchemBugT', 'mhchem bug T. Please report.']; } }, _getBond: function (a) { switch (a) { case '-': return '{-}'; case '1': return '{-}'; case '=': return '{=}'; case '2': return '{=}'; case '#': return '{\\equiv}'; case '3': return '{\\equiv}'; case '~': return '{\\tripledash}'; case '~-': return '{\\mathrlap{\\raisebox{-.1em}{$-$}}\\raisebox{.1em}{$\\tripledash$}}'; case '~=': return '{\\mathrlap{\\raisebox{-.2em}{$-$}}\\mathrlap{\\raisebox{.2em}{$\\tripledash$}}-}'; case '~--': return '{\\mathrlap{\\raisebox{-.2em}{$-$}}\\mathrlap{\\raisebox{.2em}{$\\tripledash$}}-}'; case '-~-': return '{\\mathrlap{\\raisebox{-.2em}{$-$}}\\mathrlap{\\raisebox{.2em}{$-$}}\\tripledash}'; case '...': return '{{\\cdot}{\\cdot}{\\cdot}}'; case '....': return '{{\\cdot}{\\cdot}{\\cdot}{\\cdot}}'; case '->': return '{\\rightarrow}'; case '<-': return '{\\leftarrow}'; case '<': return '{<}'; case '>': return '{>}'; default: throw ['MhchemBugT', 'mhchem bug T. Please report.']; } }, _getOperator: function (a) { switch (a) { case '+': return ' {}+{} '; case '-': return ' {}-{} '; case '=': return ' {}={} '; case '<': return ' {}<{} '; case '>': return ' {}>{} '; case '<<': return ' {}\\ll{} '; case '>>': return ' {}\\gg{} '; case '\\pm': return ' {}\\pm{} '; case '\\approx': return ' {}\\approx{} '; case '$\\approx$': return ' {}\\approx{} '; case 'v': return ' \\downarrow{} '; case '(v)': return ' \\downarrow{} '; case '^': return ' \\uparrow{} '; case '(^)': return ' \\uparrow{} '; default: throw ['MhchemBugT', 'mhchem bug T. Please report.']; } }, }; // Simple characters allowed in math mode newSymbols('0123456789/@.?!'); newSymbolRange(0x0041, 0x005a); // a-z newSymbolRange(0x0061, 0x007a); // A-Z // Quantifiers newSymbols([ ['\\forall', 0x2200], ['\\exists', 0x2203], ['\\nexists', 0x2204, 'mord', 'ams'], ['\\mid', 0x2223, 'mrel'], ['\\top', 0x22a4], ['\\bot', 0x22a5], ]); // Misc Symbols newSymbols([ ['\\sharp', 0x266f], ['\\flat', 0x266d], ['\\natural', 0x266e], ['\\#', 0x0023], ['\\&', 0x0026], ['\\clubsuit', 0x2663], ['\\heartsuit', 0x2661], ['\\spadesuit', 0x2660], ['\\diamondsuit', 0x2662], ]); // DefineSymbol( '\\cross', 0xF4A0, 'mord', MAIN], // NOTE: not a real TeX symbol, but Mathematica // defineSymbol( '\\transpose', 0xF3C7, 'mord', MAIN], // NOTE: not a real TeX symbol, but Mathematica // defineSymbol( '\\conjugate', 'conj'], MAIN, 'mord', 0xF3C8], // NOTE: not a real TeX symbol, but Mathematica // defineSymbol( '\\conjugatetranspose', 0xF3C9, 'mord', MAIN], // NOTE: not a real TeX symbol, but Mathematica // defineSymbol( '\\hermitianconjugate', 0xF3CE, 'mord', MAIN], // NOTE: not a real TeX symbol, but Mathematica newSymbols([ ['\\backslash', 0x005c], ['\\nabla', 0x2207], ['\\partial', 0x2202], ['\\ell', 0x2113], ['\\hbar', 0x210f], ['\\Q', 0x0051, 'mord', 'double-struck'], ['\\C', 0x0043, 'mord', 'double-struck'], ['\\P', 0x0050, 'mord', 'double-struck'], ['\\pounds', 0x00a3], ['\\euro', 0x20ac], // NOTE: not TeX built-in, but textcomp package // TODO Koppa, Stigma, Sampi ]); // Arrow Symbols newSymbols([ ['\\rightarrow', 0x2192], ['\\to', 0x2192], ['\\leftarrow', 0x2190], ['\\gets', 0x2190], ['\\Rightarrow', 0x21d2], ['\\Leftarrow', 0x21d0], ['\\longrightarrow', 0x27f6], ['\\longleftarrow', 0x27f5], ['\\Longrightarrow', 0x27f9], ['\\implies', 0x27f9], ['\\Longleftarrow', 0x27f8], ['\\impliedby', 0x27f8], ['\\longleftrightarrow', 0x27f7], ['\\biconditional', 0x27f7], ['\\Longleftrightarrow', 0x27fa], ['\\mapsto', 0x21a6], ['\\longmapsto', 0x27fc], ['\\uparrow', 0x2191], ['\\downarrow', 0x2193], ['\\Uparrow', 0x21d1], ['\\Downarrow', 0x21d3], ['\\updownarrow', 0x2195], ['\\Updownarrow', 0x21d5], ['\\hookrightarrow', 0x21aa], ['\\hookleftarrow', 0x21a9], ['\\rightharpoonup', 0x21c0], ['\\leftharpoonup', 0x21bc], ['\\rightharpoondown', 0x21c1], ['\\leftharpoondown', 0x21bd], ['\\searrow', 0x2198], ['\\nearrow', 0x2197], ['\\swarrow', 0x2199], ['\\nwarrow', 0x2196], ['\\originalof', 0x22b6], ['\\laplace', 0x22b6], ['\\imageof', 0x22b7], ['\\Laplace', 0x22b7], ], 'mrel'); // 'ams' Misc newSymbols([ // 'ams' Delimiters ['\\lbrace', 0x007b, 'mopen'], ['\\rbrace', 0x007d, 'mclose'], ['\\lparen', 0x0028, 'mopen'], ['\\rparen', 0x0029, 'mclose'], ['\\langle', 0x27e8, 'mopen'], ['\\rangle', 0x27e9, 'mclose'], ['\\lfloor', 0x230a, 'mopen'], ['\\rfloor', 0x230b, 'mclose'], ['\\lceil', 0x2308, 'mopen'], ['\\rceil', 0x2309, 'mclose'], ['\\vert', 0x2223], ['\\lvert', 0x2223, 'mopen'], ['\\rvert', 0x2223, 'mclose'], ['\\|', 0x2225], ['\\Vert', 0x2225], ['\\mVert', 0x2225], ['\\lVert', 0x2225, 'mopen'], ['\\rVert', 0x2225, 'mclose'], ['\\lbrack', 0x005b, 'mopen'], ['\\rbrack', 0x005d, 'mclose'], ['\\{', 0x007b, 'mopen'], ['\\}', 0x007d, 'mclose'], ['(', 0x0028, 'mopen'], [')', 0x029, 'mclose'], ['[', 0x005b, 'mopen'], [']', 0x005d, 'mclose'], ['\\ulcorner', 0x250c, 'mopen', 'ams'], ['\\urcorner', 0x2510, 'mclose', 'ams'], ['\\llcorner', 0x2514, 'mopen', 'ams'], ['\\lrcorner', 0x2518, 'mclose', 'ams'], // Large Delimiters ['\\lgroup', 0x27ee, 'mopen'], ['\\rgroup', 0x27ef, 'mclose'], ['\\lmoustache', 0x23b0, 'mopen'], ['\\rmoustache', 0x23b1, 'mclose'], // defineSymbol('\\ne', 0x2260, 'mrel'], // defineSymbol('\\neq', 0x2260, 'mrel'], // DefineSymbol( '\\longequal', 0xF7D9, 'mrel', MAIN], // NOTE: Not TeX ]); newSymbols([ // 'ams' arrows ['\\dashrightarrow', 0x21e2], ['\\dashleftarrow', 0x21e0], ['\\Rrightarrow', 0x21db], ['\\Lleftarrow', 0x21da], ['\\leftrightarrows', 0x21c6], ['\\rightleftarrows', 0x21c4], ['\\curvearrowright', 0x21b7], ['\\curvearrowleft', 0x21b6], ['\\rightrightarrows', 0x21c9], ['\\leftleftarrows', 0x21c7], ['\\upuparrows', 0x21c8], ['\\downdownarrows', 0x21ca], ['\\vartriangle', 0x25b3], ['\\triangleq', 0x225c], ['\\vartriangleleft', 0x22b2], ['\\trianglelefteq', 0x22b4], ['\\ntriangleleft', 0x22ea], ['\\ntrianglelefteq', 0x22ec], ['\\vartriangleright', 0x22b3], ['\\trianglerighteq', 0x22b5], ['\\ntriangleright', 0x22eb], ['\\ntrianglerighteq', 0x22ed], ['\\blacktriangleleft', 0x25c0], ['\\blacktriangleright', 0x25b6], ['\\leftarrowtail', 0x21a2], ['\\rightarrowtail', 0x21a3], ['\\looparrowright', 0x21ac], ['\\looparrowleft', 0x21ab], ['\\twoheadleftarrow', 0x219e], ['\\twoheadrightarrow', 0x21a0], ['\\rightleftharpoons', 0x21cc], ['\\leftrightharpoons', 0x21cb], ['\\Rsh', 0x21b1], ['\\Lsh', 0x21b0], // 'ams' Relations ['\\circlearrowright', 0x21bb], ['\\circlearrowleft', 0x21ba], ['\\restriction', 0x21be], ['\\upharpoonright', 0x21be], ['\\upharpoonleft', 0x21bf], ['\\downharpoonright', 0x21c2], ['\\downharpoonleft', 0x21c3], ['\\rightsquigarrow', 0x21dd], ['\\leadsto', 0x21dd], ['\\leftrightsquigarrow', 0x21ad], ['\\multimap', 0x22b8], // 'ams' Negated Arrows ['\\nrightarrow', 0x219b], ['\\nleftarrow', 0x219a], ['\\nRightarrow', 0x21cf], ['\\nLeftarrow', 0x21cd], ['\\nleftrightarrow', 0x21ae], ['\\nLeftrightarrow', 0x21ce], // 'ams' Negated Relations ['\\shortparallel', 0x2225], ['\\nless', 0x226e], ['\\nleqslant', 0xe010], ['\\lneq', 0x2a87], ['\\lneqq', 0x2268], ['\\nleqq', 0xe011], ['\\lvertneqq', 0xe00c], ['\\lnsim', 0x22e6], ['\\lnapprox', 0x2a89], ['\\nprec', 0x2280], ['\\npreceq', 0x22e0], ['\\precnsim', 0x22e8], ['\\precnapprox', 0x2ab9], ['\\nsim', 0x2241], ['\\nshortmid', 0xe006], ['\\nmid', 0x2224], ['\\nvdash', 0x22ac], ['\\nvDash', 0x22ad], ['\\ngtr', 0x226f], ['\\ngeqslant', 0xe00f], ['\\ngeqq', 0xe00e], ['\\gneq', 0x2a88], ['\\gneqq', 0x2269], ['\\gvertneqq', 0xe00d], ['\\gnsim', 0x22e7], ['\\gnapprox', 0x2a8a], ['\\nsucc', 0x2281], ['\\nsucceq', 0x22e1], ['\\succnsim', 0x22e9], ['\\succnapprox', 0x2aba], ['\\ncong', 0x2246], ['\\nshortparallel', 0xe007], ['\\nparallel', 0x2226], ['\\nVDash', 0x22af], ['\\nsupseteqq', 0xe018], ['\\supsetneq', 0x228b], ['\\varsupsetneq', 0xe01b], ['\\supsetneqq', 0x2acc], ['\\varsupsetneqq', 0xe019], ['\\nVdash', 0x22ae], ['\\precneqq', 0x2ab5], ['\\succneqq', 0x2ab6], ['\\nsubseteqq', 0xe016], ['\\leqslant', 0x2a7d], ['\\geqslant', 0x2a7e], ['\\gtrsim', 0x2273], ['\\approxeq', 0x224a], ['\\thickapprox', 0x2248], ['\\lessapprox', 0x2a85], ['\\gtrapprox', 0x2a86], ['\\precapprox', 0x2ab7], ['\\succapprox', 0x2ab8], ['\\thicksim', 0x223c], ['\\succsim', 0x227f], ['\\precsim', 0x227e], ['\\backsim', 0x223d], ['\\eqsim', 0x2242], ['\\backsimeq', 0x22cd], ['\\lesssim', 0x2272], ['\\nleq', 0x2270], ['\\ngeq', 0x2271], ['\\smallsmile', 0x2323], ['\\smallfrown', 0x2322], ['\\leqq', 0x2266], ['\\eqslantless', 0x2a95], ['\\lll', 0x22d8], ['\\lessgtr', 0x2276], ['\\lesseqgtr', 0x22da], ['\\lesseqqgtr', 0x2a8b], ['\\risingdotseq', 0x2253], ['\\fallingdotseq', 0x2252], ['\\subseteqq', 0x2ac5], ['\\Subset', 0x22d0], ['\\sqsubset', 0x228f], ['\\preccurlyeq', 0x227c], ['\\curlyeqprec', 0x22de], ['\\vDash', 0x22a8], ['\\Vvdash', 0x22aa], ['\\bumpeq', 0x224f], ['\\Bumpeq', 0x224e], ['\\geqq', 0x2267], ['\\eqslantgtr', 0x2a96], ['\\ggg', 0x22d9], ['\\gtrless', 0x2277], ['\\gtreqless', 0x22db], ['\\gtreqqless', 0x2a8c], ['\\supseteqq', 0x2ac6], ['\\Supset', 0x22d1], ['\\sqsupset', 0x2290], ['\\succcurlyeq', 0x227d], ['\\curlyeqsucc', 0x22df], ['\\Vdash', 0x22a9], ['\\shortmid', 0x2223], ['\\between', 0x226c], ['\\pitchfork', 0x22d4], ['\\varpropto', 0x221d], ['\\backepsilon', 0x220d], ['\\llless', 0x22d8], ['\\gggtr', 0x22d9], ['\\doteqdot', 0x2251], ['\\Doteq', 0x2251], ['\\eqcirc', 0x2256], ['\\circeq', 0x2257], ['\\therefore', 0x2234], ['\\because', 0x2235], ], 'mrel', 'ams'); // Binary Operators newSymbols([ ['+', 0x002b], ['-', 0x2212], ['\u2212', 0x2212], ['\\pm', 0x00b1], ['\\mp', 0x2213], ['*', 0x2217], ['\\times', 0x00d7], ['\\div', 0x00f7], ['\\divides', 0x2223], ['\\cdot', 0x22c5], ['\\cap', 0x2229], ['\\cup', 0x222a], ['\\setminus', 0x2216], ['\\land', 0x2227], ['\\wedge', 0x2227], ['\\lor', 0x2228], ['\\vee', 0x2228], ['\\circ', 0x2218], ['\\bigcirc', 0x25ef], ['\\bullet', 0x2219], ['\\oplus', 0x2295], ['\\ominus', 0x2296], ['\\otimes', 0x2297], ['\\odot', 0x2299], ['\\oslash', 0x2298], ['\\bigtriangleup', 0x25b3], ['\\bigtriangledown', 0x25bd], ['\\triangleleft', 0x25c3], ['\\triangleright', 0x25b9], ['\\And', 0x0026], ['\\dagger', 0x2020], ['\\dag', 0x2020], ['\\ddag', 0x2021], ['\\ddagger', 0x2021], ['\\ast', 0x2217], ['\\star', 0x22c6], ['\\bigstar', 0x2605], ['\\diamond', 0x22c4], ], 'mbin'); // 'ams' Binary Operators newSymbols([ ['\\lhd', 0x22b2], ['\\rhd', 0x22b3], ['\\lessdot', 0x22d6], ['\\gtrdot', 0x22d7], ['\\ltimes', 0x22c9], ['\\rtimes', 0x22ca], ['\\leftthreetimes', 0x22cb], ['\\rightthreetimes', 0x22cc], ['\\intercal', 0x22ba], ['\\dotplus', 0x2214], ['\\doublebarwedge', 0x2a5e], ['\\divideontimes', 0x22c7], ['\\centerdot', 0x22c5], ['\\smallsetminus', 0x2216], ['\\barwedge', 0x22bc], ['\\veebar', 0x22bb], ['\\nor', 0x22bb], ['\\curlywedge', 0x22cf], ['\\curlyvee', 0x22ce], ['\\boxminus', 0x229f], ['\\boxplus', 0x229e], ['\\boxtimes', 0x22a0], ['\\boxdot', 0x22a1], ['\\circleddash', 0x229d], ['\\circledast', 0x229b], ['\\circledcirc', 0x229a], ['\\unlhd', 0x22b4], ['\\unrhd', 0x22b5], ], 'mbin', 'ams'); // Ordinary symbols newSymbols([ ['\\surd', 0x221a], // From MnSymbol package ['\\infty', 0x221e], ['\\prime', 0x2032], ['\\doubleprime', 0x2033], ['\\angle', 0x2220], ['`', 0x2018], ['\\$', 0x0024], ['\\%', 0x0025], ['\\_', 0x005f], // Note: In TeX, greek symbols are only available in Math mode ['\\alpha', 0x03b1], ['\\beta', 0x03b2], ['\\gamma', 0x03b3], ['\\delta', 0x03b4], ['\\epsilon', 0x03f5], ['\\varepsilon', 0x03b5], ['\\zeta', 0x03b6], ['\\eta', 0x03b7], ['\\theta', 0x03b8], ['\\vartheta', 0x03d1], ['\\iota', 0x03b9], ['\\kappa', 0x03ba], ['\\varkappa', 0x03f0, 'mord', 'ams'], ['\\lambda', 0x03bb], ['\\mu', 0x03bc], ['\\nu', 0x03bd], ['\\xi', 0x03be], ['\\omicron', 0x006f], ['\\pi', 0x03c0], ['\\varpi', 0x03d6], ['\\rho', 0x03c1], ['\\varrho', 0x03f1], ['\\sigma', 0x03c3], ['\\varsigma', 0x03c2], ['\\tau', 0x03c4], ['\\phi', 0x03d5], ['\\varphi', 0x03c6], ['\\upsilon', 0x03c5], ['\\chi', 0x03c7], ['\\psi', 0x03c8], ['\\omega', 0x03c9], ['\\Gamma', 0x0393], ['\\Delta', 0x0394], ['\\Theta', 0x0398], ['\\Lambda', 0x039b], ['\\Xi', 0x039e], ['\\Pi', 0x03a0], ['\\Sigma', 0x03a3], ['\\Upsilon', 0x03a5], ['\\Phi', 0x03a6], ['\\Psi', 0x03a8], ['\\Omega', 0x03a9], // 'ams' Greek ['\\digamma', 0x03dd, 'mord', 'ams'], ['\\emptyset', 0x2205], ]); // Relational symbols newSymbols([ ['=', 0x003d], ['<', 0x003c], ['\\lt', 0x003c], ['>', 0x003e], ['\\gt', 0x003e], ['\\le', 0x2264], ['\\leq', 0x2264], ['\\ge', 0x2265], ['\\geq', 0x2265], ['\\ll', 0x226a], ['\\gg', 0x226b], ['\\coloneq', 0x2254], ['\\measeq', 0x225d], ['\\eqdef', 0x225e], ['\\questeq', 0x225f], [':', 0x003a], ['\\cong', 0x2245], ['\\equiv', 0x2261], ['\\prec', 0x227a], ['\\preceq', 0x2aaf], ['\\succ', 0x227b], ['\\succeq', 0x2ab0], ['\\perp', 0x22a5], ['\\propto', 0x221d], ['\\Colon', 0x2237], ['\\smile', 0x2323], ['\\frown', 0x2322], ['\\sim', 0x223c], ['\\doteq', 0x2250], ['\\bowtie', 0x22c8], ['\\Join', 0x22c8], ['\\asymp', 0x224d], ['\\sqsubseteq', 0x2291], ['\\sqsupseteq', 0x2292], ['\\approx', 0x2248], // However, '~' is used as an ASCII Math shortctut character, so define a \\~ // command which maps to the '~' character ['\\~', 0x007e], ['\\leftrightarrow', 0x2194], ['\\Leftrightarrow', 0x21d4], ['\\models', 0x22a8], ['\\vdash', 0x22a2], ['\\dashv', 0x22a3], ['\\roundimplies', 0x2970], ['\\in', 0x2208], ['\\notin', 0x2209], // defineSymbol('\\not', 0x0338], // defineSymbol('\\not', 0xe020], ['\\ni', 0x220b], ['\\owns', 0x220b], ['\\subset', 0x2282], ['\\supset', 0x2283], ['\\subseteq', 0x2286], ['\\supseteq', 0x2287], ['\\differencedelta', 0x2206], ['\\mvert', 0x2223], ['\\parallel', 0x2225], ['\\simeq', 0x2243], ], 'mrel'); // 'ams' Relational Symbols newSymbols([ ['\\lnot', 0x00ac], ['\\neg', 0x00ac], ['\\triangle', 0x25b3], ['\\subsetneq', 0x228a], ['\\varsubsetneq', 0xe01a], ['\\subsetneqq', 0x2acb], ['\\varsubsetneqq', 0xe017], ['\\nsubset', 0x2284], ['\\nsupset', 0x2285], ['\\nsubseteq', 0x2288], ['\\nsupseteq', 0x2289], ], 'mrel', 'ams'); newSymbols([ ['\\wp', 0x2118], ['\\aleph', 0x2135], ]); // 'ams' Ordinary Symbols newSymbols([ ['\\blacktriangle', 0x25b2], ['\\hslash', 0x210f], ['\\Finv', 0x2132], ['\\Game', 0x2141], ['\\eth', 0x00f0], ['\\mho', 0x2127], ['\\Bbbk', 0x006b], ['\\yen', 0x00a5], ['\\square', 0x25a1], ['\\Box', 0x25a1], ['\\blacksquare', 0x25a0], ['\\circledS', 0x24c8], ['\\circledR', 0x00ae], ['\\triangledown', 0x25bd], ['\\blacktriangledown', 0x25bc], ['\\checkmark', 0x2713], ['\\diagup', 0x2571], ['\\measuredangle', 0x2221], ['\\sphericalangle', 0x2222], ['\\backprime', 0x2035], ['\\backdoubleprime', 0x2036], ['\\Diamond', 0x25ca], ['\\lozenge', 0x25ca], ['\\blacklozenge', 0x29eb], ['\\varnothing', 0x2205], ['\\complement', 0x2201], ['\\maltese', 0x2720], // 'ams' Hebrew ['\\beth', 0x2136], ['\\daleth', 0x2138], ['\\gimel', 0x2137], ], 'mord', 'ams'); newSymbols([ // See http://tex.stackexchange.com/questions/41476/lengths-and-when-to-use-them ['\\ ', 0x00a0], ['~', 0x00a0], ['\\space', 0x00a0], ], 'space'); // \enspace is a TeX command (not LaTeX) equivalent to a \skip defineFunction(['!', ',', ':', ';', 'enskip', 'enspace', 'quad', 'qquad'], '', { createAtom: (command, _args, style) => new SpacingAtom(command, style), }); // Punctuation newSymbols([ ['\\colon', 0x003a], ['\\cdotp', 0x22c5], ['\\vdots', 0x22ee, 'mord'], ['\\ldotp', 0x002e], [',', 0x002c], [';', 0x003b], ], 'mpunct'); newSymbols([ ['\\cdots', 0x22ef], ['\\ddots', 0x22f1], ['\\ldots', 0x2026], ['\\mathellipsis', 0x2026], ], 'minner'); newSymbols([ ['\\/', 0x002f], ['|', 0x2223, 'mord'], ['\\imath', 0x0131], ['\\jmath', 0x0237], ['\\degree', 0x00b0], ["'", 0x2032], ['"', 0x201d], // Double Prime // defineSymbol( "\'', 0x2033, 'mord', MAIN], // Double Prime ]); class CompositionAtom extends Atom { constructor(value, options) { var _a; super('composition', { mode: (_a = options === null || options === void 0 ? void 0 : options.mode) !== null && _a !== void 0 ? _a : 'math', value }); } static fromJson(json) { return new CompositionAtom(json.value, json); } toJson() { return super.toJson(); } get computedStyle() { return {}; } render(context) { // In theory one would like to be able to draw the clauses // in an active composition. Unfortunately, there are // no API to give access to those clauses :( const result = new Box(this.value, { classes: 'ML__composition', type: 'composition', }); this.bind(context, result); if (this.caret) result.caret = this.caret; return result; } serialize(_options) { return ''; } } /** * Atom for raw latex character, while in LaTeX editing mode */ class LatexAtom extends Atom { constructor(value, options) { var _a; super('latex', { value, mode: 'latex' }); this.isSuggestion = (_a = options === null || options === void 0 ? void 0 : options.isSuggestion) !== null && _a !== void 0 ? _a : false; this.isError = false; this.verbatimLatex = value; } static fromJson(json) { const result = new LatexAtom(json.command); if (json.isSuggestion) result.isSuggestion = true; if (json.isError) result.isError = true; return result; } toJson() { const options = {}; if (this.isSuggestion) options.isSuggestion = true; if (this.isError) options.isError = true; return { ...super.toJson(), ...options }; } get computedStyle() { return {}; } render(context) { const result = new Box(this.value, { classes: this.isSuggestion ? 'ML__suggestion' : this.isError ? 'ML__error' : '', type: 'latex', maxFontSize: 1.0, }); if (!result) return null; if (this.caret) result.caret = this.caret; return this.bind(context, result); } } /** * A group that represents a raw LaTeX editing zone. * There is only one LatexGroupAtom at a time in an expression. */ class LatexGroupAtom extends Atom { constructor(latex) { super('latexgroup', { mode: 'latex' }); this.body = [...latex].map((x) => new LatexAtom(x)); this.skipBoundary = false; } static fromJson(_json) { return new LatexGroupAtom(''); } toJson() { return super.toJson(); } render(context) { const box = Atom.createBox(context, this.body, { newList: true }); if (!box) return null; if (this.caret) box.caret = this.caret; // Need to bind the group so that the DOM element can be matched // and the atom iterated recursively. Otherwise, it behaves // as if `captureSelection === true` return this.bind(context, box); } serialize(_options) { var _a, _b; return (_b = (_a = this.body) === null || _a === void 0 ? void 0 : _a.map((x) => x.value).join('')) !== null && _b !== void 0 ? _b : ''; } } function fromJson(json) { if (isArray(json)) return json.map((x) => fromJson(x)); json = { ...json }; // Restore the branches for (const branch of NAMED_BRANCHES) if (json[branch]) json[branch] = fromJson(json[branch]); if (json.array) json.array = fromJson(json.array); const type = json.type; let result = undefined; if (type === 'accent') result = AccentAtom.fromJson(json); if (type === 'array') result = ArrayAtom.fromJson(json); if (type === 'box') result = BoxAtom.fromJson(json); if (type === 'composition') result = CompositionAtom.fromJson(json); if (type === 'chem') result = ChemAtom.fromJson(json); if (type === 'delim') result = DelimAtom.fromJson(json); if (type === 'enclose') result = EncloseAtom.fromJson(json); if (type === 'error') result = ErrorAtom.fromJson(json); if (type === 'genfrac') result = GenfracAtom.fromJson(json); if (type === 'group') result = GroupAtom.fromJson(json); if (type === 'latex') result = LatexAtom.fromJson(json); if (type === 'latexgroup') result = LatexGroupAtom.fromJson(json); if (type === 'leftright') result = LeftRightAtom.fromJson(json); if (type === 'line') result = LineAtom.fromJson(json); if (type === 'macro') result = MacroAtom.fromJson(json); if (type === 'msubsup') result = SubsupAtom.fromJson(json); if (type === 'overlap') result = OverlapAtom.fromJson(json); if (type === 'overunder') result = OverunderAtom.fromJson(json); if (type === 'placeholder') { if (json.defaultValue) json.defaultValue = fromJson(json.defaultValue); result = PlaceholderAtom.fromJson(json); } if (type === 'phantom') result = PhantomAtom.fromJson(json); if (type === 'rule') result = RuleAtom.fromJson(json); if (type === 'sizeddelim') result = SizedDelimAtom.fromJson(json); if (type === 'spacing') result = SpacingAtom.fromJson(json); if (type === 'surd') result = SurdAtom.fromJson(json); if (type === 'text') result = TextAtom.fromJson(json); if (type === 'mop') result = OperatorAtom.fromJson(json); // @todo root; // @todo space; if (!result) result = Atom.fromJson(json); for (const branch of NAMED_BRANCHES) if (json[branch]) result.setChildren(json[branch], branch); // Restore properties if (json.verbatimLatex !== undefined) result.verbatimLatex = json.verbatimLatex; if (json.subsupPlacement) result.subsupPlacement = json.subsupPlacement; if (json.explicitSubsupPlacement) result.explicitSubsupPlacement = true; if (json.isFunction) result.isFunction = true; if (json.isExtensibleSymbol) result.isExtensibleSymbol = true; if (json.skipBoundary) result.skipBoundary = true; if (json.captureSelection) result.captureSelection = true; return result; } var _a$1, _b$1; // Adapted from https://jakedeichert.com/blog/2020/02/a-super-hacky-alternative-to-import-meta-url/ function getFileUrl() { const stackTraceFrames = String(new Error().stack) .replace(/^Error.*\n/, '') .split('\n'); if (stackTraceFrames.length === 0) { console.error("Can't use relative paths to specify assets location because the source" + 'file location could not be determined (unexpected stack trace format' + ` "${new Error().stack}").`); return ''; } // 0 = this getFileUrl frame (because the Error is created here) // 1 = the caller of getFileUrl (the file path we want to grab) let callerFrame = stackTraceFrames[1]; // Extract the script's complete url let m = callerFrame.match(/http.*\.ts[\?:]/); if (m) { // This is a Typescript file, therefore there's a source map that's // remapping to the source file. Use an entry further in the stack trace. callerFrame = stackTraceFrames[2]; } m = callerFrame.match(/(https?:.*):[0-9]+:[0-9]+/); if (!m) { // We might be running under node, in which case we have a file path, not a URL m = callerFrame.match(/at (.*(\.ts))[\?:]/); if (!m) m = callerFrame.match(/at (.*(\.mjs|\.js))[\?:]/); } if (!m) { console.error(stackTraceFrames); console.error("Can't use relative paths to specify assets location because the source " + 'file location could not be determined ' + `(unexpected location "${callerFrame}").`); return ''; } return m[1]; } // When using a CDN, there might be some indirections (i.e. to deal // with version numbers) before finding the actual location of the library. // When resolving relative (to find fonts/sounds), we need to have the resolved // URL let gResolvedScriptUrl = null; function resolveRelativeUrl(url) { if (gResolvedScriptUrl === null) { try { const request = new XMLHttpRequest(); // Do a `HEAD` request, we don't care about the body request.open('HEAD', gScriptUrl, false); request.send(null); if (request.status === 200) gResolvedScriptUrl = request.responseURL; } catch (e) { console.error(`Invalid URL "${url}" (relative to "${gScriptUrl}")`); } } if (gResolvedScriptUrl) return new URL(url, gResolvedScriptUrl).href; return ''; } // The URL of the bundled MathLive library. Used later to locate the `fonts` // directory, relative to the library // If loaded via a <script> tag, `document.currentScript.src` is this location // If loaded via a module (e.g. `import ...`),`import.meta.url` is this location. // However, `import.meta` is not supported by WebPack. So, use a // super-hacky-alternative to get the URL. // See https://github.com/webpack/webpack/issues/6719 // Note that in some circumstances, document.currentScript.src can be "" // (the empty string). Therefore, use the "||" operator rather than "??" // to properly apply the alternative value in this case. const gScriptUrl = ((_b$1 = (_a$1 = globalThis === null || globalThis === void 0 ? void 0 : globalThis.document) === null || _a$1 === void 0 ? void 0 : _a$1.currentScript) === null || _b$1 === void 0 ? void 0 : _b$1.src) || getFileUrl(); function makeFontFace(name, source, descriptors = {}) { return new FontFace(name, `url(${source}.woff2) format('woff2')`, descriptors); } async function loadFonts(fontsDirectory, onError) { var _a; // If we're already loading the fonts, we're done. if (!isBrowser() || document.body.classList.contains('ML__fonts-loading')) return; // If the "mathlive-fonts.css" stylesheet is included in the <head> of the // page, it will include a `--ML__static-fonts` variable. // In that case, don't load the fonts dynamically const useStaticFonts = (_a = getComputedStyle(document.documentElement).getPropertyValue('--ML__static-fonts')) !== null && _a !== void 0 ? _a : false; if (useStaticFonts) return; if ('fonts' in document) { const fontFamilies = [ 'KaTeX_Main', 'KaTeX_Math', 'KaTeX_AMS', 'KaTeX_Caligraphic', 'KaTeX_Fraktur', 'KaTeX_SansSerif', 'KaTeX_Script', 'KaTeX_Typewriter', 'KaTeX_Size1', 'KaTeX_Size2', 'KaTeX_Size3', 'KaTeX_Size4', ]; const fontsInDocument = Array.from(document.fonts).map((f) => f.family); if (fontFamilies.every((x) => fontsInDocument.includes(x))) return; // Locate the `fonts` folder relative to the script URL const fontsFolder = resolveRelativeUrl(fontsDirectory !== null && fontsDirectory !== void 0 ? fontsDirectory : './fonts'); if (!fontsFolder) return; document.body.classList.add('ML__fonts-loading'); const fonts = [ ['KaTeX_Main-Regular'], ['KaTeX_Main-BoldItalic', { style: 'italic', weight: 'bold' }], ['KaTeX_Main-Bold', { weight: 'bold' }], ['KaTeX_Main-Italic', { style: 'italic' }], ['KaTeX_Math-Italic', { style: 'italic' }], ['KaTeX_Math-BoldItalic', { style: 'italic', weight: 'bold' }], ['KaTeX_AMS-Regular'], ['KaTeX_Caligraphic-Regular'], ['KaTeX_Caligraphic-Bold', { weight: 'bold' }], ['KaTeX_Fraktur-Regular'], ['KaTeX_Fraktur-Bold', { weight: 'bold' }], ['KaTeX_SansSerif-Regular', { style: 'italic' }], ['KaTeX_SansSerif-Bold', { weight: 'bold' }], ['KaTeX_SansSerif-Italic', { style: 'italic' }], ['KaTeX_Script-Regular'], ['KaTeX_Typewriter-Regular'], ['KaTeX_Size1-Regular'], ['KaTeX_Size2-Regular'], ['KaTeX_Size3-Regular'], ['KaTeX_Size4-Regular'], ].map((x) => makeFontFace(x[0].replace(/-[a-zA-Z]+$/, ''), fontsFolder + '/' + x[0], x[1])); try { const loadedFonts = (await Promise.all(fonts.map((x) => { try { return x.load(); } catch { } return undefined; }))); // Render them at the same time loadedFonts.forEach((font) => document.fonts.add(font)); } catch (error) { console.error(`The mathlive fonts could not be loaded from "${fontsFolder}"`, { cause: error }); if (typeof onError === 'function') onError({ code: 'font-not-found', arg: error }); } // Event if an error occur, give up and pretend the fonts are // loaded (displaying something is better than nothing) document.body.classList.remove('ML__fonts-loading'); } } function inject(element, css, id) { var _a, _b; throwIfNotInBrowser(); if (!css) return null; let root = (_a = element === null || element === void 0 ? void 0 : element.getRootNode()) !== null && _a !== void 0 ? _a : document === null || document === void 0 ? void 0 : document.head; if (!root) return null; if (root === document) root = document.head; const element_ = root.querySelector(`style[data-id="${id}"]`); if (element_) { const refCount = Number.parseFloat((_b = element_.getAttribute('data-refcount')) !== null && _b !== void 0 ? _b : '0'); element_.dataset.refcount = Number(refCount + 1).toString(); } else { // Make a new node holding the stylesheet const styleNode = document.createElement('style'); // StyleNode.setAttribute('media', 'screen') // styleNode.setAttribute('media', 'only screen and (max-width : 1024px)') styleNode.dataset.id = id; styleNode.dataset.refcount = '1'; styleNode.append(document.createTextNode(css)); root.appendChild(styleNode); } return { release: () => { var _a; const element_ = document.head.querySelector(`style[data-id="${id}"]`); if (element_) { const refCount = Number.parseFloat((_a = element_.getAttribute('data-refcount')) !== null && _a !== void 0 ? _a : '0'); if (refCount === 1) element_.remove(); else element_.dataset.refcount = Number(refCount - 1).toString(); } }, }; } var css_248z$4 = ".ML__sr-only{clip:rect(0,0,0,0);border:0;-webkit-clip-path:inset(50%);clip-path:inset(50%);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px}.ML__base,.ML__isInline{display:inline-block}.ML__base{border:0;box-sizing:content-box;cursor:text;font-family:inherit;font-style:inherit;font-weight:inherit;margin:0;outline:0;padding:0;position:relative;text-decoration:none;vertical-align:baseline;visibility:inherit;width:min-content}body.ML__fonts-loading .ML__base{visibility:hidden}.ML__strut,.ML__strut--bottom{display:inline-block;min-height:.5em}.ML__small-delim{font-family:KaTeX_Main}.ML__text{font-family:var(--text-font-family,system-ui,-apple-system,BlinkMacSystemFont,\"Segoe UI\",\"Roboto\",\"Oxygen\",\"Ubuntu\",\"Cantarell\",\"Fira Sans\",\"Droid Sans\",\"Helvetica Neue\",sans-serif);white-space:pre}.ML__cmr{font-family:KaTeX_Main;font-style:normal}.ML__mathit{font-family:KaTeX_Math;font-style:italic}.ML__mathbf{font-family:KaTeX_Main;font-weight:700}.lcGreek.ML__mathbf{font-family:KaTeX_Math;font-weight:400}.ML__mathbfit{font-family:KaTeX_Math;font-style:italic;font-weight:700}.ML__ams,.ML__bb{font-family:KaTeX_AMS}.ML__cal{font-family:KaTeX_Caligraphic}.ML__frak{font-family:KaTeX_Fraktur}.ML__tt{font-family:KaTeX_Typewriter}.ML__script{font-family:KaTeX_Script}.ML__sans{font-family:KaTeX_SansSerif}.ML__series_el,.ML__series_ul{font-weight:100}.ML__series_l{font-weight:200}.ML__series_sl{font-weight:300}.ML__series_sb{font-weight:500}.ML__bold,.ML__boldsymbol{font-weight:700}.ML__series_eb{font-weight:800}.ML__series_ub{font-weight:900}.ML__series_uc{font-stretch:ultra-condensed}.ML__series_ec{font-stretch:extra-condensed}.ML__series_c{font-stretch:condensed}.ML__series_sc{font-stretch:semi-condensed}.ML__series_sx{font-stretch:semi-expanded}.ML__series_x{font-stretch:expanded}.ML__series_ex{font-stretch:extra-expanded}.ML__series_ux{font-stretch:ultra-expanded}.ML__it{font-style:italic}.ML__shape_ol{-webkit-text-stroke:1px #000;text-stroke:1px #000;color:transparent}.ML__shape_sc{font-variant:small-caps}.ML__shape_sl{font-style:oblique}.ML__emph{color:#bc2612}.ML__emph .ML__emph{color:#0c7f99}.ML__highlight{background:#edd1b0;color:#007cb2}.ML__center{text-align:center}.ML__frac-line{min-height:1px;width:100%}.ML__frac-line:after{background:currentColor;box-sizing:content-box;content:\"\";display:block;margin-top:-.04em;min-height:.04em;transform:translate(0)}.ML__sqrt,.ML__sqrt-sign{display:inline-block}.ML__sqrt-sign{font-family:KaTeX_Main;position:relative}.ML__sqrt-line{display:inline-block;height:.04em;width:100%}.ML__sqrt-line:before{background:currentColor;content:\"\";display:block;margin-top:-.04em;min-height:.04em;transform:translate(0)}.ML__sqrt-line:after{border-bottom-width:1px;content:\" \";display:block;margin-top:-.1em}.ML__sqrt-index{margin-left:.27777778em;margin-right:-.55555556em}.ML__delim-size1{font-family:KaTeX_Size1}.ML__delim-size2{font-family:KaTeX_Size2}.ML__delim-size3{font-family:KaTeX_Size3}.ML__delim-size4{font-family:KaTeX_Size4}.ML__delim-mult .delim-size1>span{font-family:KaTeX_Size1}.ML__delim-mult .delim-size4>span{font-family:KaTeX_Size4}.ML__accent-body>span{font-family:KaTeX_Main;width:0}.ML__accent-vec>span{left:.24em;position:relative}.ML__mathlive{text-rendering:auto;word-wrap:normal;direction:ltr;display:inline-block;font-family:KaTeX_Main,Times New Roman,serif;font-size-adjust:none;font-stretch:normal;font-style:normal;font-variant-caps:normal;letter-spacing:normal;line-height:1.2;text-align:left;text-indent:0;text-shadow:none;-webkit-user-select:none;user-select:none;white-space:nowrap;width:min-content;word-spacing:normal}.ML__mathlive .style-wrap{position:relative}.ML__mathlive .left-right,.ML__mathlive .mfrac{display:inline-block}.ML__mathlive .vlist-t{border-collapse:collapse;display:inline-table;table-layout:fixed}.ML__mathlive .vlist-r{display:table-row}.ML__mathlive .vlist{display:table-cell;position:relative;vertical-align:bottom}.ML__mathlive .vlist>span{display:block;height:0;position:relative}.ML__mathlive .vlist>span>span{display:inline-block}.ML__mathlive .vlist>span>.pstrut{overflow:hidden;width:0}.ML__mathlive .vlist-t2{margin-right:-2px}.ML__mathlive .vlist-s{display:table-cell;font-size:1px;min-width:2px;vertical-align:bottom;width:2px}.ML__mathlive .msubsup{text-align:left}.ML__mathlive .negativethinspace{display:inline-block;margin-left:-.16667em}.ML__mathlive .thinspace{display:inline-block;width:.16667em}.ML__mathlive .mediumspace{display:inline-block;width:.22222em}.ML__mathlive .thickspace{display:inline-block;width:.27778em}.ML__mathlive .enspace{display:inline-block;width:.5em}.ML__mathlive .quad{display:inline-block;width:1em}.ML__mathlive .qquad{display:inline-block;width:2em}.ML__mathlive .llap,.ML__mathlive .rlap{display:inline-block;position:relative;width:0}.ML__mathlive .llap>.inner,.ML__mathlive .rlap>.inner{position:absolute}.ML__mathlive .llap>.fix,.ML__mathlive .rlap>.fix{display:inline-block}.ML__mathlive .llap>.inner{right:0}.ML__mathlive .rlap>.inner{left:0}.ML__mathlive .rule{border:0 solid;box-sizing:border-box;display:inline-block;position:relative}.ML__mathlive .overline .overline-line,.ML__mathlive .underline .underline-line{width:100%}.ML__mathlive .overline .overline-line:before,.ML__mathlive .underline .underline-line:before{border-bottom-style:solid;border-bottom-width:.04em;content:\"\";display:block}.ML__mathlive .overline .overline-line:after,.ML__mathlive .underline .underline-line:after{border-bottom-style:solid;border-bottom-width:.04em;content:\"\";display:block;margin-top:-1px;min-height:thin}.ML__mathlive .stretchy{display:block;left:0;overflow:hidden;position:absolute;width:100%}.ML__mathlive .stretchy:after,.ML__mathlive .stretchy:before{content:\"\"}.ML__mathlive .stretchy svg{fill:currentColor;stroke:currentColor;fill-rule:nonzero;fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:block;height:inherit;position:absolute;width:100%}.ML__mathlive .slice-1-of-2{left:0}.ML__mathlive .slice-1-of-2,.ML__mathlive .slice-2-of-2{display:inline-flex;overflow:hidden;position:absolute;width:50.2%}.ML__mathlive .slice-2-of-2{right:0}.ML__mathlive .slice-1-of-3{display:inline-flex;left:0;overflow:hidden;position:absolute;width:25.1%}.ML__mathlive .slice-2-of-3{display:inline-flex;left:25%;overflow:hidden;position:absolute;width:50%}.ML__mathlive .slice-3-of-3{display:inline-flex;overflow:hidden;position:absolute;right:0;width:25.1%}.ML__mathlive .slice-1-of-1{display:inline-flex;left:0;overflow:hidden;position:absolute;width:100%}.ML__mathlive .nulldelimiter{display:inline-block;width:.12em}.ML__mathlive .op-group{display:inline-block}.ML__mathlive .op-symbol{position:relative}.ML__mathlive .op-symbol.small-op{font-family:KaTeX_Size1}.ML__mathlive .op-symbol.large-op{font-family:KaTeX_Size2}.ML__mathlive .accent>.vlist>span{text-align:center}.ML__mathlive .mtable .vertical-separator{box-sizing:border-box;display:inline-block;min-width:1px}.ML__mathlive .mtable .arraycolsep{display:inline-block}.ML__mathlive .mtable .col-align-m>.vlist-t{text-align:center}.ML__mathlive .mtable .col-align-c>.vlist-t{text-align:center}.ML__mathlive .mtable .col-align-l>.vlist-t{text-align:left}.ML__mathlive .mtable .col-align-r>.vlist-t{text-align:right}.ML__error{background-image:radial-gradient(ellipse at center,#cc0041,transparent 70%);background-position:0 98%;background-repeat:repeat-x;background-size:3px 3px}.ML__composition{background:#fff1c2;color:#000;-webkit-text-decoration:underline var(--caret,var(--ML__caret));text-decoration:underline var(--caret,var(--ML__caret))}@media (prefers-color-scheme:dark){.ML__composition{background:#69571c;color:#fff}}.ML__placeholder{color:var(--caret,var(--ML__caret));font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;padding-left:.4ex;padding-right:.4ex}.ML__placeholdercontainer{display:none}.ML__isReadOnly .ML__placeholdercontainer{display:block}"; /** * Return an array of potential shortcuts */ function countInlineShortcutsStartingWith(s, config) { let count = 0; for (const key of Object.keys(config.inlineShortcuts)) if (key.startsWith(s)) count += 1; return count; } /** * * @param siblings atoms preceding this potential shortcut */ function validateShortcut(siblings, shortcut) { if (!shortcut) return ''; // If it's a simple shortcut (no conditional), it's always valid if (typeof shortcut === 'string') return shortcut; // If we have no context, we assume all the shortcuts are valid if (!siblings) return shortcut.value; let nothing = false; let letter = false; let digit = false; let isFunction = false; let frac = false; let surd = false; let binop = false; let relop = false; let operator = false; let punct = false; let array = false; let openfence = false; let closefence = false; let text = false; let space = false; let sibling = siblings[siblings.length - 1]; let index = siblings.length - 1; while (sibling && /msubsup|placeholder/.test(sibling.type)) { index -= 1; sibling = siblings[index]; } nothing = !sibling || sibling.type === 'first'; // Start of a group if (sibling) { if (shortcut.mode !== undefined && sibling.mode !== shortcut.mode) return ''; text = sibling.mode === 'text'; letter = !text && sibling.type === 'mord' && LETTER.test(sibling.value); digit = !text && sibling.type === 'mord' && /\d+$/.test(sibling.value); isFunction = !text && sibling.isFunction; frac = sibling.type === 'genfrac'; surd = sibling.type === 'surd'; binop = sibling.type === 'mbin'; relop = sibling.type === 'mrel'; operator = sibling.type === 'mop'; punct = sibling.type === 'mpunct' || sibling.type === 'minner'; array = sibling.type === 'array'; openfence = sibling.type === 'mopen'; closefence = sibling.type === 'mclose' || sibling.type === 'leftright'; space = sibling.type === 'space'; } if (shortcut.after !== undefined) { // If this is a conditional shortcut, consider the conditions now if ((shortcut.after.includes('nothing') && nothing) || (shortcut.after.includes('letter') && letter) || (shortcut.after.includes('digit') && digit) || (shortcut.after.includes('function') && isFunction) || (shortcut.after.includes('frac') && frac) || (shortcut.after.includes('surd') && surd) || (shortcut.after.includes('binop') && binop) || (shortcut.after.includes('relop') && relop) || (shortcut.after.includes('operator') && operator) || (shortcut.after.includes('punct') && punct) || (shortcut.after.includes('array') && array) || (shortcut.after.includes('openfence') && openfence) || (shortcut.after.includes('closefence') && closefence) || (shortcut.after.includes('text') && text) || (shortcut.after.includes('space') && space)) return shortcut.value; return ''; } return shortcut.value; } /** * * @param context - atoms preceding the candidate, potentially used * to reduce which shortcuts are applicable. If 'null', no restrictions are * applied. * @param s - candidate inline shortcuts (e.g. `"pi"`) * @return A replacement string matching the shortcut (e.g. `"\pi"`) */ function getInlineShortcut(context, s, shortcuts) { if (!shortcuts) return ''; return validateShortcut(context, shortcuts[s]); } /** * These shortcut strings are replaced with the corresponding LaTeX expression * without requiring an escape sequence or command. */ const INLINE_SHORTCUTS = { '&': '\\&', '%': '\\%', // Primes "''": { mode: 'math', value: '^{\\doubleprime}' }, // Greek letters 'alpha': '\\alpha', 'delta': '\\delta', 'Delta': '\\Delta', 'pi': { mode: 'math', value: '\\pi' }, 'pi ': { mode: 'text', value: '\\pi ' }, 'Pi': { mode: 'math', value: '\\Pi' }, 'theta': '\\theta', 'Theta': '\\Theta', // Letter-like 'ii': { after: 'nothing+digit+function+frac+surd+binop+relop+punct+array+openfence+closefence+space+text', value: '\\imaginaryI', }, 'jj': { after: 'nothing+digit+function+frac+surd+binop+relop+punct+array+openfence+closefence+space+text', value: '\\imaginaryJ', }, 'ee': { mode: 'math', after: 'nothing+digit+function+frac+surd+binop+relop+punct+array+openfence+closefence+space+text', value: '\\exponentialE', }, 'nabla': { mode: 'math', value: '\\nabla' }, 'grad': { mode: 'math', value: '\\nabla' }, 'del': { mode: 'math', value: '\\partial' }, 'infty': { mode: 'math', value: '\\infty' }, '\u221E': '\\infty', // '∞': '\\infty', // '∞': '\\infty', 'oo': { mode: 'math', after: 'nothing+digit+frac+surd+binop+relop+punct+array+openfence+closefence+space', value: '\\infty', }, // Big operators '∑': { mode: 'math', value: '\\sum' }, 'sum': { mode: 'math', value: '\\sum_{#?}^{#?}' }, 'int': { mode: 'math', value: '\\int_{#?}^{#?}' }, 'prod': { mode: 'math', value: '\\prod_{#?}^{#?}' }, 'sqrt': { mode: 'math', value: '\\sqrt{#?}' }, // '∫': '\\int', // There's a alt-B command for this '∆': { mode: 'math', value: '\\differentialD' }, '∂': { mode: 'math', value: '\\differentialD' }, // Functions 'arcsin': { mode: 'math', value: '\\arcsin' }, 'arccos': { mode: 'math', value: '\\arccos' }, 'arctan': { mode: 'math', value: '\\arctan' }, 'sin': { mode: 'math', value: '\\sin' }, 'sinh': { mode: 'math', value: '\\sinh' }, 'cos': { mode: 'math', value: '\\cos' }, 'cosh': { mode: 'math', value: '\\cosh' }, 'tan': { mode: 'math', value: '\\tan' }, 'tanh': { mode: 'math', value: '\\tanh' }, 'sec': { mode: 'math', value: '\\sec' }, 'csc': { mode: 'math', value: '\\csc' }, 'cot': { mode: 'math', value: '\\cot' }, 'log': { mode: 'math', value: '\\log' }, 'ln': { mode: 'math', value: '\\ln' }, 'exp': { mode: 'math', value: '\\exp' }, 'lim': { mode: 'math', value: '\\lim_{#?}' }, // Differentials // According to ISO31/XI (ISO 80000-2), differentials should be upright 'dx': { mode: 'math', after: 'nothing+digit+function+frac+surd+binop+relop+punct+array+openfence+closefence+space+text', value: '\\differentialD x', }, 'dy': { mode: 'math', after: 'nothing+digit+function+frac+surd+binop+relop+punct+array+openfence+closefence+space+text', value: '\\differentialD y', }, 'dt': { mode: 'math', after: 'nothing+digit+function+frac+surd+binop+relop+punct+array+openfence+closefence+space+text', value: '\\differentialD t', }, // Logic 'AA': { mode: 'math', value: '\\forall' }, 'EE': { mode: 'math', value: '\\exists' }, '!EE': { mode: 'math', value: '\\nexists' }, '&&': { mode: 'math', value: '\\land' }, // The shortcut for the greek letter "xi" is interfering with "x in" 'xin': { mode: 'math', after: 'nothing+text+relop+punct+openfence+space', value: 'x \\in', }, 'in': { mode: 'math', after: 'nothing+letter+closefence', value: '\\in', }, '!in': { mode: 'math', value: '\\notin' }, // Sets 'NN': { mode: 'math', value: '\\mathbb{N}' }, 'ZZ': { mode: 'math', value: '\\Z' }, 'QQ': { mode: 'math', value: '\\Q' }, 'RR': { mode: 'math', value: '\\R' }, 'CC': { mode: 'math', value: '\\C' }, // Operators 'xx': { mode: 'math', value: '\\times' }, '+-': { mode: 'math', value: '\\pm' }, // Relational operators '≠': { mode: 'math', value: '\\ne' }, '!=': { mode: 'math', value: '\\ne' }, '\u2265': { mode: 'math', value: '\\ge' }, '>=': { mode: 'math', value: '\\ge' }, '\u2264': { mode: 'math', value: '\\le' }, '<=': { mode: 'math', value: '\\le' }, '<<': { mode: 'math', value: '\\ll' }, '>>': { mode: 'math', value: '\\gg' }, '~~': { mode: 'math', value: '\\approx' }, // More operators '≈': { mode: 'math', value: '\\approx' }, '?=': { mode: 'math', value: '\\questeq' }, '÷': { mode: 'math', value: '\\div' }, '¬': { mode: 'math', value: '\\neg' }, ':=': { mode: 'math', value: '\\coloneq' }, '::': { mode: 'math', value: '\\Colon' }, // Fences '(:': { mode: 'math', value: '\\langle' }, ':)': { mode: 'math', value: '\\rangle' }, // More Greek letters 'beta': '\\beta', 'chi': '\\chi', 'epsilon': '\\epsilon', 'varepsilon': '\\varepsilon', 'eta': { mode: 'math', after: 'nothing+digit+function+frac+surd+binop+relop+punct+array+openfence+closefence+space+text', value: '\\eta', }, 'eta ': { mode: 'text', after: 'nothing+digit+function+frac+surd+binop+relop+punct+array+openfence+closefence+space+text', value: '\\eta ', }, 'gamma': '\\gamma', 'Gamma': '\\Gamma', 'iota': '\\iota', 'kappa': '\\kappa', 'lambda': '\\lambda', 'Lambda': '\\Lambda', 'mu': { mode: 'math', after: 'nothing+digit+function+frac+surd+binop+relop+punct+array+openfence+closefence+space+text', value: '\\mu', }, 'mu ': { mode: 'text', after: 'nothing+digit+function+frac+surd+binop+relop+punct+array+openfence+closefence+space+text', value: '\\mu ', }, 'nu': { mode: 'math', after: 'nothing+digit+function+frac+surd+binop+relop+punct+array+openfence+closefence+space+text', value: '\\nu', }, 'nu ': { mode: 'text', after: 'nothing+digit+function+frac+surd+binop+relop+punct+array+openfence+closefence+space+text', value: '\\nu ', }, 'µ': '\\mu', 'phi': { after: 'nothing+digit+function+frac+surd+binop+relop+punct+array+openfence+closefence+space+text', value: '\\phi', }, 'Phi': { after: 'nothing+digit+function+frac+surd+binop+relop+punct+array+openfence+closefence+space+text', value: '\\Phi', }, 'varphi': '\\varphi', 'psi': { after: 'nothing+digit+function+frac+surd+binop+relop+punct+array+openfence+closefence+space+text', value: '\\psi', }, 'Psi': { after: 'nothing+digit+function+frac+surd+binop+relop+punct+array+openfence+closefence+space+text', value: '\\Psi', }, 'rho': { after: 'nothing+digit+function+frac+surd+binop+relop+punct+array+openfence+closefence+space+text', value: '\\rho', }, 'sigma': '\\sigma', 'Sigma': '\\Sigma', 'tau': { after: 'nothing+digit+function+frac+surd+binop+relop+punct+array+openfence+closefence+space+text', value: '\\tau', }, 'vartheta': '\\vartheta', 'upsilon': '\\upsilon', 'xi': { after: 'nothing+digit+function+frac+surd+binop+relop+punct+array+openfence+closefence+space+text', value: '\\xi', }, 'Xi': { after: 'nothing+digit+function+frac+surd+binop+relop+punct+array+openfence+closefence+space+text', value: '\\Xi', }, 'zeta': '\\zeta', 'omega': '\\omega', 'Omega': '\\Omega', 'Ω': '\\omega', // More Logic 'forall': { mode: 'math', value: '\\forall' }, 'exists': { mode: 'math', value: '\\exists', }, '!exists': { mode: 'math', value: '\\nexists', }, ':.': { mode: 'math', value: '\\therefore', }, // MORE FUNCTIONS // 'arg': '\\arg', 'liminf': '\\liminf_{#?}', 'limsup': '\\limsup_{#?}', 'argmin': '\\operatorname*{arg~min}_{#?}', 'argmax': '\\operatorname*{arg~max}_{#?}', 'det': '\\det', 'mod': { mode: 'math', value: '\\mod', }, 'max': { mode: 'math', value: '\\max', }, 'min': { mode: 'math', value: '\\min', }, 'erf': '\\operatorname{erf}', 'erfc': '\\operatorname{erfc}', 'bessel': { mode: 'math', value: '\\operatorname{bessel}', }, 'mean': { mode: 'math', value: '\\operatorname{mean}', }, 'median': { mode: 'math', value: '\\operatorname{median}', }, 'fft': { mode: 'math', value: '\\operatorname{fft}', }, 'lcm': { mode: 'math', value: '\\operatorname{lcm}', }, 'gcd': { mode: 'math', value: '\\operatorname{gcd}', }, 'randomReal': '\\operatorname{randomReal}', 'randomInteger': '\\operatorname{randomInteger}', 'Re': { mode: 'math', value: '\\operatorname{Re}', }, 'Im': { mode: 'math', value: '\\operatorname{Im}', }, // UNITS 'mm': { mode: 'math', after: 'nothing+digit+operator', value: '\\operatorname{mm}', // Millimeter }, 'cm': { mode: 'math', after: 'nothing+digit+operator', value: '\\operatorname{cm}', // Centimeter }, 'km': { mode: 'math', after: 'nothing+digit+operator', value: '\\operatorname{km}', // Kilometer }, 'kg': { mode: 'math', after: 'nothing+digit+operator', value: '\\operatorname{kg}', // Kilogram }, // '||': '\\lor', '...': '\\ldots', '+...': '+\\cdots', '-...': '-\\cdots', '->...': '\\to\\cdots', '->': '\\to', '|->': '\\mapsto', '-->': '\\longrightarrow', // '<-': '\\leftarrow', '<--': '\\longleftarrow', '=>': '\\Rightarrow', '==>': '\\Longrightarrow', // '<=': '\\Leftarrow', // CONFLICTS WITH LESS THAN OR EQUAL '<=>': '\\Leftrightarrow', '<->': '\\leftrightarrow', '(.)': '\\odot', '(+)': '\\oplus', '(/)': '\\oslash', '(*)': '\\otimes', '(-)': '\\ominus', // '(-)': '\\circleddash', '||': '\\Vert', '{': '\\{', '}': '\\}', '*': '\\cdot', /* // // ASCIIIMath // // Binary operation symbols '**': '\\ast', '***': '\\star', '//': '\\slash', '\\\\': '\\backslash', 'setminus': '\\backslash', '|><': '\\ltimes', '><|': '\\rtimes', '|><|': '\\bowtie', '-:': '\\div', 'divide': '\\div', '@': '\\circ', 'o+': '\\oplus', 'ox': '\\otimes', 'o.': '\\odot', '^^': '\\wedge', '^^^': '\\bigwedge', 'vv': '\\vee', 'vvv': '\\bigvee', 'nn': '\\cap', 'nnn': '\\bigcap', 'uu': '\\cup', 'uuu': '\\bigcup', // Binary relation symbols '-=': '\\equiv', '~=': '\\cong', 'lt': '<', 'lt=': '\\leq', 'gt': '>', 'gt=': '\\geq', '-<': '\\prec', '-lt': '\\prec', '-<=': '\\preceq', // '>-': '\\succ', '>-=': '\\succeq', 'prop': '\\propto', 'diamond': '\\diamond', 'square': '\\square', 'iff': '\\iff', 'sub': '\\subset', 'sup': '\\supset', 'sube': '\\subseteq', 'supe': '\\supseteq', 'uarr': '\\uparrow', 'darr': '\\downarrow', 'rarr': '\\rightarrow', 'rArr': '\\Rightarrow', 'larr': '\\leftarrow', 'lArr': '\\Leftarrow', 'harr': '\\leftrightarrow', 'hArr': '\\Leftrightarrow', 'aleph': '\\aleph', // Logic 'and': '\\land', 'or': '\\lor', 'not': '\\neg', '_|_': '\\bot', 'TT': '\\top', '|--': '\\vdash', '|==': '\\models', // Other functions '|__': '\\lfloor', '__|': '\\rfloor', '|~': '\\lceil', '~|': '\\rceil', // Arrows '>->': '\\rightarrowtail', '->>': '\\twoheadrightarrow', '>->>': '\\twoheadrightarrowtail' */ }; /** * Attempts to parse and interpret a string in an unknown format, possibly * ASCIIMath and return a canonical LaTeX string. * * The format recognized are one of these variations: * - ASCIIMath: Only supports a subset * (1/2x) * 1/2sin x -> \frac {1}{2}\sin x * 1/2sinx -> \frac {1}{2}\sin x * (1/2sin x (x^(2+1)) // Unbalanced parentheses * (1/2sin(x^(2+1)) -> \left(\frac {1}{2}\sin \left(x^{2+1}\right)\right) * alpha + (pi)/(4) -> \alpha +\frac {\pi }{4} * x=(-b +- sqrt(b^2 – 4ac))/(2a) * alpha/beta * sqrt2 + sqrtx + sqrt(1+a) + sqrt(1/2) * f(x) = x^2 "when" x >= 0 * AA n in QQ * AA x in RR "," |x| > 0 * AA x in RR "," abs(x) > 0 * * - UnicodeMath (generated by Microsoft Word): also only supports a subset * - See https://www.unicode.org/notes/tn28/UTN28-PlainTextMath-v3.1.pdf * √(3&x+1) * {a+b/c} * [a+b/c] * _a^b x * lim_(n->\infty) n * \iint_(a=0)^\infty a * * - "JavaScript LaTeX": a variant that is LaTeX, but with escaped backslashes * \\frac{1}{2} \\sin x */ function parseMathString(s, options) { var _a; let format = (_a = options === null || options === void 0 ? void 0 : options.format) !== null && _a !== void 0 ? _a : 'auto'; if (format === 'auto') [format, s] = inferFormat(s); if (format === 'ascii-math') { s = s.replace(/\u2061/gu, ''); // Remove function application s = s.replace(/\u3016/gu, '{'); // WHITE LENTICULAR BRACKET (grouping) s = s.replace(/\u3017/gu, '}'); // WHITE LENTICULAR BRACKET (grouping) s = s.replace(/([^\\])sinx/g, '$1\\sin x'); // Common typo s = s.replace(/([^\\])cosx/g, '$1\\cos x '); // Common typo s = s.replace(/\u2013/g, '-'); // EN-DASH, sometimes used as a minus sign return [ 'ascii-math', parseMathExpression(s, { inlineShortcuts: options === null || options === void 0 ? void 0 : options.inlineShortcuts }), ]; } return ['latex', s]; } function parseMathExpression(s, options) { var _a, _b, _c; if (!s) return ''; let done = false; let m; const inlineShortcuts = (_a = options.inlineShortcuts) !== null && _a !== void 0 ? _a : INLINE_SHORTCUTS; if (!done && (s.startsWith('^') || s.startsWith('_'))) { // Superscript and subscript m = parseMathArgument(s.slice(1), { inlineShortcuts, noWrap: true }); s = s[0] + '{' + m.match + '}'; s += parseMathExpression(m.rest, options); done = true; } if (!done) { m = s.match(/^(sqrt|\u221A)(.*)/); if (m) { // Square root m = parseMathArgument(m[2], { inlineShortcuts, noWrap: true }); const sqrtArgument = (_b = m.match) !== null && _b !== void 0 ? _b : '\\placeholder{}'; s = '\\sqrt{' + sqrtArgument + '}'; s += parseMathExpression(m.rest, options); done = true; } } if (!done) { m = s.match(/^(\\cbrt|\u221B)(.*)/); if (m) { // Cube root m = parseMathArgument(m[2], { inlineShortcuts, noWrap: true }); const sqrtArgument = (_c = m.match) !== null && _c !== void 0 ? _c : '\\placeholder{}'; s = '\\sqrt[3]{' + sqrtArgument + '}'; s += parseMathExpression(m.rest, options); done = true; } } if (!done) { m = s.match(/^abs(.*)/); if (m) { // Absolute value m = parseMathArgument(m[1], { inlineShortcuts, noWrap: true }); s = '\\left|' + m.match + '\\right|'; s += parseMathExpression(m.rest, options); done = true; } } if (!done) { m = s.match(/^["”“](.*?)["”“](.*)/); if (m) { // Quoted text s = '\\text{' + m[1] + '}'; s += parseMathExpression(m[2], options); done = true; } } if (!done) { m = s.match(/^([^a-zA-Z\(\{\[\_\^\\\s"]+)(.*)/); // A string of symbols... // Could be a binary or relational operator, etc... if (m) { s = paddedShortcut(m[1], inlineShortcuts); s += parseMathExpression(m[2], options); done = true; } } if (!done && /^([fgh])[^a-zA-Z]/.test(s)) { // This could be a function... m = parseMathArgument(s.slice(1), { inlineShortcuts, noWrap: true }); s = s[1] === '(' ? s[0] + '\\left(' + m.match + '\\right)' : s[0] + m.match; s += parseMathExpression(m.rest, options); done = true; } if (!done) { m = s.match(/^([a-zA-Z]+)(.*)/); if (m) { // Some alphabetical string... // Could be a function name (sin) or symbol name (alpha) s = paddedShortcut(m[1], inlineShortcuts); s += parseMathExpression(m[2], options); done = true; } } if (!done) { m = parseMathArgument(s, { inlineShortcuts, noWrap: true }); if (m.match && m.rest[0] === '/') { // Fraction const m2 = parseMathArgument(m.rest.slice(1), { inlineShortcuts, noWrap: true, }); if (m2.match) { s = '\\frac{' + m.match + '}{' + m2.match + '}' + parseMathExpression(m2.rest, options); } done = true; } else if (m.match) { s = s.startsWith('(') ? '\\left(' + m.match + '\\right)' + parseMathExpression(m.rest, options) : m.match + parseMathExpression(m.rest, options); done = true; } } if (!done) { m = s.match(/^(\s+)(.*)$/); // Whitespace if (m) { s = ' ' + parseMathExpression(m[2], options); done = true; } } return s; } /** * Parse a math argument, as defined by ASCIIMath and UnicodeMath: * - Either an expression fenced in (), {} or [] * - a number (- sign, digits, decimal point, digits) * - a single [a-zA-Z] letter (an identifier) * - a multi-letter shortcut (e.g., pi) * - a LaTeX command (\pi) (for UnicodeMath) * @return * - match: the parsed (and converted) portion of the string that is an argument * - rest: the raw, unconverted, rest of the string */ function parseMathArgument(s, options) { let match = ''; s = s.trim(); let rest = s; let lFence = s.charAt(0); let rFence = { '(': ')', '{': '}', '[': ']' }[lFence]; if (rFence) { // It's a fence let level = 1; let i = 1; while (i < s.length && level > 0) { if (s[i] === lFence) level++; if (s[i] === rFence) level--; i++; } if (level === 0) { // We've found the matching closing fence if (options.noWrap && lFence === '(') match = parseMathExpression(s.substring(1, i - 1), options); else { if (lFence === '{' && rFence === '}') { lFence = '\\{'; rFence = '\\}'; } match = '\\left' + lFence + parseMathExpression(s.substring(1, i - 1), options) + '\\right' + rFence; } rest = s.slice(Math.max(0, i)); } else { // Unbalanced fence... match = s.substring(1, i); rest = ''; } } else { let m = s.match(/^([a-zA-Z]+)/); if (m) { // It's a string of letter, maybe a shortcut let shortcut = getInlineShortcut(null, s, options.inlineShortcuts); if (shortcut) { shortcut = shortcut.replace('_{#?}', ''); shortcut = shortcut.replace('^{#?}', ''); return { match: shortcut, rest: s.slice(shortcut.length) }; } } m = s.match(/^([a-zA-Z])/); if (m) { // It's a single letter return { match: m[1], rest: s.slice(1) }; } m = s.match(/^(-)?\d+(\.\d*)?/); if (m) { // It's a number return { match: m[0], rest: s.slice(m[0].length) }; } if (!/^\\(left|right)/.test(s)) { // It's a LaTeX command (but not a \left\right) m = s.match(/^(\\[a-zA-Z]+)/); if (m) { rest = s.slice(m[1].length); match = m[1]; } } } return { match, rest }; } function paddedShortcut(s, shortcuts) { let result = getInlineShortcut(null, s, shortcuts); if (result) { result = result.replace('_{#?}', ''); result = result.replace('^{#?}', ''); result += ' '; } else result = s; return result; } const MODE_SHIFT_COMMANDS = [ ['\\[', '\\]'], ['\\(', '\\)'], ['$$', '$$'], ['$', '$'], ['\\begin{math}', '\\end{math}'], ['\\begin{displaymath}', '\\end{displaymath}'], ['\\begin{equation}', '\\end{equation}'], ['\\begin{equation*}', '\\end{equation*}'], ]; function trimModeShiftCommand(s) { const trimedString = s.trim(); for (const mode of MODE_SHIFT_COMMANDS) { if (trimedString.startsWith(mode[0]) && trimedString.endsWith(mode[1])) { return [ true, trimedString.substring(mode[0].length, trimedString.length - mode[1].length), ]; } } return [false, s]; } function inferFormat(s) { s = s.trim(); // Assume LaTeX if a single char if (s.length <= 1) return ['latex', s]; // This is not explicitly ASCIIMath. Try to infer if this is LaTex... let hasLatexModeShiftCommand; [hasLatexModeShiftCommand, s] = trimModeShiftCommand(s); if (hasLatexModeShiftCommand) return ['latex', s]; // The backtick is the default delimiter used by MathJAX for // ASCII Math if (s.startsWith('`') && s.endsWith('`')) { s = s.substring(1, s.length - 1); return ['ascii-math', s]; } if (s.includes('\\')) { // If the string includes a '\' it's probably a LaTeX string // (that's not completely true, it could be a UnicodeMath string, since // UnicodeMath supports some LaTeX commands. However, we need to pick // one in order to correctly interpret {} (which are argument delimiters // in LaTeX, and are fences in UnicodeMath) return ['latex', s]; } if (/\$.+\$/.test(s)) { // If there's a pair of $ (or possibly $$) signs, assume it's a string // such as `if $x<0$ then`, in which case it's LaTeX, but with some text // around it return ['latex', `\\text{${s}}`]; } return [undefined, s]; } function hashCode(s) { let hash = 0; for (let i = 0; i < s.length; i++) hash = (Math.imul(31, hash) + s.charCodeAt(i)) | 0; // | 0 to convert to 32-bit int return Math.abs(hash); } /* eslint no-console:0 */ function findEndOfMath(delimiter, text, startIndex) { // Adapted from // https://github.com/Khan/perseus/blob/master/src/perseus-markdown.jsx let index = startIndex; let braceLevel = 0; const delimLength = delimiter.length; while (index < text.length) { const character = text[index]; if (braceLevel <= 0 && text.slice(index, index + delimLength) === delimiter) return index; if (character === '\\') index++; else if (character === '{') braceLevel++; else if (character === '}') braceLevel--; index++; } return -1; } function splitAtDelimiters(startData, leftDelim, rightDelim, mathstyle, format = 'latex') { const finalData = []; for (const startDatum of startData) { if (startDatum.type === 'text') { const text = startDatum.data; let lookingForLeft = true; let currIndex = 0; let nextIndex; nextIndex = text.indexOf(leftDelim); if (nextIndex !== -1) { currIndex = nextIndex; if (currIndex > 0) { finalData.push({ type: 'text', data: text.slice(0, currIndex), }); } lookingForLeft = false; } let done = false; while (!done) { if (lookingForLeft) { nextIndex = text.indexOf(leftDelim, currIndex); if (nextIndex === -1) { done = true; break; } if (currIndex !== nextIndex) { finalData.push({ type: 'text', data: text.slice(currIndex, nextIndex), }); } currIndex = nextIndex; } else { nextIndex = findEndOfMath(rightDelim, text, currIndex + leftDelim.length); if (nextIndex === -1) { done = true; break; } let formula = text.slice(currIndex + leftDelim.length, nextIndex); if (format === 'ascii-math') [, formula] = parseMathString(formula, { format: 'ascii-math' }); finalData.push({ type: 'math', data: formula, rawData: text.slice(currIndex, nextIndex + rightDelim.length), mathstyle, }); currIndex = nextIndex + rightDelim.length; } lookingForLeft = !lookingForLeft; } if (currIndex < text.length) { finalData.push({ type: 'text', data: text.slice(currIndex), }); } } else finalData.push(startDatum); } return finalData; } function splitWithDelimiters(text, texDelimiters, mathAsciiDelimiters) { let data = [{ type: 'text', data: text }]; if (texDelimiters === null || texDelimiters === void 0 ? void 0 : texDelimiters.inline) { texDelimiters.inline.forEach(([openDelim, closeDelim]) => { data = splitAtDelimiters(data, openDelim, closeDelim, 'textstyle'); }); } if (texDelimiters === null || texDelimiters === void 0 ? void 0 : texDelimiters.display) { texDelimiters.display.forEach(([openDelim, closeDelim]) => { data = splitAtDelimiters(data, openDelim, closeDelim, 'displaystyle'); }); } if (mathAsciiDelimiters === null || mathAsciiDelimiters === void 0 ? void 0 : mathAsciiDelimiters.inline) { mathAsciiDelimiters.inline.forEach(([openDelim, closeDelim]) => { data = splitAtDelimiters(data, openDelim, closeDelim, 'textstyle', 'ascii-math'); }); } if (mathAsciiDelimiters === null || mathAsciiDelimiters === void 0 ? void 0 : mathAsciiDelimiters.display) { mathAsciiDelimiters.display.forEach(([openDelim, closeDelim]) => { data = splitAtDelimiters(data, openDelim, closeDelim, 'displaystyle', 'ascii-math'); }); } return data; } function createMathMLNode(latex, options) { throwIfNotInBrowser(); // Create a node for AT (Assistive Technology, e.g. screen reader) to speak, etc. // This node has a style that makes it be invisible to display but is seen by AT const span = document.createElement('span'); try { const html = "<math xmlns='http://www.w3.org/1998/Math/MathML'>" + options.renderToMathML(latex, options) + '</math>'; span.innerHTML = options.createHTML ? options.createHTML(html) : html; } catch (error) { console.error("Could not convert '" + latex + "' to MathML with ", error); span.textContent = latex; } span.className = 'ML__sr-only'; return span; } function createMarkupNode(text, options, mathstyle, createNodeOnFailure) { throwIfNotInBrowser(); // Create a node for displaying math. // This is slightly ugly because in the case of failure to create the markup, // sometimes a text node is desired and sometimes not. // 'createTextNodeOnFailure' controls this and null is returned when no node is created. // This node is made invisible to AT (screen readers) try { const html = options.renderToMarkup(text, { mathstyle: mathstyle, format: 'html', macros: options.macros, }); const element = document.createElement(mathstyle === 'displaystyle' ? 'div' : 'span'); element.setAttribute('aria-hidden', 'true'); element.innerHTML = options.createHTML ? options.createHTML(html) : html; return element; } catch (error) { console.error("Could not parse'" + text + "' with ", error); if (createNodeOnFailure) return document.createTextNode(text); } return null; } function createAccessibleMarkupPair(latex, mathstyle, options, createNodeOnFailure) { var _a; // Create a math node (a span with an accessible component and a visual component) // If there is an error in parsing the latex, 'createNodeOnFailure' controls whether // 'null' is returned or an accessible node with the text used. const markupNode = createMarkupNode(latex, options, mathstyle ? mathstyle : 'displaystyle', createNodeOnFailure); const accessibleContent = (_a = options.renderAccessibleContent) !== null && _a !== void 0 ? _a : ''; if (markupNode && /\b(mathml|speakable-text)\b/i.test(accessibleContent)) { throwIfNotInBrowser(); const fragment = document.createElement('span'); if (/\bmathml\b/i.test(accessibleContent) && options.renderToMathML) fragment.append(createMathMLNode(latex, options)); if (/\bspeakable-text\b/i.test(accessibleContent) && options.renderToSpeakableText) { const span = document.createElement('span'); const html = options.renderToSpeakableText(latex, options); span.innerHTML = options.createHTML ? options.createHTML(html) : html; span.className = 'ML__sr-only'; fragment.append(span); } fragment.append(markupNode); return fragment; } return markupNode; } function scanText$1(text, options) { var _a, _b, _c; throwIfNotInBrowser(); // If the text starts with '\begin'... (this is a MathJAX behavior) let fragment = null; if (((_a = options.TeX) === null || _a === void 0 ? void 0 : _a.processEnvironments) && /^\s*\\begin/.test(text)) { fragment = document.createDocumentFragment(); const node = createAccessibleMarkupPair(text, '', options, true); if (node) fragment.appendChild(node); } else { if (!text.trim()) return null; const data = splitWithDelimiters(text, (_b = options.TeX) === null || _b === void 0 ? void 0 : _b.delimiters, (_c = options.asciiMath) === null || _c === void 0 ? void 0 : _c.delimiters); if (data.length === 1 && data[0].type === 'text') { // This text contains no math. No need to continue processing return null; } fragment = document.createDocumentFragment(); for (const datum of data) { if (datum.type === 'text') fragment.appendChild(document.createTextNode(datum.data)); else { const node = createAccessibleMarkupPair(datum.data, datum.mathstyle === 'textstyle' ? 'textstyle' : 'displaystyle', options, true); if (node) fragment.appendChild(node); } } } return fragment; } function scanElement(element, options) { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o; if (element.childNodes.length === 1 && element.childNodes[0].nodeType === 3) { // This is a node with textual content only. Perhaps an opportunity // to simplify and avoid creating extra nested elements... const text = (_a = element.childNodes[0].textContent) !== null && _a !== void 0 ? _a : ''; if (((_b = options.TeX) === null || _b === void 0 ? void 0 : _b.processEnvironments) && /^\s*\\begin/.test(text)) { element.textContent = ''; const node = createAccessibleMarkupPair(text, '', options, true); if (node) element.append(node); return; } const data = splitWithDelimiters(text, (_c = options.TeX) === null || _c === void 0 ? void 0 : _c.delimiters, (_d = options.asciiMath) === null || _d === void 0 ? void 0 : _d.delimiters); if (data.length === 1 && data[0].type === 'math') { // The entire content is a math expression: we can replace the content // with the latex markup without creating additional wrappers. element.textContent = ''; const node = createAccessibleMarkupPair(data[0].data, data[0].mathstyle === 'textstyle' ? 'textstyle' : 'displaystyle', options, true); if (node) element.append(node); return; } if (data.length === 1 && data[0].type === 'text') { // This element only contained text with no math. No need to // do anything. return; } } // Iterate backward, as we will be replacing childNode with a documentfragment // which may insert multiple nodes (one for the accessible markup, one for // the formula) for (let i = element.childNodes.length - 1; i >= 0; i--) { const childNode = element.childNodes[i]; if (childNode.nodeType === 3) { // A text node // Look for math mode delimiters inside the text const frag = scanText$1((_e = childNode.textContent) !== null && _e !== void 0 ? _e : '', options); if (frag) { i += frag.childNodes.length - 1; childNode.replaceWith(frag); } } else if (childNode.nodeType === 1) { const el = childNode; // An element node const tag = childNode.nodeName.toLowerCase(); if (tag === 'script') { const scriptNode = childNode; if ((_f = options.processScriptTypePattern) === null || _f === void 0 ? void 0 : _f.test(scriptNode.type)) { let style = 'displaystyle'; for (const l of scriptNode.type.split(';')) { const v = l.split('='); if (v[0].toLowerCase() === 'mode') { style = v[1].toLowerCase() === 'display' ? 'displaystyle' : 'textstyle'; } } const span = createAccessibleMarkupPair((_g = scriptNode.textContent) !== null && _g !== void 0 ? _g : '', style, options, true); if (span) scriptNode.parentNode.replaceChild(span, scriptNode); } } else { // Element node // console.assert(childNode.className !== 'formula'); const shouldRender = ((_j = (_h = options.processClassPattern) === null || _h === void 0 ? void 0 : _h.test(el.className)) !== null && _j !== void 0 ? _j : false) || !(((_l = (_k = options.skipTags) === null || _k === void 0 ? void 0 : _k.includes(tag)) !== null && _l !== void 0 ? _l : false) || ((_o = (_m = options.ignoreClassPattern) === null || _m === void 0 ? void 0 : _m.test(el.className)) !== null && _o !== void 0 ? _o : false)); if (shouldRender) { if (element.childNodes.length === 1 && element.childNodes[0].nodeType === 3) { const formula = element.textContent; element.textContent = ''; const node = createAccessibleMarkupPair(formula !== null && formula !== void 0 ? formula : '', 'displaystyle', options, true); if (node) element.append(node); } else scanElement(el, options); } } } // Otherwise, it's something else, and ignore it. } } const DEFAULT_AUTO_RENDER_OPTIONS = { // Optional namespace for the `data-` attributes. namespace: '', // Name of tags whose content will not be scanned for math delimiters skipTags: [ 'math-field', 'noscript', 'style', 'textarea', 'pre', 'code', 'annotation', 'annotation-xml', ], // <script> tags of the following types will be processed. Others, ignored. processScriptType: 'math/tex', // Regex pattern of the class name of elements whose contents should not // be processed ignoreClass: 'tex2jax_ignore', // Regex pattern of the class name of elements whose contents should // be processed when they appear inside ones that are ignored. processClass: 'tex2jax_process', // Indicate the format to use to render accessible content renderAccessibleContent: 'mathml', asciiMath: { delimiters: { display: [ ['`', '`'], // ASCII Math delimiters ], }, }, TeX: { processEnvironments: true, delimiters: { inline: [['\\(', '\\)']], display: [ ['$$', '$$'], ['\\[', '\\]'], ], }, }, }; /** @internal */ function autoRenderMathInElement(element, options) { var _a, _b, _c; try { const optionsPrivate = { ...DEFAULT_AUTO_RENDER_OPTIONS, ...options, }; optionsPrivate.ignoreClassPattern = new RegExp((_a = optionsPrivate.ignoreClass) !== null && _a !== void 0 ? _a : ''); optionsPrivate.processClassPattern = new RegExp((_b = optionsPrivate.processClass) !== null && _b !== void 0 ? _b : ''); optionsPrivate.processScriptTypePattern = new RegExp((_c = optionsPrivate.processScriptType) !== null && _c !== void 0 ? _c : ''); optionsPrivate.macros = getMacros(optionsPrivate.macros); // Validate the namespace (used for `data-` attributes) if (optionsPrivate.namespace) { if (!/^[a-z]+-?$/.test(optionsPrivate.namespace)) { throw new Error('options.namespace must be a string of lowercase characters only'); } if (!optionsPrivate.namespace.endsWith('-')) optionsPrivate.namespace += '-'; } // Load the fonts and inject the stylesheet once to // avoid having to do it many times in the case of a `renderMathInDocument()` // call. void loadFonts(optionsPrivate.fontsDirectory); inject(null, css_248z$4, hashCode(css_248z$4).toString(36)); scanElement(element, optionsPrivate); } catch (error) { if (error instanceof Error) console.error('renderMathInElement(): ' + error.message); else { console.error('renderMathInElement(): Could not render math for element', element); } } } const DEFAULT_KEYBINDINGS = [ { key: 'left', command: 'moveToPreviousChar' }, { key: 'right', command: 'moveToNextChar' }, { key: 'up', command: 'moveUp' }, { key: 'down', command: 'moveDown' }, { key: 'shift+[ArrowLeft]', command: 'extendSelectionBackward' }, { key: 'shift+[ArrowRight]', command: 'extendSelectionForward' }, { key: 'shift+[ArrowUp]', command: 'extendSelectionUpward' }, { key: 'shift+[ArrowDown]', command: 'extendSelectionDownward' }, { key: '[Backspace]', command: 'deleteBackward' }, { key: 'alt+[Delete]', command: 'deleteBackward' }, { key: '[Delete]', command: 'deleteForward' }, { key: 'alt+[Backspace]', command: 'deleteForward' }, { key: 'alt+[ArrowLeft]', command: 'moveToPreviousWord' }, { key: 'alt+[ArrowRight]', command: 'moveToNextWord' }, { key: 'shift+alt+[ArrowLeft]', command: 'extendToPreviousWord' }, { key: 'shift+alt+[ArrowRight]', command: 'extendToNextWord' }, { key: 'ctrl+[ArrowLeft]', command: 'moveToGroupStart' }, { key: 'ctrl+[ArrowRight]', command: 'moveToGroupEnd' }, { key: 'shift+ctrl+[ArrowLeft]', command: 'extendToGroupStart' }, { key: 'shift+ctrl+[ArrowRight]', command: 'extendToGroupEnd' }, { key: '[Space]', ifMode: 'math', command: 'moveAfterParent' }, { key: 'shift+[Space]', ifMode: 'math', command: 'moveBeforeParent' }, { key: '[Home]', command: 'moveToMathFieldStart' }, { key: 'cmd+[ArrowLeft]', command: 'moveToMathFieldStart' }, { key: 'shift+[Home]', command: 'extendToMathFieldStart' }, { key: 'shift+cmd+[ArrowLeft]', command: 'extendToMathFieldStart' }, { key: '[End]', command: 'moveToMathFieldEnd' }, { key: 'cmd+[ArrowRight]', command: 'moveToMathFieldEnd' }, { key: 'shift+[End]', command: 'extendToMathFieldEnd' }, { key: 'shift+cmd+[ArrowRight]', command: 'extendToMathFieldEnd' }, { key: '[Pageup]', command: 'moveToGroupStart' }, { key: '[Pagedown]', command: 'moveToGroupEnd' }, { key: '[Tab]', ifMode: 'math', command: 'moveToNextPlaceholder' }, { key: 'shift+[Tab]', ifMode: 'math', command: 'moveToPreviousPlaceholder', }, { key: '[Tab]', ifMode: 'text', command: 'moveToNextPlaceholder' }, { key: 'shift+[Tab]', ifMode: 'text', command: 'moveToPreviousPlaceholder', }, { key: '[Escape]', ifMode: 'math', command: ['switchMode', 'latex'] }, { key: '[Escape]', ifMode: 'text', command: ['switchMode', 'latex'] }, { key: '\\', ifMode: 'math', command: ['switchMode', 'latex', '\\'], }, // { key: '[Backslash]', ifMode: 'math', command: ['switchMode', 'latex'] }, { key: '[IntlBackslash]', ifMode: 'math', command: ['switchMode', 'latex', '\\'], }, { key: '[Escape]', ifMode: 'latex', command: ['complete', 'complete', { selectItem: 'true' }], }, { key: '[Tab]', ifMode: 'latex', command: ['complete', 'accept-suggestion'], }, { key: '[Return]', ifMode: 'latex', command: 'complete' }, { key: '[Enter]', ifMode: 'latex', command: 'complete' }, { key: 'shift+[Escape]', ifMode: 'latex', command: ['complete', 'reject'], }, // this combination, for example in 60% keyboards it is mapped to ~ { key: '[ArrowDown]', ifMode: 'latex', command: 'nextSuggestion' }, // { key: 'ios:command:[Tab]', ifMode: 'latex',command: 'nextSuggestion' }, { key: '[ArrowUp]', ifMode: 'latex', command: 'previousSuggestion' }, { key: 'ctrl+a', ifPlatform: '!macos', command: 'selectAll' }, { key: 'cmd+a', command: 'selectAll' }, // Rare keys on some extended keyboards { key: '[Cut]', command: 'cutToClipboard' }, { key: '[Copy]', command: 'copyToClipboard' }, { key: '[Paste]', command: 'pasteFromClipboard' }, { key: '[Clear]', command: 'deleteBackward' }, { key: 'ctrl+z', ifPlatform: '!macos', command: 'undo' }, { key: 'cmd+z', command: 'undo' }, { key: '[Undo]', command: 'undo' }, { key: 'ctrl+y', ifPlatform: '!macos', command: 'redo' }, { key: 'shift+cmd+y', command: 'redo' }, { key: 'shift+ctrl+z', ifPlatform: '!macos', command: 'redo' }, { key: 'shift+cmd+z', command: 'redo' }, { key: '[Redo]', command: 'redo' }, { key: '[EraseEof]', command: 'deleteToGroupEnd' }, // EMACS/MACOS BINDINGS { key: 'ctrl+b', ifPlatform: 'macos', command: 'moveToPreviousChar' }, { key: 'ctrl+f', ifPlatform: 'macos', command: 'moveToNextChar' }, { key: 'ctrl+p', ifPlatform: 'macos', command: 'moveUp' }, { key: 'ctrl+n', ifPlatform: 'macos', command: 'moveDown' }, { key: 'ctrl+a', ifPlatform: 'macos', command: 'moveToMathFieldStart' }, { key: 'ctrl+e', ifPlatform: 'macos', command: 'moveToMathFieldEnd' }, { key: 'shift+ctrl+b', ifPlatform: 'macos', command: 'extendSelectionBackward', }, { key: 'shift+ctrl+f', ifPlatform: 'macos', command: 'extendSelectionForward', }, { key: 'shift+ctrl+p', ifPlatform: 'macos', command: 'extendSelectionUpward', }, { key: 'shift+ctrl+n', ifPlatform: 'macos', command: 'extendSelectionDownward', }, { key: 'shift+ctrl+a', ifPlatform: 'macos', command: 'extendToMathFieldStart', }, { key: 'shift+ctrl+e', ifPlatform: 'macos', command: 'extendToMathFieldEnd', }, { key: 'alt+ctrl+b', ifPlatform: 'macos', command: 'moveToPreviousWord' }, { key: 'alt+ctrl+f', ifPlatform: 'macos', command: 'moveToNextWord' }, { key: 'shift+alt+ctrl+b', ifPlatform: 'macos', command: 'extendToPreviousWord', }, { key: 'shift+alt+ctrl+f', ifPlatform: 'macos', command: 'extendToNextWord', }, { key: 'ctrl+h', ifPlatform: 'macos', command: 'deleteBackward' }, { key: 'ctrl+d', ifPlatform: 'macos', command: 'deleteForward' }, { key: 'ctrl+l', ifPlatform: 'macos', command: 'scrollIntoView' }, // { key: 'ctrl+t', ifPlatform: 'macos', command: 'transpose' }, // WOLFRAM MATHEMATICA BINDINGS { key: 'ctrl+[Digit2]', ifMode: 'math', command: ['insert', '\\sqrt{#0}'], }, { key: 'ctrl+[Digit5]', ifMode: 'math', command: 'moveToOpposite' }, { key: 'ctrl+[Digit6]', ifMode: 'math', command: 'moveToSuperscript' }, { key: 'ctrl+[Return]', ifMode: 'math', command: 'addRowAfter' }, { key: 'ctrl+[Enter]', ifMode: 'math', command: 'addRowAfter' }, { key: 'cmd+[Return]', ifMode: 'math', command: 'addRowAfter' }, { key: 'cmd+[Enter]', ifMode: 'math', command: 'addRowAfter' }, // Excel keybindings: // shift+space: select entire row, ctrl+space: select an entire column // shift+ctrl++ or ctrl+numpad+ // ctrl+- to delete a row or columns // MATHLIVE BINDINGS // { key: 'alt+a', command: ['insert', '\\theta'] }, { key: 'alt+p', ifMode: 'math', command: ['insert', '\\pi'] }, { key: 'alt+v', ifMode: 'math', command: ['insert', '\\sqrt{#0}'] }, { key: 'alt+w', ifMode: 'math', command: ['insert', '\\sum_{i=#?}^{#?}'], }, { key: 'alt+b', command: ['insert', '\\int_{#?}^{#?}'] }, { key: 'alt+u', ifMode: 'math', command: ['insert', '\\cup'] }, { key: 'alt+n', ifMode: 'math', command: ['insert', '\\cap'] }, { key: 'alt+o', ifMode: 'math', command: ['insert', '\\emptyset'] }, { key: 'alt+d', ifMode: 'math', command: ['insert', '\\differentialD'], }, { key: 'shift+alt+o', ifMode: 'math', command: ['insert', '\\varnothing'], }, { key: 'shift+alt+d', ifMode: 'math', command: ['insert', '\\partial'], }, { key: 'shift+alt+p', ifMode: 'math', command: ['insert', '\\prod_{i=#?}^{#?}'], }, { key: 'shift+alt+u', ifMode: 'math', command: ['insert', '\\bigcup'] }, { key: 'shift+alt+n', ifMode: 'math', command: ['insert', '\\bigcap'] }, { key: 'shift+alt+a', ifMode: 'math', command: ['insert', '\\forall'] }, { key: 'shift+alt+e', ifMode: 'math', command: ['insert', '\\exists'] }, { key: 'alt+[Backslash]', ifMode: 'math', command: ['insert', '\\backslash'], }, { key: '[NumpadDivide]', ifMode: 'math', command: ['insert', '\\frac{#@}{#?}'], }, { key: 'alt+[NumpadDivide]', ifMode: 'math', command: ['insert', '\\frac{#?}{#@}'], }, // Accessibility { key: 'shift+alt+k', command: 'toggleKeystrokeCaption' }, { key: 'alt+[Space]', command: 'toggleVirtualKeyboard' }, // Note: On Mac OS (as of 10.12), there is a bug/behavior that causes // a beep to be generated with certain command+control key combinations. // The workaround is to create a default binding file to silence them. // In ~/Library/KeyBindings/DefaultKeyBinding.dict add these entries: // // { // "^@\UF701" = "noop:"; // "^@\UF702" = "noop:"; // "^@\UF703" = "noop:"; // } { key: 'alt+ctrl+[ArrowUp]', command: ['speak', 'all', { withHighlighting: false }], }, { key: 'alt+ctrl+[ArrowDown]', command: ['speak', 'selection', { withHighlighting: false }], }, // // Punctuations and some non-alpha key combinations // only work with specific keyboard layouts // { key: 'alt+[Equal]', ifLayout: ['apple.en-intl', 'windows.en-intl', 'linux.en'], ifMode: 'math', command: ['applyStyle', { mode: 'text' }], }, { key: 'alt+[Equal]', ifLayout: ['apple.en-intl', 'windows.en-intl', 'linux.en'], ifMode: 'text', command: ['applyStyle', { mode: 'math' }], }, { key: 'shift+[Quote]', ifLayout: ['apple.en-intl', 'windows.en-intl', 'linux.en'], ifMode: 'math', command: ['switchMode', 'text', '', ''], }, { key: 'shift+[Quote]', ifLayout: ['apple.en-intl', 'windows.en-intl', 'linux.en'], ifMode: 'text', command: ['switchMode', 'math', '', ''], }, { key: '/', ifMode: 'math', command: ['insert', '\\frac{#@}{#?}'], }, { key: 'alt+/', ifLayout: ['apple.en-intl', 'windows.en-intl', 'linux.en'], ifMode: 'math', command: ['insert', '\\/'], }, { key: 'alt+[BracketLeft]', ifLayout: ['apple.en-intl', 'windows.en-intl', 'linux.en'], ifMode: 'math', command: ['insert', '\\left\\lbrack #0 \\right\\rbrack'], }, { key: 'ctrl+[Minus]', ifLayout: ['apple.en-intl', 'windows.en-intl', 'linux.en'], ifMode: 'math', command: 'moveToSubscript', }, { key: 'shift+alt+[BracketLeft]', ifLayout: ['apple.en-intl', 'windows.en-intl', 'linux.en'], ifMode: 'math', command: ['insert', '\\left\\lbrace #0 \\right\\rbrace'], }, { key: 'ctrl+;', ifLayout: ['apple.en-intl', 'windows.en-intl', 'linux.en'], ifMode: 'math', command: 'addRowAfter', }, { key: 'cmd+;', ifLayout: ['apple.en-intl', 'windows.en-intl', 'linux.en'], ifMode: 'math', command: 'addRowAfter', }, { key: 'shift+ctrl+;', ifLayout: ['apple.en-intl', 'windows.en-intl', 'linux.en'], ifMode: 'math', command: 'addRowBefore', }, { key: 'shift+cmd+;', ifLayout: ['apple.en-intl', 'windows.en-intl', 'linux.en'], ifMode: 'math', command: 'addRowBefore', }, { key: 'ctrl+[Comma]', ifLayout: ['apple.en-intl', 'windows.en-intl', 'linux.en'], ifMode: 'math', command: 'addColumnAfter', }, { key: 'cmd+[Comma]', ifLayout: ['apple.en-intl', 'windows.en-intl', 'linux.en'], ifMode: 'math', command: 'addColumnAfter', }, { key: 'shift+ctrl+[Comma]', ifLayout: ['apple.en-intl', 'windows.en-intl', 'linux.en'], ifMode: 'math', command: 'addColumnAfter', }, { key: 'shift+cmd+[Comma]', ifLayout: ['apple.en-intl', 'windows.en-intl', 'linux.en'], ifMode: 'math', command: 'addColumnAfter', }, { key: 'alt+[Digit5]', ifLayout: ['apple.en-intl', 'windows.en-intl', 'linux.en'], ifMode: 'math', command: ['insert', '$\\infty'], }, { key: 'alt+[Digit6]', ifLayout: ['apple.en-intl', 'windows.en-intl', 'linux.en'], ifMode: 'math', command: ['insert', '\\wedge'], }, { key: 'shift+alt+[Digit6]', ifLayout: ['apple.en-intl', 'windows.en-intl', 'linux.en'], ifMode: 'math', command: ['insert', '\\vee'], }, { key: 'alt+[Digit9]', ifLayout: ['apple.en-intl', 'windows.en-intl', 'linux.en'], ifMode: 'math', command: ['insert', '('], }, { key: 'alt+[Digit0]', ifLayout: ['apple.en-intl', 'windows.en-intl', 'linux.en'], ifMode: 'math', command: ['insert', ')'], }, { key: 'alt+|', ifLayout: ['apple.en-intl', 'windows.en-intl', 'linux.en'], ifMode: 'math', command: ['insert', '|'], }, { key: 'shift+[Backquote]', ifLayout: ['apple.en-intl', 'windows.en-intl', 'linux.en'], ifMode: 'math', command: ['insert', '\\~'], }, { key: '[Backquote]', ifLayout: ['windows.french', 'linux.french'], ifMode: 'math', command: ['insert', '^2'], }, ]; /** * Most commands can be associated to their keyboard shortcuts from the * DEFAULT_KEYBINDINGS table above, for example 'speakSelection' -> 'ctrl+KeyR' * However, those that contain complex commands are more ambiguous. * For example, '\sqrt' -> 'math:alt+KeyV'. This table provides the reverse * mapping for those more complex commands. It is used when displaying * keybindings for specific commands in the popover. */ const REVERSE_KEYBINDINGS = { '\\theta': 'alt+q', '\\sqrt': ['alt+v', 'ctrl+[Digit2]'], '\\pi': 'alt+p', '\\prod': 'shift+alt+p', '\\sum': 'alt+w', '\\int': 'alt+b', '\\cup': 'alt+u', '\\cap': 'alt+n', '\\bigcup': 'shift+alt+u', '\\bigcap': 'shift+alt+n', '\\forall': 'shift+alt+a', '\\exists': 'shift+alt+e', '\\infty': 'alt+[Digit5]', '\\wedge': 'alt+[Digit5]', '\\vee': 'shift+alt+[Digit6]', '\\differentialD': 'alt+d', '\\partial': 'shift+alt+d', '\\frac': 'Slash', '\\emptyset': 'alt+o', '\\varnothing': 'shift+alt+o', '\\~': '~', }; function keystrokeModifiersFromString(key) { const segments = key.split('+'); const result = { shift: false, alt: false, cmd: false, win: false, meta: false, ctrl: false, key: segments.pop(), }; if (segments.includes('shift')) result.shift = true; if (segments.includes('alt')) result.alt = true; if (segments.includes('ctrl')) result.ctrl = true; if (segments.includes('cmd')) result.cmd = true; if (segments.includes('win')) result.win = true; if (segments.includes('meta')) result.meta = true; return result; } function keystrokeModifiersToString(key) { let result = ''; if (key.shift) result += 'shift+'; if (key.alt) result += 'alt+'; if (key.ctrl) result += 'ctrl+'; if (key.cmd) result += 'cmd+'; if (key.win) result += 'win+'; if (key.meta) result += 'meta+'; return result + key.key; } const DEFAULT_KEYBOARD_LAYOUT = platform() === 'apple' ? { id: 'apple.en-intl', displayName: 'English (international)', virtualLayout: 'qwerty', platform: 'apple', locale: 'en', score: 0, mapping: { KeyA: ['a', 'A', 'å', 'Å'], KeyB: ['b', 'B', '∫', 'ı'], KeyC: ['c', 'C', 'ç', 'Ç'], KeyD: ['d', 'D', '∂', 'Î'], KeyE: ['e', 'E', '´', '´'], KeyF: ['f', 'F', 'ƒ', 'Ï'], KeyG: ['g', 'G', '©', '˝'], KeyH: ['h', 'H', '˙', 'Ó'], KeyI: ['i', 'I', 'ˆ', 'ˆ'], KeyJ: ['j', 'J', '∆', 'Ô'], KeyK: ['k', 'K', '˚', ''], KeyL: ['l', 'L', '¬', 'Ò'], KeyM: ['m', 'M', 'µ', 'Â'], KeyN: ['n', 'N', '˜', '˜'], KeyO: ['o', 'O', 'ø', 'Ø'], KeyP: ['p', 'P', 'π', '∏'], KeyQ: ['q', 'Q', 'œ', 'Œ'], KeyR: ['r', 'R', '®', '‰'], KeyS: ['s', 'S', 'ß', 'Í'], KeyT: ['t', 'T', '†', 'ˇ'], KeyU: ['u', 'U', '¨', '¨'], KeyV: ['v', 'V', '√', '◊'], KeyW: ['w', 'W', '∑', '„'], KeyX: ['x', 'X', '≈', '˛'], KeyY: ['y', 'Y', '¥', 'Á'], KeyZ: ['z', 'Z', 'Ω', '¸'], Digit1: ['1', '!', '¡', '⁄'], Digit2: ['2', '@', '™', '€'], Digit3: ['3', '#', '£', '‹'], Digit4: ['4', '$', '¢', '›'], Digit5: ['5', '%', '∞', 'fi'], Digit6: ['6', '^', '§', 'fl'], Digit7: ['7', '&', '¶', '‡'], Digit8: ['8', '*', '•', '°'], Digit9: ['9', '(', 'ª', '·'], Digit0: ['0', ')', 'º', '‚'], Space: [' ', ' ', ' ', ' '], Minus: ['-', '_', '–', '—'], Equal: ['=', '+', '≠', '±'], BracketLeft: ['[', '{', '“', '”'], BracketRight: [']', '}', '‘', '’'], Backslash: ['\\', '|', '«', '»'], Semicolon: [';', ':', '…', 'Ú'], Quote: ["'", '"', 'æ', 'Æ'], Backquote: ['`', '˜', '`', '`'], Comma: [',', '<', '≤', '¯'], Period: ['.', '>', '≥', '˘'], Slash: ['/', '?', '÷', '¿'], NumpadDivide: ['/', '/', '/', '/'], NumpadMultiply: ['*', '*', '*', '*'], NumpadSubtract: ['-', '-', '-', '-'], NumpadAdd: ['+', '+', '+', '+'], Numpad1: ['1', '1', '1', '1'], Numpad2: ['2', '2', '2', '2'], Numpad3: ['3', '3', '3', '3'], Numpad4: ['4', '4', '4', '4'], Numpad5: ['5', '5', '5', '5'], Numpad6: ['6', '6', '6', '6'], Numpad7: ['7', '7', '7', '7'], Numpad8: ['8', '8', '8', '8'], Numpad9: ['9', '9', '9', '9'], Numpad0: ['0', '0', '0', '0'], NumpadDecimal: ['.', '.', '.', '.'], IntlBackslash: ['§', '±', '§', '±'], NumpadEqual: ['=', '=', '=', '='], AudioVolumeUp: ['', '=', '', '='], }, } : platform() === 'windows' ? { id: 'windows.en-intl', displayName: 'English (international)', platform: 'windows', virtualLayout: 'qwerty', locale: 'en', score: 0, mapping: { KeyA: ['a', 'A', 'á', 'Á'], KeyB: ['b', 'B', '', ''], KeyC: ['c', 'C', '©', '¢'], KeyD: ['d', 'D', 'ð', 'Ð'], KeyE: ['e', 'E', 'é', 'É'], KeyF: ['f', 'F', '', ''], KeyG: ['g', 'G', '', ''], KeyH: ['h', 'H', '', ''], KeyI: ['i', 'I', 'í', 'Í'], KeyJ: ['j', 'J', '', ''], KeyK: ['k', 'K', '', ''], KeyL: ['l', 'L', 'ø', 'Ø'], KeyM: ['m', 'M', 'µ', ''], KeyN: ['n', 'N', 'ñ', 'Ñ'], KeyO: ['o', 'O', 'ó', 'Ó'], KeyP: ['p', 'P', 'ö', 'Ö'], KeyQ: ['q', 'Q', 'ä', 'Ä'], KeyR: ['r', 'R', '®', ''], KeyS: ['s', 'S', 'ß', '§'], KeyT: ['t', 'T', 'þ', 'Þ'], KeyU: ['u', 'U', 'ú', 'Ú'], KeyV: ['v', 'V', '', ''], KeyW: ['w', 'W', 'å', 'Å'], KeyX: ['x', 'X', '', ''], KeyY: ['y', 'Y', 'ü', 'Ü'], KeyZ: ['z', 'Z', 'æ', 'Æ'], Digit1: ['1', '!', '¡', '¹'], Digit2: ['2', '@', '²', ''], Digit3: ['3', '#', '³', ''], Digit4: ['4', '$', '¤', '£'], Digit5: ['5', '%', '€', ''], Digit6: ['6', '^', '¼', ''], Digit7: ['7', '&', '½', ''], Digit8: ['8', '*', '¾', ''], Digit9: ['9', '(', '‘', ''], Digit0: ['0', ')', '’', ''], Space: [' ', ' ', '', ''], Minus: ['-', '_', '¥', ''], Equal: ['=', '+', '×', '÷'], BracketLeft: ['[', '{', '«', ''], BracketRight: [']', '}', '»', ''], Backslash: ['\\', '|', '¬', '¦'], Semicolon: [';', ':', '¶', '°'], Quote: ["'", '"', '´', '¨'], Backquote: ['`', '~', '', ''], Comma: [',', '<', 'ç', 'Ç'], Period: ['.', '>', '', ''], Slash: ['/', '?', '¿', ''], NumpadDivide: ['/', '/', '', ''], NumpadMultiply: ['*', '*', '', ''], NumpadSubtract: ['-', '-', '', ''], NumpadAdd: ['+', '+', '', ''], IntlBackslash: ['\\', '|', '', ''], }, } : { id: 'linux.en', displayName: 'English', platform: 'linux', virtualLayout: 'qwerty', locale: 'en', score: 0, mapping: { KeyA: ['a', 'A', 'a', 'A'], KeyB: ['b', 'B', 'b', 'B'], KeyC: ['c', 'C', 'c', 'C'], KeyD: ['d', 'D', 'd', 'D'], KeyE: ['e', 'E', 'e', 'E'], KeyF: ['f', 'F', 'f', 'F'], KeyG: ['g', 'G', 'g', 'G'], KeyH: ['h', 'H', 'h', 'H'], KeyI: ['i', 'I', 'i', 'I'], KeyJ: ['j', 'J', 'j', 'J'], KeyK: ['k', 'K', 'k', 'K'], KeyL: ['l', 'L', 'l', 'L'], KeyM: ['m', 'M', 'm', 'M'], KeyN: ['n', 'N', 'n', 'N'], KeyO: ['o', 'O', 'o', 'O'], KeyP: ['p', 'P', 'p', 'P'], KeyQ: ['q', 'Q', 'q', 'Q'], KeyR: ['r', 'R', 'r', 'R'], KeyS: ['s', 'S', 's', 'S'], KeyT: ['t', 'T', 't', 'T'], KeyU: ['u', 'U', 'u', 'U'], KeyV: ['v', 'V', 'v', 'V'], KeyW: ['w', 'W', 'w', 'W'], KeyX: ['x', 'X', 'x', 'X'], KeyY: ['y', 'Y', 'y', 'Y'], KeyZ: ['z', 'Z', 'z', 'Z'], Digit1: ['1', '!', '1', '!'], Digit2: ['2', '@', '2', '@'], Digit3: ['3', '#', '3', '#'], Digit4: ['4', '$', '4', '$'], Digit5: ['5', '%', '5', '%'], Digit6: ['6', '^', '6', '^'], Digit7: ['7', '&', '7', '&'], Digit8: ['8', '*', '8', '*'], Digit9: ['9', '(', '9', '('], Digit0: ['0', ')', '0', ')'], Space: [' ', ' ', ' ', ' '], Minus: ['-', '_', '-', '_'], Equal: ['=', '+', '=', '+'], BracketLeft: ['[', '{', '[', '{'], BracketRight: [']', '}', ']', '}'], Backslash: ['\\', '|', '\\', '|'], Semicolon: [';', ':', ';', ':'], Quote: ["'", '"', "'", '"'], Backquote: ['`', '~', '`', '~'], Comma: [',', '<', ',', '<'], Period: ['.', '>', '.', '>'], Slash: ['/', '?', '/', '?'], NumpadDivide: ['/', '/', '/', '/'], NumpadMultiply: ['*', '*', '*', '*'], NumpadSubtract: ['-', '-', '-', '-'], NumpadAdd: ['+', '+', '+', '+'], Numpad1: ['1', '1', '1', '1'], Numpad2: ['2', '2', '2', '2'], Numpad3: ['3', '3', '3', '3'], Numpad4: ['4', '4', '4', '4'], Numpad5: ['5', '5', '5', '5'], Numpad6: ['6', '6', '6', '6'], Numpad7: ['7', '7', '7', '7'], Numpad8: ['8', '8', '8', '8'], Numpad9: ['9', '9', '9', '9'], Numpad0: ['0', '0', '0', '0'], NumpadDecimal: ['', '.', '', '.'], IntlBackslash: ['<', '>', '|', '¦'], NumpadEqual: ['=', '=', '=', '='], NumpadComma: ['.', '.', '.', '.'], NumpadParenLeft: ['(', '(', '(', '('], NumpadParenRight: [')', ')', ')', ')'], }, }; /* prettier-ignore */ const BASE_LAYOUT_MAPPING = { enter: '[Enter]', escape: '[Escape]', backspace: '[Backspace]', tab: '[Tab]', space: '[Space]', pausebreak: '[Pause]', insert: '[Insert]', home: '[Home]', pageup: '[PageUp]', delete: '[Delete]', end: '[End]', pagedown: '[PageDown]', right: '[ArrowRight]', left: '[ArrowLeft]', down: '[ArrowDown]', up: '[ArrowUp]', numpad0: '[Numpad0]', numpad1: '[Numpad1]', numpad2: '[Numpad2]', numpad3: '[Numpad3]', numpad4: '[Numpad4]', numpad5: '[Numpad5]', numpad6: '[Numpad6]', numpad7: '[Numpad7]', numpad8: '[Numpad8]', numpad9: '[Numpad9]', 'numpad_divide': '[NumpadDivide]', 'numpad_multiply': '[NumpadMultiply]', 'numpad_subtract': '[NumpadSubtract]', 'numpad_add': '[NumpadAdd]', 'numpad_decimal': '[NumpadDecimal]', 'numpad_separator': '[NumpadComma]', capslock: '[Capslock]', f1: '[F1]', f2: '[F2]', f3: '[F3]', f4: '[F4]', f5: '[F5]', f6: '[F6]', f7: '[F7]', f8: '[F8]', f9: '[F9]', f10: '[F10]', f11: '[F11]', f12: '[F12]', f13: '[F13]', f14: '[F14]', f15: '[F15]', f16: '[F16]', f17: '[F17]', f18: '[F18]', f19: '[F19]', }; const gKeyboardLayouts = []; function platform() { switch (osPlatform()) { case 'macos': case 'ios': return 'apple'; case 'windows': return 'windows'; // case 'android': // case 'chromeos': } return 'linux'; } function register$1(layout) { if (!layout.platform || layout.platform === platform()) gKeyboardLayouts.push(layout); } /** Given the current estimated keyboard layout, * return the unmodified key for the event. * For example, on AZERTY option+shift+'A' = 'Æ' -> 'a' * (event though the code is KeyQ) */ // export function getUnmodifiedKey(evt: KeyboardEvent): string { // if (!evt.shiftKey && !evt.altKey) { // return evt.key; // } // // @todo: iterate over the entries for the current layout, // // with the alt+shift modifiers set accordingly // // and find the (first) entry that matches // const layout = gKeyboardLayouts[0] ?? DEFAULT_KEYBOARD_LAYOUT; // const index = // evt.shiftKey && evt.altKey ? 3 : evt.altKey ? 2 : evt.shiftKey ? 1 : 0; // for (const [key, value] of Object.entries(layout.mapping)) { // if (key === evt.code && value[index] === evt.key) { // return value[0]; // } // } // // We did not find a perfect match... // // Look for an entry even if the keycode doesn't match... // for (const [, value] of Object.entries(layout.mapping)) { // if (value[index] === evt.key) { // return value[0]; // } // } // // Really? Nothing matched?! Just return the key... // return evt.key; // } function getCodeForKey(k, layout) { var _a; const result = { shift: false, alt: false, cmd: false, win: false, meta: false, ctrl: false, key: '', }; if (!k) return result; for (const [key, value] of Object.entries(layout.mapping)) { if (value[0] === k) { result.key = `[${key}]`; return result; } if (value[1] === k) { result.shift = true; result.key = `[${key}]`; return result; } if (value[2] === k) { result.alt = true; result.key = `[${key}]`; return result; } if (value[3] === k) { result.shift = true; result.alt = true; result.key = `[${key}]`; return result; } } result.key = (_a = BASE_LAYOUT_MAPPING[k]) !== null && _a !== void 0 ? _a : ''; return result; } function normalizeKeyboardEvent(evt) { if (!evt.code) { // For virtual keyboards (iOS, Android) and Microsoft Edge (!) // the `evt.code`, which represents the physical key pressed, is set // to undefined. In that case, map the virtual key ("q") to a // pseudo-hardware key ("KeyQ") const mapping = Object.entries(getActiveKeyboardLayout().mapping); let altKey = false; let shiftKey = false; let code = ''; for (let index = 0; index < 4; index++) { for (const [key, value] of mapping) { if (value[index] === evt.key) { code = key; if (index === 3) { altKey = true; shiftKey = true; } else if (index === 2) altKey = true; else if (index === 1) shiftKey = true; break; } } if (code) break; } return new KeyboardEvent(evt.type, { ...evt, altKey, shiftKey, code }); } return new KeyboardEvent(evt.type, evt); } // Given this keyboard event, and the `code`, `key` and modifiers // in it, increase the score of layouts that do match it. // Calling repeatedly this function will improve the accuracy of the // keyboard layout estimate. function validateKeyboardLayout(evt) { var _a, _b; if (!evt) return; if (evt.key === 'Unidentified') return; // Dead keys do not have enough info to validate the keyboard // (we dont' know what char they could produce, only the physical key associated with them ) if (evt.key === 'Dead') return; const index = evt.shiftKey && evt.altKey ? 3 : evt.altKey ? 2 : evt.shiftKey ? 1 : 0; for (const layout of gKeyboardLayouts) { if (((_a = layout.mapping[evt.code]) === null || _a === void 0 ? void 0 : _a[index]) === evt.key) { // Increase the score of the layouts that have a mapping compatible with // this keyboard event. layout.score += 1; } else if ((_b = layout.mapping[evt.code]) === null || _b === void 0 ? void 0 : _b[index]) { // There is a mapping, but it's not compatible with this keystroke: // zero-out the score layout.score = 0; } } gKeyboardLayouts.sort((a, b) => b.score - a.score); } function getActiveKeyboardLayout() { return gKeyboardLayouts[0]; } register$1(DEFAULT_KEYBOARD_LAYOUT); register$1({ id: 'apple.french', locale: 'fr', displayName: 'French', platform: 'apple', virtualLayout: 'azerty', score: 0, mapping: { KeyA: ['q', 'Q', '‡', 'Ω'], KeyB: ['b', 'B', 'ß', '∫'], KeyC: ['c', 'C', '©', '¢'], KeyD: ['d', 'D', '∂', '∆'], KeyE: ['e', 'E', 'ê', 'Ê'], KeyF: ['f', 'F', 'ƒ', '·'], KeyG: ['g', 'G', 'fi', 'fl'], KeyH: ['h', 'H', 'Ì', 'Î'], KeyI: ['i', 'I', 'î', 'ï'], KeyJ: ['j', 'J', 'Ï', 'Í'], KeyK: ['k', 'K', 'È', 'Ë'], KeyL: ['l', 'L', '¬', '|'], KeyM: [',', '?', '∞', '¿'], KeyN: ['n', 'N', '~', 'ı'], KeyO: ['o', 'O', 'œ', 'Œ'], KeyP: ['p', 'P', 'π', '∏'], KeyQ: ['a', 'A', 'æ', 'Æ'], KeyR: ['r', 'R', '®', '‚'], KeyS: ['s', 'S', 'Ò', '∑'], KeyT: ['t', 'T', '†', '™'], KeyU: ['u', 'U', 'º', 'ª'], KeyV: ['v', 'V', '◊', '√'], KeyW: ['z', 'Z', 'Â', 'Å'], KeyX: ['x', 'X', '≈', '⁄'], KeyY: ['y', 'Y', 'Ú', 'Ÿ'], KeyZ: ['w', 'W', '‹', '›'], Digit1: ['&', '1', '', '´'], Digit2: ['é', '2', 'ë', '„'], Digit3: ['"', '3', '“', '”'], Digit4: ["'", '4', '‘', '’'], Digit5: ['(', '5', '{', '['], Digit6: ['§', '6', '¶', 'å'], Digit7: ['è', '7', '«', '»'], Digit8: ['!', '8', '¡', 'Û'], Digit9: ['ç', '9', 'Ç', 'Á'], Digit0: ['à', '0', 'ø', 'Ø'], Space: [' ', ' ', ' ', ' '], Minus: [')', '°', '}', ']'], Equal: ['-', '_', '—', '–'], BracketLeft: ['^', '¨', 'ô', 'Ô'], BracketRight: ['$', '*', '€', '¥'], Backslash: ['`', '£', '@', '#'], Semicolon: ['m', 'M', 'µ', 'Ó'], Quote: ['ù', '%', 'Ù', '‰'], Backquote: ['<', '>', '≤', '≥'], Comma: [';', '.', '…', '•'], Period: [':', '/', '÷', '\\'], Slash: ['=', '+', '≠', '±'], NumpadDivide: ['/', '/', '/', '/'], NumpadMultiply: ['*', '*', '*', '*'], NumpadSubtract: ['-', '-', '-', '-'], NumpadAdd: ['+', '+', '+', '+'], NumpadDecimal: [',', '.', ',', '.'], IntlBackslash: ['@', '#', '•', 'Ÿ'], NumpadEqual: ['=', '=', '=', '='], }, }); register$1({ id: 'apple.spanish', locale: 'es', displayName: 'Spanish ISO', platform: 'apple', virtualLayout: 'qwerty', score: 0, mapping: { KeyA: ['a', 'A', 'å', 'Å'], KeyB: ['b', 'B', 'ß', ''], KeyC: ['c', 'C', '©', ' '], KeyD: ['d', 'D', '∂', '∆'], KeyE: ['e', 'E', '€', '€'], KeyF: ['f', 'F', 'ƒ', 'fi'], KeyG: ['g', 'G', '', 'fl'], KeyH: ['h', 'H', '™', ' '], KeyI: ['i', 'I', ' ', ' '], KeyJ: ['j', 'J', '¶', '¯'], KeyK: ['k', 'K', '§', 'ˇ'], KeyL: ['l', 'L', ' ', '˘'], KeyM: ['m', 'M', 'µ', '˚'], KeyN: ['n', 'N', ' ', '˙'], KeyO: ['o', 'O', 'ø', 'Ø'], KeyP: ['p', 'P', 'π', '∏'], KeyQ: ['q', 'Q', 'œ', 'Œ'], KeyR: ['r', 'R', '®', ' '], KeyS: ['s', 'S', '∫', ' '], KeyT: ['t', 'T', '†', '‡'], KeyU: ['u', 'U', ' ', ' '], KeyV: ['v', 'V', '√', '◊'], KeyW: ['w', 'W', 'æ', 'Æ'], KeyX: ['x', 'X', '∑', '›'], KeyY: ['y', 'Y', '¥', ' '], KeyZ: ['z', 'Z', 'Ω', '‹'], Digit1: ['1', '!', '|', 'ı'], Digit2: ['2', '"', '@', '˝'], Digit3: ['3', '·', '#', '•'], Digit4: ['4', '$', '¢', '£'], Digit5: ['5', '%', '∞', '‰'], Digit6: ['6', '&', '¬', ' '], Digit7: ['7', '/', '÷', '⁄'], Digit8: ['8', '(', '“', '‘'], Digit9: ['9', ')', '”', '’'], Digit0: ['0', '=', '≠', '≈'], Space: [' ', ' ', ' ', ' '], Minus: ["'", '?', '´', '¸'], Equal: ['¡', '¿', '‚', '˛'], BracketLeft: ['`', '^', '[', 'ˆ'], BracketRight: ['+', '*', ']', '±'], Backslash: ['ç', 'Ç', '}', '»'], Semicolon: ['ñ', 'Ñ', '~', '˜'], Quote: ['´', '¨', '{', '«'], Backquote: ['<', '>', '≤', '≥'], Comma: [',', ';', '„', ''], Period: ['.', ':', '…', '…'], Slash: ['-', '_', '–', '—'], NumpadDivide: ['/', '/', '/', '/'], NumpadMultiply: ['*', '*', '*', '*'], NumpadSubtract: ['-', '-', '-', '-'], NumpadAdd: ['+', '+', '+', '+'], Numpad1: ['1', '1', '1', '1'], Numpad2: ['2', '2', '2', '2'], Numpad3: ['3', '3', '3', '3'], Numpad4: ['4', '4', '4', '4'], Numpad5: ['5', '5', '5', '5'], Numpad6: ['6', '6', '6', '6'], Numpad7: ['7', '7', '7', '7'], Numpad8: ['8', '8', '8', '8'], Numpad9: ['9', '9', '9', '9'], Numpad0: ['0', '0', '0', '0'], NumpadDecimal: [',', ',', ',', ','], IntlBackslash: ['º', 'ª', '\\', '°'], }, }); register$1({ id: 'windows.spanish', locale: 'es', displayName: 'Spanish', platform: 'windows', virtualLayout: 'qwerty', score: 0, mapping: { KeyA: ['a', 'A', '', ''], KeyB: ['b', 'B', '', ''], KeyC: ['c', 'C', '', ''], KeyD: ['d', 'D', '', ''], KeyE: ['e', 'E', '€', ''], KeyF: ['f', 'F', '', ''], KeyG: ['g', 'G', '', ''], KeyH: ['h', 'H', '', ''], KeyI: ['i', 'I', '', ''], KeyJ: ['j', 'J', '', ''], KeyK: ['k', 'K', '', ''], KeyL: ['l', 'L', '', ''], KeyM: ['m', 'M', '', ''], KeyN: ['n', 'N', '', ''], KeyO: ['o', 'O', '', ''], KeyP: ['p', 'P', '', ''], KeyQ: ['q', 'Q', '', ''], KeyR: ['r', 'R', '', ''], KeyS: ['s', 'S', '', ''], KeyT: ['t', 'T', '', ''], KeyU: ['u', 'U', '', ''], KeyV: ['v', 'V', '', ''], KeyW: ['w', 'W', '', ''], KeyX: ['x', 'X', '', ''], KeyY: ['y', 'Y', '', ''], KeyZ: ['z', 'Z', '', ''], Digit1: ['1', '!', '|', ''], Digit2: ['2', '"', '@', ''], Digit3: ['3', '·', '#', ''], Digit4: ['4', '$', '~', ''], Digit5: ['5', '%', '€', ''], Digit6: ['6', '&', '¬', ''], Digit7: ['7', '/', '', ''], Digit8: ['8', '(', '', ''], Digit9: ['9', ')', '', ''], Digit0: ['0', '=', '', ''], Space: [' ', ' ', '', ''], Minus: ["'", '?', '', ''], Equal: ['¡', '¿', '', ''], BracketLeft: ['`', '^', '[', ''], BracketRight: ['+', '*', ']', ''], Backslash: ['ç', 'Ç', '}', ''], Semicolon: ['ñ', 'Ñ', '', ''], Quote: ['´', '¨', '{', ''], Backquote: ['º', 'ª', '\\', ''], Comma: [',', ';', '', ''], Period: ['.', ':', '', ''], Slash: ['-', '_', '', ''], NumpadDivide: ['/', '/', '', ''], NumpadMultiply: ['*', '*', '', ''], NumpadSubtract: ['-', '-', '', ''], NumpadAdd: ['+', '+', '', ''], IntlBackslash: ['<', '>', '', ''], }, }); register$1({ id: 'linux.spanish', locale: 'es', displayName: 'Spanish', platform: 'linux', virtualLayout: 'qwerty', score: 0, mapping: { KeyA: ['a', 'A', 'æ', 'Æ'], KeyB: ['b', 'B', '”', '’'], KeyC: ['c', 'C', '¢', '©'], KeyD: ['d', 'D', 'ð', 'Ð'], KeyE: ['e', 'E', '€', '¢'], KeyF: ['f', 'F', 'đ', 'ª'], KeyG: ['g', 'G', 'ŋ', 'Ŋ'], KeyH: ['h', 'H', 'ħ', 'Ħ'], KeyI: ['i', 'I', '→', 'ı'], KeyJ: ['j', 'J', '̉', '̛'], KeyK: ['k', 'K', 'ĸ', '&'], KeyL: ['l', 'L', 'ł', 'Ł'], KeyM: ['m', 'M', 'µ', 'º'], KeyN: ['n', 'N', 'n', 'N'], KeyO: ['o', 'O', 'ø', 'Ø'], KeyP: ['p', 'P', 'þ', 'Þ'], KeyQ: ['q', 'Q', '@', 'Ω'], KeyR: ['r', 'R', '¶', '®'], KeyS: ['s', 'S', 'ß', '§'], KeyT: ['t', 'T', 'ŧ', 'Ŧ'], KeyU: ['u', 'U', '↓', '↑'], KeyV: ['v', 'V', '“', '‘'], KeyW: ['w', 'W', 'ł', 'Ł'], KeyX: ['x', 'X', '»', '>'], KeyY: ['y', 'Y', '←', '¥'], KeyZ: ['z', 'Z', '«', '<'], Digit1: ['1', '!', '|', '¡'], Digit2: ['2', '"', '@', '⅛'], Digit3: ['3', '·', '#', '£'], Digit4: ['4', '$', '~', '$'], Digit5: ['5', '%', '½', '⅜'], Digit6: ['6', '&', '¬', '⅝'], Digit7: ['7', '/', '{', '⅞'], Digit8: ['8', '(', '[', '™'], Digit9: ['9', ')', ']', '±'], Digit0: ['0', '=', '}', '°'], Enter: ['\r', '\r', '\r', '\r'], Escape: ['\u001B', '\u001B', '\u001B', '\u001B'], Backspace: ['\b', '\b', '\b', '\b'], Tab: ['\t', '', '\t', ''], Space: [' ', ' ', ' ', ' '], Minus: ["'", '?', '\\', '¿'], Equal: ['¡', '¿', '̃', '~'], BracketLeft: ['̀', '̂', '[', '̊'], BracketRight: ['+', '*', ']', '̄'], Backslash: ['ç', 'Ç', '}', '̆'], Semicolon: ['ñ', 'Ñ', '~', '̋'], Quote: ['́', '̈', '{', '{'], Backquote: ['º', 'ª', '\\', '\\'], Comma: [',', ';', '─', '×'], Period: ['.', ':', '·', '÷'], Slash: ['-', '_', '̣', '̇'], NumpadDivide: ['/', '/', '/', '/'], NumpadMultiply: ['*', '*', '*', '*'], NumpadSubtract: ['-', '-', '-', '-'], NumpadAdd: ['+', '+', '+', '+'], NumpadEnter: ['\r', '\r', '\r', '\r'], Numpad1: ['', '1', '', '1'], Numpad2: ['', '2', '', '2'], Numpad3: ['', '3', '', '3'], Numpad4: ['', '4', '', '4'], Numpad5: ['', '5', '', '5'], Numpad6: ['', '6', '', '6'], Numpad7: ['', '7', '', '7'], Numpad8: ['', '8', '', '8'], Numpad9: ['', '9', '', '9'], Numpad0: ['', '0', '', '0'], NumpadDecimal: ['', '.', '', '.'], IntlBackslash: ['<', '>', '|', '¦'], NumpadEqual: ['=', '=', '=', '='], NumpadComma: ['.', '.', '.', '.'], NumpadParenLeft: ['(', '(', '(', '('], NumpadParenRight: [')', ')', ')', ')'], }, }); register$1({ id: 'linux.french', locale: 'fr', displayName: 'French', virtualLayout: 'azerty', platform: 'linux', score: 0, mapping: { KeyA: ['q', 'Q', '@', 'Ω'], KeyB: ['b', 'B', '”', '’'], KeyC: ['c', 'C', '¢', '©'], KeyD: ['d', 'D', 'ð', 'Ð'], KeyE: ['e', 'E', '€', '¢'], KeyF: ['f', 'F', 'đ', 'ª'], KeyG: ['g', 'G', 'ŋ', 'Ŋ'], KeyH: ['h', 'H', 'ħ', 'Ħ'], KeyI: ['i', 'I', '→', 'ı'], KeyJ: ['j', 'J', '̉', '̛'], KeyK: ['k', 'K', 'ĸ', '&'], KeyL: ['l', 'L', 'ł', 'Ł'], KeyM: [',', '?', '́', '̋'], KeyN: ['n', 'N', 'n', 'N'], KeyO: ['o', 'O', 'ø', 'Ø'], KeyP: ['p', 'P', 'þ', 'Þ'], KeyQ: ['a', 'A', 'æ', 'Æ'], KeyR: ['r', 'R', '¶', '®'], KeyS: ['s', 'S', 'ß', '§'], KeyT: ['t', 'T', 'ŧ', 'Ŧ'], KeyU: ['u', 'U', '↓', '↑'], KeyV: ['v', 'V', '“', '‘'], KeyW: ['z', 'Z', '«', '<'], KeyX: ['x', 'X', '»', '>'], KeyY: ['y', 'Y', '←', '¥'], KeyZ: ['w', 'W', 'ł', 'Ł'], Digit1: ['&', '1', '¹', '¡'], Digit2: ['é', '2', '~', '⅛'], Digit3: ['"', '3', '#', '£'], Digit4: ["'", '4', '{', '$'], Digit5: ['(', '5', '[', '⅜'], Digit6: ['-', '6', '|', '⅝'], Digit7: ['è', '7', '`', '⅞'], Digit8: ['_', '8', '\\', '™'], Digit9: ['ç', '9', '^', '±'], Digit0: ['à', '0', '@', '°'], Enter: ['\r', '\r', '\r', '\r'], Escape: ['\u001B', '\u001B', '\u001B', '\u001B'], Backspace: ['\b', '\b', '\b', '\b'], Tab: ['\t', '', '\t', ''], Space: [' ', ' ', ' ', ' '], Minus: [')', '°', ']', '¿'], Equal: ['=', '+', '}', '̨'], BracketLeft: ['̂', '̈', '̈', '̊'], BracketRight: ['$', '£', '¤', '̄'], Backslash: ['*', 'µ', '̀', '̆'], Semicolon: ['m', 'M', 'µ', 'º'], Quote: ['ù', '%', '̂', '̌'], Backquote: ['²', '~', '¬', '¬'], Comma: [';', '.', '─', '×'], Period: [':', '/', '·', '÷'], Slash: ['!', '§', '̣', '̇'], NumpadMultiply: ['*', '*', '*', '*'], NumpadSubtract: ['-', '-', '-', '-'], NumpadAdd: ['+', '+', '+', '+'], NumpadDecimal: ['', '.', '', '.'], IntlBackslash: ['<', '>', '|', '¦'], }, }); register$1({ id: 'windows.french', locale: 'fr', displayName: 'French', virtualLayout: 'azerty', platform: 'windows', score: 0, mapping: { KeyA: ['q', 'Q', '', ''], KeyB: ['b', 'B', '', ''], KeyC: ['c', 'C', '', ''], KeyD: ['d', 'D', '', ''], KeyE: ['e', 'E', '€', ''], KeyF: ['f', 'F', '', ''], KeyG: ['g', 'G', '', ''], KeyH: ['h', 'H', '', ''], KeyI: ['i', 'I', '', ''], KeyJ: ['j', 'J', '', ''], KeyK: ['k', 'K', '', ''], KeyL: ['l', 'L', '', ''], KeyM: [',', '?', '', ''], KeyN: ['n', 'N', '', ''], KeyO: ['o', 'O', '', ''], KeyP: ['p', 'P', '', ''], KeyQ: ['a', 'A', '', ''], KeyR: ['r', 'R', '', ''], KeyS: ['s', 'S', '', ''], KeyT: ['t', 'T', '', ''], KeyU: ['u', 'U', '', ''], KeyV: ['v', 'V', '', ''], KeyW: ['z', 'Z', '', ''], KeyX: ['x', 'X', '', ''], KeyY: ['y', 'Y', '', ''], KeyZ: ['w', 'W', '', ''], Digit1: ['&', '1', '', ''], Digit2: ['é', '2', '~', ''], Digit3: ['"', '3', '#', ''], Digit4: ["'", '4', '{', ''], Digit5: ['(', '5', '[', ''], Digit6: ['-', '6', '|', ''], Digit7: ['è', '7', '`', ''], Digit8: ['_', '8', '\\', ''], Digit9: ['ç', '9', '^', ''], Digit0: ['à', '0', '@', ''], Space: [' ', ' ', '', ''], Minus: [')', '°', ']', ''], Equal: ['=', '+', '}', ''], BracketLeft: ['^', '¨', '', ''], BracketRight: ['$', '£', '¤', ''], Backslash: ['*', 'µ', '', ''], Semicolon: ['m', 'M', '', ''], Quote: ['ù', '%', '', ''], Backquote: ['²', '', '', ''], Comma: [';', '.', '', ''], Period: [':', '/', '', ''], Slash: ['!', '§', '', ''], NumpadDivide: ['/', '/', '', ''], NumpadMultiply: ['*', '*', '', ''], NumpadSubtract: ['-', '-', '', ''], NumpadAdd: ['+', '+', '', ''], IntlBackslash: ['<', '>', '', ''], }, }); register$1({ id: 'windows.german', locale: 'de', displayName: 'German', platform: 'windows', virtualLayout: 'qwertz', score: 0, mapping: { KeyA: ['a', 'A', '', ''], KeyB: ['b', 'B', '', ''], KeyC: ['c', 'C', '', ''], KeyD: ['d', 'D', '', ''], KeyE: ['e', 'E', '€', ''], KeyF: ['f', 'F', '', ''], KeyG: ['g', 'G', '', ''], KeyH: ['h', 'H', '', ''], KeyI: ['i', 'I', '', ''], KeyJ: ['j', 'J', '', ''], KeyK: ['k', 'K', '', ''], KeyL: ['l', 'L', '', ''], KeyM: ['m', 'M', 'µ', ''], KeyN: ['n', 'N', '', ''], KeyO: ['o', 'O', '', ''], KeyP: ['p', 'P', '', ''], KeyQ: ['q', 'Q', '@', ''], KeyR: ['r', 'R', '', ''], KeyS: ['s', 'S', '', ''], KeyT: ['t', 'T', '', ''], KeyU: ['u', 'U', '', ''], KeyV: ['v', 'V', '', ''], KeyW: ['w', 'W', '', ''], KeyX: ['x', 'X', '', ''], KeyY: ['z', 'Z', '', ''], KeyZ: ['y', 'Y', '', ''], Digit1: ['1', '!', '', ''], Digit2: ['2', '"', '²', ''], Digit3: ['3', '§', '³', ''], Digit4: ['4', '$', '', ''], Digit5: ['5', '%', '', ''], Digit6: ['6', '&', '', ''], Digit7: ['7', '/', '{', ''], Digit8: ['8', '(', '[', ''], Digit9: ['9', ')', ']', ''], Digit0: ['0', '=', '}', ''], Space: [' ', ' ', '', ''], Minus: ['ß', '?', '\\', 'ẞ'], Equal: ['´', '`', '', ''], BracketLeft: ['ü', 'Ü', '', ''], BracketRight: ['+', '*', '~', ''], Backslash: ['#', "'", '', ''], Semicolon: ['ö', 'Ö', '', ''], Quote: ['ä', 'Ä', '', ''], Backquote: ['^', '°', '', ''], Comma: [',', ';', '', ''], Period: ['.', ':', '', ''], Slash: ['-', '_', '', ''], NumpadDivide: ['/', '/', '', ''], NumpadMultiply: ['*', '*', '', ''], NumpadSubtract: ['-', '-', '', ''], NumpadAdd: ['+', '+', '', ''], IntlBackslash: ['<', '>', '|', ''], }, }); register$1({ id: 'apple.german', locale: 'de', displayName: 'German', virtualLayout: 'qwertz', platform: 'apple', score: 0, mapping: { KeyA: ['a', 'A', 'å', 'Å'], KeyB: ['b', 'B', '∫', '‹'], KeyC: ['c', 'C', 'ç', 'Ç'], KeyD: ['d', 'D', '∂', '™'], KeyE: ['e', 'E', '€', '‰'], KeyF: ['f', 'F', 'ƒ', 'Ï'], KeyG: ['g', 'G', '©', 'Ì'], KeyH: ['h', 'H', 'ª', 'Ó'], KeyI: ['i', 'I', '⁄', 'Û'], KeyJ: ['j', 'J', 'º', 'ı'], KeyK: ['k', 'K', '∆', 'ˆ'], KeyL: ['l', 'L', '@', 'fl'], KeyM: ['m', 'M', 'µ', '˘'], KeyN: ['n', 'N', '~', '›'], KeyO: ['o', 'O', 'ø', 'Ø'], KeyP: ['p', 'P', 'π', '∏'], KeyQ: ['q', 'Q', '«', '»'], KeyR: ['r', 'R', '®', '¸'], KeyS: ['s', 'S', '‚', 'Í'], KeyT: ['t', 'T', '†', '˝'], KeyU: ['u', 'U', '¨', 'Á'], KeyV: ['v', 'V', '√', '◊'], KeyW: ['w', 'W', '∑', '„'], KeyX: ['x', 'X', '≈', 'Ù'], KeyY: ['z', 'Z', 'Ω', 'ˇ'], KeyZ: ['y', 'Y', '¥', '‡'], Digit1: ['1', '!', '¡', '¬'], Digit2: ['2', '"', '“', '”'], Digit3: ['3', '§', '¶', '#'], Digit4: ['4', '$', '¢', '£'], Digit5: ['5', '%', '[', 'fi'], Digit6: ['6', '&', ']', '^'], Digit7: ['7', '/', '|', '\\'], Digit8: ['8', '(', '{', '˜'], Digit9: ['9', ')', '}', '·'], Digit0: ['0', '=', '≠', '¯'], Space: [' ', ' ', ' ', ' '], Minus: ['ß', '?', '¿', '˙'], Equal: ['´', '`', "'", '˚'], BracketLeft: ['ü', 'Ü', '•', '°'], BracketRight: ['+', '*', '±', ''], Backslash: ['#', "'", '‘', '’'], Semicolon: ['ö', 'Ö', 'œ', 'Œ'], Quote: ['ä', 'Ä', 'æ', 'Æ'], Backquote: ['<', '>', '≤', '≥'], Comma: [',', ';', '∞', '˛'], Period: ['.', ':', '…', '÷'], Slash: ['-', '_', '–', '—'], NumpadDivide: ['/', '/', '/', '/'], NumpadMultiply: ['*', '*', '*', '*'], NumpadSubtract: ['-', '-', '-', '-'], NumpadAdd: ['+', '+', '+', '+'], NumpadDecimal: [',', ',', '.', '.'], IntlBackslash: ['^', '°', '„', '“'], NumpadEqual: ['=', '=', '=', '='], }, }); register$1({ id: 'dvorak', locale: 'en', displayName: 'Dvorak', virtualLayout: 'dvorak', platform: '', score: 0, mapping: { KeyA: ['a', 'A', 'å', 'Å'], KeyB: ['x', 'X', '≈', '˛'], KeyC: ['j', 'J', '∆', 'Ô'], KeyD: ['e', 'E', '´', '´'], KeyE: ['.', '>', '≥', '˘'], KeyF: ['u', 'U', '¨', '¨'], KeyG: ['i', 'I', 'ˆ', 'ˆ'], KeyH: ['d', 'D', '∂', 'Î'], KeyI: ['c', 'C', 'ç', 'Ç'], KeyJ: ['h', 'H', '˙', 'Ó'], KeyK: ['t', 'T', '†', 'ˇ'], KeyL: ['n', 'N', '˜', '˜'], KeyM: ['m', 'M', 'µ', 'Â'], KeyN: ['b', 'B', '∫', 'ı'], KeyO: ['r', 'R', '®', '‰'], KeyP: ['l', 'L', '¬', 'Ò'], KeyQ: ["'", '"', 'æ', 'Æ'], KeyR: ['p', 'P', 'π', '∏'], KeyS: ['o', 'O', 'ø', 'Ø'], KeyT: ['y', 'Y', '¥', 'Á'], KeyU: ['g', 'G', '©', '˝'], KeyV: ['k', 'K', '˚', ''], KeyW: [',', '<', '≤', '¯'], KeyX: ['q', 'Q', 'œ', 'Œ'], KeyY: ['f', 'F', 'ƒ', 'Ï'], KeyZ: [';', ':', '…', 'Ú'], Digit1: ['1', '!', '¡', '⁄'], Digit2: ['2', '@', '™', '€'], Digit3: ['3', '#', '£', '‹'], Digit4: ['4', '$', '¢', '›'], Digit5: ['5', '%', '∞', 'fi'], Digit6: ['6', '^', '§', 'fl'], Digit7: ['7', '&', '¶', '‡'], Digit8: ['8', '*', '•', '°'], Digit9: ['9', '(', 'ª', '·'], Digit0: ['0', ')', 'º', '‚'], Space: [' ', ' ', ' ', ' '], Minus: ['[', '{', '“', '”'], Equal: [']', '}', '‘', '’'], BracketLeft: ['/', '?', '÷', '¿'], BracketRight: ['=', '+', '≠', '±'], Backslash: ['\\', '|', '«', '»'], Semicolon: ['s', 'S', 'ß', 'Í'], Quote: ['-', '_', '–', '—'], Backquote: ['`', '~', '`', '`'], Comma: ['w', 'W', '∑', '„'], Period: ['v', 'V', '√', '◊'], Slash: ['z', 'Z', 'Ω', '¸'], NumpadDivide: ['/', '/', '/', '/'], NumpadMultiply: ['*', '*', '*', '*'], NumpadSubtract: ['-', '-', '-', '-'], NumpadAdd: ['+', '+', '+', '+'], Numpad1: ['1', '1', '1', '1'], Numpad2: ['2', '2', '2', '2'], Numpad3: ['3', '3', '3', '3'], Numpad4: ['4', '4', '4', '4'], Numpad5: ['5', '5', '5', '5'], Numpad6: ['6', '6', '6', '6'], Numpad7: ['7', '7', '7', '7'], Numpad8: ['8', '8', '8', '8'], Numpad9: ['9', '9', '9', '9'], Numpad0: ['0', '0', '0', '0'], NumpadDecimal: ['.', '.', '.', '.'], IntlBackslash: ['§', '±', '§', '±'], NumpadEqual: ['=', '=', '=', '='], AudioVolumeUp: ['', '=', '', '='], }, }); /** * @param p The platform to test against. */ function matchPlatform(p) { if (isBrowser()) { const plat = osPlatform(); const isNeg = p.startsWith('!'); const isMatch = p.endsWith(plat); if (isNeg && !isMatch) return true; if (!isNeg && isMatch) return true; } if (p === '!other') return false; return p === 'other'; } /** * Return the selector matching the keystroke. * The keybindings and keystroke should be in normalized form * (i.e. using key code, e.g. `[KeyQ]`) * */ function getCommandForKeybinding(keybindings, mode, inKeystroke) { if (keybindings.length === 0) return ''; // Normalize keystroke to the format (order of modifiers) expected by keybindings const keystroke = keystrokeModifiersToString(keystrokeModifiersFromString(inKeystroke)); // Try to match using a virtual keystroke for (let i = keybindings.length - 1; i >= 0; i--) { if (keybindings[i].key === keystroke) { if (!keybindings[i].ifMode || keybindings[i].ifMode === mode) return keybindings[i].command; } } return ''; } function commandToString(command) { let result = command; if (isArray(result)) { result = result.length > 0 ? result[0] + '(' + result.slice(1).join('') + ')' : ''; } return result; } function getKeybindingsForCommand(keybindings, command) { let result = []; if (typeof command === 'string') { const candidate = REVERSE_KEYBINDINGS[command]; if (isArray(candidate)) result = candidate.slice(); else if (candidate) result.push(candidate); } // A command can be either a simple selector, or a selector // with arguments. Normalize it to a string const normalizedCommand = commandToString(command); const regex = new RegExp('^' + normalizedCommand .replace('\\', '\\\\') .replace('|', '\\|') .replace('*', '\\*') .replace('$', '\\$') .replace('^', '\\^') + '([^*a-zA-Z]|$)'); for (const keybinding of keybindings) { if (regex.test(commandToString(keybinding.command))) result.push(keybinding.key); } return result.map(getKeybindingMarkup); } /** * Return a human readable representation of a shortcut as a markup string * @revisit */ function getKeybindingMarkup(keystroke) { var _a; const useSymbol = /macos|ios|/.test(osPlatform()); const segments = keystroke.split('+'); let result = ''; for (const segment of segments) { if (!useSymbol && result) result += '<span class="ML__shortcut-join">+</span>'; if (segment.startsWith('[Key')) result += segment.slice(4, 5); else if (segment.startsWith('Key')) result += segment.slice(3, 4); else if (segment.startsWith('[Digit')) result += segment.slice(6, 7); else if (segment.startsWith('Digit')) result += segment.slice(5, 6); else { result += (_a = { 'cmd': '\u2318', 'meta': useSymbol ? '\u2318' : 'command', 'shift': useSymbol ? '\u21E7' : 'shift', 'alt': useSymbol ? '\u2325' : 'alt', 'ctrl': useSymbol ? '\u2303' : 'control', '\n': useSymbol ? '\u23CE' : 'return', '[return]': useSymbol ? '\u23CE' : 'return', '[enter]': useSymbol ? '\u2324' : 'enter', '[tab]': useSymbol ? '\u21E5' : 'tab', // 'Esc': useSymbol ? '\u238b' : 'esc', '[escape]': 'esc', '[backspace]': useSymbol ? '\u232B' : 'backspace', '[delete]': useSymbol ? '\u2326' : 'del', '[pageup]': useSymbol ? '\u21DE' : 'page up', '[pagedown]': useSymbol ? '\u21DF' : 'page down', '[home]': useSymbol ? '\u2912' : 'home', '[end]': useSymbol ? '\u2913' : 'end', '[space]': 'space', '[equal]': '=', '[minus]': '-', '[comma]': ',', '[slash]': '/', '[backslash]': '\\', '[bracketleft]': '[', '[bracketright]': ']', 'semicolon': ';', 'period': '.', 'comma': ',', 'minus': '-', 'equal': '=', 'quote': "'", 'bracketLeft': '[', 'bracketRight': ']', 'backslash': '\\', 'intlbackslash': '\\', 'backquote': '`', 'slash': '/', 'numpadmultiply': '* 🔢', 'numpaddivide': '/ 🔢', 'numpadsubtract': '- 🔢', 'numpadadd': '+ 🔢', 'numpaddecimal': '. 🔢', 'numpadcomma': ', 🔢', 'help': 'help', 'left': '\u21E0', 'up': '\u21E1', 'right': '\u21E2', 'down': '\u21E3', '[arrowleft]': '\u21E0', '[arrowup]': '\u21E1', '[arrowright]': '\u21E2', '[arrowdown]': '\u21E3', '[digit0]': '0', '[digit1]': '1', '[digit2]': '2', '[digit3]': '3', '[digit4]': '4', '[digit5]': '5', '[digit6]': '6', '[digit7]': '7', '[digit8]': '8', '[digit9]': '9', }[segment.toLowerCase()]) !== null && _a !== void 0 ? _a : segment.toUpperCase(); } } return result; } function normalizeKeybinding(keybinding, layout) { if (keybinding.ifPlatform && !/^!?(macos|windows|android|ios|chromeos|other)$/.test(keybinding.ifPlatform)) { throw new Error(`Unexpected platform "${keybinding.ifPlatform}" for keybinding ${keybinding.key}`); } if (keybinding.ifLayout !== undefined && (layout.score === 0 || !keybinding.ifLayout.includes(layout.id))) return undefined; const modifiers = keystrokeModifiersFromString(keybinding.key); let platform = keybinding.ifPlatform; if (modifiers.cmd) { if (platform && platform !== 'macos' && platform !== 'ios') { throw new Error('Unexpected "cmd" modifier with platform "' + platform + '"' + '\n' + '"cmd" modifier can only be used with macOS or iOS platform.'); } if (!platform) platform = osPlatform() === 'ios' ? 'ios' : 'macos'; modifiers.win = false; modifiers.cmd = false; modifiers.meta = true; } if (modifiers.win) { if (platform && platform !== 'windows') { throw new Error('Unexpected "win" modifier with platform "' + platform + '"' + '\n' + '"win" modifier can only be used with Windows platform.'); } platform = 'windows'; modifiers.win = false; modifiers.cmd = false; modifiers.meta = true; } if (platform && !matchPlatform(platform)) return undefined; if (/^\[.+\]$/.test(modifiers.key)) { // This is a keybinding specified with a key code (e.g. `[KeyW]`) return { ...keybinding, ifPlatform: platform, key: keystrokeModifiersToString(modifiers), }; } // This is not a key code (e.g. `[KeyQ]`) it's a simple key (e.g. `a`). // Convert it to a key code. const code = getCodeForKey(modifiers.key, layout); if (!code) throw new Error('Invalid keybinding key "' + keybinding.key + '"'); if ((code.shift && modifiers.shift) || (code.alt && modifiers.alt)) { throw new Error(`The keybinding ${keybinding.key} (${selectorToString(keybinding.command)}) is conflicting with the key combination ${keystrokeModifiersToString(code)} using the ${layout.displayName} keyboard layout`); } code.shift = code.shift || modifiers.shift; code.alt = code.alt || modifiers.alt; code.meta = modifiers.meta; code.ctrl = modifiers.ctrl; return { ...keybinding, ifPlatform: platform, key: keystrokeModifiersToString(code), }; } function selectorToString(selector) { if (Array.isArray(selector)) { const sel = [...selector]; return (sel.shift() + '(' + sel .map((x) => (typeof x === 'string' ? `"${x}"` : x.toString())) .join(', ') + ')'); } return selector; } /** * Parse the input keybindings and return them normalized: * - 'keys' are transformed to 'code' according to the current keyboard layout * - keybindings that don't apply to the current platform are removed */ function normalizeKeybindings(keybindings, layout, onError) { const result = []; const errors = []; for (const x of keybindings) { try { const keybinding = normalizeKeybinding(x, layout); if (keybinding) { const matches = result.filter((x) => x.key === keybinding.key && x.ifMode === keybinding.ifMode); if (matches.length > 0) { throw new Error(`Ambiguous key binding ${x.key} (${selectorToString(x.command)}) matches ${matches[0].key} (${selectorToString(matches[0].command)}) with the ${layout.displayName} keyboard layout`); } result.push(keybinding); } } catch (error) { if (error instanceof Error) errors.push(error.message); } } if (errors.length > 0) onError(errors); return result; } const SPECIAL_IDENTIFIERS = { '\\ne': '≠', '\\neq': '≠', '\u2212': '-', '-': '-', '\\alpha': 'alpha', '\\beta': 'beta', '\\gamma': 'gamma', '\\delta': 'delta', '\\epsilon': 'epsilon', '\\varepsilon': 'varepsilon', '\\zeta': 'zeta', '\\eta': 'eta', '\\theta': 'theta', '\\vartheta': 'vartheta', '\\iota': 'iota', '\\kappa': 'kappa', '\\lambda': 'lambda', '\\mu': 'mu', '\\nu': 'nu', '\\xi': 'xi', '\\pi': 'pi', '\\rho': 'rho', '\\sigma': 'sigma', '\\tau': 'tau', '\\upsilon': 'upsilon', '\\phi': 'phi', '\\varphi': 'varphi', '\\chi': 'chi', '\\psi': 'psi', '\\omega': 'omega', '\\Gamma': 'Gamma', '\\Delta': 'Delta', '\\Theta': 'Theta', '\\Lambda': 'Lambda', '\\Xi': 'Xi', '\\Pi': 'Pi', '\\Sigma': 'Sigma', '\\Phi': 'Phi', '\\Psi': 'Psi', '\\Omega': 'Omega', '\\exponentialE': 'e', '\\imaginaryI': 'i', '\\imaginaryJ': 'j', }; const SPECIAL_OPERATORS$1 = { '\\pm': '+-', '\\times': 'xx', '\\colon': ':', '\\vert': '|', '\\Vert': '||', '\\mid': '|', '\\lbrace': '{', '\\rbrace': '}', '\\lparen': '(', '\\rparen': ')', '\\langle': '(:', '\\rangle': ':)', // '\\lfloor': '\u230a', // '\\rfloor': '\u230b', // '\\lceil': '\u2308', // '\\rceil': '\u2309', // '\\vec': '⃗', // '\\acute': '´', // '\\grave': '`', // '\\dot': '˙', // '\\ddot': '¨', // '\\tilde': '~', // '\\bar': '¯', // '\\breve': '˘', // '\\check': 'ˇ', // '\\hat': '^' }; function atomToAsciiMath(atom) { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l; if (!atom) return ''; if (isArray(atom)) { if (atom.length === 0) return ''; if (atom[0].type === 'first') atom = atom.slice(1); if (atom.length === 0) return ''; let result = ''; if (atom[0].mode === 'latex') for (const x of atom) result += atomToAsciiMath(x); else if (atom[0].mode === 'text') { // Text mode... put it in (ASCII) quotes let i = 0; result = '"'; while (((_a = atom[i]) === null || _a === void 0 ? void 0 : _a.mode) === 'text') { result += atom[i].body ? atomToAsciiMath(atom[i].body) : atom[i].value; i++; } result += '"' + atomToAsciiMath(atom.slice(i)); } else if (atom[0].mode === 'math') { let i = 0; while (atom[i] && atom[i].mode === 'math') { result += atomToAsciiMath(atom[i]); i++; } result += atomToAsciiMath(atom.slice(i)); } else console.warn('toASCIIMath: Unexpected mode'); return result.trim(); } if (atom.mode === 'text') return '"' + atom.value + '"'; // Text -- add in (ASCII) quotes let result = ''; const { command } = atom; let m; switch (atom.type) { case 'first': return ''; case 'group': case 'root': if (SPECIAL_IDENTIFIERS[atom.command]) result = SPECIAL_IDENTIFIERS[atom.command]; else result = atomToAsciiMath(atom.body); break; case 'genfrac': { const genfracAtom = atom; if (genfracAtom.leftDelim || genfracAtom.rightDelim) { result += genfracAtom.leftDelim === '.' || !genfracAtom.leftDelim ? '{:' : genfracAtom.leftDelim; } if (genfracAtom.hasBarLine) { result += '('; result += atomToAsciiMath(genfracAtom.above); result += ')/('; result += atomToAsciiMath(genfracAtom.below); result += ')'; } else { // No bar line, i.e. \choose, etc... result += '(' + atomToAsciiMath(genfracAtom.above) + '),'; result += '(' + atomToAsciiMath(genfracAtom.below) + ')'; } if (genfracAtom.leftDelim || genfracAtom.rightDelim) { result += genfracAtom.rightDelim === '.' || !genfracAtom.rightDelim ? '{:' : genfracAtom.rightDelim; } } break; case 'surd': result += !atom.hasEmptyBranch('above') ? 'root(' + atomToAsciiMath(atom.above) + ')(' + atomToAsciiMath(atom.body) + ')' : 'sqrt(' + atomToAsciiMath(atom.body) + ')'; break; case 'latex': result = atom.value; break; case 'leftright': { const leftrightAtom = atom; result += leftrightAtom.leftDelim === '.' || !leftrightAtom.leftDelim ? '{:' : leftrightAtom.leftDelim; result += atomToAsciiMath(leftrightAtom.body); result += leftrightAtom.rightDelim === '.' || !leftrightAtom.rightDelim ? ':}' : leftrightAtom.rightDelim; } break; case 'sizeddelim': case 'delim': // Result += '<mo separator="true"' + makeID(atom.id, options) + '>' + (SPECIAL_OPERATORS[atom.delim] || atom.delim) + '</mo>'; break; case 'overlap': break; case 'overunder': break; case 'mord': result = (_c = (_b = SPECIAL_IDENTIFIERS[command]) !== null && _b !== void 0 ? _b : command) !== null && _c !== void 0 ? _c : (typeof atom.value === 'string' ? atom.value : ''); if (result.startsWith('\\')) result += ' '; m = command ? command.match(/{?\\char"([\dabcdefABCDEF]*)}?/) : null; if (m) { // It's a \char command result = String.fromCodePoint(Number.parseInt('0x' + m[1])); } else if (result.length > 0 && result.startsWith('\\')) { // Atom is an identifier with no special handling. Use the // Unicode value result = typeof atom.value === 'string' ? atom.value.charAt(0) : atom.command; } break; case 'mbin': case 'mrel': case 'minner': result = (_e = (_d = SPECIAL_IDENTIFIERS[command]) !== null && _d !== void 0 ? _d : SPECIAL_OPERATORS$1[command]) !== null && _e !== void 0 ? _e : atom.value; break; case 'mopen': case 'mclose': result += atom.value; break; case 'mpunct': result = (_f = SPECIAL_OPERATORS$1[command]) !== null && _f !== void 0 ? _f : command; break; case 'mop': if (atom.value !== '\u200B') { // Not ZERO-WIDTH result = ''; result += command === '\\operatorname' ? atomToAsciiMath(atom.body) : (_g = atom.value) !== null && _g !== void 0 ? _g : command; result += ' '; } break; case 'array': const array = atom.array; const environment = atom.environmentName; const rowDelim = (_h = { 'bmatrix': ['[', ']'], 'bmatrix*': ['[', ']'], }[environment]) !== null && _h !== void 0 ? _h : ['(', ')']; const rows = []; for (const row of array) { const cells = []; for (const cell of row) cells.push(rowDelim[0] + atomToAsciiMath(cell) + rowDelim[1]); rows.push(cells.join(',')); } const delim = (_j = { 'bmatrix': ['[', ']'], 'bmatrix*': ['[', ']'], 'cases': ['{', ':}'], }[environment]) !== null && _j !== void 0 ? _j : ['(', ')']; result = delim[0] + rows.join(',') + delim[1]; break; case 'box': break; case 'spacing': break; case 'enclose': result = '(' + atomToAsciiMath(atom.body) + ')'; break; case 'space': result = ' '; break; case 'msubsup': result = ''; break; case 'macro': result = (_l = (_k = SPECIAL_IDENTIFIERS[command]) !== null && _k !== void 0 ? _k : SPECIAL_OPERATORS$1[command]) !== null && _l !== void 0 ? _l : atomToAsciiMath(atom.body); break; } // Subscripts before superscripts (according to the ASCIIMath spec) if (!atom.hasEmptyBranch('subscript')) { result += '_'; const arg = atomToAsciiMath(atom.subscript); result += arg.length > 1 ? '(' + arg + ')' : arg; } if (!atom.hasEmptyBranch('superscript')) { result += '^'; const arg = atomToAsciiMath(atom.superscript); result += arg.length > 1 ? '(' + arg + ')' : arg; } return result; } /** * This module contains utilities to debug mathlive internal data structures. * * It is also used by the automated test suite. */ function latexToAsciiMath(latex, mode = 'math') { const root = new Atom('root'); root.body = parseLatex(latex, { parseMode: mode }); return atomToAsciiMath(root); } function asciiMathToLatex(ascii) { return parseMathString(ascii, { format: 'ascii-math' })[1]; } const MathliveDebug = { latexToAsciiMath, asciiMathToLatex, FUNCTIONS: LATEX_COMMANDS, MATH_SYMBOLS, TEXT_SYMBOLS, ENVIRONMENTS, DEFAULT_KEYBINDINGS, getKeybindingMarkup, }; const SPECIAL_OPERATORS = { '\\ne': '<mo>≠</mo>', '\\neq': '<mo>&neq;</mo>', '\\pm': '±', '\\times': '×', '\\colon': ':', '\\vert': '|', '\\Vert': '\u2225', '\\mid': '\u2223', '\\lbrace': '{', '\\rbrace': '}', '\\lparen': '(', '\\rparen': ')', '\\langle': '\u27E8', '\\rangle': '\u27E9', '\\lfloor': '\u230A', '\\rfloor': '\u230B', '\\lceil': '\u2308', '\\rceil': '\u2309', '\\vec': '⃗', '\\acute': '´', '\\grave': '`', '\\dot': '˙', '\\ddot': '¨', '\\tilde': '~', '\\bar': '¯', '\\breve': '˘', '\\check': 'ˇ', '\\hat': '^', }; const APPLY_FUNCTION = '⁡'; const INVISIBLE_TIMES = '⁢'; function xmlEscape(string) { return (string // .replace(/&/g, '&') .replace(/"/g, '"') .replace(/'/g, ''') .replace(/</g, '<') .replace(/>/g, '>')); } function makeID(id, options) { if (!id || !options.generateID) return ''; // Note: the 'extid' attribute is recognized by SRE as an attribute // to be passed to SSML as a <mark> tag. return ` extid="${id}"`; } function scanIdentifier(stream, final, options) { let result = false; final = final !== null && final !== void 0 ? final : stream.atoms.length; let mathML = ''; let body = ''; const atom = stream.atoms[stream.index]; if (stream.index < final && (atom.type === 'mord' || atom.type === 'macro') && !atom.isDigit()) { body = atomToMathML(atom, options); stream.index += 1; } if (body.length > 0) { result = true; mathML = body; if ((stream.lastType === 'mi' || stream.lastType === 'mn' || stream.lastType === 'mtext' || stream.lastType === 'fence') && !/^<mo>(.*)<\/mo>$/.test(mathML)) mathML = `<mo>${INVISIBLE_TIMES}</mo>${mathML}`; // ⁢ if (body.endsWith('>f</mi>') || body.endsWith('>g</mi>')) { mathML += `<mo>${APPLY_FUNCTION}</mo>`; // ⁡ stream.lastType = 'applyfunction'; } else stream.lastType = /^<mo>(.*)<\/mo>$/.test(mathML) ? 'mo' : 'mi'; if (!parseSubsup(body, stream, options)) stream.mathML += mathML; } return result; } /** * Return true if the current atom is a standalone superscript atom * i.e. an atom with no content, except of a superscript. * Superscripts can be encoded either as an attribute on the last atom * or as a standalone, empty, atom following the one to which it applies. * @param {object} stream */ function isSuperscriptAtom(stream) { return (stream.index < stream.atoms.length && stream.atoms[stream.index].superscript && stream.atoms[stream.index].type === 'msubsup'); } function isSubscriptAtom(stream) { return (stream.index < stream.atoms.length && stream.atoms[stream.index].subscript && stream.atoms[stream.index].type === 'msubsup'); } function indexOfSuperscriptInNumber(stream) { let result = -1; let i = stream.index; let done = false; let found = false; while (i < stream.atoms.length && !done && !found) { const atom = stream.atoms[i]; done = !atom.isDigit(); found = !done && atom.superscript !== undefined; i++; } if (found) result = i - 1; return result; } function parseSubsup(base, stream, options) { let atom = stream.atoms[stream.index - 1]; if (!atom) return false; if (!atom.superscript && !atom.subscript) { if (isSuperscriptAtom(stream) || isSubscriptAtom(stream)) { atom = stream.atoms[stream.index]; stream.index += 1; } } if (!atom) return false; const superscript = toMathML(atom.superscript, 0, 0, options).mathML; const subscript = toMathML(atom.subscript, 0, 0, options).mathML; if (!superscript && !subscript) return false; let mathML = ''; if (superscript && subscript) mathML = `<msubsup>${base}${subscript}${superscript}</msubsup>`; else if (superscript) mathML = `<msup>${base}${superscript}</msup>`; else if (subscript) mathML = `<msub>${base}${subscript}</msub>`; stream.mathML += mathML; stream.lastType = ''; return true; } function scanText(stream, final, options) { final = final !== null && final !== void 0 ? final : stream.atoms.length; const initial = stream.index; let mathML = ''; while (stream.index < final && stream.atoms[stream.index].mode === 'text') { mathML += stream.atoms[stream.index].value ? stream.atoms[stream.index].value : ' '; stream.index += 1; } if (mathML.length > 0) { stream.mathML += `<mtext ${makeID(stream.atoms[initial].id, options)} >${mathML}</mtext>`; stream.lastType = 'mtext'; return true; } return false; } function scanNumber(stream, final, options) { final = final !== null && final !== void 0 ? final : stream.atoms.length; const initial = stream.index; let mathML = ''; let superscript = indexOfSuperscriptInNumber(stream); if (superscript >= 0 && superscript < final) final = superscript; while (stream.index < final && stream.atoms[stream.index].isDigit()) { mathML += stream.atoms[stream.index].asDigit(); stream.index += 1; } if (mathML.length <= 0) return false; mathML = '<mn' + makeID(stream.atoms[initial].id, options) + '>' + mathML + '</mn>'; if (superscript < 0 && isSuperscriptAtom(stream)) { superscript = stream.index; stream.index += 1; } if (!parseSubsup(mathML, stream, options)) { stream.mathML += mathML; stream.lastType = 'mn'; } return true; } function scanFence(stream, final, options) { let result = false; final = final !== null && final !== void 0 ? final : stream.atoms.length; let mathML = ''; let lastType = ''; if (stream.index < final && stream.atoms[stream.index].type === 'mopen') { let found = false; let depth = 0; const openIndex = stream.index; let closeIndex = -1; let index = openIndex + 1; while (index < final && !found) { if (stream.atoms[index].type === 'mopen') depth += 1; else if (stream.atoms[index].type === 'mclose') depth -= 1; if (depth === -1) { found = true; closeIndex = index; } index += 1; } if (found) { mathML = '<mrow>'; mathML += toMo(stream.atoms[openIndex], options); mathML += toMathML(stream.atoms, openIndex + 1, closeIndex, options).mathML; mathML += toMo(stream.atoms[closeIndex], options); mathML += '</mrow>'; if (stream.lastType === 'mi' || stream.lastType === 'mn' || stream.lastType === 'mfrac' || stream.lastType === 'fence') mathML = `<mo>${INVISIBLE_TIMES}</mo>${mathML}`; // ⁢ stream.index = closeIndex + 1; if (parseSubsup(mathML, stream, options)) { result = true; stream.lastType = ''; mathML = ''; } lastType = 'fence'; } } if (mathML.length > 0) { result = true; stream.mathML += mathML; stream.lastType = lastType; } return result; } function scanOperator(stream, final, options) { let result = false; final = final !== null && final !== void 0 ? final : stream.atoms.length; let mathML = ''; let lastType = ''; const atom = stream.atoms[stream.index]; if (stream.index < final && (atom.type === 'mbin' || atom.type === 'mrel')) { mathML += atomToMathML(stream.atoms[stream.index], options); stream.index += 1; lastType = 'mo'; } else if (stream.index < final && atom.type === 'mop') { // MathML += '<mrow>'; if (atom.subsupPlacement === 'over-under' && (atom.superscript || atom.subscript)) { // Operator with limits, e.g. \sum const op = toMo(atom, options); if (atom.superscript && atom.subscript) { // Both superscript and subscript mathML += '<munderover>' + op; mathML += toMathML(atom.subscript, 0, 0, options).mathML; mathML += toMathML(atom.superscript, 0, 0, options).mathML; mathML += '</munderover>'; } else if (atom.superscript) { // Superscript only mathML += '<mover>' + op; mathML += toMathML(atom.superscript, 0, 0, options).mathML; mathML += '</mover>'; } else { // Subscript only mathML += '<munder>' + op; mathML += toMathML(atom.subscript, 0, 0, options).mathML; mathML += '</munder>'; } lastType = 'mo'; } else { const atom = stream.atoms[stream.index]; const isUnit = atom.value === '\\operatorname'; const op = isUnit ? '<mi class="MathML-Unit"' + makeID(atom.id, options) + '>' + toString(atom.value) + '</mi>' : toMo(atom, options); mathML += op; if (!isUnit && !/^<mo>(.*)<\/mo>$/.test(op)) { mathML += `<mo>${APPLY_FUNCTION}</mo>`; // APPLY FUNCTION // mathML += scanArgument(stream); lastType = 'applyfunction'; } else lastType = isUnit ? 'mi' : 'mo'; } if ((stream.lastType === 'mi' || stream.lastType === 'mn') && !/^<mo>(.*)<\/mo>$/.test(mathML)) mathML = `<mo>${INVISIBLE_TIMES}</mo>${mathML}`; // ⁢ stream.index += 1; } if (!parseSubsup(mathML, stream, options)) { if (mathML.length > 0) { result = true; stream.mathML += mathML; stream.lastType = lastType; } } return result; } /** * Given an atom or an array of atoms, return their MathML representation as * a string. * @param {string|Atom|Atom[]} input * @param initial index of the input to start conversion from * @param final last index of the input to stop conversion to */ function toMathML(input, initial, final, options) { const result = { atoms: [], index: initial !== null && initial !== void 0 ? initial : 0, mathML: '', lastType: '', }; if (typeof input === 'number' || typeof input === 'boolean') result.mathML = input.toString(); else if (typeof input === 'string') result.mathML = input; else if (input instanceof Atom) result.mathML = atomToMathML(input, options); else if (Array.isArray(input)) { result.atoms = input; let count = 0; final = final ? final : input ? input.length : 0; while (result.index < final) { if (scanText(result, final, options) || scanNumber(result, final, options) || scanIdentifier(result, final, options) || scanOperator(result, final, options) || scanFence(result, final, options)) count += 1; else if (result.index < final) { let mathML = atomToMathML(result.atoms[result.index], options); if (result.lastType === 'mn' && mathML.length > 0 && result.atoms[result.index].type === 'genfrac') { // If this is a fraction preceded by a number (e.g. 2 1/2), // add an "invisible plus" (U+0264) character in front of it mathML = '<mo>⁤</mo>' + mathML; } if (result.atoms[result.index].type === 'genfrac') result.lastType = 'mfrac'; else result.lastType = ''; result.index += 1; if (!parseSubsup(mathML, result, options)) { if (mathML.length > 0) { result.mathML += mathML; count += 1; } } else count += 2; } } // If there are more than a single element, wrap them in a mrow tag. if (count > 1) result.mathML = '<mrow>' + result.mathML + '</mrow>'; } return result; } function toMo(atom, options) { let result = ''; const body = toString(atom.value); if (body) result = '<mo' + makeID(atom.id, options) + '>' + body + '</mo>'; return result; } function toString(atoms) { if (!atoms) return ''; if (typeof atoms === 'string') return xmlEscape(atoms); if (!Array.isArray(atoms) && typeof atoms.body === 'string') return xmlEscape(atoms.body); let result = ''; for (const atom of atoms) if (typeof atom.value === 'string') result += atom.value; return xmlEscape(result); } /** * Return a MathML fragment representation of a single atom * */ function atomToMathML(atom, options) { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r; // For named SVG atoms, map to a Unicode char const SVG_CODE_POINTS = { widehat: '^', widecheck: 'ˇ', widetilde: '~', utilde: '~', overleftarrow: '\u2190', underleftarrow: '\u2190', xleftarrow: '\u2190', overrightarrow: '\u2192', underrightarrow: '\u2192', xrightarrow: '\u2192', underbrace: '\u23DF', overbrace: '\u23DE', overgroup: '\u23E0', undergroup: '\u23E1', overleftrightarrow: '\u2194', underleftrightarrow: '\u2194', xleftrightarrow: '\u2194', Overrightarrow: '\u21D2', xRightarrow: '\u21D2', overleftharpoon: '\u21BC', xleftharpoonup: '\u21BC', overrightharpoon: '\u21C0', xrightharpoonup: '\u21C0', xLeftarrow: '\u21D0', xLeftrightarrow: '\u21D4', xhookleftarrow: '\u21A9', xhookrightarrow: '\u21AA', xmapsto: '\u21A6', xrightharpoondown: '\u21C1', xleftharpoondown: '\u21BD', xrightleftharpoons: '\u21CC', xleftrightharpoons: '\u21CB', xtwoheadleftarrow: '\u219E', xtwoheadrightarrow: '\u21A0', xlongequal: '=', xtofrom: '\u21C4', xrightleftarrows: '\u21C4', xrightequilibrium: '\u21CC', xleftequilibrium: '\u21CB', // None better available. }; const SPECIAL_IDENTIFIERS = { '\\exponentialE': 'ⅇ', '\\imaginaryI': 'ⅈ', '\\differentialD': 'ⅆ', '\\capitalDifferentialD': 'ⅅ', '\\alpha': 'α', '\\pi': 'π', '\\infty': '∞', '\\forall': '∀', '\\nexists': '∄', '\\exists': '∃', '\\hbar': '\u210F', '\\cdotp': '\u22C5', '\\ldots': '\u2026', '\\cdots': '\u22EF', '\\ddots': '\u22F1', '\\vdots': '\u22EE', '\\ldotp': '\u002E', }; const MATH_VARIANTS = { cal: 'script', frak: 'fraktur', bb: 'double-struck', scr: 'script', cmtt: 'monospace', cmss: 'sans-serif', }; const SPACING = { '\\!': -3 / 18, '\\ ': 6 / 18, '\\,': 3 / 18, '\\:': 4 / 18, '\\;': 5 / 18, '\\enspace': 0.5, '\\quad': 1, '\\qquad': 2, '\\enskip': 0.5, }; let result = ''; let sep = ''; let col; let row; let i; let underscript; let overscript; let body; let variant = (_b = MATH_VARIANTS[(_a = atom.fontFamily) !== null && _a !== void 0 ? _a : atom.font]) !== null && _b !== void 0 ? _b : ''; if (variant) variant = ` mathvariant="${variant}"`; const { command } = atom; if (atom.mode === 'text') result = `<mi${makeID(atom.id, options)}>${atom.value}</mi>`; else { switch (atom.type) { case 'first': break; // Nothing to do case 'group': case 'root': if (SPECIAL_OPERATORS[atom.command]) result = SPECIAL_OPERATORS[atom.command]; else result = toMathML(atom.body, 0, 0, options).mathML; break; case 'array': if ((atom.leftDelim && atom.leftDelim !== '.') || (atom.rightDelim && atom.rightDelim !== '.')) { result += '<mrow>'; if (atom.leftDelim && atom.leftDelim !== '.') { result += '<mo>' + (SPECIAL_OPERATORS[atom.leftDelim] || atom.leftDelim) + '</mo>'; } } result += '<mtable'; if (atom.colFormat) { result += ' columnalign="'; for (i = 0; i < atom.colFormat.length; i++) { if (atom.colFormat[i].align) { result += { l: 'left', c: 'center', r: 'right' }[atom.colFormat[i].align] + ' '; } } result += '"'; } result += '>'; for (row = 0; row < atom.array.length; row++) { result += '<mtr>'; for (col = 0; col < atom.array[row].length; col++) { result += '<mtd>' + toMathML(atom.array[row][col], 0, 0, options).mathML + '</mtd>'; } result += '</mtr>'; } result += '</mtable>'; if ((atom.leftDelim && atom.leftDelim !== '.') || (atom.rightDelim && atom.rightDelim !== '.')) { if (atom.rightDelim && atom.rightDelim !== '.') { result += '<mo>' + (SPECIAL_OPERATORS[atom.leftDelim] || atom.rightDelim) + '</mo>'; } result += '</mrow>'; } break; case 'genfrac': if (atom.leftDelim || atom.rightDelim) result += '<mrow>'; if (atom.leftDelim && atom.leftDelim !== '.') { result += '<mo' + makeID(atom.id, options) + '>' + (SPECIAL_OPERATORS[atom.leftDelim] || atom.leftDelim) + '</mo>'; } if (atom.hasBarLine) { result += '<mfrac>'; result += toMathML(atom.above, 0, 0, options).mathML || '<mi> </mi>'; result += toMathML(atom.below, 0, 0, options).mathML || '<mi> </mi>'; result += '</mfrac>'; } else { // No bar line, i.e. \choose, etc... result += '<mtable' + makeID(atom.id, options) + '>'; result += '<mtr>' + toMathML(atom.above, 0, 0, options).mathML + '</mtr>'; result += '<mtr>' + toMathML(atom.below, 0, 0, options).mathML + '</mtr>'; result += '</mtable>'; } if (atom.rightDelim && atom.rightDelim !== '.') { result += '<mo' + makeID(atom.id, options) + '>' + (SPECIAL_OPERATORS[atom.rightDelim] || atom.rightDelim) + '</mo>'; } if (atom.leftDelim || atom.rightDelim) result += '</mrow>'; break; case 'surd': if (!atom.hasEmptyBranch('above')) { result += '<mroot' + makeID(atom.id, options) + '>'; result += toMathML(atom.body, 0, 0, options).mathML; result += toMathML(atom.above, 0, 0, options).mathML; result += '</mroot>'; } else { result += '<msqrt' + makeID(atom.id, options) + '>'; result += toMathML(atom.body, 0, 0, options).mathML; result += '</msqrt>'; } break; case 'leftright': // TODO: could add fence=true attribute result = '<mrow>'; if (atom.leftDelim && atom.leftDelim !== '.') { result += '<mo' + makeID(atom.id, options) + '>' + ((_c = SPECIAL_OPERATORS[atom.leftDelim]) !== null && _c !== void 0 ? _c : atom.leftDelim) + '</mo>'; } if (atom.body) result += toMathML(atom.body, 0, 0, options).mathML; if (atom.rightDelim && atom.rightDelim !== '.') { result += '<mo' + makeID(atom.id, options) + '>' + ((_d = SPECIAL_OPERATORS[atom.rightDelim]) !== null && _d !== void 0 ? _d : atom.rightDelim) + '</mo>'; } result += '</mrow>'; break; case 'sizeddelim': case 'delim': result += '<mo separator="true"' + makeID(atom.id, options) + '>' + (SPECIAL_OPERATORS[atom.delim] || atom.delim) + '</mo>'; break; case 'accent': result += '<mover accent="true"' + makeID(atom.id, options) + '>'; result += toMathML(atom.body, 0, 0, options).mathML; result += '<mo>' + (SPECIAL_OPERATORS[command] || atom.accent) + '</mo>'; result += '</mover>'; break; case 'line': case 'overlap': break; case 'overunder': overscript = atom.above; underscript = atom.below; if ((atom.svgAbove || overscript) && (atom.svgBelow || underscript)) body = atom.body; else if (overscript && overscript.length > 0) { body = atom.body; if ((_f = (_e = atom.body) === null || _e === void 0 ? void 0 : _e[0]) === null || _f === void 0 ? void 0 : _f.below) { underscript = atom.body[0].below; body = atom.body[0].body; } else if (((_h = (_g = atom.body) === null || _g === void 0 ? void 0 : _g[0]) === null || _h === void 0 ? void 0 : _h.type) === 'first' && ((_k = (_j = atom.body) === null || _j === void 0 ? void 0 : _j[1]) === null || _k === void 0 ? void 0 : _k.below)) { underscript = atom.body[1].below; body = atom.body[1].body; } } else if (underscript && underscript.length > 0) { body = atom.body; if ((_m = (_l = atom.body) === null || _l === void 0 ? void 0 : _l[0]) === null || _m === void 0 ? void 0 : _m.above) { overscript = atom.body[0].above; body = atom.body[0].body; } else if (((_p = (_o = atom.body) === null || _o === void 0 ? void 0 : _o[0]) === null || _p === void 0 ? void 0 : _p.type) === 'first' && ((_r = (_q = atom.body) === null || _q === void 0 ? void 0 : _q[1]) === null || _r === void 0 ? void 0 : _r.above)) { overscript = atom.body[1].overscript; body = atom.body[1].body; } } if ((atom.svgAbove || overscript) && (atom.svgBelow || underscript)) { result += `<munderover ${variant} ${makeID(atom.id, options)}>`; result += SVG_CODE_POINTS[atom.svgBody] || toMathML(body, 0, 0, options).mathML; result += SVG_CODE_POINTS[atom.svgBelow] || toMathML(underscript, 0, 0, options).mathML; result += SVG_CODE_POINTS[atom.svgAbove] || toMathML(overscript, 0, 0, options).mathML; result += '</munderover>'; } else if (atom.svgAbove || overscript) { result += `<mover ${variant} ${makeID(atom.id, options)}>` + (SVG_CODE_POINTS[atom.svgBody] || toMathML(body, 0, 0, options).mathML); result += SVG_CODE_POINTS[atom.svgAbove] || toMathML(overscript, 0, 0, options).mathML; result += '</mover>'; } else if (atom.svgBelow || underscript) { result += `<munder ${variant} ${makeID(atom.id, options)}>` + (SVG_CODE_POINTS[atom.svgBody] || toMathML(body, 0, 0, options).mathML); result += SVG_CODE_POINTS[atom.svgBelow] || toMathML(underscript, 0, 0, options).mathML; result += '</munder>'; } break; case 'placeholder': // No real equivalent in MathML -- will generate a '?'qq case 'mord': { result = SPECIAL_IDENTIFIERS[command] || command || (typeof atom.value === 'string' ? atom.value : ''); const m = command ? command.match(/{?\\char"([\dabcdefABCDEF]*)}?/) : null; if (m) { // It's a \char command result = '&#x' + m[1] + ';'; } else if (result.length > 0 && result.startsWith('\\')) { // This is an identifier with no special handling. Use the // Unicode value if (typeof atom.value === 'string' && atom.value.charCodeAt(0) > 255) { result = '&#x' + ('000000' + atom.value.charCodeAt(0).toString(16)).slice(-4) + ';'; } else if (typeof atom.value === 'string') result = atom.value.charAt(0); else { console.log('Did not expect this'); result = ''; } } const tag = /\d/.test(result) ? 'mn' : 'mi'; result = `<${tag}${variant}${makeID(atom.id, options)}>${xmlEscape(result)}</${tag}>`; break; } case 'mbin': case 'mrel': case 'minner': if (command && SPECIAL_IDENTIFIERS[command]) { // Some 'textord' are actually identifiers. Check them here. result = '<mi' + makeID(atom.id, options) + '>' + SPECIAL_IDENTIFIERS[command] + '</mi>'; } else if (command && SPECIAL_OPERATORS[command]) { result = '<mo' + makeID(atom.id, options) + '>' + SPECIAL_OPERATORS[command] + '</mo>'; } else result = toMo(atom, options); break; case 'mpunct': result = '<mo separator="true"' + makeID(atom.id, options) + '>' + (SPECIAL_OPERATORS[command] || command) + '</mo>'; break; case 'mop': if (atom.body !== '\u200B') { // Not ZERO-WIDTH result = '<mo' + makeID(atom.id, options) + '>'; result += command === '\\operatorname' ? atom.body : command || atom.body; result += '</mo>'; } break; // Case 'mathstyle': // TODO: mathstyle is a switch. Need to figure out its scope to properly wrap it around a <mstyle> tag // if (atom.mathstyle === 'displaystyle') { // result += '<mstyle displaystyle="true">'; // result += '</mstyle>'; // } else { // result += '<mstyle displaystyle="false">'; // result += '</mstyle>'; // }; // break; case 'box': result = '<menclose notation="box"'; if (atom.backgroundcolor) result += ' mathbackground="' + atom.backgroundcolor + '"'; result += makeID(atom.id, options) + '>' + toMathML(atom.body, 0, 0, options).mathML + '</menclose>'; break; case 'spacing': result += '<mspace width="' + (SPACING[command] || 0) + 'em"/>'; break; case 'enclose': result = '<menclose notation="'; for (const notation in atom.notation) { if (Object.prototype.hasOwnProperty.call(atom.notation, notation) && atom.notation[notation]) { result += sep + notation; sep = ' '; } } result += makeID(atom.id, options) + '">' + toMathML(atom.body, 0, 0, options).mathML + '</menclose>'; break; case 'space': result += ' '; break; case 'msubsup': // if (atom.superscript && atom.subscript) { // result = '<msubsup>' + base; // result += toMathML(atom.subscript, 0, 0, options).mathML; // result += toMathML(atom.superscript, 0, 0, options).mathML; // result += '</msubsup>'; // } else if (atom.superscript) { // result = '<msup>' + base; // result += toMathML(atom.superscript, 0, 0, options).mathML; // result += '</msup>'; // } else if (atom.subscript) { // result = '<msub>' + base; // result += toMathML(atom.subscript, 0, 0, options).mathML; // result += '</msub>'; // } break; case 'phantom': break; case 'composition': break; case 'rule': console.log('In conversion to MathML, unknown type : ' + atom.type); break; case 'chem': break; case 'mopen': result += toMo(atom, options); break; case 'mclose': result += toMo(atom, options); break; case 'macro': { const body = atom.command + toString(atom.macroArgs); if (body) result += `<mo ${makeID(atom.id, options)}>${body}</mo>`; } break; case 'error': console.log('In conversion to MathML, unknown type : ' + atom.type); break; case 'latex': result += '<mtext' + makeID(atom.id, options) + '>' + atom.value + '</mtext>'; break; default: console.log('In conversion to MathML, unknown type : ' + atom.type); } } return result; } function atomsToMathML(atoms, options) { return toMathML(atoms, 0, 0, options).mathML; } // Markup // Two common flavor of markups: SSML and 'mac'. The latter is only available // when using the native TTS synthesizer on Mac OS. // Use SSML in the production rules below. The markup will either be striped // off or replaced with the 'mac' markup as necessary. // // SSML Mac // ---- ---- // <emphasis>WORD</emphasis> [[emph +]]WORD // <break time="150ms"/> [[slc 150]] // <say-as interpret-as="character">A</say-as> [[char LTRL] A [[char NORM]] // https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/SpeechSynthesisProgrammingGuide/FineTuning/FineTuning.html#//apple_ref/doc/uid/TP40004365-CH5-SW3 // https://pdfs.semanticscholar.org/8887/25b82b8dbb45dd4dd69b36a65f092864adb0.pdf // "<audio src='non_existing_file.au'>File could not be played.</audio>" // "I am now <prosody rate='+0.06'>speaking 6% faster.</prosody>" const PRONUNCIATION = { '\\alpha': 'alpha ', '\\mu': 'mew ', '\\sigma': 'sigma ', '\\pi': 'pie ', '\\imaginaryI': 'eye ', '\\sum': 'Summation ', '\\prod': 'Product ', 'a': '<phoneme alphabet="ipa" ph="eɪ">a</phoneme>', 'A': 'capital <phoneme alphabet="ipa" ph="eɪ">A</phoneme>', '+': 'plus ', '-': 'minus ', ';': '<break time="150ms"/> semi-colon <break time="150ms"/>', ',': '<break time="150ms"/> comma <break time="150ms"/>', '|': '<break time="150ms"/>Vertical bar<break time="150ms"/>', '(': '<break time="150ms"/>Open paren. <break time="150ms"/>', ')': '<break time="150ms"/> Close paren. <break time="150ms"/>', '=': 'equals ', '<': 'is less than ', '\\lt': 'is less than ', '<=': 'is less than or equal to ', '\\le': 'is less than or equal to ', '\\gt': 'is greater than ', '>': 'is greater than ', '\\ge': 'is greater than or equal to ', '\\geq': 'is greater than or equal to ', '\\leq': 'is less than or equal to ', '!': 'factorial ', '\\sin': 'sine ', '\\cos': 'cosine ', '\u200B': '', '\u2212': 'minus ', ':': '<break time="150ms"/> such that <break time="200ms"/> ', '\\colon': '<break time="150ms"/> such that <break time="200ms"/> ', '\\hbar': 'etch bar ', '\\iff': '<break time="200ms"/>if, and only if, <break time="200ms"/>', '\\Longleftrightarrow': '<break time="200ms"/>if, and only if, <break time="200ms"/>', '\\land': 'and ', '\\lor': 'or ', '\\neg': 'not ', '\\div': 'divided by ', '\\forall': 'for all ', '\\exists': 'there exists ', '\\nexists': 'there does not exists ', '\\in': 'element of ', '\\N': 'the set <break time="150ms"/><say-as interpret-as="character">n</say-as>', '\\C': 'the set <break time="150ms"/><say-as interpret-as="character">c</say-as>', '\\Z': 'the set <break time="150ms"/><say-as interpret-as="character">z</say-as>', '\\Q': 'the set <break time="150ms"/><say-as interpret-as="character">q</say-as>', '\\infty': 'infinity ', '\\nabla': 'nabla ', '\\partial': 'partial derivative of ', '\\cdot': 'times ', '\\cdots': 'dot dot dot ', '\\Rightarrow': 'implies ', '\\lparen': '<break time="150ms"/>open paren<break time="150ms"/>', '\\rparen': '<break time="150ms"/>close paren<break time="150ms"/>', '\\lbrace': '<break time="150ms"/>open brace<break time="150ms"/>', '\\{': '<break time="150ms"/>open brace<break time="150ms"/>', '\\rbrace': '<break time="150ms"/>close brace<break time="150ms"/>', '\\}': '<break time="150ms"/>close brace<break time="150ms"/>', '\\langle': '<break time="150ms"/>left angle bracket<break time="150ms"/>', '\\rangle': '<break time="150ms"/>right angle bracket<break time="150ms"/>', '\\lfloor': '<break time="150ms"/>open floor<break time="150ms"/>', '\\rfloor': '<break time="150ms"/>close floor<break time="150ms"/>', '\\lceil': '<break time="150ms"/>open ceiling<break time="150ms"/>', '\\rceil': '<break time="150ms"/>close ceiling<break time="150ms"/>', '\\vert': '<break time="150ms"/>vertical bar<break time="150ms"/>', '\\mvert': '<break time="150ms"/>divides<break time="150ms"/>', '\\lvert': '<break time="150ms"/>left vertical bar<break time="150ms"/>', '\\rvert': '<break time="150ms"/>right vertical bar<break time="150ms"/>', // '\\lbrack': 'left bracket', // '\\rbrack': 'right bracket', '\\lbrack': '<break time="150ms"/> open square bracket <break time="150ms"/>', '\\rbrack': '<break time="150ms"/> close square bracket <break time="150ms"/>', // Need to add code to detect singluar/plural. Until then spoken as plural since that is vastly more common // note: need to worry about intervening ⁢. // note: need to also do this when in numerator of fraction and number preceeds fraction // note: need to do this for <msup> 'mm': 'millimeters', 'cm': 'centimeters', 'km': 'kilometers', 'kg': 'kilograms', }; function getSpokenName(latex) { let result = ''; if (latex.startsWith('\\')) result = ' ' + latex.replace('\\', '') + ' '; return result; } function isAtomic(atoms) { let count = 0; if (isArray(atoms)) for (const atom of atoms) if (atom.type !== 'first') count += 1; return count === 1; } function atomicID(atoms) { if (isArray(atoms)) { for (const atom of atoms) if (atom.type !== 'first' && atom.id) return atom.id.toString(); } return ''; } function atomicValue(atoms) { let result = ''; if (isArray(atoms)) { for (const atom of atoms) { if (atom.type !== 'first' && typeof atom.value === 'string') result += atom.value; } } return result; } function atomsAsText(atoms, _options) { if (!atoms) return ''; return atoms.map((atom) => atom.value).join(''); } function atomToSpeakableFragment(mode, atom, options) { var _a, _b, _c, _d; function letter(c) { if (!options.textToSpeechMarkup) { if (/[a-z]/.test(c)) return " '" + c.toUpperCase() + "'"; if (/[A-Z]/.test(c)) return " 'capital " + c.toUpperCase() + "'"; return c; } if (/[a-z]/.test(c)) return ` <say-as interpret-as="character">${c}</say-as>`; if (/[A-Z]/.test(c)) return `capital <say-as interpret-as="character">${c.toLowerCase()}</say-as>`; return c; } function emph(s) { return '<emphasis>' + s + '</emphasis>'; } if (!atom) return ''; let result = ''; if (isArray(atom)) { let isInDigitRun = false; // Need to group sequence of digits let isInTextRun = false; // Need to group text for (let i = 0; i < atom.length; i++) { if (atom[i].mode !== 'text') isInTextRun = false; if (i < atom.length - 2 && atom[i].type === 'mopen' && atom[i + 2].type === 'mclose' && atom[i + 1].type === 'mord') { result += ' of '; result += emph(atomToSpeakableFragment(mode, atom[i + 1], options)); i += 2; } else if (atom[i].mode === 'text') { if (isInTextRun) result += (_a = atom[i].value) !== null && _a !== void 0 ? _a : ' '; else { isInTextRun = true; result += atomToSpeakableFragment('text', atom[i], options); } // '.' and ',' should only be allowed if prev/next entry is a digit // However, if that isn't the case, this still works because 'toSpeakableFragment' is called in either case. // Note: the first char in a digit/text run potentially needs to have a 'mark', hence the call to 'toSpeakableFragment' } else if (atom[i].isDigit()) { if (isInDigitRun) result += atom[i].asDigit(); else { isInDigitRun = true; result += atomToSpeakableFragment(mode, atom[i], options); } } else { isInDigitRun = false; result += atomToSpeakableFragment(mode, atom[i], options); } } } else if (atom.mode === 'text') { if (atom.id && mode === 'math') result += '<mark name="' + atom.id.toString() + '"/>'; result += atom.value; } else { if (atom.id && mode === 'math') result += '<mark name="' + atom.id.toString() + '"/>'; let numer = ''; let denom = ''; let body = ''; let supsubHandled = false; switch (atom.type) { case 'group': case 'root': result += atomToSpeakableFragment('math', atom.body, options); break; case 'genfrac': numer = atomToSpeakableFragment('math', atom.above, options); denom = atomToSpeakableFragment('math', atom.below, options); if (isAtomic(atom.above) && isAtomic(atom.below)) { const COMMON_FRACTIONS = { '1/2': ' half ', '1/3': ' one third ', '2/3': ' two third', '1/4': ' one quarter ', '3/4': ' three quarter ', '1/5': ' one fifth ', '2/5': ' two fifths ', '3/5': ' three fifths ', '4/5': ' four fifths ', '1/6': ' one sixth ', '5/6': ' five sixths ', '1/8': ' one eight ', '3/8': ' three eights ', '5/8': ' five eights ', '7/8': ' seven eights ', '1/9': ' one ninth ', '2/9': ' two ninths ', '4/9': ' four ninths ', '5/9': ' five ninths ', '7/9': ' seven ninths ', '8/9': ' eight ninths ', // '1/10': ' one tenth ', // '1/12': ' one twelfth ', // 'x/2': ' <say-as interpret-as="character">X</say-as> over 2', }; const commonFraction = COMMON_FRACTIONS[atomicValue(atom.above) + '/' + atomicValue(atom.below)]; if (commonFraction) result = commonFraction; else result += numer + ' over ' + denom; } else { result += ' the fraction <break time="150ms"/>' + numer + ', over <break time="150ms"/>' + denom + '.<break time="150ms"/> End fraction.<break time="150ms"/>'; } break; case 'surd': body = atomToSpeakableFragment('math', atom.body, options); if (atom.hasEmptyBranch('above')) { result += isAtomic(atom.body) ? ' the square root of ' + body + ' , ' : ' the square root of <break time="200ms"/>' + body + '. <break time="200ms"/> End square root'; } else { let index = atomToSpeakableFragment('math', atom.above, options); index = index.trim(); const index2 = index.replace(/<mark([^/]*)\/>/g, ''); if (index2 === '3') { result += ' the cube root of <break time="200ms"/>' + body + '. <break time="200ms"/> End cube root'; } else if (index2 === 'n') { result += ' the nth root of <break time="200ms"/>' + body + '. <break time="200ms"/> End root'; } else { result += ' the root with index: <break time="200ms"/>' + index + ', of <break time="200ms"/>' + body + '. <break time="200ms"/> End root'; } } break; case 'leftright': { const delimAtom = atom; result += (_b = (delimAtom.leftDelim ? PRONUNCIATION[delimAtom.leftDelim] : undefined)) !== null && _b !== void 0 ? _b : delimAtom.leftDelim; result += atomToSpeakableFragment('math', atom.body, options); result += (_c = (delimAtom.rightDelim ? PRONUNCIATION[delimAtom.rightDelim] : undefined)) !== null && _c !== void 0 ? _c : delimAtom.rightDelim; } break; case 'rule': // @todo break; case 'overunder': // @todo break; case 'overlap': // @todo break; case 'placeholder': result += 'placeholder '; break; case 'delim': case 'sizeddelim': case 'mord': case 'minner': case 'mbin': case 'mrel': case 'mpunct': case 'mopen': case 'mclose': { const { command } = atom; if (command === '\\mathbin' || command === '\\mathrel' || command === '\\mathopen' || command === '\\mathclose' || command === '\\mathpunct' || command === '\\mathord' || command === '\\mathinner') { result = atomToSpeakableFragment(mode, atom.body, options); break; } let atomValue = atom.isDigit() ? atom.asDigit() : atom.value; let latexValue = atom.command; if (atom.type === 'delim' || atom.type === 'sizeddelim') { latexValue = atom.value; atomValue = latexValue; } if (mode === 'text') result += atomValue; else { if (atom.type === 'mbin') result += '<break time="150ms"/>'; if (atomValue) { const value = PRONUNCIATION[atomValue] || (latexValue ? PRONUNCIATION[latexValue.trim()] : ''); if (value) result += ' ' + value; else { const spokenName = latexValue ? getSpokenName(latexValue.trim()) : ''; result += spokenName ? spokenName : letter(atomValue); } } else result += atomToSpeakableFragment('math', atom.body, options); if (atom.type === 'mbin') result += '<break time="150ms"/>'; } break; } case 'mop': // @todo if (atom.value !== '\u200B') { // Not ZERO-WIDTH const trimLatex = atom.command; if (trimLatex === '\\sum') { if (!atom.hasEmptyBranch('superscript') && !atom.hasEmptyBranch('subscript')) { let sup = atomToSpeakableFragment('math', atom.superscript, options); sup = sup.trim(); let sub = atomToSpeakableFragment('math', atom.subscript, options); sub = sub.trim(); result += ' the summation from <break time="200ms"/>' + sub + '<break time="200ms"/> to <break time="200ms"/>' + sup + '<break time="200ms"/> of <break time="150ms"/>'; supsubHandled = true; } else if (!atom.hasEmptyBranch('subscript')) { let sub = atomToSpeakableFragment('math', atom.subscript, options); sub = sub.trim(); result += ' the summation from <break time="200ms"/>' + sub + '<break time="200ms"/> of <break time="150ms"/>'; supsubHandled = true; } else result += ' the summation of'; } else if (trimLatex === '\\prod') { if (!atom.hasEmptyBranch('superscript') && !atom.hasEmptyBranch('subscript')) { let sup = atomToSpeakableFragment('math', atom.superscript, options); sup = sup.trim(); let sub = atomToSpeakableFragment('math', atom.subscript, options); sub = sub.trim(); result += ' the product from <break time="200ms"/>' + sub + '<break time="200ms"/> to <break time="200ms"/>' + sup + '<break time="200ms"/> of <break time="150ms"/>'; supsubHandled = true; } else if (!atom.hasEmptyBranch('subscript')) { let sub = atomToSpeakableFragment('math', atom.subscript, options); sub = sub.trim(); result += ' the product from <break time="200ms"/>' + sub + '<break time="200ms"/> of <break time="150ms"/>'; supsubHandled = true; } else result += ' the product of '; } else if (trimLatex === '\\int') { if (!atom.hasEmptyBranch('superscript') && !atom.hasEmptyBranch('subscript')) { let sup = atomToSpeakableFragment('math', atom.superscript, options); sup = sup.trim(); let sub = atomToSpeakableFragment('math', atom.subscript, options); sub = sub.trim(); result += ' the integral from <break time="200ms"/>' + emph(sub) + '<break time="200ms"/> to <break time="200ms"/>' + emph(sup) + ' <break time="200ms"/> of '; supsubHandled = true; } else result += ' the integral of <break time="200ms"/> '; } else if (typeof atom.value === 'string') { const value = (_d = PRONUNCIATION[atom.value]) !== null && _d !== void 0 ? _d : (atom.command ? PRONUNCIATION[atom.command] : undefined); result += value ? value : ' ' + atom.value; } else if (atom.command) { if (atom.command === '\\mathop') result += atomToSpeakableFragment('math', atom.body, options); else if (atom.command === '\\operatorname') result += atomsAsText(atom.body); else { result += atom.command.startsWith('\\') ? ' ' + atom.command.slice(1) : ' ' + atom.command; } } } break; case 'enclose': body = atomToSpeakableFragment('math', atom.body, options); result += isAtomic(atom.body) ? ' crossed out ' + body + ' , ' : ' crossed out ' + body + '. End cross out'; break; } if (!supsubHandled && !atom.hasEmptyBranch('superscript')) { let sup = atomToSpeakableFragment(mode, atom.superscript, options); sup = sup.trim(); const sup2 = sup.replace(/<[^>]*>/g, ''); if (isAtomic(atom.superscript)) { if (mode === 'math') { const id = atomicID(atom.superscript); if (id) result += '<mark name="' + id + '"/>'; } if (sup2 === '\u2032') result += ' prime '; else if (sup2 === '2') result += ' squared '; else if (sup2 === '3') result += ' cubed '; else if (Number.isNaN(Number.parseInt(sup2))) result += ' to the ' + sup + '; '; else { result += ' to the <say-as interpret-as="ordinal">' + sup2 + '</say-as> power; '; } } else if (Number.isNaN(Number.parseInt(sup2))) result += ' raised to the ' + sup + '; '; else { result += ' raised to the <say-as interpret-as="ordinal">' + sup2 + '</say-as> power; '; } } if (!supsubHandled && !atom.hasEmptyBranch('subscript')) { let sub = atomToSpeakableFragment('math', atom.subscript, options); sub = sub.trim(); result += isAtomic(atom.subscript) ? ' sub ' + sub : ' subscript ' + sub + '. End subscript. '; } } return result; } /** * @param atoms The atoms to represent as speakable text. */ function atomToSpeakableText(atoms, speechOptions) { var _a; const options = { ...speechOptions, textToSpeechRulesOptions: { ...speechOptions.textToSpeechRulesOptions }, }; if (options.textToSpeechRules === 'sre' && isBrowser() && 'sre' in window) { const mathML = atomsToMathML(atoms, options); if (mathML) { if (options.textToSpeechMarkup) { options.textToSpeechRulesOptions = (_a = options.textToSpeechRulesOptions) !== null && _a !== void 0 ? _a : {}; options.textToSpeechRulesOptions.markup = options.textToSpeechMarkup; if (options.textToSpeechRulesOptions.markup === 'ssml') options.textToSpeechRulesOptions.markup = 'ssml_step'; options.textToSpeechRulesOptions.rate = options.speechEngineRate; } if (options.textToSpeechRulesOptions) { window.sre.System.getInstance().setupEngine(options.textToSpeechRulesOptions); } return window.sre.System.getInstance().toSpeech(mathML); } return ''; } let result = atomToSpeakableFragment('math', atoms, options); if (options.textToSpeechMarkup === 'ssml') { let prosody = ''; if (options.speechEngineRate) prosody = '<prosody rate="' + options.speechEngineRate + '">'; result = `<?xml version="1.0"?><speak version="1.1" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="en-US">` + '<amazon:auto-breaths>' + prosody + '<p><s>' + result + '</s></p>' + (prosody ? '</prosody>' : '') + '</amazon:auto-breaths>' + '</speak>'; } else if (options.textToSpeechMarkup === 'mac' && osPlatform() === 'macos') { // Convert SSML to Mac markup result = result .replace(/<mark([^/]*)\/>/g, '') .replace(/<emphasis>/g, '[[emph+]]') .replace(/<\/emphasis>/g, '') .replace(/<break time="(\d*)ms"\/>/g, '[[slc $1]]') .replace(/<say-as[^>]*>/g, '') .replace(/<\/say-as>/g, ''); } else { // If no markup was requested, or 'mac' markup, but we're not on a mac, // remove any that we may have // Strip out the SSML markup result = result.replace(/<[^>]*>/g, '').replace(/\s{2,}/g, ' '); } return result; } /// ^.*('\\.*').* // Frequency of a symbol. // String constants corresponding to frequency values, // which are the number of results returned by latexsearch.com // When the precise number is known, it is provided. Otherwise, // the following constants are used to denote an estimate. const CRYPTIC = 0; const ARCANE = 200; const RARE = 1200; const UNCOMMON = 2000; const COMMON = 3000; const SUPERCOMMON = 4000; /* * Set the metadata for the specified symbols * * metadata('Functions', ['\\sin', '\\cos'], COMMON, '$0{a}') * */ function metadata(category, symbols, frequency = COMMON, template = '$0') { for (const symbol of symbols) { if (MATH_SYMBOLS[symbol]) { MATH_SYMBOLS[symbol].frequency = frequency; MATH_SYMBOLS[symbol].category = category; MATH_SYMBOLS[symbol].template = template.replace(/\$0/g, symbol); } if (LATEX_COMMANDS[symbol]) { // Make a copy of the entry, since it could be shared by multiple // symbols LATEX_COMMANDS[symbol] = { ...LATEX_COMMANDS[symbol], frequency, category, template: template.replace(/\$0/g, symbol), }; } } } metadata('Trigonometry', ['\\cos', '\\sin', '\\tan'], SUPERCOMMON); metadata('Trigonometry', [ '\\arcsin', '\\arccos', '\\arctan', '\\arctg', '\\arcctg', '\\arcsec', '\\arccsc', '\\arsinh', '\\arcosh', '\\artanh', '\\arcsech', '\\arccsch', '\\arg', '\\ch', '\\cosec', '\\cosh', '\\cot', '\\cotg', '\\coth', '\\csc', '\\ctg', '\\cth', '\\lg', '\\lb', '\\sec', '\\sinh', '\\sh', '\\tanh', '\\tg', '\\th', ], UNCOMMON); metadata('Functions', ['\\ln', '\\log', '\\exp', '\\lim'], SUPERCOMMON); metadata('Functions', ['\\dim', '\\ker', '\\deg', '\\det', '\\mod', '\\min', '\\max'], COMMON); metadata('Functions', ['\\hom'], RARE); metadata('Decoration', ['\\rule'], ARCANE, '$0{2em}{1em}'); metadata('Decoration', ['\\color', '\\textcolor'], ARCANE, '{$0{m0}A}{$0{m1}B}{$0{m2}C }{$0{m3}a}{$0{m4}b}{$0{m5}c}{$0{m6}8}'); metadata('Decoration', ['\\overline', '\\underline'], COMMON, '$0{\\placeholder{}}'); metadata('Decoration', ['\\enclose'], RARE, '\\enclose{updiagonalstrike,roundedbox}[1px solid red, mathbackground="#fbc0bd"]{x=0}'); metadata('Decoration', ['\\fcolorbox'], RARE, '\\fcolorbox{#cd0030}{#ffd400}{\\unicode{"2B1A}}'); metadata('Decoration', ['\\colorbox'], RARE, '\\colorbox{#fbc0bd}{\\unicode{"2B1A}}'); metadata('Decoration', ['\\boxed', '\\cancel', '\\bcancel', '\\xcancel'], RARE, '$0{\\placeholder{}}'); metadata('Decoration', ['\\bbox'], RARE, '\\bbox[#ffd400, solid 2px #ffd400]{\\unicode{"2B1A}}'); metadata('Styling', ['\\mathbb'], SUPERCOMMON, '$0{Don Knuth}'); metadata('Styling', [ '\\textbf', '\\textup', '\\textit', '\\textrm', '\\textsf', '\\texttt', '\\textnormal', '\\textmd', '\\textsl', '\\textsc', '\\mathsf', '\\mathtt', '\\mathrm', '\\mathfrak', '\\mathcal', '\\mathscr', '\\mathbf', '\\mathmd', '\\mathit', '\\text', '\\mbox', '\\Bbb', '\\bold', '\\bm', '\\boldsymbol', ], COMMON, '$0{Don Knuth}'); metadata('Styling', [ '\\frak', '\\tt', '\\bf', '\\it', '\\rmfamily', '\\sffamily', '\\ttfamily', '\\class', '\\cssId', '\\htmlData', '\\htmlStyle', ], RARE, '{$0 Don Knuth}'); metadata('Styling', ['\\bfseries', '\\mdseries', '\\upshape', '\\slshape', '\\scshape'], RARE, '\\text{$0 Don Knuth}'); metadata('Styling', ['\\class', '\\cssId'], RARE, '$0{testIdentifier}{Don Knuth}'); // Note: In LaTeX, \fontseries, \fontfamily, \fontshape are applicable to // text mode only metadata('Styling', ['\\fontseries'], RARE, '\\text{$0{b}Don Knuth}'); metadata('Styling', ['\\fontfamily'], RARE, '\\text{$0{cmtt}Don Knuth}'); metadata('Styling', ['\\fontshape'], RARE, '\\text{$0{sc}Don Knuth}'); metadata('Styling', ['\\selectfont'], RARE, '\\text{$0}'); // No-op, but only valid in text mode metadata('Styling', ['\\emph'], RARE, 'Don$0{Knuth}'); metadata('Styling', ['\\em'], RARE, 'Don{$0 Knuth}'); metadata('Layout', [ '\\mathop', '\\mathbin', '\\mathrel', '\\mathopen', '\\mathclose', '\\mathpunct', '\\mathord', '\\mathinner', '\\operatorname', '\\operatorname*', ], RARE, 'x=$0{arg}=0'); metadata('Layout', ['\\middle'], RARE, '\\left\\{x$0|x>0\\right\\}'); metadata('Layout', ['\\overset', '\\underset', '\\stackrel', '\\stackbin'], RARE, '$0{arg}{x=0}'); metadata('Layout', ['\\rlap', '\\mathrlap'], RARE, '$0{/}0'); metadata('Layout', ['\\llap', '\\mathllap'], RARE, 'o$0{/}'); metadata('Fractions', ['\\frac'], SUPERCOMMON, '$0{\\placeholder{}}{\\placeholder{}}'); metadata('Fractions', [ '\\binom', '\\dfrac', '\\tfrac', '\\dbinom', '\\tbinom', '\\pdiff', '\\cfrac', ], RARE, '$0{\\placeholder{}}{\\placeholder{}}'); metadata('Fractions', ['\\over', '\\atop', '\\choose'], RARE, '\\unicode{"2B1A} $0 \\unicode{"2B1A}'); metadata('Fractions', ['\\overwithdelims', '\\atopwithdelims'], RARE, '{\\unicode{"2B1A} $0{\\lbrace}{\\rbrace} \\unicode{"2B1A}}'); metadata('Extensible Operators', ['\\sum', '\\prod', '\\bigcap', '\\bigcup', '\\int'], SUPERCOMMON); metadata('Extensible Operators', ['\\bigoplus', '\\smallint', '\\iint', '\\oint'], COMMON); metadata('Extensible Operators', [ '\\bigwedge', '\\bigvee', '\\coprod', '\\bigsqcup', '\\bigotimes', '\\bigodot', '\\biguplus', '\\intop', '\\sqcup', '\\sqcap', '\\uplus', '\\wr', '\\Cap', '\\Cup', '\\doublecap', '\\doublecup', '\\amalg', '\\iiint', '\\oiint', '\\oiiint', '\\intclockwise', '\\varointclockwise', '\\ointctrclockwise', '\\intctrclockwise', ], RARE); metadata('Accents', ['\\vec'], SUPERCOMMON); metadata('Accents', ['\\bar', '\\ddot', '\\acute', '\\tilde', '\\check'], COMMON, '$0{\\placeholder{}}'); metadata('Accents', ['\\^', '\\`', "\\'"], RARE, '$0{e}'); metadata('Accents', ['\\c'], RARE, '$0{c}'); metadata('Accents', ['\\~'], RARE, '$0{n}'); metadata('Accents', ['\\mathring', '\\hat', '\\dot', '\\breve', '\\grave'], RARE, '$0{\\placeholder{}}'); metadata('Extensible Symbols', [ '\\overrightarrow', '\\overleftarrow', '\\Overrightarrow', '\\overleftharpoon', '\\overrightharpoon', '\\overleftrightarrow', '\\overbrace', '\\overlinesegment', '\\overgroup', '\\widehat', '\\widecheck', '\\widetilde', ], COMMON, '$0{ABC}'); metadata('Extensible Symbols', [ '\\underrightarrow', '\\underleftarrow', '\\underleftrightarrow', '\\underbrace', '\\underlinesegment', '\\undergroup', '\\utilde', ], COMMON, '$0{ABC}'); metadata('Sizing', [ '\\tiny', '\\scriptsize', '\\footnotesize', '\\small', '\\normalsize', '\\large', '\\Large', '\\LARGE', '\\huge', '\\Huge', ], RARE, '$0{x=0}'); metadata('Sizing', ['\\big', '\\Big', '\\bigg', '\\Bigg'], RARE, '$0($0)'); metadata('Sizing', ['\\bigl', '\\Bigl', '\\biggl', '\\Biggl'], RARE, '$0('); metadata('Sizing', ['\\bigr', '\\Bigr', '\\biggr', '\\Biggr'], RARE, '$0)'); metadata('Sizing', ['\\bigm', '\\Bigm', '\\biggm', '\\Biggm'], RARE, '$0|'); metadata('Letterlike Symbols', [ '\\nabla', '\\partial', '\\N', '\\R', '\\Q', '\\C', '\\Z', '\\exponentialE', '\\forall', '\\exists', '\\nexists', ], SUPERCOMMON); metadata('Letterlike Symbols', [ '\\doubleStruckCapitalP', '\\P', '\\ell', '\\hbar', '\\hslash', '\\imath', '\\jmath', '\\imaginaryI', '\\imaginaryJ', '\\differentialD', '\\rd', '\\capitalDifferentialD', '\\doubleStruckCapitalN', '\\doubleStruckCapitalR', '\\doubleStruckCapitalQ', '\\doubleStruckCapitalC', '\\doubleStruckCapitalZ', '\\rD', '\\differencedelta', '\\mid', '@', '\\Re', '\\Im', '\\$', '\\%', '\\And', '\\degree', ], COMMON); metadata('Letterlike Symbols', [ '\\top', '\\bot', '\\scriptCapitalE', '\\scriptCapitalH', '\\scriptCapitalL', '\\gothicCapitalC', '\\gothicCapitalH', '\\gothicCapitalI', '\\gothicCapitalR', '\\Bbbk', '\\Finv', '\\Game', '\\wp', '\\eth', '\\mho', '\\pounds', '\\yen', '\\euro', ], RARE); metadata('Crosses', ['\\dagger', '\\dag'], SUPERCOMMON); metadata('Crosses', ['\\ddag', '\\ddagger', '\\maltese', '\\textdagger', '\\textdaggerdbl'], RARE); metadata('Various', [ '\\checkmark', '\\diagup', '\\diagdown', '\\angle', '\\measuredangle', '\\sphericalangle', '\\prime', '\\doubleprime', '\\backprime', '\\backdoubleprime', '\\sharp', '\\flat', '\\natural', '\\&', '\\#', '\\clubsuit', '\\spadesuit', '\\diamondsuit', '\\heartsuit', '\\backslash', '\\infty', '/', '\\_', '\\/', '|', "'", ], RARE); metadata('Various', ['\\unicode'], RARE, '$0{"2B1A}'); metadata('Arrows', ['\\longrightarrow', '\\rightarrow', '\\Longrightarrow', '\\Rightarrow'], SUPERCOMMON); metadata('Arrows', [ '\\longmapsto', '\\mapsto', '\\Longleftrightarrow', '\\rightleftarrows', '\\leftarrow', '\\curvearrowleft', '\\uparrow', '\\downarrow', '\\hookrightarrow', '\\rightharpoonup', '\\rightleftharpoons', ], COMMON); metadata('Arrows', [ '\\Leftarrow', '\\longleftrightarrow', '\\longleftarrow', '\\Longleftarrow', '\\searrow', '\\nearrow', '\\swarrow', '\\nwarrow', '\\Uparrow', '\\Downarrow', '\\updownarrow', '\\Updownarrow', '\\hookleftarrow', '\\leftharpoonup', '\\leftharpoondown', '\\rightharpoondown', '\\leftrightarrows', '\\dashrightarrow', '\\dashleftarrow', '\\leftleftarrows', '\\Lleftarrow', '\\twoheadleftarrow', '\\leftarrowtail', '\\looparrowleft', '\\leftrightharpoons', '\\circlearrowleft', '\\Lsh', '\\upuparrows', '\\downharpoonleft', '\\multimap', '\\leftrightsquigarrow', '\\twoheadrightarrow', '\\rightarrowtail', '\\looparrowright', '\\curvearrowright', '\\circlearrowright', '\\Rsh', '\\downdownarrows', '\\upharpoonright', '\\downharpoonright', '\\rightsquigarrow', '\\leadsto', '\\Rrightarrow', '\\restriction', ], RARE); metadata('Arrows', ['\\upharpoonleft', '\\rightrightarrows'], CRYPTIC); // AMS Negated Arrows metadata('Negated Arrows', [ '\\nrightarrow', '\\nRightarrow', '\\nleftrightarrow', '\\nLeftrightarrow', '\\nleftarrow', '\\nLeftarrow', ], RARE); metadata('Extensible Symbols', [ '\\xrightarrow', '\\xleftarrow', '\\xRightarrow', '\\xLeftarrow', '\\xleftharpoonup', '\\xleftharpoondown', '\\xrightharpoonup', '\\xrightharpoondown', '\\xlongequal', '\\xtwoheadleftarrow', '\\xtwoheadrightarrow', '\\xleftrightarrow', '\\xLeftrightarrow', '\\xrightleftharpoons', '\\xleftrightharpoons', '\\xhookleftarrow', '\\xhookrightarrow', '\\xmapsto', '\\xtofrom', '\\xrightleftarrows', '\\xrightequilibrium', '\\xleftequilibrium', ], RARE, '$0[below]{above}=0'); // AMS Negated Binary Relations metadata('Negated Relations', [ '\\nless', '\\nleqslant', '\\lneq', '\\lneqq', '\\nleqq', '\\unlhd', '\\unrhd', '\\lvertneqq', '\\lnsim', '\\lnapprox', '\\nprec', '\\npreceq', '\\precnsim', '\\precnapprox', '\\nsim', '\\nshortmid', '\\nmid', '\\nvdash', '\\nvDash', '\\ngtr', '\\ngeqslant', '\\ngeqq', '\\gneq', '\\gneqq', '\\gvertneqq', '\\gnsim', '\\nsucc', '\\succnsim', '\\ncong', '\\nshortparallel', '\\nparallel', '\\nVDash', '\\nVdash', '\\precneqq', '\\succneqq', '\\gnapprox', '\\succnapprox', '\\nsucceq', ], RARE); // AMS Hebrew metadata('Hebrew', ['\\aleph', '\\beth', '\\daleth', '\\gimel'], RARE); metadata('Fences', [ '\\lbrace', '\\rbrace', '\\vert', '\\Vert', '\\{', '\\}', '(', ')', '[', ']', ], SUPERCOMMON); metadata('Fences', [ '\\langle', '\\rangle', '\\lfloor', '\\rfloor', '\\lceil', '\\rceil', '\\mvert', '\\|', '\\mVert', ], COMMON); metadata('Fences', [ '\\lvert', '\\rvert', '\\lVert', '\\rVert', '\\lbrack', '\\rbrack', '\\ulcorner', '\\urcorner', '\\llcorner', '\\lrcorner', '\\lgroup', '\\rgroup', '\\lmoustache', '\\rmoustache', ], RARE); metadata('Relations', ['=', '\\ne', '\\neq', '<', '>', '\\leqslant', '\\geqslant', '\\approx'], SUPERCOMMON); metadata('Relations', [ '\\lt', '\\gt', '\\le', '\\ge', '\\leq', '\\geq', '\\gg', '\\cong', '\\equiv', '\\prec', '\\preceq', '\\succ', '\\perp', '\\parallel', '\\propto', '\\smile', '\\frown', '\\sim', '\\gtrsim', ], COMMON); metadata('Relations', [ '\\coloneq', '\\succeq', '\\approxeq', '\\thickapprox', '\\lessapprox', '\\gtrapprox', '\\precapprox', '\\succapprox', '\\thicksim', '\\succsim', '\\precsim', '\\backsim', '\\eqsim', '\\backsimeq', '\\simeq', '\\lesssim', '\\nleq', '\\ngeq', '\\smallsmile', '\\smallfrown', '\\bowtie', '\\asymp', '\\leqq', '\\eqslantless', '\\ll', '\\lll', '\\lessgtr', '\\lesseqgtr', '\\lesseqqgtr', '\\risingdotseq', '\\fallingdotseq', '\\preccurlyeq', '\\curlyeqprec', '\\vDash', '\\Vvdash', '\\bumpeq', '\\Bumpeq', '\\geqq', '\\eqslantgtr', '\\ggg', '\\gtrless', '\\gtreqless', '\\gtreqqless', '\\succcurlyeq', '\\curlyeqsucc', '\\Vdash', '\\shortmid', '\\shortparallel', '\\between', '\\pitchfork', '\\varpropto', '\\backepsilon', '\\llless', '\\gggtr', '\\lhd', '\\rhd', '\\Join', '\\doteq', '\\doteqdot', '\\Doteq', '\\eqcirc', '\\circeq', '\\lessdot', '\\gtrdot', '\\measeq', '\\eqdef', '\\questeq', ], RARE); metadata('Logic', ['\\leftrightarrow', '\\Leftrightarrow', '\\to'], SUPERCOMMON); metadata('Logic', ['\\models', '\\vdash'], COMMON); metadata('Logic', [ '\\therefore', '\\because', '\\implies', '\\gets', '\\dashv', '\\impliedby', '\\biconditional', '\\roundimplies', ], RARE); metadata('Operators', ['+', '-', '*', '\\cdot'], SUPERCOMMON); metadata('Operators', ['\\sqrt'], SUPERCOMMON, '$0{\\placeholder{}}'); metadata('Operators', ['\\pm', '\\mp', '\\times', '\\div', '\\surd'], COMMON); metadata('Operators', [ '\\ltimes', '\\rtimes', '\\leftthreetimes', '\\rightthreetimes', '\\intercal', '\\dotplus', '\\centerdot', '\\doublebarwedge', '\\divideontimes', '\\divides', ], RARE); metadata('Logic', ['\\wedge', '\\vee', '\\neg'], SUPERCOMMON); metadata('Logic', ['\\lnot'], COMMON); metadata('Logic', [ '\\land', '\\lor', '\\barwedge', '\\veebar', '\\nor', '\\curlywedge', '\\curlyvee', ], RARE); metadata('Greek', [ '\\alpha', '\\beta', '\\gamma', '\\delta', '\\epsilon', '\\varepsilon', '\\zeta', '\\eta', '\\theta', '\\vartheta', '\\iota', '\\kappa', '\\varkappa', '\\lambda', '\\mu', '\\nu', '\\xi', '\\pi', '\\varpi', '\\rho', '\\varrho', '\\sigma', '\\varsigma', '\\tau', '\\phi', '\\varphi', '\\upsilon', '\\chi', '\\psi', '\\omega', '\\Gamma', '\\Delta', '\\Theta', '\\Lambda', '\\Xi', '\\Pi', '\\Sigma', '\\Upsilon', '\\Phi', '\\Psi', '\\Omega', '\\digamma', '\\omicron', ], COMMON); metadata('Sets', [ '\\emptyset', '\\varnothing', '\\cap', '\\cup', '\\in', '\\notin', '\\subset', '\\supset', '\\subseteq', '\\supseteq', '\\sqsubseteq', ], SUPERCOMMON); metadata('Sets', [ '\\setminus', '\\not', '\\ni', '\\sqsupseteq', '\\nsupseteqq', '\\supsetneq', '\\varsupsetneq', '\\supsetneqq', '\\varsupsetneqq', ], COMMON); metadata('Sets', [ '\\smallsetminus', '\\complement', '\\owns', '\\subsetneq', '\\varsubsetneq', '\\subsetneqq', '\\varsubsetneqq', '\\nsubset', '\\nsupset', '\\nsubseteq', '\\nsupseteq', '\\nsubseteqq', '\\subseteqq', '\\Subset', '\\sqsubset', '\\supseteqq', '\\Supset', '\\sqsupset', ], RARE); metadata('Spacing', ['\\space, \\quad', '\\qquad'], COMMON, '\\unicode{"203A}$0\\unicode{"2039}'); metadata('Spacing', ['\\!', '\\,', '\\:', '\\;', '\\enskip', '\\enspace'], RARE, '\\unicode{"203A}$0\\unicode{"2039}'); metadata('Spacing', ['\\hspace', '\\hspace*'], RARE, '\\unicode{"203A}$0{1em}\\unicode{"2039}'); metadata('Punctuation', [ '\\colon', '\\cdotp', '\\ldots', '\\cdots', '\\ddots', '\\vdots', '?', '!', ':', '"', ',', '.', ';', ], COMMON); metadata('Punctuation', ['\\mathellipsis', '\\ldotp', '\\Colon'], RARE); metadata('Boxes', ['\\square', '\\Box'], COMMON); metadata('Boxes', ['\\blacksquare', '\\boxminus', '\\boxplus', '\\boxtimes', '\\boxdot'], RARE); metadata('Circles', ['\\circ', '\\bullet', '\\circleddash', '\\oplus', '\\otimes'], COMMON); metadata('Circles', [ '\\bigcirc', '\\circledast', '\\ominus', '\\circledcirc', '\\oslash', '\\circledS', '\\circledR', '\\odot', ], RARE); metadata('Triangles', ['\\triangle', '\\triangleq'], COMMON); metadata('Triangles', [ '\\bigtriangleup', '\\vartriangle', '\\triangledown', '\\bigtriangledown', '\\triangleleft', '\\vartriangleleft', '\\trianglelefteq', '\\ntriangleleft', '\\ntrianglelefteq', '\\triangleright', '\\vartriangleright', '\\trianglerighteq', '\\ntriangleright', '\\ntrianglerighteq', '\\blacktriangle', '\\blacktriangledown', '\\blacktriangleleft', '\\blacktriangleright', ], RARE); metadata('Shapes', ['\\ast', '\\star'], COMMON); metadata('Shapes', ['\\diamond', '\\Diamond', '\\lozenge', '\\blacklozenge', '\\bigstar'], RARE); const STRINGS = { en: { 'keyboard.tooltip.functions': 'Functions', 'keyboard.tooltip.symbols': 'Symbols', 'keyboard.tooltip.greek': 'Greek Letters', 'keyboard.tooltip.command': 'LaTeX Command Mode', 'keyboard.tooltip.numeric': 'Numeric', 'keyboard.tooltip.roman': 'Roman Letters', 'tooltip.copy to clipboard': 'Copy to Clipboard', 'tooltip.redo': 'Redo', 'tooltip.toggle virtual keyboard': 'Toggle Virtual Keyboard', 'tooltip.undo': 'Undo', 'menu.insert matrix': 'Insert Matrix', 'menu.insert vector': 'Insert Vector', 'submenu.array.matrix delimiters': 'Matrix Delimiters', 'menu.array.add row above': 'Add Row After', 'menu.array.add row below': 'Add Row Before', 'menu.array.add column after': 'Add Column After', 'menu.array.add column before': 'Add Column Before', 'menu.array.delete row': 'Delete Row', 'menu.array.delete rows': 'Delete Selected Rows', 'menu.array.delete column': 'Delete Column', 'menu.array.delete columns': 'Delete Selected Columns', 'submenu.array.insert separator': 'Insert Separator', 'menu.insert table': 'Insert Table', 'submenu.table style': 'Table Style', }, // Arabic ar: { 'keyboard.tooltip.functions': 'مهام', 'keyboard.tooltip.symbols': 'حرف او رمز', 'keyboard.tooltip.greek': 'حروف يونانية', 'keyboard.tooltip.command': 'حالة تلقي الأوامر اللاتك', 'keyboard.tooltip.numeric': 'الرقمية', 'keyboard.tooltip.roman': 'رموز الاحرف الرومانية', 'tooltip.copy to clipboard': 'نسخ إلى الحافظة', 'tooltip.redo': 'الإعادة', 'tooltip.toggle virtual keyboard': 'تبديل لوحة المفاتيح الإفتراضية', 'tooltip.undo': 'إلغاء', 'menu.insert matrix': 'أدخل المصفوفة', 'menu.insert vector': 'أدخل متجه', 'submenu.array.matrix delimiters': 'محددات المصفوفة', 'menu.array.add row above': 'أضف صفًا بعد ذلك', 'menu.array.add row below': 'أضف الصف قبل', 'menu.array.add column after': 'أضف العمود بعد ذلك', 'menu.array.add column before': 'أضف العمود قبل', 'menu.array.delete row': 'احذف صف', 'menu.array.delete rows': 'حذف الصفوف المحددة', 'menu.array.delete column': 'حذف العمود', 'menu.array.delete columns': 'حذف الأعمدة المحددة', 'submenu.array.insert separator': 'أدخل فاصل', 'menu.insert table': 'إدراج جدول', 'submenu.table style': 'نمط الجدول', }, //Bulgarian bg: { 'keyboard.tooltip.functions': 'Функции', 'keyboard.tooltip.symbols': 'Символи', 'keyboard.tooltip.greek': 'Гръцки букви', 'keyboard.tooltip.command': 'Команден режим на латекс', 'keyboard.tooltip.numeric': 'Числови', 'keyboard.tooltip.roman': 'Римски букви', 'tooltip.copy to clipboard': 'Копиране в клипборда', 'tooltip.redo': 'Повторно', 'tooltip.toggle virtual keyboard': 'Превключване на виртуална клавиатура', 'tooltip.undo': 'Отмяна', 'menu.insert matrix': 'Вмъкване на матрица', 'menu.insert vector': 'Вмъкване на вектор', 'submenu.array.matrix delimiters': 'Матрични разделители', 'menu.array.add row above': 'Добавяне на ред след', 'menu.array.add row below': 'Добавяне на ред преди', 'menu.array.add column after': 'Добавяне на колона след', 'menu.array.add column before': 'Добавяне на колона преди', 'menu.array.delete row': 'Изтриване на реда', 'menu.array.delete rows': 'Изтриване на избраните редове', 'menu.array.delete column': 'Изтриване на колона', 'menu.array.delete columns': 'Изтриване на избраните колони', 'submenu.array.insert separator': 'Поставете разделител', 'menu.insert table': 'Вмъкване на таблица', 'submenu.table style': 'Табличен стил', }, // Bosnian bs: { 'keyboard.tooltip.functions': 'Funkcije', 'keyboard.tooltip.symbols': 'Simboli', 'keyboard.tooltip.greek': 'Grčka slova', 'keyboard.tooltip.command': 'LaTeX naredbeni način', 'keyboard.tooltip.numeric': 'Numerički', 'keyboard.tooltip.roman': 'Rimska slova', 'tooltip.copy to clipboard': 'Kopirati u clipboard', 'tooltip.redo': 'Ponovi', 'tooltip.toggle virtual keyboard': 'Uključi / isključi virtualnu tipkovnicu', 'tooltip.undo': 'Poništi', 'menu.insert matrix': 'Umetni matricu', 'menu.insert vector': 'Umetni vektor', 'submenu.array.matrix delimiters': 'Matrični razdjelnici', 'menu.array.add row above': 'Dodaj redak nakon', 'menu.array.add row below': 'Dodaj red prije', 'menu.array.add column after': 'Dodaj stupac nakon', 'menu.array.add column before': 'Dodaj stupac prije', 'menu.array.delete row': 'Izbriši red', 'menu.array.delete rows': 'Izbriši odabrane redove', 'menu.array.delete column': 'Izbriši stupac', 'menu.array.delete columns': 'Izbriši odabrane stupce', 'submenu.array.insert separator': 'Umetni separator', 'menu.insert table': 'Ubaci tabelu', 'submenu.table style': 'Stil tabele', }, // Czech cs: { 'keyboard.tooltip.functions': 'Funkce', 'keyboard.tooltip.symbols': 'Symboly', 'keyboard.tooltip.greek': 'Řecké dopisy', 'keyboard.tooltip.command': 'Příkazový režim LaTeX', 'keyboard.tooltip.numeric': 'Číselné', 'keyboard.tooltip.roman': 'Římské dopisy', 'tooltip.copy to clipboard': 'Zkopírovat do schránky', 'tooltip.redo': 'Předělat', 'tooltip.toggle virtual keyboard': 'Přepnout virtuální klávesnici', 'tooltip.undo': 'Vrátit', 'menu.insert matrix': 'Vložte matici', 'menu.insert vector': 'Vložit vektor', 'submenu.array.matrix delimiters': 'Oddělovače matic', 'menu.array.add row above': 'Přidat řádek za', 'menu.array.add row below': 'Přidat řádek před', 'menu.array.add column after': 'Přidat sloupec za', 'menu.array.add column before': 'Přidat sloupec dříve', 'menu.array.delete row': 'Odstranit řádek', 'menu.array.delete rows': 'Odstranit vybrané řádky', 'menu.array.delete column': 'Odstranit sloupec', 'menu.array.delete columns': 'Odstranit vybrané sloupce', 'submenu.array.insert separator': 'Vložte oddělovač', 'menu.insert table': 'Vložit tabulku', 'submenu.table style': 'Styl tabulky', }, // Dannish da: { 'keyboard.tooltip.functions': 'Funktioner', 'keyboard.tooltip.symbols': 'Symboler', 'keyboard.tooltip.greek': 'Græske bogstaver', 'keyboard.tooltip.command': 'LaTeX kommandotilstand', 'keyboard.tooltip.numeric': 'Numerisk', 'keyboard.tooltip.roman': 'Romerske breve', 'tooltip.copy to clipboard': 'Kopier til udklipsholder', 'tooltip.redo': 'Gentag igen', 'tooltip.toggle virtual keyboard': 'Skift virtuelt tastatur', 'tooltip.undo': 'Fortryd', 'menu.insert matrix': 'Indsæt matrix', 'menu.insert vector': 'Indsæt vektor', 'submenu.array.matrix delimiters': 'Matrixafgrænsere', 'menu.array.add row above': 'Tilføj række efter', 'menu.array.add row below': 'Tilføj række før', 'menu.array.add column after': 'Tilføj kolonne efter', 'menu.array.add column before': 'Tilføj kolonne før', 'menu.array.delete row': 'Slet række', 'menu.array.delete rows': 'Slet valgte rækker', 'menu.array.delete column': 'Slet kolonne', 'menu.array.delete columns': 'Slet valgte kolonner', 'submenu.array.insert separator': 'Indsæt separator', 'menu.insert table': 'Indsæt tabel', 'submenu.table style': 'Tabelstil', }, // German de: { 'keyboard.tooltip.functions': 'Funktionen', 'keyboard.tooltip.symbols': 'Symbole', 'keyboard.tooltip.greek': 'Griechische Buchstaben', 'keyboard.tooltip.command': 'LaTeX-Befehlsmodus', 'keyboard.tooltip.numeric': 'Numerisch', 'keyboard.tooltip.roman': 'Römische Buchstaben', 'tooltip.copy to clipboard': 'In die Zwischenablage kopieren', 'tooltip.redo': 'Wiederholen', 'tooltip.toggle virtual keyboard': 'Virtuelle Tastatur umschalten', 'tooltip.undo': 'Widerrufen', 'menu.insert matrix': 'Matrix einfügen', 'menu.insert vector': 'Vektor einfügen', 'submenu.array.matrix delimiters': 'Matrixtrennzeichen', 'menu.array.add row above': 'Zeile hinzufügen nach', 'menu.array.add row below': 'Zeile hinzufügen vor', 'menu.array.add column after': 'Spalte hinzufügen nach', 'menu.array.add column before': 'Spalte hinzufügen vor', 'menu.array.delete row': 'Zeile löschen', 'menu.array.delete rows': 'Ausgewählte Zeilen löschen', 'menu.array.delete column': 'Spalte löschen', 'menu.array.delete columns': 'Ausgewählte Spalten löschen', 'submenu.array.insert separator': 'Trennzeichen einfügen', 'menu.insert table': 'Tabelle einfügen', 'submenu.table style': 'Tabellenstil', }, // Greek el: { 'keyboard.tooltip.functions': 'συναρτήσεις', 'keyboard.tooltip.symbols': 'σύμβολα', 'keyboard.tooltip.greek': 'ελληνικά γράμματα', 'keyboard.tooltip.command': 'Λειτουργία εντολών LaTeX', 'keyboard.tooltip.numeric': 'Αριθμητικός', 'keyboard.tooltip.roman': 'Ρωμαϊκά γράμματα', 'tooltip.copy to clipboard': 'Αντιγραφή στο πρόχειρο', 'tooltip.redo': 'Ξανακάνω', 'tooltip.toggle virtual keyboard': 'Εναλλαγή εικονικού πληκτρολογίου', 'tooltip.undo': 'Ξεκάνω', 'menu.insert matrix': 'Εισαγωγή Matrix', 'menu.insert vector': 'Εισαγωγή διανύσματος', 'submenu.array.matrix delimiters': 'Οριοθέτες Matrix', 'menu.array.add row above': 'Προσθήκη σειράς μετά', 'menu.array.add row below': 'Προσθήκη σειράς πριν', 'menu.array.add column after': 'Προσθήκη στήλης μετά', 'menu.array.add column before': 'Προσθήκη στήλης πριν', 'menu.array.delete row': 'Διαγραφή σειράς', 'menu.array.delete rows': 'Διαγραφή επιλεγμένων σειρών', 'menu.array.delete column': 'Διαγραφή στήλης', 'menu.array.delete columns': 'Διαγραφή επιλεγμένων στηλών', 'submenu.array.insert separator': 'Εισαγωγή διαχωριστικού', 'menu.insert table': 'Εισαγωγή πίνακα', 'submenu.table style': 'Στυλ πίνακα', }, // Spanish es: { 'keyboard.tooltip.functions': 'Funciones', 'keyboard.tooltip.symbols': 'Símbolos', 'keyboard.tooltip.greek': 'Letras griegas', 'keyboard.tooltip.command': 'Modo Comando LaTeX', 'keyboard.tooltip.numeric': 'Numérico', 'keyboard.tooltip.roman': 'Letras romanas', 'tooltip.copy to clipboard': 'Copiar al portapapeles', 'tooltip.redo': 'Rehacer', 'tooltip.toggle virtual keyboard': 'Alternar teclado virtual', 'tooltip.undo': 'Deshacer', 'menu.insert matrix': 'Añadir Matriz', 'menu.insert vector': 'Añadir vector', 'submenu.array.matrix delimiters': 'Delimitadores de Matriz', 'menu.array.add row above': 'Añadir Línea Antes', 'menu.array.add row below': 'Añadir Línea Despues', 'menu.array.add column after': 'Añadir Columna Despues', 'menu.array.add column before': 'Añadir Columna Antes', 'menu.array.delete row': 'Borrar Línea', 'menu.array.delete rows': 'Borrar Líneas Seleccionadas', 'menu.array.delete column': 'Borrar Columna', 'menu.array.delete columns': 'Borrar Columnas Seleccionadas', 'submenu.array.insert separator': 'Insertar un Separador', 'menu.insert table': 'Insertar Tabla', 'submenu.table style': 'Estilo de Tabla', }, // Estonian et: { 'keyboard.tooltip.functions': 'Funktsioonid', 'keyboard.tooltip.symbols': 'Sümbolid', 'keyboard.tooltip.greek': 'Kreeka kirjad', 'keyboard.tooltip.command': 'LaTeXi käsurežiim', 'keyboard.tooltip.numeric': 'Numbriline', 'keyboard.tooltip.roman': 'Rooma kirjad', 'tooltip.copy to clipboard': 'Kopeerida lõikelauale', 'tooltip.redo': 'Tee uuesti', 'tooltip.toggle virtual keyboard': 'Lülitage sisse virtuaalne klaviatuur', 'tooltip.undo': 'Võta tagasi', 'menu.insert matrix': 'Sisesta maatriks', 'menu.insert vector': 'Sisesta vektor', 'submenu.array.matrix delimiters': 'Maatriksi eraldajad', 'menu.array.add row above': 'Lisa rida pärast', 'menu.array.add row below': 'Lisa rida enne', 'menu.array.add column after': 'Lisa veerg pärast', 'menu.array.add column before': 'Lisa veerg enne', 'menu.array.delete row': 'Kustuta rida', 'menu.array.delete rows': 'Kustuta valitud read', 'menu.array.delete column': 'Kustuta veerg', 'menu.array.delete columns': 'Kustuta valitud veerud', 'submenu.array.insert separator': 'Sisestage eraldaja', 'menu.insert table': 'Sisesta tabeli', 'submenu.table style': 'Tabeli stiilis', }, // Farsi fa: { 'keyboard.tooltip.functions': 'توابع', 'keyboard.tooltip.symbols': 'نمادها', 'keyboard.tooltip.greek': 'حروف یونانی', 'keyboard.tooltip.command': 'حالت دستور لاتک', 'keyboard.tooltip.numeric': 'عددی', 'keyboard.tooltip.roman': 'حروف رومی', 'tooltip.copy to clipboard': 'کپی به کلیپبورد', 'tooltip.redo': 'بازگشت به بعد', 'tooltip.toggle virtual keyboard': 'نمایش/نهفتن کیبورد مجازی', 'tooltip.undo': 'بازگشت به قبل', 'menu.insert matrix': 'ماتریس را وارد کنید', 'menu.insert vector': 'درج بردار', 'submenu.array.matrix delimiters': 'مرزهای ماتریس', 'menu.array.add row above': 'بعد از آن ردیف اضافه کنید', 'menu.array.add row below': 'ردیف را قبل اضافه کنید', 'menu.array.add column after': 'اضافه کردن ستون بعد', 'menu.array.add column before': 'ستون قبل را اضافه کنید', 'menu.array.delete row': 'ردیف را حذف کنید', 'menu.array.delete rows': 'ردیف های انتخاب شده را حذف کنید', 'menu.array.delete column': 'حذف ستون', 'menu.array.delete columns': 'ستون های انتخاب شده را حذف کنید', 'submenu.array.insert separator': 'درج جدا کننده', 'menu.insert table': 'قرار دادن جدول', 'submenu.table style': 'سبک میز', }, // Finnish fi: { 'keyboard.tooltip.functions': 'Toiminnot', 'keyboard.tooltip.symbols': 'Symbolit', 'keyboard.tooltip.greek': 'Kreikkalaiset kirjeet', 'keyboard.tooltip.command': 'LaTeX-komentotila', 'keyboard.tooltip.numeric': 'Numeerinen', 'keyboard.tooltip.roman': 'Roomalaiset kirjeet', 'tooltip.copy to clipboard': 'Kopioi leikepöydälle', 'tooltip.redo': 'Tee uudelleen', 'tooltip.toggle virtual keyboard': 'Vaihda virtuaalinäppäimistö', 'tooltip.undo': 'Kumoa', 'menu.insert matrix': 'Lisää matriisi', 'menu.insert vector': 'Lisää vektori', 'submenu.array.matrix delimiters': 'Matriisin erottimet', 'menu.array.add row above': 'Lisää rivi jälkeen', 'menu.array.add row below': 'Lisää rivi ennen', 'menu.array.add column after': 'Lisää sarake jälkeen', 'menu.array.add column before': 'Lisää sarake ennen', 'menu.array.delete row': 'Poista rivi', 'menu.array.delete rows': 'Poista valitut rivit', 'menu.array.delete column': 'Poista sarake', 'menu.array.delete columns': 'Poista valitut sarakkeet', 'submenu.array.insert separator': 'Aseta erotin', 'menu.insert table': 'Lisää taulukko', 'submenu.table style': 'Taulukon tyyli', }, // French fr: { 'keyboard.tooltip.functions': 'Fonctions', 'keyboard.tooltip.symbols': 'Symboles', 'keyboard.tooltip.greek': 'Lettres grecques', 'keyboard.tooltip.command': 'Mode de commandes LaTeX', 'keyboard.tooltip.numeric': 'Numérique', 'keyboard.tooltip.roman': 'Lettres romaines', 'tooltip.copy to clipboard': 'Copier dans le presse-papiers', 'tooltip.redo': 'Rétablir', 'tooltip.toggle virtual keyboard': 'Afficher/Masquer le clavier virtuel', 'tooltip.undo': 'Annuler', 'menu.insert matrix': 'Insérer une Matrice', 'menu.insert vector': 'Insérer un Vecteur', 'submenu.array.matrix delimiters': 'Délimiteurs de la Matrice', 'menu.array.add row above': 'Ajouter une Ligne Avant', 'menu.array.add row below': 'Ajouter une Ligne Après', 'menu.array.add column before': 'Ajouter une Colonne Avant', 'menu.array.add column after': 'Ajouter une Colonne Après', 'menu.array.delete row': 'Enlever une Ligne', 'menu.array.delete rows': 'Enlever les Lignes Sélectionées', 'menu.array.delete column': 'Enlever une Colone', 'menu.array.delete columns': 'Enlever les Colonnes Sélectionées', 'submenu.array.insert separator': 'Insérer un Séparateur', 'menu.insert table': 'Insérer une Table', 'submenu.table style': 'Style de la Table', }, // Irish ga: { 'keyboard.tooltip.functions': 'Feidhmeanna', 'keyboard.tooltip.symbols': 'Siombailí', 'keyboard.tooltip.greek': 'Litreacha na Gréige', 'keyboard.tooltip.command': 'Mód Ordú LaTeX', 'keyboard.tooltip.numeric': 'Uimhriúil', 'keyboard.tooltip.roman': 'Litreacha Rómhánacha', 'tooltip.copy to clipboard': 'Cóipeáil chuig an Ghearrthaisce', 'tooltip.redo': 'Athdhéan', 'tooltip.toggle virtual keyboard': 'Méarchláir Fíorúil a Fháil', 'tooltip.undo': 'Cealaigh', 'menu.insert matrix': 'Cuir isteach Maitrís', 'menu.insert vector': 'Cuir isteach Veicteoir', 'submenu.array.matrix delimiters': 'Delimiters Maitrís', 'menu.array.add row above': 'Cuir Rae Tar éis', 'menu.array.add row below': 'Cuir Rae Roimh', 'menu.array.add column after': 'Cuir Colún Tar éis', 'menu.array.add column before': 'Cuir Colún Roimh', 'menu.array.delete row': 'Scrios Rae', 'menu.array.delete rows': 'Scrios Sraitheanna Roghnaithe', 'menu.array.delete column': 'Scrios Colún', 'menu.array.delete columns': 'Scrios Colúin Roghnaithe', 'submenu.array.insert separator': 'Cuir Deighilteoir isteach', 'menu.insert table': 'Ionsáigh Tábla', 'submenu.table style': 'Stíl Tábla', }, // Hebrew (Israel) he: { 'keyboard.tooltip.functions': 'פונקציות', 'keyboard.tooltip.symbols': 'סמלים', 'keyboard.tooltip.greek': 'אותיות יווניות', 'keyboard.tooltip.command': 'מצב פקודה לטקס', 'keyboard.tooltip.numeric': 'מספרי', 'keyboard.tooltip.roman': 'מכתבים רומיים', 'tooltip.copy to clipboard': 'העתק ללוח', 'tooltip.redo': 'לַעֲשׂוֹת שׁוּב', 'tooltip.toggle virtual keyboard': 'החלף את המקלדת הווירטואלית', 'tooltip.undo': 'לבטל', 'menu.insert matrix': 'הכנס מטריקס', 'menu.insert vector': 'הכנס וקטור', 'submenu.array.matrix delimiters': 'מפרידי מטריקס', 'menu.array.add row above': 'הוסף שורה אחרי', 'menu.array.add row below': 'הוסף שורה לפני', 'menu.array.add column after': 'הוסף עמודה אחרי', 'menu.array.add column before': 'הוסף עמודה לפני', 'menu.array.delete row': 'מחק שורה', 'menu.array.delete rows': 'מחק שורות שנבחרו', 'menu.array.delete column': 'מחק עמודה', 'menu.array.delete columns': 'מחק עמודות שנבחרו', 'submenu.array.insert separator': 'הכנס מפריד', 'menu.insert table': 'הכנס טבלה', 'submenu.table style': 'טבלה סִגְנוֹן', }, // Croatian hr: { 'keyboard.tooltip.functions': 'Funkcije', 'keyboard.tooltip.symbols': 'Simboli', 'keyboard.tooltip.greek': 'Grčka slova', 'keyboard.tooltip.command': 'LaTeX naredbeni način', 'keyboard.tooltip.numeric': 'Numerički', 'keyboard.tooltip.roman': 'Rimska slova', 'tooltip.copy to clipboard': 'Kopirati u međuspremnik', 'tooltip.redo': 'Ponovi', 'tooltip.toggle virtual keyboard': 'Uključi / isključi virtualnu tipkovnicu', 'tooltip.undo': 'Poništi', 'menu.insert matrix': 'Umetni matricu', 'menu.insert vector': 'Umetni vektor', 'submenu.array.matrix delimiters': 'Matrični razdjelnici', 'menu.array.add row above': 'Dodaj redak nakon', 'menu.array.add row below': 'Dodaj redak prije', 'menu.array.add column after': 'Dodaj stupac nakon', 'menu.array.add column before': 'Dodaj stupac prije', 'menu.array.delete row': 'Izbriši redak', 'menu.array.delete rows': 'Izbriši odabrane retke', 'menu.array.delete column': 'Izbriši stupac', 'menu.array.delete columns': 'Izbriši odabrane stupce', 'submenu.array.insert separator': 'Umetni separator', 'menu.insert table': 'Umetni tablicu', 'submenu.table style': 'Stil tabele', }, /// Indonesian id: { 'keyboard.tooltip.functions': 'Fungsi', 'keyboard.tooltip.symbols': 'Simbol', 'keyboard.tooltip.greek': 'Huruf Yunani', 'keyboard.tooltip.command': 'Mode Perintah LaTeX', 'keyboard.tooltip.numeric': 'Numerik', 'keyboard.tooltip.roman': 'Surat Romawi', 'tooltip.copy to clipboard': 'Menyalin ke clipboard', 'tooltip.redo': 'Mengulangi', 'tooltip.toggle virtual keyboard': 'Alihkan Keyboard Virtual', 'tooltip.undo': 'Membuka', 'menu.insert matrix': 'Sisipkan Matriks', 'menu.insert vector': 'Sisipkan Vektor', 'submenu.array.matrix delimiters': 'Pembatas Matriks', 'menu.array.add row above': 'Tambahkan Baris Setelah', 'menu.array.add row below': 'Tambahkan Baris Sebelumnya', 'menu.array.add column after': 'Tambahkan Kolom Setelah', 'menu.array.add column before': 'Tambahkan Kolom Sebelumnya', 'menu.array.delete row': 'Hapus Baris', 'menu.array.delete rows': 'Hapus Baris yang Dipilih', 'menu.array.delete column': 'Hapus Kolom', 'menu.array.delete columns': 'Hapus Kolom yang Dipilih', 'submenu.array.insert separator': 'Sisipkan Pemisah', 'menu.insert table': 'Sisipkan Tabel', 'submenu.table style': 'Gaya Tabel', }, // Hindi (India) hi: { 'keyboard.tooltip.functions': 'कार्यों', 'keyboard.tooltip.symbols': 'प्रतीक', 'keyboard.tooltip.greek': 'ग्रीक अक्षर', 'keyboard.tooltip.command': 'लाटेक्स कमांड मोड', 'keyboard.tooltip.numeric': 'संख्यात्मक', 'keyboard.tooltip.roman': 'रोमन पत्र', 'tooltip.copy to clipboard': 'क्लिपबोर्ड पर कॉपी करें', 'tooltip.redo': 'फिर से करें', 'tooltip.toggle virtual keyboard': 'वर्चुअल कीबोर्ड टॉगल करें', 'tooltip.undo': 'पूर्ववत', 'menu.insert matrix': 'मैट्रिक्स डालें', 'menu.insert vector': 'वेक्टर डालें', 'submenu.array.matrix delimiters': 'मैट्रिक्स सीमांकक', 'menu.array.add row above': 'बाद में पंक्ति जोड़ें', 'menu.array.add row below': 'पहले पंक्ति जोड़ें', 'menu.array.add column after': 'बाद में कॉलम जोड़ें', 'menu.array.add column before': 'पहले कॉलम जोड़ें', 'menu.array.delete row': 'पंक्ति को हटाएं', 'menu.array.delete rows': 'चयनित पंक्तियों को हटाएं', 'menu.array.delete column': 'कॉलम हटाएं', 'menu.array.delete columns': 'चयनित कॉलम हटाएं', 'submenu.array.insert separator': 'विभाजक डालें', 'menu.insert table': 'टेबल इंसर्ट करें', 'submenu.table style': 'टेबल स्टाइल', }, // Hungarian hu: { 'keyboard.tooltip.functions': 'Funkciók', 'keyboard.tooltip.symbols': 'Szimbólumok', 'keyboard.tooltip.greek': 'Görög levelek', 'keyboard.tooltip.command': 'LaTeX Parancs mód', 'keyboard.tooltip.numeric': 'Numerikus', 'keyboard.tooltip.roman': 'Római levelek', 'tooltip.copy to clipboard': 'Másolja a vágólapra', 'tooltip.redo': 'Újra', 'tooltip.toggle virtual keyboard': 'Váltás a virtuális billentyűzetre', 'tooltip.undo': 'Visszavonás', 'menu.insert matrix': 'Helyezze be a Mátrixot', 'menu.insert vector': 'Vektor beszúrása', 'submenu.array.matrix delimiters': 'Mátrixhatárolók', 'menu.array.add row above': 'Sor hozzáadása után', 'menu.array.add row below': 'Add Add Sor előtt', 'menu.array.add column after': 'Oszlop hozzáadása után', 'menu.array.add column before': 'Add oszlop előtt', 'menu.array.delete row': 'Sor törlése', 'menu.array.delete rows': 'Kijelölt sorok törlése', 'menu.array.delete column': 'Oszlop törlése', 'menu.array.delete columns': 'A kijelölt oszlopok törlése', 'submenu.array.insert separator': 'Helyezze be az elválasztót', 'menu.insert table': 'Helyezze be a táblázatot', 'submenu.table style': 'Táblázatos stílus', }, // Italian it: { 'keyboard.tooltip.functions': 'Funzioni', 'keyboard.tooltip.symbols': 'Simboli', 'keyboard.tooltip.greek': 'Lettere greche', 'keyboard.tooltip.command': 'Modalità di comando LaTeX', 'keyboard.tooltip.numeric': 'Numerico', 'keyboard.tooltip.roman': 'Lettere romane', 'tooltip.copy to clipboard': 'Copia negli appunti', 'tooltip.redo': 'Rifare', 'tooltip.toggle virtual keyboard': 'Attiva / disattiva la tastiera virtuale', 'tooltip.undo': 'Disfare', 'menu.insert matrix': 'Inserisci una Matrice', 'menu.insert vector': 'Inserisci Vettore', 'submenu.array.matrix delimiters': 'Delimitatori di Matrice', 'menu.array.add row above': 'Aggiungi una Riga Prima', 'menu.array.add row below': 'Aggiungi una Riga Dopo', 'menu.array.add column before': 'Aggiungi una Colonna Prima', 'menu.array.add column after': 'Aggiungi una Colonna Dopo', 'menu.array.delete row': 'Rimuovi una Riga', 'menu.array.delete rows': 'Rimuovi le Righe Selezionate', 'menu.array.delete column': 'Rimuovi una Colonna', 'menu.array.delete columns': 'Rimuovi le Colonne Selezionate', 'submenu.array.insert separator': 'Inserisci un Separatore', 'menu.insert table': 'Inserisci Tabella', 'submenu.table style': 'Stile tabella', }, // Icelandic is: { 'keyboard.tooltip.functions': 'Aðgerðir', 'keyboard.tooltip.symbols': 'Tákn', 'keyboard.tooltip.greek': 'Grísk bréf', 'keyboard.tooltip.command': 'LaTeX stjórnunarstilling', 'keyboard.tooltip.numeric': 'Tölulegt', 'keyboard.tooltip.roman': 'Rómversk bréf', 'tooltip.copy to clipboard': 'Afritaðu á klemmuspjald', 'tooltip.redo': 'Endurtaka', 'tooltip.toggle virtual keyboard': 'Skiptu um sýndarlyklaborð', 'tooltip.undo': 'Afturkalla', 'menu.insert matrix': 'Settu fylki inn', 'menu.insert vector': 'Settu inn Vector', 'submenu.array.matrix delimiters': 'Matrix afmörkun', 'menu.array.add row above': 'Bæta við röð á eftir', 'menu.array.add row below': 'Bæta við röð áður', 'menu.array.add column after': 'Bæta við dálki á eftir', 'menu.array.add column before': 'Bæta við dálki áður', 'menu.array.delete row': 'Eyða röð', 'menu.array.delete rows': 'Eyða völdum línum', 'menu.array.delete column': 'Eyða dálki', 'menu.array.delete columns': 'Eyða völdum dálkum', 'submenu.array.insert separator': 'Settu skiljuna í', 'menu.insert table': 'Settu inn töflu', 'submenu.table style': 'Töflu stíl', }, // Japanese ja: { 'keyboard.tooltip.functions': '関数', 'keyboard.tooltip.symbols': 'シンボル', 'keyboard.tooltip.greek': 'ギリシャ文字', 'keyboard.tooltip.command': 'ラテックスコマンドモード', 'keyboard.tooltip.numeric': '数値', 'keyboard.tooltip.roman': 'ローマ字', 'tooltip.copy to clipboard': 'クリップボードにコピー', 'tooltip.redo': 'やり直し', 'tooltip.toggle virtual keyboard': '仮想キーボードの切り替え', 'tooltip.undo': '元に戻す', 'menu.insert matrix': 'マトリックスを挿入', 'menu.insert vector': 'ベクトルを挿入', 'submenu.array.matrix delimiters': '行列区切り文字', 'menu.array.add row above': '後に行を追加', 'menu.array.add row below': '前に行を追加', 'menu.array.add column after': '後に列を追加', 'menu.array.add column before': '前に列を追加', 'menu.array.delete row': '行を削除', 'menu.array.delete rows': '選択した行を削除する', 'menu.array.delete column': '列を削除', 'menu.array.delete columns': '選択した列を削除する', 'submenu.array.insert separator': 'セパレーターを挿入', 'menu.insert table': 'テーブルを挿入', 'submenu.table style': 'テーブルスタイル', }, // Korean ko: { 'keyboard.tooltip.functions': '기능', 'keyboard.tooltip.symbols': '기호', 'keyboard.tooltip.greek': '그리스 문자', 'keyboard.tooltip.command': '유액 명령 모드', 'keyboard.tooltip.numeric': '숫자', 'keyboard.tooltip.roman': '로마 문자', 'tooltip.copy to clipboard': '클립 보드에 복사', 'tooltip.redo': '다시 하다', 'tooltip.toggle virtual keyboard': '가상 키보드 전환', 'tooltip.undo': '실행 취소', 'menu.insert matrix': '매트릭스 삽입', 'menu.insert vector': '벡터 삽입', 'submenu.array.matrix delimiters': '행렬 구분 기호', 'menu.array.add row above': '뒤에 행 추가', 'menu.array.add row below': '앞에 행 추가', 'menu.array.add column after': '뒤에 열 추가', 'menu.array.add column before': '앞에 열 추가', 'menu.array.delete row': '행 삭제', 'menu.array.delete rows': '선택한 행 삭제', 'menu.array.delete column': '열 삭제', 'menu.array.delete columns': '선택한 열 삭제', 'submenu.array.insert separator': '구분자 삽입', 'menu.insert table': '표 삽입', 'submenu.table style': '테이블 스타일', }, // Lettish lv: { 'keyboard.tooltip.functions': 'Funkcijas', 'keyboard.tooltip.symbols': 'Simboli', 'keyboard.tooltip.greek': 'Grieķu burti', 'keyboard.tooltip.command': 'LaTeX komandu režīms', 'keyboard.tooltip.numeric': 'Ciparu skaitlis', 'keyboard.tooltip.roman': 'Romiešu vēstules', 'tooltip.copy to clipboard': 'Kopēt starpliktuvē', 'tooltip.redo': 'Pārtaisīt', 'tooltip.toggle virtual keyboard': 'Pārslēgt virtuālo tastatūru', 'tooltip.undo': 'Atsaukt', 'menu.insert matrix': 'Ievietojiet matricu', 'menu.insert vector': 'Ievietot vektoru', 'submenu.array.matrix delimiters': 'Matricas norobežotāji', 'menu.array.add row above': 'Pievienot rindu pēc', 'menu.array.add row below': 'Pievienot rindu pirms', 'menu.array.add column after': 'Pievienot kolonnu pēc', 'menu.array.add column before': 'Pievienot kolonnu pirms', 'menu.array.delete row': 'Dzēst rindu', 'menu.array.delete rows': 'Dzēst atlasītās rindas', 'menu.array.delete column': 'Dzēst kolonnu', 'menu.array.delete columns': 'Dzēst atlasītās kolonnas', 'submenu.array.insert separator': 'Ievietojiet atdalītāju', 'menu.insert table': 'Ievietojiet tabulu', 'submenu.table style': 'Galda stils', }, // Lithuanian lt: { 'keyboard.tooltip.functions': 'Funkcijos', 'keyboard.tooltip.symbols': 'Simboliai', 'keyboard.tooltip.greek': 'Graikiškos raidės', 'keyboard.tooltip.command': 'LaTeX komandų režimas', 'keyboard.tooltip.numeric': 'Skaitmeninis', 'keyboard.tooltip.roman': 'Romos laiškai', 'tooltip.copy to clipboard': 'Nukopijuoti į iškarpinę', 'tooltip.redo': 'Perdaryti', 'tooltip.toggle virtual keyboard': 'Perjungti virtualiąją klaviatūrą', 'tooltip.undo': 'Atšaukti', 'menu.insert matrix': 'Ievietojiet matricu', 'menu.insert vector': 'Ievietot vektoru', 'submenu.array.matrix delimiters': 'Matricas norobežotāji', 'menu.array.add row above': 'Pievienot rindu pēc', 'menu.array.add row below': 'Pievienot rindu pirms', 'menu.array.add column after': 'Pievienot kolonnu pēc', 'menu.array.add column before': 'Pievienot kolonnu pirms', 'menu.array.delete row': 'Dzēst rindu', 'menu.array.delete rows': 'Dzēst atlasītās rindas', 'menu.array.delete column': 'Dzēst kolonnu', 'menu.array.delete columns': 'Dzēst atlasītās kolonnas', 'submenu.array.insert separator': 'Ievietojiet atdalītāju', 'menu.insert table': 'Ievietojiet tabulu', 'submenu.table style': 'Tabulas stili', }, /// Luxembourgish lu: { 'keyboard.tooltip.functions': 'Funktiounen', 'keyboard.tooltip.symbols': 'Symboler', 'keyboard.tooltip.greek': 'Griichesch Bréiwer', 'keyboard.tooltip.command': 'LaTeX Kommando Modus', 'keyboard.tooltip.numeric': 'Numeresch', 'keyboard.tooltip.roman': 'Réimesch Bréiwer', 'tooltip.copy to clipboard': 'Kopéiert op Clipboard', 'tooltip.redo': 'Nees nei maachen', 'tooltip.toggle virtual keyboard': 'Wiesselt Virtuell Tastatur', 'tooltip.undo': 'Undoen', 'menu.insert matrix': 'Matrix asetzen', 'menu.insert vector': 'Insert Vector', 'submenu.array.matrix delimiters': 'Matrix Ofgrenzer', 'menu.array.add row above': 'Dobäizemaachen Rei No', 'menu.array.add row below': 'Füügt Rei vir', 'menu.array.add column after': 'Dobäizemaachen Kolonn No', 'menu.array.add column before': 'Kolonn derbäi Virun', 'menu.array.delete row': 'Rad läschen', 'menu.array.delete rows': 'Läscht Ausgewielte Reien', 'menu.array.delete column': 'Läscht Kolonn', 'menu.array.delete columns': 'Läscht Ausgewielte Kolonnen', 'submenu.array.insert separator': 'Insert Separator', 'menu.insert table': 'Dësch anzeginn', 'submenu.table style': 'Dësch Style', }, // Dutch nl: { 'keyboard.tooltip.functions': 'Functies', 'keyboard.tooltip.symbols': 'Symbolen', 'keyboard.tooltip.greek': 'Griekse letters', 'keyboard.tooltip.command': 'LaTeX commando mode', 'keyboard.tooltip.numeric': 'Numeriek', 'keyboard.tooltip.roman': 'Romeinse letters', 'tooltip.copy to clipboard': 'Kopiëren naar klembord', 'tooltip.redo': 'Opnieuw', 'tooltip.toggle virtual keyboard': 'Schakel naar virtueel toetsenbord', 'tooltip.undo': 'Ongedaan maken', 'menu.insert matrix': 'Matrix invoegen', 'menu.insert vector': 'Vector invoegen', 'submenu.array.matrix delimiters': 'Matrixscheidingstekens', 'menu.array.add row above': 'Rij toevoegen na', 'menu.array.add row below': 'Rij toevoegen eerder', 'menu.array.add column after': 'Kolom toevoegen na', 'menu.array.add column before': 'Kolom toevoegen voor', 'menu.array.delete row': 'Verwijder rij', 'menu.array.delete rows': 'Geselecteerde rijen verwijderen', 'menu.array.delete column': 'Kolom verwijderen', 'menu.array.delete columns': 'Geselecteerde kolommen verwijderen', 'submenu.array.insert separator': 'Scheidingsteken invoegen', 'menu.insert table': 'Tabel invoegen', 'submenu.table style': 'Tabelstijl', }, // Norwegian no: { 'keyboard.tooltip.functions': 'Funksjoner', 'keyboard.tooltip.symbols': 'Symboler', 'keyboard.tooltip.greek': 'Greske bokstaver', 'keyboard.tooltip.command': 'LaTeX kommandomodus', 'keyboard.tooltip.numeric': 'Numerisk', 'keyboard.tooltip.roman': 'Romerske bokstaver', 'tooltip.copy to clipboard': 'Kopiere til utklippstavle', 'tooltip.redo': 'Gjøre om', 'tooltip.toggle virtual keyboard': 'Bytt virtuelt tastatur', 'tooltip.undo': 'Angre', 'menu.insert matrix': 'Sett inn matrise', 'menu.insert vector': 'Sett inn vektor', 'submenu.array.matrix delimiters': 'Matrix avgrensere', 'menu.array.add row above': 'Legg til rad etter', 'menu.array.add row below': 'Legg til rad før', 'menu.array.add column after': 'Legg til kolonne etter', 'menu.array.add column before': 'Legg til kolonne før', 'menu.array.delete row': 'Slett rad', 'menu.array.delete rows': 'Slett valgte rader', 'menu.array.delete column': 'Slett kolonne', 'menu.array.delete columns': 'Slett valgte kolonner', 'submenu.array.insert separator': 'Sett inn skilletegn', 'menu.insert table': 'Sett inn tabell', 'submenu.table style': 'Tabellstil', }, // Macedonian mk: { 'keyboard.tooltip.functions': 'Функции', 'keyboard.tooltip.symbols': 'Симболи', 'keyboard.tooltip.greek': 'Грчки букви', 'keyboard.tooltip.command': 'Режим на команда во латекс', 'keyboard.tooltip.numeric': 'Нумерички', 'keyboard.tooltip.roman': 'Римски писма', 'tooltip.copy to clipboard': 'Копирајте во клипборд', 'tooltip.redo': 'Повторно', 'tooltip.toggle virtual keyboard': 'Вклучете ја виртуелната тастатура', 'tooltip.undo': 'Врати', 'menu.insert matrix': 'Вметнете матрица', 'menu.insert vector': 'Вметни вектор', 'submenu.array.matrix delimiters': 'Разграничувачи на матрица', 'menu.array.add row above': 'Додадете ред после', 'menu.array.add row below': 'Додади ред пред тоа', 'menu.array.add column after': 'Додадете колона после', 'menu.array.add column before': 'Додадете колона пред тоа', 'menu.array.delete row': 'Избриши го редот', 'menu.array.delete rows': 'Избришете ги избраните редови', 'menu.array.delete column': 'Избриши ја колоната', 'menu.array.delete columns': 'Избриши ја колоната', 'submenu.array.insert separator': 'Вметнете сепаратор', 'menu.insert table': 'Вметни табела', 'submenu.table style': 'Табела стил', }, // Polish pl: { 'keyboard.tooltip.functions': 'Funkcje', 'keyboard.tooltip.symbols': 'Symbolika', 'keyboard.tooltip.greek': 'Litery greckie', 'keyboard.tooltip.command': 'Tryb poleceń LaTeX', 'keyboard.tooltip.numeric': 'Numeryczne', 'keyboard.tooltip.roman': 'Litery rzymskie', 'tooltip.copy to clipboard': 'Kopiuj do Schowka', 'tooltip.redo': 'Przywróć', 'tooltip.toggle virtual keyboard': 'Przełącz wirtualną klawiaturę', 'tooltip.undo': 'Cofnij', 'menu.insert matrix': 'Wstaw macierz', 'menu.insert vector': 'Wstaw wektor', 'submenu.array.matrix delimiters': 'Ograniczniki macierzy', 'menu.array.add row above': 'Dodaj wiersz po', 'menu.array.add row below': 'Dodaj wiersz przed', 'menu.array.add column after': 'Dodaj kolumnę po', 'menu.array.add column before': 'Dodaj kolumnę przed', 'menu.array.delete row': 'Usuń wiersz', 'menu.array.delete rows': 'Usuń wybrane wiersze', 'menu.array.delete column': 'Usuń kolumnę', 'menu.array.delete columns': 'Usuń wybrane kolumny', 'submenu.array.insert separator': 'Wstaw separator', 'menu.insert table': 'Wypełnij tabelę', 'submenu.table style': 'Styl tabelę', }, // Portuguese pt: { 'keyboard.tooltip.functions': 'Functions', 'keyboard.tooltip.symbols': 'Símbolos', 'keyboard.tooltip.greek': 'Letras gregas', 'keyboard.tooltip.command': 'Modo de Comando LaTeX', 'keyboard.tooltip.numeric': 'Numérico', 'keyboard.tooltip.roman': 'Letras romanas', 'tooltip.copy to clipboard': 'Copiar para área de transferência', 'tooltip.redo': 'Refazer', 'tooltip.toggle virtual keyboard': 'Alternar teclado virtual', 'tooltip.undo': 'Desfazer', 'menu.insert matrix': 'Inserir Matriz', 'menu.insert vector': 'Inserir vetor', 'submenu.array.matrix delimiters': 'Delimitadores de matriz', 'menu.array.add row above': 'Adicionar linha depois', 'menu.array.add row below': 'Adicionar linha antes', 'menu.array.add column after': 'Adicionar coluna depois', 'menu.array.add column before': 'Adicionar coluna antes', 'menu.array.delete row': 'Excluir linha', 'menu.array.delete rows': 'Excluir linhas selecionadas', 'menu.array.delete column': 'Apagar Coluna', 'menu.array.delete columns': 'Excluir Colunas Selecionadas', 'submenu.array.insert separator': 'Inserir Separador', 'menu.insert table': 'Insira a tabela', 'submenu.table style': 'Estilo tabela', }, //Romaninan ro: { 'keyboard.tooltip.functions': 'Funcții', 'keyboard.tooltip.symbols': 'Simboluri', 'keyboard.tooltip.greek': 'Scrisori grecești', 'keyboard.tooltip.command': 'Modul de comandă latex', 'keyboard.tooltip.numeric': 'Numeric', 'keyboard.tooltip.roman': 'Scrisori romane', 'tooltip.copy to clipboard': 'Copiați în clipboard', 'tooltip.redo': 'A reface', 'tooltip.toggle virtual keyboard': 'Comutați tastatura virtuală', 'tooltip.undo': 'Anula', 'menu.insert matrix': 'Introduceți Matrix', 'menu.insert vector': 'Inserați Vector', 'submenu.array.matrix delimiters': 'Delimitatori de matrice', 'menu.array.add row above': 'Adăugați rândul după', 'menu.array.add row below': 'Adăugați rândul înainte', 'menu.array.add column after': 'Adăugați o coloană după', 'menu.array.add column before': 'Adăugați o coloană înainte', 'menu.array.delete row': 'Ștergeți rândul', 'menu.array.delete rows': 'Ștergeți rândurile selectate', 'menu.array.delete column': 'Ștergeți coloana', 'menu.array.delete columns': 'Ștergeți coloanele selectate', 'submenu.array.insert separator': 'Introduceți separatorul', 'menu.insert table': 'Introduceți tabelul', 'submenu.table style': 'Table style', }, // Russian ru: { 'keyboard.tooltip.functions': 'Функции', 'keyboard.tooltip.symbols': 'Символы', 'keyboard.tooltip.greek': 'Греческие буквы', 'keyboard.tooltip.command': 'Режим командной строки Латекс', 'keyboard.tooltip.numeric': 'числовой', 'keyboard.tooltip.roman': 'Латинские буквы', 'tooltip.copy to clipboard': 'Скопировать в буфер обмена', 'tooltip.redo': 'переделывать', 'tooltip.toggle virtual keyboard': 'Переключить виртуальную клавиатуру', 'tooltip.undo': 'расстегивать', 'menu.insert matrix': 'Вставить матрицу', 'menu.insert vector': 'Вставить вектор', 'submenu.array.matrix delimiters': 'Матричные разделители', 'menu.array.add row above': 'Добавить строку после', 'menu.array.add row below': 'Добавить строку перед', 'menu.array.add column after': 'Добавить столбец после', 'menu.array.add column before': 'Добавить столбец перед', 'menu.array.delete row': 'Удалить строку', 'menu.array.delete rows': 'Удалить выбранные строки', 'menu.array.delete column': 'Удалить столбец', 'menu.array.delete columns': 'Удалить выбранные столбцы', 'submenu.array.insert separator': 'Вставить разделитель', 'menu.insert table': 'Вставить таблицу', 'submenu.table style': 'Табличный стиль', }, // Slovak sk: { 'keyboard.tooltip.functions': 'Functions', 'keyboard.tooltip.symbols': 'Symboly', 'keyboard.tooltip.greek': 'Grécke listy', 'keyboard.tooltip.command': 'Príkazový režim LaTeX', 'keyboard.tooltip.numeric': 'Numerické', 'keyboard.tooltip.roman': 'Rímske listy', 'tooltip.copy to clipboard': 'Skopírovať do schránky', 'tooltip.redo': 'Znova', 'tooltip.toggle virtual keyboard': 'Prepnúť virtuálnu klávesnicu', 'tooltip.undo': 'Vrátenie späť', 'menu.insert matrix': 'Vložte maticu', 'menu.insert vector': 'Vložte vektor', 'submenu.array.matrix delimiters': 'Oddeľovače matíc', 'menu.array.add row above': 'Pridajte riadok za', 'menu.array.add row below': 'Pridajte riadok pred', 'menu.array.add column after': 'Pridať stĺpec za', 'menu.array.add column before': 'Pridajte stĺpec predtým', 'menu.array.delete row': 'Odstrániť riadok', 'menu.array.delete rows': 'Odstrániť vybraté riadky', 'menu.array.delete column': 'Odstrániť stĺpec', 'menu.array.delete columns': 'Odstrániť vybraté stĺpce', 'submenu.array.insert separator': 'Vložte oddeľovač', 'menu.insert table': 'Vložte tabuľku', 'submenu.table style': 'Štýl tabuľky', }, // Slovenian sl: { 'keyboard.tooltip.functions': 'Funkcije', 'keyboard.tooltip.symbols': 'Simboli', 'keyboard.tooltip.greek': 'Grška pisma', 'keyboard.tooltip.command': 'Ukazni način LaTeX', 'keyboard.tooltip.numeric': 'Številsko', 'keyboard.tooltip.roman': 'Rimska pisma', 'tooltip.copy to clipboard': 'Kopirati v odložišče', 'tooltip.redo': 'Ponovi', 'tooltip.toggle virtual keyboard': 'Preklop navidezne tipkovnice', 'tooltip.undo': 'Razveljavi', 'menu.insert matrix': 'Vstavi matrico', 'menu.insert vector': 'Vstavi vektor', 'submenu.array.matrix delimiters': 'Matrični ločevalniki', 'menu.array.add row above': 'Dodaj vrstico po', 'menu.array.add row below': 'Dodaj vrstico prej', 'menu.array.add column after': 'Dodaj stolpec po', 'menu.array.add column before': 'Dodaj stolpec prej', 'menu.array.delete row': 'Izbriši vrstico', 'menu.array.delete rows': 'Izbriši izbrane vrstice', 'menu.array.delete column': 'Izbriši stolpec', 'menu.array.delete columns': 'Izbriši izbrane stolpce', 'submenu.array.insert separator': 'Vstavi ločilo', 'menu.insert table': 'Vstavi tabelo', 'submenu.table style': 'Tabela slog', }, /// Albanian sq: { 'keyboard.tooltip.functions': 'Funksione', 'keyboard.tooltip.symbols': 'Simbolet', 'keyboard.tooltip.greek': 'Letrat Greke', 'keyboard.tooltip.command': 'Modaliteti i komandës latex', 'keyboard.tooltip.numeric': 'Numerike', 'keyboard.tooltip.roman': 'Letrat romake', 'tooltip.copy to clipboard': 'Kopjoni në Clipboard', 'tooltip.redo': 'Riparo', 'tooltip.toggle virtual keyboard': 'Aktivizo tastierën virtuale', 'tooltip.undo': 'Zhbëj', 'menu.insert matrix': 'Vendosni Matricën', 'menu.insert vector': 'Vendos vektorin', 'submenu.array.matrix delimiters': 'Përcaktuesit e matricës', 'menu.array.add row above': 'Shto Rreshtin Pas', 'menu.array.add row below': 'Shto Rreshtin Para', 'menu.array.add column after': 'Shto kolonën pas', 'menu.array.add column before': 'Shto kolonën para', 'menu.array.delete row': 'Fshi Rreshtin', 'menu.array.delete rows': 'Fshi rreshtat e zgjedhur', 'menu.array.delete column': 'Fshi kolonën', 'menu.array.delete columns': 'Fshi kolonat e zgjedhura', 'submenu.array.insert separator': 'Vendos Ndarësin', 'menu.insert table': 'Vendos tabelën', 'submenu.table style': 'Stili tabelën', }, // Serbian sr: { 'keyboard.tooltip.functions': 'Функције', 'keyboard.tooltip.symbols': 'Симболи', 'keyboard.tooltip.greek': 'Греек Леттерс', 'keyboard.tooltip.command': 'ЛаТеКс командни режим', 'keyboard.tooltip.numeric': 'Нумерички', 'keyboard.tooltip.roman': 'Римска писма', 'tooltip.copy to clipboard': 'Копирајте у међуспремник', 'tooltip.redo': 'Понови', 'tooltip.toggle virtual keyboard': 'Укључи / искључи виртуелну тастатуру', 'tooltip.undo': 'Опозови', 'menu.insert matrix': 'Уметни матрицу', 'menu.insert vector': 'Уметни вектор', 'submenu.array.matrix delimiters': 'Матрик Делимитерс', 'menu.array.add row above': 'Додај ред после', 'menu.array.add row below': 'Додај ред пре', 'menu.array.add column after': 'Додај колону после', 'menu.array.add column before': 'Додај колону пре', 'menu.array.delete row': 'Избриши ред', 'menu.array.delete rows': 'Избриши изабране редове', 'menu.array.delete column': 'Избриши колону', 'menu.array.delete columns': 'Избриши изабране колоне', 'submenu.array.insert separator': 'Уметни сепаратор', 'menu.insert table': 'Убаци табелу', 'submenu.table style': 'Табеларни стил', }, // Swedish sv: { 'keyboard.tooltip.functions': 'Funktioner', 'keyboard.tooltip.symbols': 'Symboler', 'keyboard.tooltip.greek': 'Grekiska bokstäver', 'keyboard.tooltip.command': 'LaTeX kommandoläge', 'keyboard.tooltip.numeric': 'Numerisk', 'keyboard.tooltip.roman': 'Romerska bokstäver', 'tooltip.copy to clipboard': 'Kopiera till Urklipp', 'tooltip.redo': 'Göra om', 'tooltip.toggle virtual keyboard': 'Växla virtuellt tangentbord', 'tooltip.undo': 'Ångra', 'menu.insert matrix': 'Sätt in matris', 'menu.insert vector': 'Infoga vektor', 'submenu.array.matrix delimiters': 'Matrisavgränsare', 'menu.array.add row above': 'Lägg till rad efter', 'menu.array.add row below': 'Lägg till rad före', 'menu.array.add column after': 'Lägg till kolumn efter', 'menu.array.add column before': 'Lägg till kolumn före', 'menu.array.delete row': 'Radera rad', 'menu.array.delete rows': 'Ta bort valda rader', 'menu.array.delete column': 'Ta bort kolumn', 'menu.array.delete columns': 'Ta bort valda kolumner', 'submenu.array.insert separator': 'Sätt i separator', 'menu.insert table': 'Infoga tabell', 'submenu.table style': 'Tabellstil', }, // Thai th: { 'keyboard.tooltip.functions': 'ฟังก์ชั่น', 'keyboard.tooltip.symbols': 'สัญลักษณ์', 'keyboard.tooltip.greek': 'อักษรกรีก', 'keyboard.tooltip.command': 'โหมดคำสั่ง น้ำยาง', 'keyboard.tooltip.numeric': 'ตัวเลข', 'keyboard.tooltip.roman': 'อักษรโรมัน', 'tooltip.copy to clipboard': 'คัดลอกไปที่คลิปบอร์ด', 'tooltip.redo': 'ทำซ้ำ', 'tooltip.toggle virtual keyboard': 'สลับแป้นพิมพ์เสมือน', 'tooltip.undo': 'เลิกทำ', 'menu.insert matrix': 'แทรกเมทริกซ์', 'menu.insert vector': 'แทรกเวกเตอร์', 'submenu.array.matrix delimiters': 'ตัวคั่นเมทริกซ์', 'menu.array.add row above': 'เพิ่มแถวหลัง', 'menu.array.add row below': 'เพิ่มแถวก่อน', 'menu.array.add column after': 'เพิ่มคอลัมน์หลัง', 'menu.array.add column before': 'เพิ่มคอลัมน์ก่อน', 'menu.array.delete row': 'ลบแถว', 'menu.array.delete rows': 'ลบแถวที่เลือก', 'menu.array.delete column': 'ลบคอลัมน์', 'menu.array.delete columns': 'ลบคอลัมน์ที่เลือก', 'submenu.array.insert separator': 'ตัวคั่นแทรก', 'menu.insert table': 'แทรกตาราง', 'submenu.table style': 'สไตล์ตาราง', }, // Turkish tr: { 'keyboard.tooltip.functions': 'Fonksiyonlar', 'keyboard.tooltip.symbols': 'Semboller', 'keyboard.tooltip.greek': 'Yunan harfleri', 'keyboard.tooltip.command': 'LaTeX Komut Modu', 'keyboard.tooltip.numeric': 'Sayısal', 'keyboard.tooltip.roman': 'Roma Harfleri', 'tooltip.copy to clipboard': 'Panoya kopyala', 'tooltip.redo': 'Yeniden yap', 'tooltip.toggle virtual keyboard': 'Sanal Klavyeyi Aç/Kapat', 'tooltip.undo': 'Geri alma', 'menu.insert matrix': 'Matris Ekle', 'menu.insert vector': 'Vektör Ekle', 'submenu.array.matrix delimiters': 'Matris Sınırlayıcılar', 'menu.array.add row above': 'Satırdan Sonra Ekle', 'menu.array.add row below': 'Önce Satır Ekle', 'menu.array.add column after': 'Sonra Sütun Ekle', 'menu.array.add column before': 'Önce Sütun Ekle', 'menu.array.delete row': 'Sırayı sil', 'menu.array.delete rows': 'Seçili Satırları Sil', 'menu.array.delete column': 'Sütunu Sil', 'menu.array.delete columns': 'Seçili Sütunları Sil', 'submenu.array.insert separator': 'Ayırıcı Ekle', 'menu.insert table': 'Tablo Ekle', 'submenu.table style': 'Tablo Stili', }, //Ukrainian uk: { 'keyboard.tooltip.functions': 'Функції', 'keyboard.tooltip.symbols': 'Символи', 'keyboard.tooltip.greek': 'Грецькі літери', 'keyboard.tooltip.command': 'Командний режим латексу', 'keyboard.tooltip.numeric': 'Числовий', 'keyboard.tooltip.roman': 'Римські літери', 'tooltip.copy to clipboard': 'Копіювати в буфер обміну', 'tooltip.redo': 'Повторити', 'tooltip.toggle virtual keyboard': 'Переключити віртуальну клавіатуру', 'tooltip.undo': 'Скасувати', 'menu.insert matrix': 'Вставити матрицю', 'menu.insert vector': 'Вставити вектор', 'submenu.array.matrix delimiters': 'Матричні роздільники', 'menu.array.add row above': 'Додати рядок після', 'menu.array.add row below': 'Додати рядок до', 'menu.array.add column after': 'Додати стовпець після', 'menu.array.add column before': 'Додати стовпець перед', 'menu.array.delete row': 'Видалити рядок', 'menu.array.delete rows': 'Видалити вибрані рядки', 'menu.array.delete column': 'Видалити стовпець', 'menu.array.delete columns': 'Видалити вибрані стовпці', 'submenu.array.insert separator': 'Вставте роздільник', 'menu.insert table': 'Вставити таблицю', 'submenu.table style': 'Стиль таблиці', }, //Vietnamese vi: { 'keyboard.tooltip.functions': 'Chức năng', 'keyboard.tooltip.symbols': 'Ký hiệu', 'keyboard.tooltip.greek': 'Chữ Hy Lạp', 'keyboard.tooltip.command': 'Chế độ lệnh LaTeX', 'keyboard.tooltip.numeric': 'Số', 'keyboard.tooltip.roman': 'Chữ cái La mã', 'tooltip.copy to clipboard': 'Sao chép vào clipboard', 'tooltip.redo': 'Làm lại', 'tooltip.toggle virtual keyboard': 'Chuyển đổi bàn phím ảo', 'tooltip.undo': 'Hoàn tác', 'menu.insert matrix': 'Chèn ma trận', 'menu.insert vector': 'Insert Vector', 'submenu.array.matrix delimiters': 'Dấu phân cách ma trận', 'menu.array.add row above': 'Thêm hàng sau', 'menu.array.add row below': 'Thêm hàng trước', 'menu.array.add column after': 'Thêm cột sau', 'menu.array.add column before': 'Thêm cột trước', 'menu.array.delete row': 'Xóa hàng', 'menu.array.delete rows': 'Xóa hàng đã chọn', 'menu.array.delete column': 'Xóa cột', 'menu.array.delete columns': 'Xóa các cột đã chọn', 'submenu.array.insert separator': 'Chèn dấu phân cách', 'menu.insert table': 'Chèn bảng', 'submenu.table style': 'Kiểu bảng', }, // Simplified Chinese zh_cn: { 'keyboard.tooltip.functions': '职能', 'keyboard.tooltip.symbols': '符号', 'keyboard.tooltip.greek': '希腊字母', 'keyboard.tooltip.command': '乳胶 命令模式', 'keyboard.tooltip.numeric': '数字', 'keyboard.tooltip.roman': '罗马字母', 'tooltip.copy to clipboard': '复制到剪贴板', 'tooltip.redo': '重做', 'tooltip.toggle virtual keyboard': '切换虚拟键盘', 'tooltip.undo': '撤消', 'menu.insert matrix': '插入矩阵', 'menu.insert vector': '插入向量', 'submenu.array.matrix delimiters': '矩阵分隔符', 'menu.array.add row above': '在后面添加行', 'menu.array.add row below': '在前面添加行', 'menu.array.add column after': '在后面添加列r', 'menu.array.add column before': '在前面添加列', 'menu.array.delete row': '删除行', 'menu.array.delete rows': '删除选定行', 'menu.array.delete column': '删除列', 'menu.array.delete columns': '删除选定的列', 'submenu.array.insert separator': '插入分隔符', 'menu.insert table': '插入表格', 'submenu.table style': '表格样式', }, // Traditional Chinese zh_tw: { 'keyboard.tooltip.functions': '職能', 'keyboard.tooltip.symbols': '符號', 'keyboard.tooltip.greek': '希臘字母', 'keyboard.tooltip.command': '乳膠命令模式', 'keyboard.tooltip.numeric': '數字', 'keyboard.tooltip.roman': '羅馬字母', 'tooltip.copy to clipboard': '複製到剪貼板', 'tooltip.redo': '重做', 'tooltip.toggle virtual keyboard': '切換虛擬鍵盤', 'tooltip.undo': '撤消', 'menu.insert matrix': '插入矩陣', 'menu.insert vector': '插入向量', 'submenu.array.matrix delimiters': '矩陣分隔符', 'menu.array.add row above': '在後面添加行', 'menu.array.add row below': '在前面添加行', 'menu.array.add column after': '在後面添加列', 'menu.array.add column before': '在前面添加列', 'menu.array.delete row': '刪除行', 'menu.array.delete rows': '刪除選定行', 'menu.array.delete column': '刪除列', 'menu.array.delete columns': '刪除選定的列', 'submenu.array.insert separator': '插入分隔符', 'menu.insert table': '插入表格', 'submenu.table style': '表格樣式', }, }; // Import { Keys } from '../types-utils'; // Type '{ strings: { en: { 'keyboard.tooltip.functions': string; // 'keyboard.tooltip.symbols': string; 'keyboard.tooltip.greek': string; // 'keyboard.tooltip.command': string; 'keyboard.tooltip.numeric': string; ... 4 // more ...; 'tooltip.undo': string; }; ... 9 more ...; ru: { ...; }; }; ... 8 // more ...; merge(locale: string | Rec...' is missing the following properties // from type 'L10n': _ordinal, _cardinalPluralCategories, // _cardinalEnglishPluralCategories, _cardinal, _locale const l10n = { strings: STRINGS, _locale: '', // Add getter and setter for the _locale property of l10n get locale() { // Use the browser defined language as the default language, // "english" if not running in a browser (node.js) if (!l10n._locale) l10n._locale = isBrowser() ? navigator.language.slice(0, 5) : 'en'; return l10n._locale; }, set locale(value) { l10n._locale = value; }, /* * Two forms for this function: * - merge(locale, strings) * Merge a dictionary of keys -> values for the specified locale * - merge(strings) * Merge a dictionary of locale code -> dictionary of keys -> values * */ merge(locale, strings) { if (locale && strings) { const savedLocale = l10n._locale; l10n.locale = locale; // Load the necessary json file l10n.strings[locale] = { ...l10n.strings[locale], ...strings, }; l10n.locale = savedLocale; } else if (locale && !strings) { for (const l of Object.keys(locale)) l10n.merge(l, locale[l]); } }, }; /** * Return a localised string for the `key`. */ function localize(key) { if (key === undefined) return undefined; const language = l10n.locale.slice(0, 2); let result = ''; // Attempt to find a match for the current locale if (l10n.strings[l10n.locale]) result = l10n.strings[l10n.locale][key]; // If none is found, attempt to find a match for the language if (!result && l10n.strings[language]) result = l10n.strings[language][key]; // If none is found, try english if (!result) result = l10n.strings.en[key]; // If that didn't work, return undefined if (!result) return undefined; return result; } function on(element, inSelectors, listener, options) { const selectors = inSelectors.split(' '); for (const sel of selectors) { const m = sel.match(/(.*):(.*)/); if (m) { const options2 = options !== null && options !== void 0 ? options : {}; if (m[2] === 'active') options2.passive = false; else options2[m[2]] = true; element.addEventListener(m[1], listener, options2); } else element.addEventListener(sel, listener, options); } } function off(element, inSelectors, listener, options) { const selectors = inSelectors.split(' '); for (const sel of selectors) { const m = sel.match(/(.*):(.*)/); if (m) { const options2 = options !== null && options !== void 0 ? options : {}; if (m[2] === 'active') options2.passive = false; else options2[m[2]] = true; element.removeEventListener(m[1], listener, options2); } else element.removeEventListener(sel, listener, options); } } function getSharedElement(id) { var _a; throwIfNotInBrowser(); let result = document.getElementById(id); if (result) { result.dataset.refcount = Number(Number.parseInt((_a = result.getAttribute('data-refcount')) !== null && _a !== void 0 ? _a : '0') + 1).toString(); } else { result = document.createElement('div'); result.setAttribute('aria-hidden', 'true'); result.dataset.refcount = '1'; result.id = id; document.body.append(result); } return result; } // @revisit: check the elements are correctly released function releaseSharedElement(element) { var _a; if (!element) return; const refcount = Number.parseInt((_a = element.getAttribute('data-refcount')) !== null && _a !== void 0 ? _a : '0'); if (refcount <= 1) element.remove(); else element.dataset.refcount = Number(refcount - 1).toString(); } /** * Checks if the argument is a valid Mathfield. * After a Mathfield has been destroyed (for example by calling `dispose()` * the Mathfield is no longer valid. However, there may be some pending * operations invoked via requestAnimationFrame() for example, that would * need to ensure the mathfield is still valid by the time they're executed. */ function isValidMathfield(mf) { var _a; return ((_a = mf.element) === null || _a === void 0 ? void 0 : _a.mathfield) === mf; } /** * Return the element which has the caret */ function findElementWithCaret(element) { var _a, _b; return ((_b = (_a = element.querySelector('.ML__caret')) !== null && _a !== void 0 ? _a : element.querySelector('.ML__text-caret')) !== null && _b !== void 0 ? _b : element.querySelector('.ML__latex-caret')); } /** * Return the (x,y) client coordinates of the caret */ function getCaretPoint(element) { const caret = findElementWithCaret(element); if (!caret) return null; const bounds = caret.getBoundingClientRect(); return { x: bounds.right, y: bounds.bottom, height: bounds.height, }; } function branchId(atom) { var _a; if (!atom.parent) return 'root'; let result = (_a = atom.parent.id) !== null && _a !== void 0 ? _a : ''; result += typeof atom.treeBranch === 'string' ? '-' + atom.treeBranch : `-${atom.treeBranch[0]}/${atom.treeBranch[0]}`; return result; } function adjustForScrolling(mathfield, rect) { if (!rect) return null; const fieldRect = mathfield.field.getBoundingClientRect(); const w = rect.right - rect.left; const h = rect.bottom - rect.top; const left = Math.ceil(rect.left - fieldRect.left + mathfield.field.scrollLeft); const top = Math.ceil(rect.top - fieldRect.top); return { left, right: left + w, top, bottom: top + h, }; } function getNodeBounds(node) { const bounds = node.getBoundingClientRect(); const marginRight = parseInt(getComputedStyle(node).marginRight); const result = { top: bounds.top - 1, bottom: bounds.bottom, left: bounds.left, right: bounds.right - 1 + marginRight, }; if (node.children.length > 0 && node.tagName.toUpperCase() !== 'SVG') { for (const child of node.children) { if (child.nodeType === 1) { if ('atomId' in child.dataset && !child.classList.contains('pstrut')) { const r = getNodeBounds(child); result.left = Math.min(result.left, r.left); result.right = Math.max(result.right, r.right); result.top = Math.min(result.top, r.top); result.bottom = Math.max(result.bottom, r.bottom); } } } } return result; } function getAtomBounds(mathfield, atom) { var _a, _b; if (!atom.id) return null; let result = (_b = (_a = mathfield._atomBoundsCache) === null || _a === void 0 ? void 0 : _a.get(atom.id)) !== null && _b !== void 0 ? _b : null; if (result !== null) return result; const node = mathfield.field.querySelector(`[data-atom-id="${atom.id}"]`); result = node ? getNodeBounds(node) : null; if (mathfield._atomBoundsCache) { if (result) mathfield._atomBoundsCache.set(atom.id, result); else mathfield._atomBoundsCache.delete(atom.id); } return result !== null && result !== void 0 ? result : null; } /* * Return an array of bounds for the specified range, at most * one rect per branch. */ function getRangeBounds(mathfield, range, options) { // The key of the map is a 'branchId', i.e. "atom id + branch" const rects = new Map(); for (const atom of mathfield.model.getAtoms(range, { includeChildren: true, })) { if ((options === null || options === void 0 ? void 0 : options.excludeAtomsWithBackground) && atom.style.backgroundColor) break; const bounds = adjustForScrolling(mathfield, getAtomBounds(mathfield, atom)); if (bounds) { const id = branchId(atom); if (rects.has(id)) { const r = rects.get(id); rects.set(id, { left: Math.min(r.left, bounds.left), right: Math.max(r.right, bounds.right), top: Math.min(r.top, bounds.top), bottom: Math.max(r.bottom, bounds.bottom), }); } else rects.set(id, bounds); } } return [...rects.values()]; } function getSelectionBounds(mathfield, options) { return mathfield.model.selection.ranges.reduce((acc, x) => acc.concat(...getRangeBounds(mathfield, x, options)), []); } function validateOrigin(origin, originValidator) { if (origin === '*') return true; if (originValidator === 'none') return true; if (originValidator === 'same-origin') return !window.origin || origin === window.origin; if (typeof originValidator === 'function') return originValidator(origin); return false; } /** * Attach event handlers to an element so that it will react by executing * a command when pressed. * `"command"` can be: * - a string, a single selector * - an array, whose first element is a selector followed by one or more arguments. * - an object, with the following keys: * * 'default': command performed on up, with a down + up sequence with no * delay between down and up * * 'alt', 'shift', 'altshift' keys: command performed on up with * one of these modifiers pressed * * 'pressed': command performed on 'down' * * 'pressAndHoldStart': command performed after a tap/down followed by a * delay (optional) * * 'pressAndHoldEnd': command performed on up, if there was a delay * between down and up, if absent, 'default' is performed * The value of the keys specify which selector (string * or array) to perform depending on the keyboard state when the button is * pressed. * * The 'pressed' and 'active' classes will get added to * the element, as the :hover and :active pseudo-classes are not reliable * (at least on Chrome Android). * */ function attachButtonHandlers(executeCommand, element, command) { if (typeof command === 'object' && ('default' in command || 'pressed' in command)) { // Attach the default (no modifiers pressed) command to the element if (command.default) element.dataset.command = JSON.stringify(command.default); if (command.alt) element.dataset.commandAlt = JSON.stringify(command.alt); if (command.altshift) element.dataset.commandAltshift = JSON.stringify(command.altshift); if (command.shift) element.dataset.commandShift = JSON.stringify(command.shift); // .pressed: command to perform when the button is pressed (i.e. // on mouse down/touch). Otherwise the command is performed when // the button is released if (command.pressed) element.dataset.commandPressed = JSON.stringify(command.pressed); if (command.pressAndHoldStart) { element.dataset.commandPressAndHoldStart = JSON.stringify(command.pressAndHoldStart); } if (command.pressAndHoldEnd) { element.dataset.commandPressAndHoldEnd = JSON.stringify(command.pressAndHoldEnd); } } else { // We need to turn the command into a string to attach it to the dataset // associated with the button (the command could be an array made of a // selector and one or more parameters) element.dataset.command = JSON.stringify(command); } let pressHoldStart; let pressHoldElement; let touchID; let syntheticTarget; // Target while touch move let pressAndHoldTimer; on(element, 'mousedown touchstart:passive', (ev) => { if (ev.type !== 'mousedown' || ev.buttons === 1) { // The primary button was pressed or the screen was tapped. ev.stopPropagation(); // Can't preventDefault() in a passive listener if (ev.type !== 'touchstart') ev.preventDefault(); // Safari on iOS will aggressively attempt to select when there is a long // press. Prevent userSelect for the entire document. // document.body.style.userSelect = 'none'; // document.body.style['-webkit-touch-callout'] = 'none'; document.body.style['-webkit-user-select'] = 'none'; element.classList.add('is-pressed'); pressHoldStart = Date.now(); // Record the ID of the primary touch point for tracking on touchmove if (ev.type === 'touchstart') touchID = ev.changedTouches[0].identifier; // Parse the JSON to get the command (and its optional arguments) // and perform it immediately const command = element.getAttribute('data-command-pressed'); if (command) executeCommand(JSON.parse(command)); // If there is a `press and hold start` command, perform it // after a delay, if we're still pressed by then. const pressAndHoldStartCommand = element.getAttribute('data-command-press-and-hold-start'); if (pressAndHoldStartCommand) { pressHoldElement = element; if (pressAndHoldTimer) clearTimeout(pressAndHoldTimer); pressAndHoldTimer = setTimeout(() => { if (element.classList.contains('is-pressed')) executeCommand(JSON.parse(pressAndHoldStartCommand)); }, 300); } } }); on(element, 'mouseleave touchcancel', () => { element.classList.remove('is-pressed'); }); on(element, 'touchmove:passive', (ev) => { // Unlike with mouse tracking, touch tracking only sends events // to the target that was originally tapped on. For consistency, // we want to mimic the behavior of the mouse interaction by // tracking the touch events and dispatching them to potential targets // ev.preventDefault(); // can't preventDefault inside a passive event handler for (let i = 0; i < ev.changedTouches.length; i++) { if (ev.changedTouches[i].identifier === touchID) { // Found a touch matching our primary/tracked touch const target = document.elementFromPoint(ev.changedTouches[i].clientX, ev.changedTouches[i].clientY); if (target !== syntheticTarget && syntheticTarget) { syntheticTarget.dispatchEvent(new MouseEvent('mouseleave'), { bubbles: true, }); syntheticTarget = null; } if (target) { syntheticTarget = target; target.dispatchEvent(new MouseEvent('mouseenter', { bubbles: true, buttons: 1, })); } } } }); on(element, 'mouseenter', (ev) => { if (ev.buttons === 1) element.classList.add('is-pressed'); }); on(element, 'mouseup touchend click', (ev) => { document.body.style['-webkit-user-select'] = ''; if (syntheticTarget) { ev.stopPropagation(); ev.preventDefault(); const target = syntheticTarget; syntheticTarget = null; target.dispatchEvent(new MouseEvent('mouseup', { bubbles: true })); return; } element.classList.remove('is-pressed'); element.classList.add('is-active'); if (ev.type === 'click' && ev.detail !== 0) { // This is a click event triggered by a mouse interaction // (and not a keyboard interaction) // Ignore it, we'll handle the mouseup (or touchend) instead. ev.stopPropagation(); ev.preventDefault(); return; } // Since we want the active state to be visible for a while, // use a timer to remove it after a short delay setTimeout(() => element.classList.remove('is-active'), 150); let command = element.getAttribute('data-command-press-and-hold-end'); const now = Date.now(); // If the button has not been pressed for very long or if we were // not the button that started the press and hold, don't consider // it a press-and-hold. if (element !== pressHoldElement || now < pressHoldStart + 300) command = null; if (!command && ev.altKey && ev.shiftKey) command = element.getAttribute('data-command-altshift'); if (!command && ev.altKey) command = element.getAttribute('data-command-alt'); if (!command && ev.shiftKey) command = element.getAttribute('data-command-shift'); if (!command) command = element.getAttribute('data-command'); if (command) { // Parse the JSON to get the command (and its optional arguments) // and perform it executeCommand(JSON.parse(command)); } ev.stopPropagation(); ev.preventDefault(); }); } var css_248z$3 = ".ML__keyboard{--hue:206;--keyboard-background:rgba(209,213,217,.97);--keyboard-text:#000;--keyboard-text-active:var(--primary,hsl(var(--hue),40%,50%));--keyboard-background-border:#ddd;--keycap-background:#fff;--keycap-background-active:#e5e5e5;--keycap-background-border:#e5e6e9;--keycap-background-border-bottom:#8d8f92;--keycap-text:#000;--keycap-text-active:#fff;--keycap-secondary-text:#000;--keycap-modifier-background:#b9bdc7;--keycap-modifier-border:#c5c9d0;--keycap-modifier-border-bottom:#989da6;--keyboard-alternate-background:#fff;--keyboard-alternate-background-active:var(--primary,hsl(var(--hue),40%,50%));--keyboard-alternate-text:var(--keycap-text,#000);--keyboard-alternate-text-active:#fff;--keyboard-alternate-key-length:70px;--keyboard-alternate-key-font-size:30px;--keyboard-alternate-key-aside-font-size:12px;--keyboard-height:276px;--keycap-height:52px;--keycap-font-size:20px;--keycap-small-font-size:calc(var(--keycap-font-size)*0.8);--keycap-extra-small-font-size:calc(var(--keycap-font-size)/1.42);--keycap-tt-font-size:calc(var(--keycap-font-size)*1.5);height:100%;left:0;pointer-events:none;position:fixed;top:0;width:100%;z-index:var(--keyboard-zindex,105)}.ML__keyboard.is-visible .ML__keyboard--plate{opacity:1;transform:translateY(calc(var(--keyboard-height, 276px)*-1));transition-timing-function:cubic-bezier(.4,0,1,1);visibility:visible}.ML__keyboard.alternate-keys{align-content:center;background-color:var(--keyboard-alternate-background);border-radius:6px;bottom:auto;box-shadow:0 14px 28px rgba(0,0,0,.25),0 10px 10px rgba(0,0,0,.22);box-sizing:content-box;display:flex;flex-direction:row;justify-content:center;max-width:286px;position:fixed;text-align:center;top:0;transform:none;transition:none;visibility:hidden;z-index:calc(var(--keyboard-zindex, 105) + 1)}@media only screen and (max-height:412px){.ML__keyboard.alternate-keys{max-width:320px}}.ML__keyboard.alternate-keys.is-visible{visibility:visible}.ML__keyboard.alternate-keys.compact{--keyboard-alternate-key-length:50px;--keyboard-alternate-key-font-size:24px;--keyboard-alternate-key-aside-font-size:10px}.ML__keyboard.alternate-keys ul{display:flex;flex-flow:row wrap-reverse;justify-content:center;list-style:none;margin:3px;padding:0}.ML__keyboard.alternate-keys ul>li{fill:currentColor;align-items:center;background:transparent;border:1px solid transparent;border-radius:5px;box-sizing:border-box;color:var(--keyboard-alternate-text);display:flex;flex-flow:column;font-size:var(--keyboard-alternate-key-font-size);height:var(--keyboard-alternate-key-length);justify-content:center;margin:0;pointer-events:all;width:var(--keyboard-alternate-key-length)}@media only screen and (max-height:412px){.ML__keyboard.alternate-keys ul>li{font-size:24px;height:50px;width:50px}}.ML__keyboard.alternate-keys ul>li.is-active,.ML__keyboard.alternate-keys ul>li.is-pressed,.ML__keyboard.alternate-keys ul>li:hover{background:var(--keyboard-alternate-background-active);color:var(--keyboard-alternate-text-active)}.ML__keyboard.alternate-keys ul>li.small{font-size:var(--keycap-small-font-size,16px)}.ML__keyboard.alternate-keys ul>li.small-button{background:#fbfbfb;height:42px;margin:2px;width:42px}.ML__keyboard.alternate-keys ul>li.small-button:hover{background:var(--keyboard-alternate-background-active)}.ML__keyboard.alternate-keys ul>li.box>div,.ML__keyboard.alternate-keys ul>li.box>span{border:1px dashed rgba(0,0,0,.24)}.ML__keyboard.alternate-keys ul>li .warning{align-items:center;background:#cd0030;border-radius:5px;color:#fff;display:flex;justify-content:center;min-height:60px;min-width:60px;padding:5px}.ML__keyboard.alternate-keys ul>li .warning.is-active,.ML__keyboard.alternate-keys ul>li .warning.is-pressed,.ML__keyboard.alternate-keys ul>li .warning:hover{background:red}.ML__keyboard.alternate-keys ul>li .warning svg.svg-glyph{height:50px;width:50px}.ML__keyboard.alternate-keys ul>li aside{font-size:var(--keyboard-alternate-key-aside-font-size);line-height:12px;opacity:.78;padding-top:2px}.ML__keyboard .ML__keyboard--plate{-webkit-backdrop-filter:grayscale(50%);backdrop-filter:grayscale(50%);background-color:var(--keyboard-background);border:1px solid var(--keyboard-background-border);bottom:calc(var(--keyboard-height, 276px)*-1);box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23);box-sizing:border-box;cursor:pointer;font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;font-size:16px;font-weight:400;left:0;margin:0;opacity:0;padding-top:5px;pointer-events:all;position:absolute;text-shadow:none;touch-action:none;transform:translate(0);transition:.28s cubic-bezier(0,0,.2,1);transition-property:transform,opacity;-webkit-user-select:none;user-select:none;visibility:hidden;width:100%}.ML__keyboard .ML__keyboard--plate .tex{font-family:KaTeX_Math,KaTeX_Main,Cambria Math,Asana Math,OpenSymbol,Symbola,STIX,Times,serif!important}.ML__keyboard .ML__keyboard--plate .tex-math{font-family:KaTeX_Math,Cambria Math,Asana Math,OpenSymbol,Symbola,STIX,Times,serif!important}.ML__keyboard .ML__keyboard--plate .tt{font-family:IBM Plex Mono,Source Code Pro,Consolas,Roboto Mono,Menlo,Bitstream Vera Sans Mono,DejaVu Sans Mono,Monaco,Courier,monospace!important;font-size:var(--keycap-tt-font-size,30px);font-weight:400}.ML__keyboard .ML__keyboard--plate>div.keyboard-layer{display:none;outline:none}.ML__keyboard .ML__keyboard--plate>div.keyboard-layer.is-visible{display:flex;flex-flow:column}.ML__keyboard .ML__keyboard--plate>div>div.keyboard-toolbar{align-self:center;display:flex;flex-flow:row;justify-content:space-between;min-width:736px}@media only screen and (min-width:768px) and (max-width:1024px){.ML__keyboard .ML__keyboard--plate>div>div.keyboard-toolbar{min-width:556px}}@media only screen and (max-width:767px){.ML__keyboard .ML__keyboard--plate>div>div.keyboard-toolbar{max-width:100vw;min-width:365px;width:365px}}.ML__keyboard .ML__keyboard--plate>div>div.keyboard-toolbar svg{height:20px;width:20px}@media only screen and (max-width:767px){.ML__keyboard .ML__keyboard--plate>div>div.keyboard-toolbar svg{height:13px;width:17px}}.ML__keyboard .ML__keyboard--plate>div>div.keyboard-toolbar>.left{display:flex;flex-flow:row;justify-content:flex-start;position:relative}.ML__keyboard .ML__keyboard--plate>div>div.keyboard-toolbar>.right{display:flex;flex-flow:row;justify-content:flex-end}.ML__keyboard .ML__keyboard--plate>div>div.keyboard-toolbar>div>div{fill:currentColor;align-items:baseline;background:0;border:none;border-bottom:2px solid transparent;box-shadow:none;color:var(--keyboard-text);cursor:pointer;display:flex;font-size:110%;justify-content:center;margin:7px 4px 6px;min-height:0;padding:4px 10px}.ML__keyboard .ML__keyboard--plate>div>div.keyboard-toolbar>div>div.disabled svg,.ML__keyboard .ML__keyboard--plate>div>div.keyboard-toolbar>div>div.disabled.is-pressed svg,.ML__keyboard .ML__keyboard--plate>div>div.keyboard-toolbar>div>div.disabled:hover svg{color:var(--keyboard-text);opacity:.2}@media only screen and (max-width:414px){.ML__keyboard .ML__keyboard--plate>div>div.keyboard-toolbar>div>div{font-size:100%;padding:0 6px 0 0}}@media only screen and (max-width:767px){.ML__keyboard .ML__keyboard--plate>div>div.keyboard-toolbar>div>div{font-size:90%;padding-left:4px;padding-right:4px}}.ML__keyboard .ML__keyboard--plate>div>div.keyboard-toolbar>div>div.is-active,.ML__keyboard .ML__keyboard--plate>div>div.keyboard-toolbar>div>div.is-pressed,.ML__keyboard .ML__keyboard--plate>div>div.keyboard-toolbar>div>div:active,.ML__keyboard .ML__keyboard--plate>div>div.keyboard-toolbar>div>div:hover{color:var(--keyboard-text-active)}.ML__keyboard .ML__keyboard--plate>div>div.keyboard-toolbar>div>div.selected{border-bottom:2px solid var(--keyboard-text-active);color:var(--keyboard-text-active);margin-bottom:8px;padding-bottom:0}.ML__keyboard .ML__keyboard--plate [data-tooltip]{position:relative}.ML__keyboard .ML__keyboard--plate [data-tooltip]:after{background:#616161;border-radius:2px;bottom:100%;box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2);color:#fff;content:attr(data-tooltip);display:inline-table;font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;font-size:12px;font-weight:400;max-width:200px;opacity:0;padding:8px;position:absolute;text-align:center;top:inherit;transform:scale(.5);transition:all .15s cubic-bezier(.4,0,1,1) 1s;width:max-content;z-index:2}@media only screen and (max-width:767px){.ML__keyboard .ML__keyboard--plate [data-tooltip]:after{font-size:16px;padding:8px 16px}}.ML__keyboard .ML__keyboard--plate [data-tooltip]:hover{position:relative}.ML__keyboard .ML__keyboard--plate [data-tooltip]:hover:after{opacity:1;transform:scale(1)}.ML__keyboard .ML__keyboard--plate div .rows{align-items:center;border:0;border-collapse:separate;clear:both;display:flex;flex-flow:column;margin:auto}.ML__keyboard .ML__keyboard--plate div .rows>ul{height:40px;list-style:none;margin:0 0 3px;padding:0}.ML__keyboard .ML__keyboard--plate div .rows>ul>li{fill:currentColor;-webkit-tap-highlight-color:transparent;align-items:center;background:var(--keycap-background);border:1px solid var(--keycap-background-border);border-bottom-color:var(--keycap-background-border-bottom);border-radius:5px;box-sizing:border-box;color:var(--keycap-text);display:flex;flex-flow:column;float:left;font-size:var(--keycap-font-size,20px);height:40px;justify-content:center;margin-right:2px;overflow:hidden;padding:8px 0;position:relative;text-align:center;-webkit-user-select:none;user-select:none;vertical-align:top;width:34px}.ML__keyboard .ML__keyboard--plate div .rows>ul>li:last-child{margin-right:0}.ML__keyboard .ML__keyboard--plate div .rows>ul>li.small{font-size:var(--keycap-small-font-size,16px)}.ML__keyboard .ML__keyboard--plate div .rows>ul>li.tt{color:var(--keyboard-text-active)}.ML__keyboard .ML__keyboard--plate div .rows>ul>li.bottom{justify-content:flex-end}.ML__keyboard .ML__keyboard--plate div .rows>ul>li.left{align-items:flex-start;padding-left:4px}.ML__keyboard .ML__keyboard--plate div .rows>ul>li.right{align-items:flex-end;padding-right:4px}.ML__keyboard .ML__keyboard--plate div .rows>ul>li svg.svg-glyph{height:20px;width:20px}.ML__keyboard .ML__keyboard--plate div .rows>ul>li .warning{align-items:center;background:#cd0030;border-radius:100%;color:#fff;display:flex;height:25px;justify-content:center;margin-bottom:-2px;min-height:25px;min-width:25px;padding:5px;width:25px}.ML__keyboard .ML__keyboard--plate div .rows>ul>li .warning svg.svg-glyph{height:16px;width:16px}@media only screen and (max-width:768px){.ML__keyboard .ML__keyboard--plate div .rows>ul>li .warning{height:16px;min-height:16px;min-width:16px;width:16px}.ML__keyboard .ML__keyboard--plate div .rows>ul>li .warning svg.svg-glyph{height:14px;width:14px}}.ML__keyboard .ML__keyboard--plate div .rows>ul>li>.w0{width:0}.ML__keyboard .ML__keyboard--plate div .rows>ul>li>.w5{width:16px}.ML__keyboard .ML__keyboard--plate div .rows>ul>li>.w15{width:52px}.ML__keyboard .ML__keyboard--plate div .rows>ul>li>.w20{width:70px}.ML__keyboard .ML__keyboard--plate div .rows>ul>li>.w50{width:178px}.ML__keyboard .ML__keyboard--plate div .rows>ul>li.separator{background:transparent;border:none;pointer-events:none}@media only screen and (max-width:560px){.ML__keyboard .ML__keyboard--plate div .rows>ul>li.if-wide{display:none}}.ML__keyboard .ML__keyboard--plate div .rows>ul>li.tex-math{font-size:25px}.ML__keyboard .ML__keyboard--plate div .rows>ul>li.is-pressed,.ML__keyboard .ML__keyboard--plate div .rows>ul>li:hover{background:var(--keycap-background-active);color:var(--keyboard-text-active)}.ML__keyboard .ML__keyboard--plate div .rows>ul>li.action.is-active,.ML__keyboard .ML__keyboard--plate div .rows>ul>li.action:active,.ML__keyboard .ML__keyboard--plate div .rows>ul>li.keycap.is-active,.ML__keyboard .ML__keyboard--plate div .rows>ul>li.keycap:active{color:var(--keyboard-text-active);transform:translateY(calc(var(--keycap-height, 52px)*-.2)) scale(1.4);z-index:calc(var(--keyboard-zindex, 105) - 5)}.ML__keyboard .ML__keyboard--plate div .rows>ul>li.modifier.is-active,.ML__keyboard .ML__keyboard--plate div .rows>ul>li.modifier:active{background:var(--keyboard-text-active);color:var(--keycap-text-active)}.ML__keyboard .ML__keyboard--plate div .rows>ul>li.action.font-glyph,.ML__keyboard .ML__keyboard--plate div .rows>ul>li.modifier.font-glyph{font-size:18px}@media only screen and (max-width:767px){.ML__keyboard .ML__keyboard--plate div .rows>ul>li.action.font-glyph,.ML__keyboard .ML__keyboard--plate div .rows>ul>li.modifier.font-glyph{font-size:16px}}@media only screen and (max-width:767px){.ML__keyboard .ML__keyboard--plate div .rows>ul>li.fnbutton{font-size:12px}}.ML__keyboard .ML__keyboard--plate div .rows>ul>li.bigfnbutton{font-size:var(--keycap-extra-small-font-size,14px)}@media only screen and (max-width:767px){.ML__keyboard .ML__keyboard--plate div .rows>ul>li.bigfnbutton{font-size:calc(var(--keycap-extra-small-font-size, 14px)/1.55)}}.ML__keyboard .ML__keyboard--plate div .rows>ul>li.action,.ML__keyboard .ML__keyboard--plate div .rows>ul>li.modifier{background-color:var(--keycap-modifier-background);border-color:var(--keycap-modifier-border);border-bottom-color:var(--keycap-modifier-border-bottom);font-size:65%;font-weight:100}.ML__keyboard .ML__keyboard--plate div .rows>ul>li.action.selected,.ML__keyboard .ML__keyboard--plate div .rows>ul>li.modifier.selected{color:var(--keyboard-text-active)}.ML__keyboard .ML__keyboard--plate div .rows>ul>li.action.selected.is-active,.ML__keyboard .ML__keyboard--plate div .rows>ul>li.action.selected.is-pressed,.ML__keyboard .ML__keyboard--plate div .rows>ul>li.action.selected:active,.ML__keyboard .ML__keyboard--plate div .rows>ul>li.action.selected:hover,.ML__keyboard .ML__keyboard--plate div .rows>ul>li.modifier.selected.is-active,.ML__keyboard .ML__keyboard--plate div .rows>ul>li.modifier.selected.is-pressed,.ML__keyboard .ML__keyboard--plate div .rows>ul>li.modifier.selected:active,.ML__keyboard .ML__keyboard--plate div .rows>ul>li.modifier.selected:hover{color:#fff}.ML__keyboard .ML__keyboard--plate div .rows>ul>li.keycap.w50{font-size:80%;font-weight:100;padding-top:10px}.ML__keyboard .ML__keyboard--plate div .rows>ul>li small{color:#555}@media only screen and (max-width:767px){.ML__keyboard .ML__keyboard--plate div .rows>ul>li small{font-size:9px}}.ML__keyboard .ML__keyboard--plate div .rows>ul>li aside{color:#666;font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;font-size:10px;line-height:10px}@media only screen and (max-width:767px){.ML__keyboard .ML__keyboard--plate div .rows>ul>li aside{display:none}}@media only screen and (max-width:414px){.ML__keyboard .ML__keyboard--plate div .rows>ul>li{margin-right:2px;width:calc(10vw - 2px)}.ML__keyboard .ML__keyboard--plate div .rows>ul>.w5{width:calc(5vw - 2px)}.ML__keyboard .ML__keyboard--plate div .rows>ul>.w15{width:calc(15vw - 2px)}.ML__keyboard .ML__keyboard--plate div .rows>ul>.w20{width:calc(20vw - 2px)}.ML__keyboard .ML__keyboard--plate div .rows>ul>.w50{width:calc(50vw - 2px)}}@media only screen and (min-width:415px) and (max-width:768px){.ML__keyboard .ML__keyboard--plate div .rows>ul>li{margin-right:3px;width:37px}.ML__keyboard .ML__keyboard--plate div .rows>ul>.w5{width:17px}.ML__keyboard .ML__keyboard--plate div .rows>ul>.w15{width:57px}.ML__keyboard .ML__keyboard--plate div .rows>ul>.w20{width:77px}.ML__keyboard .ML__keyboard--plate div .rows>ul>.w50{width:197px}}@media only screen and (min-width:768px) and (max-width:1024px){.ML__keyboard .ML__keyboard--plate div .rows>ul{height:var(--keycap-height,52px)}.ML__keyboard .ML__keyboard--plate div .rows>ul>li{height:var(--keycap-height,52px);margin-right:4px;width:51px}.ML__keyboard .ML__keyboard--plate div .rows>ul>.w5{width:23.5px}.ML__keyboard .ML__keyboard--plate div .rows>ul>.w15{width:78.5px}.ML__keyboard .ML__keyboard--plate div .rows>ul>.w20{width:106px}.ML__keyboard .ML__keyboard--plate div .rows>ul>.w50{width:271px}}@media only screen and (min-width:1025px){.ML__keyboard .ML__keyboard--plate div .rows>ul{height:var(--keycap-height,52px)}.ML__keyboard .ML__keyboard--plate div .rows>ul>li{height:var(--keycap-height,52px);margin-right:6px;width:66px}.ML__keyboard .ML__keyboard--plate div .rows>ul>.action,.ML__keyboard .ML__keyboard--plate div .rows>ul>.modifier{font-size:80%}.ML__keyboard .ML__keyboard--plate div .rows>ul>.w5{width:30px}.ML__keyboard .ML__keyboard--plate div .rows>ul>.w15{width:102px}.ML__keyboard .ML__keyboard--plate div .rows>ul>.w20{width:138px}.ML__keyboard .ML__keyboard--plate div .rows>ul>.w50{width:354px}}@media (prefers-color-scheme:dark){body:not([theme=light]) .ML__keyboard{--hue:206;--keyboard-background:#4e6373;--keyboard-background:#4e6073;--keyboard-background:hsl(var(--hue),19%,38%);--keyboard-text:#f0f0f0;--keyboard-text-active:hsl(var(--hue),100%,60%);--keyboard-background-border:#333;--keycap-background:hsl(var(--hue),25%,39%);--keycap-background-active:hsl(var(--hue),35%,42%);--keycap-background-border:hsl(var(--hue),25%,35%);--keycap-background-border-bottom:#426b8a;--keycap-text:#d0d0d0;--keycap-text-active:#000;--keycap-secondary-text:#fff;--keycap-modifier-background:hsl(var(--hue),35%,40%);--keycap-modifier-border:hsl(var(--hue),35%,35%);--keycap-modifier-border-bottom:hsl(var(--hue),35%,42%);--keyboard-alternate-background:hsl(var(--hue),19%,38%)}}body[theme=dark] .ML__keyboard{--hue:206;--keyboard-background:hsl(var(--hue),19%,38%);--keyboard-text:#f0f0f0;--keyboard-text-active:hsl(var(--hue),100%,60%);--keyboard-background-border:#333;--keycap-background:hsl(var(--hue),25%,39%);--keycap-background-active:hsl(var(--hue),35%,42%);--keycap-background-border:hsl(var(--hue),25%,35%);--keycap-background-border-bottom:#426b8a;--keycap-text:#d0d0d0;--keycap-text-active:#000;--keycap-secondary-text:#fff;--keycap-modifier-background:hsl(var(--hue),35%,40%);--keycap-modifier-border:hsl(var(--hue),35%,35%);--keycap-modifier-border-bottom:hsl(var(--hue),35%,42%);--keyboard-alternate-background:hsl(var(--hue),19%,38%)}div.ML__keyboard.material{--keyboard-background:rgba(209,213,217,.9);--keyboard-background-border:#ddd;--keycap-background:transparent;--keycap-background-active:#cccfd1;--keycap-background-border:transparent;--keyboard-alternate-background:#efefef;--keyboard-alternate-text:#000;font-family:Roboto,sans-serif}div.ML__keyboard.material.alternate-keys{background:var(--keyboard-alternate-background);border:1px solid transparent;border-radius:5px;box-shadow:0 14px 28px rgba(0,0,0,.25),0 10px 10px rgba(0,0,0,.22)}div.ML__keyboard.material.alternate-keys ul li.is-active,div.ML__keyboard.material.alternate-keys ul li.is-pressed,div.ML__keyboard.material.alternate-keys ul li:active,div.ML__keyboard.material.alternate-keys ul li:hover{fill:currentColor;background:#5f97fc;border:1px solid transparent;color:#fff}div.ML__keyboard.material .ML__keyboard__plate .keyboard-toolbar>div>div{font-size:16px}div.ML__keyboard.material .ML__keyboard__plate .keyboard-toolbar div div:active,div.ML__keyboard.material .ML__keyboard__plate .keyboard-toolbar div div:hover,div.ML__keyboard.material .ML__keyboard__plate .keyboard-toolbar div.div.is-active,div.ML__keyboard.material .ML__keyboard__plate .keyboard-toolbar div.div.is-pressed{fill:currentColor;color:#5f97fc}div.ML__keyboard.material .ML__keyboard__plate .keyboard-toolbar>div>.selected{fill:currentColor;border-bottom:2px solid #5f97fc;color:#5f97fc;margin-bottom:8px;padding-bottom:0}div.ML__keyboard.material .ML__keyboard__plate div>.rows>ul>.keycap{fill:currentColor;background:transparent;border:1px solid transparent;border-radius:5px;color:var(--keycap-text);transition:none}div.ML__keyboard.material .ML__keyboard__plate div>.rows>ul>.keycap.tt{color:#5f97fc}div.ML__keyboard.material .ML__keyboard__plate div>.rows>ul>.keycap[data-key=\" \"]{background:#e0e0e0;height:20px;margin-bottom:10px;margin-top:10px}div.ML__keyboard.material .ML__keyboard__plate div>.rows>ul>.keycap[data-key=\" \"].is-active,div.ML__keyboard.material .ML__keyboard__plate div>.rows>ul>.keycap[data-key=\" \"].is-pressed,div.ML__keyboard.material .ML__keyboard__plate div>.rows>ul>.keycap[data-key=\" \"]:active,div.ML__keyboard.material .ML__keyboard__plate div>.rows>ul>.keycap[data-key=\" \"]:hover{background:#d0d0d0;box-shadow:none;transform:none}div.ML__keyboard.material .ML__keyboard__plate div>.rows>ul>.keycap:not([data-key=\" \"]):hover{background:var(--keycap-background-active);border:1px solid transparent;box-shadow:none}div.ML__keyboard.material .ML__keyboard__plate div>.rows>ul>.keycap:not([data-key=\" \"]).is-active,div.ML__keyboard.material .ML__keyboard__plate div>.rows>ul>.keycap:not([data-key=\" \"]).is-pressed,div.ML__keyboard.material .ML__keyboard__plate div>.rows>ul>.keycap:not([data-key=\" \"]):active{background:var(--keyboard-alternate-background);box-shadow:0 10px 20px rgba(0,0,0,.19),0 6px 6px rgba(0,0,0,.23);color:var(--keyboard-alternate-text)}@media only screen and (max-width:767px){div.ML__keyboard.material .ML__keyboard__plate div>.rows>ul>.keycap:not([data-key=\" \"]).is-active,div.ML__keyboard.material .ML__keyboard__plate div>.rows>ul>.keycap:not([data-key=\" \"]).is-pressed,div.ML__keyboard.material .ML__keyboard__plate div>.rows>ul>.keycap:not([data-key=\" \"]):active{box-shadow:0 10px 20px rgba(0,0,0,.19),0 6px 6px rgba(0,0,0,.23);font-size:10px;justify-content:flex-start;margin-left:10px;margin-right:10px;padding:2px 0 0;transform:translateY(-10px) scale(2);transition:none;vertical-align:top;width:19.5px;z-index:calc(var(--ML_keyboard-zindex, 105) - 5)}}@media only screen and (max-width:414px){div.ML__keyboard.material .ML__keyboard__plate div>.rows>ul>.keycap:not([data-key=\" \"]).is-active,div.ML__keyboard.material .ML__keyboard__plate div>.rows>ul>.keycap:not([data-key=\" \"]).is-pressed,div.ML__keyboard.material .ML__keyboard__plate div>.rows>ul>.keycap:not([data-key=\" \"]):active{width:16.5px}}@media only screen and (max-width:767px){div.ML__keyboard.material .ML__keyboard__plate div>.rows>ul>.keycap:last-child.is-active,div.ML__keyboard.material .ML__keyboard__plate div>.rows>ul>.keycap:last-child:active{margin-left:14px;margin-right:0}}div.ML__keyboard.material .ML__keyboard__plate div div.rows ul li.action,div.ML__keyboard.material .ML__keyboard__plate div div.rows ul li.modifier{fill:currentColor;background:transparent;border:0;color:#869096;font-size:16px;transition:none}div.ML__keyboard.material .ML__keyboard__plate div div.rows ul li.action.selected,div.ML__keyboard.material .ML__keyboard__plate div div.rows ul li.modifier.selected{border-bottom:2px solid #5f97fc;border-radius:0;color:#5f97fc}div.ML__keyboard.material .ML__keyboard__plate div div.rows ul li.action.is-active,div.ML__keyboard.material .ML__keyboard__plate div div.rows ul li.action.is-pressed,div.ML__keyboard.material .ML__keyboard__plate div div.rows ul li.action:active,div.ML__keyboard.material .ML__keyboard__plate div div.rows ul li.action:hover,div.ML__keyboard.material .ML__keyboard__plate div div.rows ul li.modifier.is-active,div.ML__keyboard.material .ML__keyboard__plate div div.rows ul li.modifier.is-pressed,div.ML__keyboard.material .ML__keyboard__plate div div.rows ul li.modifier:active,div.ML__keyboard.material .ML__keyboard__plate div div.rows ul li.modifier:hover{background:var(--keycap-background-active);border:0;box-shadow:none;color:var(--keycap-text)}div.ML__keyboard.material .ML__keyboard__plate div div.rows ul li.bigfnbutton,div.ML__keyboard.material .ML__keyboard__plate div div.rows ul li.fnbutton{background:transparent;border:0}div.ML__keyboard.material .ML__keyboard__plate div div.rows ul li.bigfnbutton.selected,div.ML__keyboard.material .ML__keyboard__plate div div.rows ul li.fnbutton.selected{fill:currentColor;border-bottom:2px solid #5f97fc;border-radius:0;color:#5f97fc}div.ML__keyboard.material .ML__keyboard__plate div div.rows ul li.bigfnbutton.is-active,div.ML__keyboard.material .ML__keyboard__plate div div.rows ul li.bigfnbutton.is-pressed,div.ML__keyboard.material .ML__keyboard__plate div div.rows ul li.bigfnbutton:active,div.ML__keyboard.material .ML__keyboard__plate div div.rows ul li.bigfnbutton:hover,div.ML__keyboard.material .ML__keyboard__plate div div.rows ul li.fnbutton.is-active,div.ML__keyboard.material .ML__keyboard__plate div div.rows ul li.fnbutton.is-pressed,div.ML__keyboard.material .ML__keyboard__plate div div.rows ul li.fnbutton:active,div.ML__keyboard.material .ML__keyboard__plate div div.rows ul li.fnbutton:hover{fill:currentColor;background:var(--keycap-background-active);border:0;box-shadow:none;color:#5f97fc}@media (prefers-color-scheme:dark){body:not([theme=light]) div.ML__keyboard.material{--hue:198;--keyboard-background:hsl(var(--hue),19%,18%);--keyboard-text:#d4d6d7;--keyboard-text-active:#5f97fc;--keyboard-background-border:#333;--keycap-background:hsl(var(--hue),25%,39%);--keycap-background-active:#5f97fc;--keycap-background-border:transparent;--keycap-background-border-bottom:transparent;--keycap-text:#d0d0d0;--keycap-text-active:#d4d6d7;--keycap-secondary-text:#5f97fc;--keycap-modifier-background:hsl(var(--hue),35%,40%);--keycap-modifier-border:hsl(var(--hue),35%,35%);--keycap-modifier-border-bottom:hsl(var(--hue),35%,42%);--keyboard-alternate-background:hsl(var(--hue),8%,2%);--keyboard-alternate-background-active:hsl(var(--hue),35%,42%);--keyboard-alternate-text:#d1d1d1}}body[theme=dark] div.ML__keyboard.material{--hue:198;--keyboard-background:hsl(var(--hue),19%,18%);--keyboard-text:#d4d6d7;--keyboard-text-active:#5f97fc;--keyboard-background-border:#333;--keycap-background:hsl(var(--hue),25%,39%);--keycap-background-active:#5f97fc;--keycap-background-border:transparent;--keycap-background-border-bottom:transparent;--keycap-text:#d0d0d0;--keycap-text-active:#d4d6d7;--keycap-secondary-text:#5f97fc;--keycap-modifier-background:hsl(var(--hue),35%,40%);--keycap-modifier-border:hsl(var(--hue),35%,35%);--keycap-modifier-border-bottom:hsl(var(--hue),35%,42%);--keyboard-alternate-background:hsl(var(--hue),8%,2%);--keyboard-alternate-background-active:hsl(var(--hue),35%,42%);--keyboard-alternate-text:#d1d1d1}"; /* eslint-disable no-new */ // Each entry indicate the font-name (to be used to calculate font metrics) // and the CSS classes (for proper markup styling) for each possible // variant combinations. const VARIANTS = { // Handle some special characters which are only available in "main" font (not "math") 'main': ['Main-Regular', 'ML__cmr'], 'main-italic': ['Main-Italic', 'ML__cmr ML__it'], 'main-bold': ['Main-Bold', 'ML__cmr ML__bold'], 'main-bolditalic': ['Main-BoldItalic', 'ML__cmr ML__bold ML__it'], 'normal': ['Main-Regular', 'ML__cmr'], 'normal-bold': ['Main-Bold', 'ML__mathbf'], 'normal-italic': ['Math-Italic', 'ML__mathit'], 'normal-bolditalic': ['Math-BoldItalic', 'ML__mathbfit'], // Extended math symbols, arrows, etc.. at their standard Unicode codepoints 'ams': ['AMS-Regular', 'ML__ams'], 'ams-bold': ['AMS-Regular', 'ML__ams'], 'ams-italic': ['AMS-Regular', 'ML__ams'], 'ams-bolditalic': ['AMS-Regular', 'ML__ams'], 'sans-serif': ['SansSerif-Regular', 'ML__sans'], 'sans-serif-bold': ['SansSerif-Regular', 'ML__sans ML__bold'], 'sans-serif-italic': ['SansSerif-Regular', 'ML__sans'], 'sans-serif-bolditalic': ['SansSerif-Regular', 'ML__sans'], 'calligraphic': ['Caligraphic-Regular', 'ML__cal'], 'calligraphic-bold': ['Caligraphic-Regular', 'ML__cal ML__bold'], 'calligraphic-italic': ['Caligraphic-Regular', 'ML__cal ML__it'], 'calligraphic-bolditalic': ['Caligraphic-Regular', 'ML__cal ML__bold ML__it'], 'script': ['Script-Regular', 'ML__script'], 'script-bold': ['Script-Regular', 'ML__script ML__bold'], 'script-italic': ['Script-Regular', 'ML__script ML__it'], 'script-bolditalic': ['Script-Regular', 'ML__script ML__bold ML__it'], 'fraktur': ['Fraktur-Regular', 'ML__frak'], 'fraktur-bold': ['Fraktur-Regular', 'ML__frak'], 'fraktur-italic': ['Fraktur-Regular', 'ML__frak'], 'fraktur-bolditalic': ['Fraktur-Regular', 'ML__frak'], 'monospace': ['Typewriter-Regular', 'ML__tt'], 'monospace-bold': ['Typewriter-Regular', 'ML__tt ML__bold'], 'monospace-italic': ['Typewriter-Regular', 'ML__tt ML__it'], 'monospace-bolditalic': ['Typewriter-Regular', 'ML__tt ML__bold ML__it'], // Blackboard characters are 'A-Z' in the AMS font 'double-struck': ['AMS-Regular', 'ML__bb'], 'double-struck-bold': ['AMS-Regular', 'ML__bb'], 'double-struck-italic': ['AMS-Regular', 'ML__bb'], 'double-struck-bolditalic': ['AMS-Regular', 'ML__bb'], }; const VARIANT_REPERTOIRE = { 'double-struck': /^[A-Z ]$/, 'script': /^[A-Z ]$/, 'calligraphic': /^[\dA-Z ]$/, 'fraktur': /^[\dA-Za-z ]$|^[!"#$%&'()*+,\-./:;=?[]^’‘]$/, 'monospace': /^[\dA-Za-z ]$|^[!"&'()*+,\-./:;=?@[\]^_~\u0131\u0237\u0393\u0394\u0398\u039B\u039E\u03A0\u03A3\u03A5\u03A8\u03A9]$/, 'sans-serif': /^[\dA-Za-z ]$|^[!"&'()*+,\-./:;=?@[\]^_~\u0131\u0237\u0393\u0394\u0398\u039B\u039E\u03A0\u03A3\u03A5\u03A8\u03A9]$/, }; const GREEK_LOWERCASE = /^[\u03B1-\u03C9]|\u03D1|\u03D5|\u03D6|\u03F1|\u03F5]$/; const GREEK_UPPERCASE = /^[\u0393|\u0394\u0398\u039B\u039E\u03A0\u03A3\u03A5\u03A6\u03A8\u03A9]$/; const LETTER_SHAPE_RANGES = [ /^[a-z]$/, /^[A-Z]$/, GREEK_LOWERCASE, GREEK_UPPERCASE, ]; // The letterShapeStyle property indicates which characters should be // automatically italicized (see LETTER_SHAPE_RANGES) const LETTER_SHAPE_MODIFIER = { iso: ['it', 'it', 'it', 'it'], tex: ['it', 'it', 'it', 'up'], french: ['it', 'up', 'up', 'up'], upright: ['up', 'up', 'up', 'up'], }; // See http://ctan.math.illinois.edu/macros/latex/base/fntguide.pdf class MathMode extends Mode { constructor() { super('math'); } createAtom(command, style) { var _a, _b; const info = getInfo(command, 'math'); const result = new Atom((_a = info === null || info === void 0 ? void 0 : info.type) !== null && _a !== void 0 ? _a : 'mord', { mode: 'math', command, value: (info === null || info === void 0 ? void 0 : info.codepoint) ? String.fromCodePoint(info === null || info === void 0 ? void 0 : info.codepoint) : command, style, }); if ((_b = info === null || info === void 0 ? void 0 : info.isFunction) !== null && _b !== void 0 ? _b : false) result.isFunction = true; if (command.startsWith('\\')) result.verbatimLatex = command; return result; } serialize(run, options) { const { parent } = run[0]; const contextFontsize = parent === null || parent === void 0 ? void 0 : parent.computedStyle.fontSize; return joinLatex(getPropertyRuns(run, 'fontSize').map((x) => { const result = emitBackgroundColorRun$1(x, options); const fontsize = x[0].computedStyle.fontSize; if (fontsize && (!parent || contextFontsize !== fontsize)) { return ('\\' + [ '', 'tiny', 'scriptsize', 'footnotesize', 'small', 'normalsize', 'large', 'Large', 'LARGE', 'huge', 'Huge', ][fontsize] + ' ' + result); } return result; })); } applyStyle(box, style) { // If no variant specified, don't change the font if (style.variant === undefined) return ''; // LetterShapeStyle will usually be set automatically, except when the // locale cannot be determined, in which case its value will be 'auto' // which we default to 'tex' const letterShapeStyle = style.letterShapeStyle === 'auto' || !style.letterShapeStyle ? 'tex' : style.letterShapeStyle; let { variant } = style; let { variantStyle } = style; // 1. Remap to "main" font some characters that don't exist // in the "math" font // There are two fonts that include the roman italic characters, "main-it" and "math" // They are similar, but the "math" font has some different kernings ('f') // and some slightly different character shape. It doesn't include a few // characters, so for those characters, "main" has to be used instead // \imath, \jmath and \pound don't exist in "math" font, // so use "main" italic instead. if (variant === 'normal' && !variantStyle && /[\u00A3\u0131\u0237]/.test(box.value)) { variant = 'main'; variantStyle = 'italic'; } // 2. If no explicit variant style, auto-italicize some symbols, // depending on the letterShapeStyle if (variant === 'normal' && !variantStyle && box.value.length === 1) { LETTER_SHAPE_RANGES.forEach((x, i) => { if (x.test(box.value) && LETTER_SHAPE_MODIFIER[letterShapeStyle][i] === 'it') variantStyle = 'italic'; }); } // 3. Map the variant + variantStyle to a font if (variantStyle === 'up') variantStyle = undefined; const styledVariant = variantStyle ? variant + '-' + variantStyle : variant; console.assert(VARIANTS[styledVariant] !== undefined); const [fontName, classes] = VARIANTS[styledVariant]; // 4. If outside the font repertoire, switch to system font // (return NULL to use default metrics) if (VARIANT_REPERTOIRE[variant] && !VARIANT_REPERTOIRE[variant].test(box.value)) { // Map to unicode character box.value = mathVariantToUnicode(box.value, variant, variantStyle); // Return NULL to use default metrics return null; } // Lowercase greek letters have an incomplete repertoire (no bold) // so, for \mathbf to behave correctly, add a 'lcGreek' class. if (GREEK_LOWERCASE.test(box.value)) box.classes += ' lcGreek'; // 5. Assign classes based on the font if (classes) box.classes += ' ' + classes; return fontName; } } function emitVariantRun(run, options) { var _a; const { parent } = run[0]; const contextVariant = variantString(parent); const parentMode = (_a = parent === null || parent === void 0 ? void 0 : parent.mode) !== null && _a !== void 0 ? _a : 'math'; return joinLatex(getPropertyRuns(run, 'variant').map((x) => { const variant = variantString(x[0]); // Check if all the atoms in this run have a base // variant identical to the current variant // If so, we can skip wrapping them if (x.every((x) => { const info = getInfo(x.command, parentMode); if (!info || !info.variant) return false; return variantString(x) === variant; })) return joinLatex(x.map((x) => Atom.serialize(x, options))); let command = ''; if (variant && variant !== contextVariant) { command = { 'calligraphic': '\\mathcal{', 'fraktur': '\\mathfrak{', 'double-struck': '\\mathbb{', 'script': '\\mathscr{', 'monospace': '\\mathtt{', 'sans-serif': '\\mathsf{', 'normal': '\\mathrm{', 'normal-italic': '\\mathnormal{', 'normal-bold': '\\mathbf{', 'normal-bolditalic': '\\mathbfit{', 'ams': '', 'ams-italic': '\\mathit{', 'ams-bold': '\\mathbf{', 'ams-bolditalic': '\\mathbfit{', 'main': '', 'main-italic': '\\mathit{', 'main-bold': '\\mathbf{', 'main-bolditalic': '\\mathbfit{', // There are a few rare font families possible, which // are not supported: // mathbbm, mathbbmss, mathbbmtt, mathds, swab, goth // In addition, the 'main' and 'math' font technically // map to \mathnormal{} }[variant]; console.assert(command !== undefined); } if (!command) return joinLatex(x.map((x) => Atom.serialize(x, options))); return (command + joinLatex(x.map((x) => Atom.serialize(x, options))) + '}'); })); } function emitColorRun$1(run, options) { const { parent } = run[0]; const contextColor = parent === null || parent === void 0 ? void 0 : parent.computedStyle.color; return joinLatex(getPropertyRuns(run, 'color').map((x) => { var _a, _b; const result = emitVariantRun(x, options); const style = x[0].computedStyle; if (!((_a = options.skipStyles) !== null && _a !== void 0 ? _a : false) && style.color && (!parent || contextColor !== style.color)) { return ('\\mathcolor{' + ((_b = style.verbatimColor) !== null && _b !== void 0 ? _b : style.color) + '}{' + result + '}'); } return result; })); } function emitBackgroundColorRun$1(run, options) { const { parent } = run[0]; const parentColor = parent === null || parent === void 0 ? void 0 : parent.computedStyle.backgroundColor; return joinLatex(getPropertyRuns(run, 'backgroundColor').map((x) => { var _a, _b; let result = emitColorRun$1(x, options); const style = x[0].computedStyle; if (!((_a = options.skipStyles) !== null && _a !== void 0 ? _a : false) && result.trim() && style.backgroundColor && (!parent || parentColor !== style.backgroundColor) && (x.length > 0 || !(x[0] instanceof BoxAtom))) { result = `\\ensuremath{${result}}`; result = `\\colorbox{${(_b = style.verbatimBackgroundColor) !== null && _b !== void 0 ? _b : style.backgroundColor}}{${result}}`; } return result; })); } function variantString(atom) { if (!atom) return ''; const { style } = atom; if (style.variant === undefined) return ''; let result = style.variant; if (style.variantStyle && style.variantStyle !== 'up') result += '-' + style.variantStyle; return result; } // Singleton class new MathMode(); function join(segments) { return [ joinLatex(segments.map((x) => x[0])), segments.map((x) => x[1]).some((x) => x === true), ]; } /* Return a LaTeX string and a boolean indicating if the string needs to be wrapped with a mode changing command or not. */ function emitStringTextRun(run, options) { return [joinLatex(run.map((x) => Atom.serialize(x, options))), true]; } function emitFontShapeTextRun(run, options) { return join(getPropertyRuns(run, 'fontShape').map((x) => { const [s, needsWrap] = emitStringTextRun(x, options); const { fontShape } = x[0].style; if (fontShape === 'it') return ['\\textit{' + s + '}', false]; if (fontShape === 'sl') return ['\\textsl{' + s + '}', false]; if (fontShape === 'sc') return ['\\textsc{' + s + '}', false]; if (fontShape === 'n') return ['\\textup{' + s + '}', false]; if (fontShape) return [`{\\fontshape{${x[0].style.fontShape}}${s}`, false]; return [s, needsWrap]; })); } function emitFontSeriesTextRun(run, options) { return join(getPropertyRuns(run, 'fontSeries').map((x) => { const [s, needsWrap] = emitFontShapeTextRun(x, options); const { fontSeries } = x[0].style; if (fontSeries === 'b') return [`\\textbf{${s}}`, false]; if (fontSeries === 'l') return [`\\textlf{${s}}`, false]; if (fontSeries === 'm') return [`\\textmd{${s}}`, false]; if (fontSeries) return [`\\fontseries{${fontSeries}}${s}`, false]; return [s, needsWrap]; })); } function emitSizeTextRun(run, options) { return join(getPropertyRuns(run, 'fontSize').map((x) => { var _a, _b; const [s, needsWrap] = emitFontSeriesTextRun(x, options); const command = (_b = [ '', 'tiny', 'scriptsize', 'footnotesize', 'small', 'normalsize', 'large', 'Large', 'LARGE', 'huge', 'Huge', ][(_a = x[0].style.fontSize) !== null && _a !== void 0 ? _a : '']) !== null && _b !== void 0 ? _b : ''; if (command) return [`{\\${command} ${s}}`, needsWrap]; return [s, needsWrap]; })); } function emitFontFamilyTextRun(run, options) { return join(getPropertyRuns(run, 'fontFamily').map((x) => { var _a, _b; const [s, needsWrap] = emitSizeTextRun(x, options); const command = (_b = { 'roman': 'textrm', 'monospace': 'texttt', 'sans-serif': 'textsf', }[(_a = x[0].style.fontFamily) !== null && _a !== void 0 ? _a : '']) !== null && _b !== void 0 ? _b : ''; if (command) return [`\\${command}{${s}}`, false]; if (x[0].style.fontFamily) return [`\\fontfamily{${x[0].style.fontFamily}}${s}`, needsWrap]; return [s, needsWrap]; })); } function emitStyledTextRun(run, options) { return emitFontFamilyTextRun(run, options); } function emitBackgroundColorRun(run, options) { return join(getPropertyRuns(run, 'backgroundColor').map((x) => { var _a, _b; const [s, needsWrap] = emitColorRun(x, options); const style = x[0].computedStyle; if (!((_a = options.skipStyles) !== null && _a !== void 0 ? _a : false) && style.backgroundColor && style.backgroundColor !== 'none') { return [ `\\colorbox{${(_b = style.verbatimBackgroundColor) !== null && _b !== void 0 ? _b : style.backgroundColor}}{${s}}`, false, ]; } return [s, needsWrap]; })); } function emitColorRun(run, options) { var _a; if (!run || run.length === 0) return ['', false]; const parentColor = (_a = run[0].parent) === null || _a === void 0 ? void 0 : _a.style.color; return join(getPropertyRuns(run, 'color').map((x) => { var _a, _b; const [s, needsWrap] = emitStyledTextRun(x, options); if (!((_a = options.skipStyles) !== null && _a !== void 0 ? _a : false) && x[0].style.color && x[0].style.color !== 'none' && parentColor !== x[0].style.color) { // If there is a color specified, and it is different // from our context color, output a command return [ `\\textcolor{${(_b = x[0].style.verbatimColor) !== null && _b !== void 0 ? _b : x[0].style.color}}{${s}}`, false, ]; } return [s, needsWrap]; })); } const TEXT_FONT_CLASS = { 'roman': '', 'sans-serif': 'ML__sans', 'monospace': 'ML__tt', }; class TextMode extends Mode { constructor() { super('text'); } createAtom(command, style) { const info = getInfo(command, 'text'); return new TextAtom(command, (info === null || info === void 0 ? void 0 : info.codepoint) ? String.fromCodePoint(info.codepoint) : command, style); } serialize(run, options) { var _a; let [result, needWrapper] = emitBackgroundColorRun(run, options); if (((_a = options.skipModeCommand) !== null && _a !== void 0 ? _a : false) === true) needWrapper = false; if (needWrapper) result = `\\text{${result}}`; return result; } /** * Return the font-family name */ applyStyle(box, style) { var _a, _b, _c, _d, _e, _f; const { fontFamily } = style; if (TEXT_FONT_CLASS[fontFamily]) box.classes += (_a = ' ' + TEXT_FONT_CLASS[fontFamily]) !== null && _a !== void 0 ? _a : ''; else if (fontFamily) { // Not a well-known family. Use a style. box.setStyle('font-family', fontFamily); } if (style.fontShape) { box.classes += ' '; box.classes += (_b = { it: 'ML__it', sl: 'ML__shape_sl', sc: 'ML__shape_sc', ol: 'ML__shape_ol', // Outline }[style.fontShape]) !== null && _b !== void 0 ? _b : ''; } if (style.fontSeries) { const m = style.fontSeries.match(/(.?[lbm])?(.?[cx])?/); if (m) { box.classes += ' '; box.classes += (_d = { ul: 'ML__series_ul', el: 'ML__series_el', l: 'ML__series_l', sl: 'ML__series_sl', m: '', sb: 'ML__series_sb', b: 'ML__bold', eb: 'ML__series_eb', ub: 'ML__series_ub', }[(_c = m[1]) !== null && _c !== void 0 ? _c : '']) !== null && _d !== void 0 ? _d : ''; box.classes += ' '; box.classes += (_f = { uc: 'ML__series_uc', ec: 'ML__series_ec', c: 'ML__series_c', sc: 'ML__series_sc', n: '', sx: 'ML__series_sx', x: 'ML__series_x', ex: 'ML__series_ex', ux: 'ML__series_ux', }[(_e = m[2]) !== null && _e !== void 0 ? _e : '']) !== null && _f !== void 0 ? _f : ''; } } // Always use the metrics of 'Main-Regular' in text mode return 'Main-Regular'; } // Given an array of tokens, return an array of atoms // options.args // options.macros // options.smartFence // options.style // options.parser parse(tokens, error, options) { let result = []; let atom; while (tokens.length > 0) { const token = tokens.shift(); if (token === '<space>') result.push(new TextAtom(' ', ' ', options.style)); else if (!!token && token.startsWith('\\')) { // Invoke the 'main' parser to handle the command tokens.unshift(token); let atoms; [atoms, tokens] = options.parse('text', tokens, options); result = [...result, ...atoms]; } else if (token === '<$>' || token === '<$$>') { // Mode-shift const subtokens = tokens.slice(0, tokens.findIndex((x) => x === token)); tokens = tokens.slice(subtokens.length + 1); const [atoms] = options.parse('math', subtokens, options); result = [...result, ...atoms]; } else if (token === '<{>' || token === '<}>') ; else if (token) { const info = getInfo(token, 'text', options.macros); if (!info || (info.ifMode && !info.ifMode.includes('text'))) error({ code: 'unexpected-token' }); else if (info.codepoint) { atom = new TextAtom(token, String.fromCodePoint(info.codepoint), options.style); atom.verbatimLatex = charToLatex('text', token.codePointAt(0)); result.push(atom); } } } return [result, tokens]; } } // Singleton class new TextMode(); /* eslint-disable no-new */ class LatexMode extends Mode { constructor() { super('latex'); } createAtom(command, _style) { return new LatexAtom(command); } serialize(run, _options) { return run .filter((x) => x instanceof LatexAtom && !x.isSuggestion) .map((x) => x.value) .join(''); } applyStyle() { return null; } } new LatexMode(); /** * Registers * * Registers are scoped to the current group. * * - When accessing a register, the scope chain is used to resolve it. * Unless `\global` is used, in which case the global register is returned. * * - When modifying a register, the local one is modified and all the parent * ones, except for the global one are cleared. * ```tex \newcount\R global $R = $\the\abc ~ (default value is 0) { \par before top $R =$ \the\R ~ ($= 0$ access global) \R=1 \par local top $R =$ \the\R ~ ($ = 1$ modifies local register) { \par inner before $R = $ \the\R ~ ($= 1$ access parent register) \abc=2 \par inner after $R = $ \the\R ~ ($= 2$ local value) global R $R = $ \the\global\R ~ ($= 0$ global value) \global\R=1000 $R = $ \the\R ~ ($=1000 $ sets global and clear all locals) } \par after top $R = $ \the\R ~($= 1000$ local cleared) } \par global $R = $\the\R ~($= 1000$) ``` * * */ const DEFAULT_DIMENSION_REGISTERS = { 'p@': '1pt ', 'z@': '0pt', 'maxdimen': '16383.99999pt', 'hfuzz': '0.1pt', 'vfuzz': '0.1pt', 'overfullrule': '5pt', 'hsize': '6.5in', 'vsize': '8.9in', 'parindent': '20pt', 'maxdepth': '4pt', 'splitmaxdepth': '\\maxdimen', 'boxmaxdepth': '\\maxdimen', 'delimitershortfall': '5pt', 'nulldelimiterspace': '1.2pt', 'scriptspace': '0.5pt', 'topskip': '10pt', 'splittopskip': '10pt', 'normalbaselineskip': '12pt', 'normallineskip': '1pt', 'normallineskiplimit': '0pt', // @todo: The vertical space between the lines for all math expressions which // allow multiple lines (see array, multline) 'jot': '3pt', // The space between adjacent `|` columns in an array definition. // From article.cls.txt:455 'doublerulesep': '2pt', // The width of separator lines in {array} environments. 'arrayrulewidth': '0.4pt', 'arraycolsep': '5pt', // Two values from LaTeX source2e: 'fboxsep': '3pt', 'fboxrule': '0.4pt', // From letter.dtx:1627 }; const DEFAULT_GLUE_REGISTERS = { 'z@skip': '0pt plust0pt minus0pt', 'hideskip': '-1000pt plust 1fill', '@flushglue': '0pt plust 1fill', 'parskip': '0pt plus 1pt', // @todo the "shortskip" are used if the formula starts to the right of the // line before (i.e. centered and short line before) 'abovedisplayskip': '12pt plus 3pt minus 9pt', 'abovedisplayshortskip': '0pt plus 3pt', 'belowdisplayskip': '12pt plus 3pt minus 9pt', 'belowdisplayshortskip': '7pt plus 3pt minus 4pt', 'parfillskip': '0pt plus 1fil', 'thinmuskip': '3mu', 'medmuskip': '4mu plus 2mu minus 4mu', 'thickmuskip': '5mu plus 5mu', 'smallskipamount': '3pt plus1pt minus1pt', 'medskipamount': '6pt plus2pt minus2pt', 'bigskipamount': '12pt plus4pt minus4pt', }; // From TeXBook p.348 // See also https://ctan.math.washington.edu/tex-archive/info/macros2e/macros2e.pdf const DEFAULT_NUMBER_REGISTERS = { // 'voidb@x' pretolerance: 100, tolerance: 200, hbadness: 1000, vbadness: 1000, linepenalty: 10, hyphenpenalty: 50, exhyphenpenalty: 50, binoppenalty: 700, relpenalty: 500, clubpenalty: 150, widowpenalty: 150, displaywidowpenalty: 50, brokenpenalty: 100, predisplaypenalty: 10000, doublehyphendemerits: 10000, finalhyphendemerits: 5000, adjdemerits: 10000, tracinglostchars: 1, uchyph: 1, delimiterfactor: 901, defaulthyphenchar: '\\-', defaultskewchar: -1, newlinechar: -1, showboxbreadth: 5, showboxdepth: 3, errorcontextlines: 5, interdisplaylinepenalty: 100, interfootnotelinepenalty: 100, baselineSkip: 1.2, // @todo: arraystretch: '', month: new Date().getMonth() + 1, day: new Date().getDate(), year: new Date().getFullYear(), }; let _DEFAULT_REGISTERS; function getDefaultRegisters() { var _a, _b; if (_DEFAULT_REGISTERS) return _DEFAULT_REGISTERS; _DEFAULT_REGISTERS = { ...DEFAULT_NUMBER_REGISTERS, }; for (const reg of Object.keys(DEFAULT_DIMENSION_REGISTERS)) { _DEFAULT_REGISTERS[reg] = (_a = convertToDimension(DEFAULT_DIMENSION_REGISTERS[reg], _DEFAULT_REGISTERS)) !== null && _a !== void 0 ? _a : 0; } for (const reg of Object.keys(DEFAULT_GLUE_REGISTERS)) { _DEFAULT_REGISTERS[reg] = (_b = convertToGlue(DEFAULT_GLUE_REGISTERS[reg], _DEFAULT_REGISTERS)) !== null && _b !== void 0 ? _b : 0; } return _DEFAULT_REGISTERS; } /** * Apply typsetting rules to a mathlist. * * The glue of math Atoms is calculated based on the type ('ord', 'rel', etc...) * of the atoms around them. * * TeXBook, p. 170 * * > In fact, TEX’s rules for spacing in formulas are fairly simple. A formula is * > converted to a math list as described at the end of Chapter 17, and the math * > list consists chiefly of “atoms” of eight basic types: Ord (ordinary), * > Op (large operator), Bin (binary operation), Rel (relation), Open (opening), * > Close (closing), Punct (punctuation), and Inner (a delimited subformula). * > Other kinds of atoms, which arise from commands like \overline or * > \mathaccent or \vcenter, etc., are all treated as type Ord; fractions are * > treated as type Inner. * * > The following table is used to determine the spacing between pair of adjacent * > atoms. * * In this table * - "3" = `\thinmuskip` * - "4" = `\medmuskip` * - "5" = `\thickmuskip` * */ // const INTER_ATOM_SPACING = { // mord: { mop: 3, mbin: 4, mrel: 5, minner: 3 }, // mop: { mord: 3, mop: 3, rel: 5, minner: 3 }, // mbin: { mord: 4, mop: 4, mopen: 4, minner: 4 }, // mrel: { mord: 5, mop: 5, mopen: 5, minner: 5 }, // mclose: { mop: 3, mbin: 4, mrel: 5, minner: 3 }, // mpunct: { mord: 3, mop: 3, mrel: 3, mopen: 3, mpunct: 3, minner: 3 }, // minner: { mord: 3, mop: 3, mbin: 4, mrel: 5, mopen: 3, mpunct: 3, minner: 3 }, // }; // /** // * This table is used when the mathstyle is 'tight' (scriptstyle or // * scriptscriptstyle). // */ // const INTER_ATOM_TIGHT_SPACING = { // mord: { mop: 3 }, // mop: { mord: 3, mop: 3 }, // mclose: { mop: 3 }, // minner: { mop: 3 }, // }; function typesetRecursive(atoms, _options) { // 1. Apply inter-atom spacing rules return atoms; } function typeset(atoms, options) { // 1. Apply inter-atom spacing rules return typesetRecursive(atoms, { registers: (options === null || options === void 0 ? void 0 : options.registers) ? options.registers : getDefaultRegisters(), }); } var css_248z$2 = "#mathlive-popover-panel{background-color:rgba(97,97,97,.95);border-radius:6px;box-shadow:0 14px 28px rgba(0,0,0,.25),0 10px 10px rgba(0,0,0,.22);color:#fff;display:flex;flex-direction:column;justify-content:center;min-width:160px;position:fixed;text-align:center;transition:all .2s cubic-bezier(.64,.09,.08,1);visibility:hidden;z-index:1}#mathlive-popover-panel:after{border-bottom:5px solid rgba(97,97,97,.9);border-left:5px solid transparent;border-right:5px solid transparent;content:\"\";font-size:1rem;height:0;left:calc(50% - 3px);position:absolute;top:-5px;width:0}.ML__popover--reverse-direction:after{border-bottom:0;border-top:5px solid rgba(97,97,97,.9);bottom:-5px;top:auto}#mathlive-popover-panel.is-visible{animation:ML__fade-in .15s cubic-bezier(0,0,.2,1);visibility:inherit}@keyframes ML__fade-in{0%{opacity:0}to{opacity:1}}.ML__popover__content{border-radius:6px;cursor:pointer;display:flex;flex-direction:column;justify-content:center;margin-left:8px;margin-right:8px;min-height:100px;padding:2px}.ML__popover__content a{color:#5ea6fd;display:block;margin-top:.4em;padding-top:.3em}.ML__popover__content a:hover{color:#5ea6fd;text-decoration:underline}.ML__popover__content.is-active,.ML__popover__content.is-pressed,.ML__popover__content:hover{background:hsla(0,0%,100%,.1)}.ML__popover__command{font-family:KaTeX_Main;font-size:1.6rem}.ML__popover__prev-shortcut{cursor:pointer;height:31px;margin-left:8px;margin-right:8px;opacity:.1;padding-bottom:2px;padding-top:4px}.ML__popover__next-shortcut:hover,.ML__popover__prev-shortcut:hover{opacity:.3}.ML__popover__next-shortcut.is-active,.ML__popover__next-shortcut.is-pressed,.ML__popover__prev-shortcut.is-active,.ML__popover__prev-shortcut.is-pressed{opacity:1}.ML__popover__next-shortcut>span,.ML__popover__prev-shortcut>span{border-radius:8px;display:inline-block;height:20px;padding:5px;width:20px}.ML__popover__prev-shortcut>span>span{display:block;margin-top:-2px}.ML__popover__next-shortcut>span>span{display:block;margin-top:2px}.ML__popover__next-shortcut:hover>span,.ML__popover__prev-shortcut:hover>span{background:hsla(0,0%,100%,.1)}.ML__popover__next-shortcut{cursor:pointer;height:34px;margin-left:8px;margin-right:8px;opacity:.1;padding-bottom:4px;padding-top:2px}.ML__popover__shortcut{font-size:.8em;margin-top:.25em}.ML__popover__note,.ML__popover__shortcut{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;opacity:.7;padding-top:.25em}.ML__popover__note{font-size:.8rem;line-height:1em;padding-left:.5em;padding-right:.5em}.ML__shortcut-join{opacity:.5}"; let POPOVER_STYLESHEET_HASH = undefined; let gPopoverStylesheet = null; let gCoreStylesheet$1 = null; // A textual description of a LaTeX command. // The value can be either a single string, or an array of string // in order to provide alternatives or additional context. // In that case, the first string in the array should be appropriate // to be spoken for accessibility. const NOTES = { '\\text': 'roman text', '\\textrm': 'roman text', '\\textnormal': 'roman text', '\\textit': 'italic text', '\\textbf': 'bold text', '\\texttt': 'monospaced text', '\\textsf': 'sans-serif text', '\\mathrm': ['roman', '(upright)'], '\\mathbf': 'bold', '\\bf': 'bold', '\\bold': 'bold', '\\mathit': 'italic', '\\mathbb': 'blackboard', '\\mathscr': 'script', '\\mathtt': ['typewriter', '(monospaced)'], '\\mathsf': 'sans-serif', '\\mathcal': 'caligraphic', '\\frak': ['fraktur', '(gothic)'], '\\mathfrak': ['fraktur', '(gothic)'], '\\textcolor': 'text color', '\\color': 'color', '\\forall': 'for all', '\\exists': 'there exists', '\\nexists': 'there does not exist', '\\frac': 'fraction', '\\dfrac': 'display fraction', '\\cfrac': 'continuous fraction', '\\tfrac': 'text fraction', '\\binom': 'binomial coefficient', '\\dbinom': 'display binomial coefficient', '\\tbinom': 'text binomial coefficient', '\\pdiff': 'partial differential', '\\vec': 'vector', '\\check': 'caron', '\\acute': 'acute', '\\breve': 'breve', '\\tilde': 'tilde', '\\dot': 'dot', '\\hat': ['hat', 'circumflex'], '\\ddot': 'double dot', '\\bar': 'bar', '\\prime': 'prime', '\\doubleprime': 'double prime', '\\varnothing': 'empty set', '\\emptyset': 'empty set', '\\subseteq': 'subset of or <br>equal to', '\\supseteq': 'superset of or <br>equal to', '\\supset': 'superset of', '\\subset': 'subset of', '\\partial': 'partial derivative', '\\bigcup': 'union', '\\bigcap': 'intersection', '\\approx': 'approximately equal to', '\\notin': 'not an element of', '\\in': ['element of', 'included in'], '\\infty': 'infinity', '\\land': 'logical and', '\\sqrt': 'square root', '\\prod': 'product', '\\sum': 'summation', '\\amalg': ['amalgamation', 'coproduct', 'free product', 'disjoint union'], '\\cup': 'union with', '\\cap': 'intersection with', '\\int': 'integral', '\\iint': 'surface integral', '\\oint': 'curve integral', '\\iiint': 'volume integral', '\\iff': 'if and only if', '\\ln': 'natural logarithm', '\\boldsymbol': 'bold', '\\setminus': 'set subtraction', '\\stackrel': 'relation with symbol above', '\\stackbin': 'operator with symbol above', '\\underset': 'symbol with annotation below', '\\overset': 'symbol with annotation above', '\\hslash': ['h-bar', 'Planck constant'], '\\gtrsim': 'greater than or <br>similar to', '\\propto': 'proportional to', '\\equiv': 'equivalent to', '\\!': ['negative thin space', '(-3 mu)'], '\\ ': ['space', '(6 mu)'], '\\,': ['thin space', '(3 mu)'], '\\:': ['medium space', '(4 mu)'], '\\;': ['thick space', '(5 mu)'], '\\quad': ['1 em space', '(18 mu)'], '\\qquad': ['2 em space', '(36 mu)'], '\\enskip': ['½ em space', '(9 mu)'], '\\mp': 'minus or plus', '\\pm': 'plus or minus', '\\Im': 'Imaginary part of', '\\Re': 'Real part of', '\\gothicCapitalR': 'Real part of', '\\gothicCapitalI': 'Imaginary part part of', '\\differentialD': 'differential d', '\\aleph': [ 'aleph', 'infinite cardinal', '<a target="_blank" href="https://en.wikipedia.org/wiki/Cardinal_number">Wikipedia <big>›</big></a>', ], '\\beth': [ 'beth', 'beth number', '<a target="_blank" href="https://en.wikipedia.org/wiki/Beth_number">Wikipedia <big>›</big></a>', ], '\\gimel': [ 'gimel', 'gimel function', '<a target="_blank" href="https://en.wikipedia.org/wiki/Gimel_function">Wikipedia <big>›</big></a>', ], '\\O': 'empty set', '\\N': 'set of <br>natural numbers', '\\Z': 'set of <br>integers', '\\Q': 'set of <br>rational numbers', '\\C': 'set of <br>complex numbers', '\\R': 'set of <br>real numbers', '\\P': 'set of <br>prime numbers', '\\lesseqqgtr': 'less than, equal to or<br> greater than', '\\gnapprox': 'greater than and <br>not approximately', '\\lnapprox': 'lesser than and <br>not approximately', '\\j': 'dotless j', '\\i': 'dotless i', '\\cdot': 'centered dot', '\\lmoustache': 'left moustache', '\\rmoustache': 'right moustache', '\\nabla': ['nabla', 'del', 'differential vector operator'], '\\square': [ 'square', 'd’Alembert operator', '<a target="_blank" href="https://en.wikipedia.org/wiki/D%27Alembert_operator">Wikipedia <big>›</big></a>', ], '\\blacksquare': [ 'black square', 'end of proof', 'tombstone', 'Halmos symbol', ], '\\Box': 'end of proof', '\\colon': ['such that', 'ratio'], '\\coloneq': ['is defined by', 'is assigned'], '\\Colon': ['is defined by', 'as'], '\\_': ['underbar', 'underscore'], '\\ll': 'much less than', '\\gg': 'much greater than', '\\doteq': 'approximately equal to', '\\Doteq': 'approximately equal to', '\\doteqdot': 'approximately equal to', '\\cong': ['isomorphism of', '(for algebras, modules...)'], '\\det': ['determinant of', '(of a matrix)'], '\\dotplus': 'Cartesian product algebra', '\\otimes': [ 'tensor product', '(of algebras)', 'Kronecker product', '(of matrices)', ], '\\oplus': ['direct sum', '(of modules)'], '\\lb': 'base-2 logarithm', '\\lg': 'base-10 logarithm', '\\wp': [ 'Weierstrass P', '<a target="_blank" href="https://en.wikipedia.org/wiki/Weierstrass%27s_elliptic_functions">Wikipedia <big>›</big></a>', ], '\\wr': [ 'wreath product', '<a target="_blank" href="https://en.wikipedia.org/wiki/Wreath_product">Wikipedia <big>›</big></a>', ], '\\top': ['tautology', 'Proposition P is universally true'], '\\bot': ['contradiction', 'Proposition P is contradictory'], '\\mid': ['probability', 'of event A given B'], '\\mho': [ 'Siemens', 'electrical conductance in SI unit', '<a target="_blank" href="https://en.wikipedia.org/wiki/Siemens_(unit)">Wikipedia <big>›</big></a>', ], '\\Longrightarrow': 'implies', '\\Longleftrightarrow': 'if, and only if,', '\\prec': 'precedes', '\\preceq': 'precedes or is equal to', '\\succ': 'succeedes', '\\succeq': 'succeedes or is equal to', '\\perp': ['is perpendicular to', 'is independent of'], '\\models': [ 'entails', 'double-turnstyle, models', 'is a semantic consequence of', '<a target="_blank" href="https://en.wikipedia.org/wiki/Double_turnstile">Wikipedia <big>›</big></a>', ], '\\vdash': [ 'satisfies', 'turnstyle, assertion sign', 'syntactic inference', '<a target="_blank" href="https://en.wikipedia.org/wiki/Turnstile_(symbol)">Wikipedia <big>›</big></a>', ], '\\implies': ['implies', 'logical consequence'], '\\impliedby': ['implied by', 'logical consequence'], '\\surd': ['surd', 'root of', 'checkmark'], '\\ltimes': [ 'semi direct product', '<a target="_blank" href="https://en.wikipedia.org/wiki/Semidirect_product">Wikipedia <big>›</big></a>', ], '\\rtimes': [ 'semi direct product', '<a target="_blank" href="https://en.wikipedia.org/wiki/Semidirect_product">Wikipedia <big>›</big></a>', ], '\\leftthreetimes': [ 'semi direct product', '<a target="_blank" href="https://en.wikipedia.org/wiki/Semidirect_product">Wikipedia <big>›</big></a>', ], '\\rightthreetimes': [ 'semi direct product', '<a target="_blank" href="https://en.wikipedia.org/wiki/Semidirect_product">Wikipedia <big>›</big></a>', ], '\\divideontimes': ['divide on times'], '\\curlywedge': 'nor', '\\curlyvee': 'nand', '\\simeq': 'is group isomorphic with', '\\vartriangleleft': ['is a normal subgroup of', 'is an ideal ring of'], '\\circ': ['circle', 'ring', 'function composition'], '\\rlap': ['overlap right', '\\rlap{x}o'], '\\llap': ['overlap left', 'o\\llap{/}'], '\\colorbox': ['color box', '\\colorbox{#fbc0bd}{...}'], '\\ast': ['asterisk', 'reflexive closure (as a superscript)'], '\\bullet': 'bullet', '\\lim': 'limit', }; function getNote(symbol) { var _a; let result = (_a = NOTES[symbol]) !== null && _a !== void 0 ? _a : ''; if (isArray(result)) result = result.join('<br>'); return result; } function latexToMarkup$1(latex) { const root = new Atom('root'); root.body = typeset(parseLatex(latex, { parseMode: 'math', macros: getMacros(), registers: getDefaultRegisters(), })); const box = coalesce(adjustInterAtomSpacing(new Box(root.render(new Context({ registers: getDefaultRegisters(), smartFence: false, }, { fontSize: DEFAULT_FONT_SIZE, }, 'displaystyle')), { classes: 'ML__base' }))); return makeStruts(box, { classes: 'ML__mathlive' }).toMarkup(); } function showPopoverWithLatex(mf, latex, displayArrows) { if (!latex || latex.length === 0) { hidePopover(mf); return; } const command = latex; const commandMarkup = latexToMarkup$1(latex); const commandNote = getNote(command); const keybinding = getKeybindingsForCommand(mf.keybindings, command).join('<br>'); let template = displayArrows ? '<div class="ML__popover__prev-shortcut" role="button" aria-label="Previous suggestion"><span><span>▲</span></span></div>' : ''; template += '<span class="ML__popover__content" role="button">'; template += '<div class="ML__popover__command">' + commandMarkup + '</div>'; if (commandNote) template += '<div class="ML__popover__note">' + commandNote + '</div>'; if (keybinding) template += '<div class="ML__popover__shortcut">' + keybinding + '</div>'; template += '</span>'; template += displayArrows ? '<div class="ML__popover__next-shortcut" role="button" aria-label="Next suggestion"><span><span>▼</span></span></div>' : ''; mf.popover = createPopover(mf, template); let element = mf.popover.querySelectorAll('.ML__popover__content'); if (element && element.length > 0) { attachButtonHandlers((command) => mf.executeCommand(command), element[0], { default: ['complete', 'accept-suggestion'], }); } element = mf.popover.querySelectorAll('.ML__popover__prev-shortcut'); if (element && element.length > 0) { attachButtonHandlers((command) => mf.executeCommand(command), element[0], 'previousSuggestion'); } element = mf.popover.querySelectorAll('.ML__popover__next-shortcut'); if (element && element.length > 0) { attachButtonHandlers((command) => mf.executeCommand(command), element[0], 'nextSuggestion'); } setTimeout(() => { const caretPoint = getCaretPoint(mf.field); if (caretPoint) setPopoverPosition(mf, caretPoint); if (mf.popover) { mf.popover.classList.add('is-visible'); mf.popoverVisible = true; } }, 32); } function updatePopoverPosition(mf, options) { var _a; // Check that the mathfield is still valid // (we're calling ourselves from requestAnimationFrame() and the mathfield // could have gotten destroyed if (!mf.element || mf.element.mathfield !== mf) return; if (!mf.popover || !mf.popoverVisible) return; // If the popover pane is visible... if (options === null || options === void 0 ? void 0 : options.deferred) { // Call ourselves again later, typically after the // rendering/layout of the DOM has been completed // (don't do it on next frame, it might be too soon) setTimeout(() => updatePopoverPosition(mf), 100); return; } if (((_a = mf.model.at(mf.model.position)) === null || _a === void 0 ? void 0 : _a.type) !== 'latex') hidePopover(mf); else { // ... get the caret position const caretPoint = getCaretPoint(mf.field); if (caretPoint) setPopoverPosition(mf, caretPoint); } } function setPopoverPosition(mf, position) { throwIfNotInBrowser(); if (!mf.popover || !mf.popoverVisible) return; // Get screen width & height (browser compatibility) const screenHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight; const screenWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth; // Get scrollbar size. This would be 0 in mobile device (also no needed). const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth; const scrollbarHeight = window.innerHeight - document.documentElement.clientHeight; const virtualkeyboardHeight = mf.virtualKeyboard.height; // Prevent screen overflow horizontal. if (position.x + mf.popover.offsetWidth / 2 > screenWidth - scrollbarWidth) { mf.popover.style.left = `${screenWidth - mf.popover.offsetWidth - scrollbarWidth}px`; } else if (position.x - mf.popover.offsetWidth / 2 < 0) mf.popover.style.left = '0'; else mf.popover.style.left = `${position.x - mf.popover.offsetWidth / 2}px`; // And position the popover right below or above the caret if (position.y + mf.popover.offsetHeight + 5 > screenHeight - scrollbarHeight - virtualkeyboardHeight) { mf.popover.classList.add('ML__popover--reverse-direction'); mf.popover.style.top = `${position.y - position.height - mf.popover.offsetHeight - 5}px`; } else { mf.popover.classList.remove('ML__popover--reverse-direction'); mf.popover.style.top = `${position.y + 5}px`; } } function hidePopover(mf) { mf.suggestionIndex = 0; mf.popoverVisible = false; if (mf.popover) { mf.popover.classList.remove('is-visible'); mf.popover.innerHTML = ''; } } function createPopover(mf, html) { if (mf.popover) { mf.popover.innerHTML = mf.options.createHTML(html); return mf.popover; } mf.popover = getSharedElement('mathlive-popover-panel'); if (POPOVER_STYLESHEET_HASH === undefined) POPOVER_STYLESHEET_HASH = hashCode(css_248z$2).toString(36); gPopoverStylesheet = inject(null, css_248z$2, POPOVER_STYLESHEET_HASH); gCoreStylesheet$1 = inject(null, css_248z$4, hashCode(css_248z$4).toString(36)); mf.popover.innerHTML = mf.options.createHTML(html); return mf.popover; } function disposePopover(mf) { releaseSharedElement(mf.popover); if (gPopoverStylesheet) gPopoverStylesheet.release(); if (gCoreStylesheet$1) gCoreStylesheet$1.release(); delete mf.popover; } /* * Return a hash (32-bit integer) representing the content of the mathfield * (but not the selection state) */ function hash(latex) { let result = 0; for (let i = 0; i < latex.length; i++) { result = result * 31 + latex.charCodeAt(i); result = result | 0; // Force it to a 32-bit integer } return Math.abs(result); } function requestUpdate(mathfield, options) { if (!mathfield.dirty) { mathfield.dirty = true; requestAnimationFrame(() => { if (isValidMathfield(mathfield) && mathfield.dirty) { mathfield._atomBoundsCache = new Map(); render(mathfield, options); mathfield._atomBoundsCache = undefined; } }); } } /** * Lay-out the mathfield and generate the DOM. * * This is usually done automatically, but if the font-size, or other geometric * attributes are modified, outside of MathLive, this function may need to be * called explicitly. * */ function render(mathfield, renderOptions) { var _a, _b; throwIfNotInBrowser(); if (!isValidMathfield(mathfield)) return; renderOptions = renderOptions !== null && renderOptions !== void 0 ? renderOptions : {}; mathfield.dirty = false; const { model } = mathfield; // // 1. Stop and reset read aloud state // if (!('mathlive' in window)) window.mathlive = {}; // // 2. Update selection state and blinking cursor (caret) // model.root.caret = ''; model.root.isSelected = false; model.root.containsCaret = true; for (const atom of model.atoms) { atom.caret = ''; atom.isSelected = false; atom.containsCaret = false; } const hasFocus = !mathfield.options.readOnly && document.hasFocus() && mathfield.hasFocus(); if (model.selectionIsCollapsed) model.at(model.position).caret = hasFocus ? mathfield.mode : ''; else { for (const atom of model.getAtoms(model.selection, { includeChildren: true, })) atom.isSelected = true; } if (hasFocus) { let ancestor = model.at(model.position).parent; while (ancestor) { ancestor.containsCaret = true; ancestor = ancestor.parent; } } // // 3. Render boxes // const base = model.root.render(new Context({ registers: mathfield.options.registers, atomIdsSettings: { // Using the hash as a seed for the ID // keeps the IDs the same until the content of the field changes. seed: renderOptions.forHighlighting ? hash(Atom.serialize(model.root, { expandMacro: false, defaultMode: mathfield.options.defaultMode, })) : 'random', // The `groupNumbers` flag indicates that extra boxes should be generated // to represent group of atoms, for example, a box to group // consecutive digits to represent a number. groupNumbers: (_a = renderOptions.forHighlighting) !== null && _a !== void 0 ? _a : false, }, smartFence: mathfield.options.smartFence, renderPlaceholder: mathfield.options.readOnly ? (context, p) => { if (p.placeholderId) { const field = mathfield.getPlaceholderField(p.placeholderId); return p.createMathfieldBox(context, { placeholderId: p.placeholderId, element: field, }); } return p.createBox(context); } : undefined, }, { fontSize: DEFAULT_FONT_SIZE, letterShapeStyle: mathfield.options.letterShapeStyle, }, mathfield.options.defaultMode === 'inline-math' ? 'textstyle' : 'displaystyle')); // // 4. Construct struts around the boxes // const wrapper = makeStruts(adjustInterAtomSpacing(base, mathfield.options.horizontalSpacingScale), { classes: 'ML__mathlive', attributes: { // Sometimes Google Translate kicks in an attempts to 'translate' math // This doesn't work very well, so turn off translate 'translate': 'no', // Hint to screen readers to not attempt to read this <span>. // They should use instead the 'aria-label' attribute. 'aria-hidden': 'true', }, }); // // 5. Generate markup and accessible node // const field = mathfield.field; const isFocused = field.classList.contains('ML__focused'); if (isFocused && !hasFocus) field.classList.remove('ML__focused'); else if (!isFocused && hasFocus) field.classList.add('ML__focused'); field.innerHTML = mathfield.options.createHTML(wrapper.toMarkup()); mathfield.fieldContent = field.querySelector('.ML__mathlive'); mathfield.accessibleNode.innerHTML = mathfield.options.createHTML('<math xmlns="http://www.w3.org/1998/Math/MathML">' + atomsToMathML(model.root, mathfield.options) + '</math>'); // // 6. Render the selection/caret // renderSelection(mathfield); if (mathfield.options.readOnly) mathfield.attachNestedMathfield(); if (!((_b = renderOptions.interactive) !== null && _b !== void 0 ? _b : false)) { // (re-render a bit later because the layout may not be up to date right // now. This happens in particular when first loading and the fonts are // not yet available. ) setTimeout(() => renderSelection(mathfield), 32); } } function renderSelection(mathfield) { throwIfNotInBrowser(); // In some rare cases, we can get called (via a timeout) when the field // is either no longer ready, or not yet ready. Bail. if (!mathfield.field) return; // Remove existing selection for (const element of mathfield.field.querySelectorAll('.ML__selection, .ML__contains-highlight')) element.remove(); if (!mathfield.hasFocus() || !document.hasFocus()) return; const model = mathfield.model; if (model.selectionIsCollapsed) { // // 1.1. Display the popover relative to the location of the caret // setTimeout(() => updatePopoverPosition(mathfield), 32); // // 1.2. Display the 'contains' highlight // let atom = model.at(model.position); while (atom && !(atom.containsCaret && atom.displayContainsHighlight)) atom = atom.parent; if ((atom === null || atom === void 0 ? void 0 : atom.containsCaret) && atom.displayContainsHighlight) { const bounds = adjustForScrolling(mathfield, getAtomBounds(mathfield, atom)); if (bounds) { const element = document.createElement('div'); element.classList.add('ML__contains-highlight'); element.style.position = 'absolute'; element.style.left = `${bounds.left}px`; element.style.top = `${bounds.top}px`; element.style.width = `${Math.ceil(bounds.right - bounds.left)}px`; element.style.height = `${Math.ceil(bounds.bottom - bounds.top - 1)}px`; mathfield.field.insertBefore(element, mathfield.field.childNodes[0]); } } return; } // // 2. Display the non-collapsed selection // for (const x of unionRects(getSelectionBounds(mathfield, { excludeAtomsWithBackground: true }))) { const selectionElement = document.createElement('div'); selectionElement.classList.add('ML__selection'); selectionElement.style.position = 'absolute'; selectionElement.style.left = `${x.left}px`; selectionElement.style.top = `${x.top}px`; selectionElement.style.width = `${Math.ceil(x.right - x.left)}px`; selectionElement.style.height = `${Math.ceil(x.bottom - x.top - 1)}px`; mathfield.field.insertBefore(selectionElement, mathfield.field.childNodes[0]); } } /** * Return the rects that are not entirely contained by other rects. */ function unionRects(rects) { let result = []; // Remove duplicate rects for (const rect of rects) { let found = false; for (const rect2 of result) { if (rect.left === rect2.left && rect.right === rect2.right && rect.top === rect2.top && rect.bottom === rect2.bottom) { found = true; break; } } if (!found) result.push(rect); } rects = result; result = []; for (const rect of rects) { let count = 0; for (const rect2 of rects) { if (rect.left >= rect2.left && rect.right <= rect2.right && rect.top >= rect2.top && rect.bottom <= rect2.bottom) { count += 1; if (count > 1) break; } } if (count === 1) result.push(rect); } return result; } function compareSelection(a, b) { if (a.direction === b.direction) { const l = a.ranges.length; if (b.ranges.length === l) { let i = 0; while (i < l && compareRange(a.ranges[i], b.ranges[i]) === 'equal') i++; return i === l ? 'equal' : 'different'; } } return 'different'; } function compareRange(a, b) { if (a[0] === b[0] && a[1] === b[1]) return 'equal'; return 'different'; } /** * Return the smallest and largest offsets in a selection */ function range(selection) { let first = Infinity; let last = -Infinity; for (const range of selection.ranges) { first = Math.min(first, range[0], range[1]); last = Math.max(last, range[0], range[1]); } return [first, last]; } const CLIPBOARD_LATEX_BEGIN = '$$'; const CLIPBOARD_LATEX_END = '$$'; const defaultExportHook = (_from, latex, _range) => { // Add a wrapper around the LaTeX to be exported, if necessary if (!MODE_SHIFT_COMMANDS.some((x) => latex.startsWith(x[0]) && latex.endsWith(x[1]))) latex = `${CLIPBOARD_LATEX_BEGIN} ${latex} ${CLIPBOARD_LATEX_END}`; return latex; }; class ModeEditor { constructor(name) { ModeEditor._registry[name] = this; } static onPaste(mode, mathfield, ev) { return ModeEditor._registry[mode].onPaste(mathfield, ev); } static onCopy(mathfield, ev) { if (!ev.clipboardData) return; const model = mathfield.model; const exportRange = model.selectionIsCollapsed ? [0, model.lastOffset] : range(model.selection); let atoms = model.getAtoms(exportRange); if (atoms.every((x) => x.mode === 'text' || !x.mode)) { // If the entire selection is in text mode, put the selection as plain // text on the clipboard ev.clipboardData.setData('text/plain', atoms .filter((x) => x instanceof TextAtom) .map((x) => x.value) .join('')); } else if (atoms.every((x) => x.mode === 'latex')) { // If the entire selection is in LaTeX mode, put the selection as plain // text on the clipboard ev.clipboardData.setData('text/plain', model .getAtoms(exportRange, { includeChildren: true }) .map((x) => { var _a; return (_a = x.value) !== null && _a !== void 0 ? _a : ''; }) .join('')); } else { // // 1. Get LaTeX of selection // let latex; if (atoms.length === 1 && atoms[0].verbatimLatex !== undefined) latex = atoms[0].verbatimLatex; else latex = model.getValue(exportRange, 'latex-expanded'); // // 2. Put latex flavor on clipboard // ev.clipboardData.setData('application/x-latex', latex); // // 3. Put text flavor on clipboard // (see defaultExportHook) // try { ev.clipboardData.setData('text/plain', mathfield.options.onExport(mathfield, latex, exportRange)); } catch { } // // 4. Put serialized atoms on clipboard // if (atoms.length === 1 && (atoms[0].type === 'root' || atoms[0].type === 'group')) atoms = atoms[0].body.filter((x) => x.type !== 'first'); try { ev.clipboardData.setData('application/json+mathlive', JSON.stringify(atoms.map((x) => x.toJson()))); } catch { } // // 5. Put other flavors on the clipboard (MathJSON) // try { const ce = mathfield.computeEngine; ce.jsonSerializationOptions = { metadata: ['latex'], }; const expr = ce.parse(latex); const mathJson = JSON.stringify(expr.json); if (mathJson) ev.clipboardData.setData('application/json', mathJson); } catch { } } // Prevent the current document selection from being written to the clipboard. ev.preventDefault(); } static insert(mode, model, text, options = {}) { return ModeEditor._registry[mode].insert(model, text, options); } // eslint-disable-next-line @typescript-eslint/no-empty-function onPaste(_mathfield, _ev) { return false; } insert(_model, _text, _options) { return false; } } ModeEditor._registry = {}; function selectionDidChange(model) { var _a; if (typeof ((_a = model.listeners) === null || _a === void 0 ? void 0 : _a.onSelectionDidChange) === 'function' && !model.suppressChangeNotifications) { model.suppressChangeNotifications = true; model.listeners.onSelectionDidChange(model); model.suppressChangeNotifications = false; } } function contentWillChange(model, options = {}) { var _a; let result = true; if (typeof ((_a = model.listeners) === null || _a === void 0 ? void 0 : _a.onContentWillChange) === 'function' && !model.suppressChangeNotifications) { model.suppressChangeNotifications = true; result = model.listeners.onContentWillChange(model, options); model.suppressChangeNotifications = false; } return result; } function contentDidChange(model, options) { var _a; if (typeof ((_a = model.listeners) === null || _a === void 0 ? void 0 : _a.onContentDidChange) === 'function' && !model.suppressChangeNotifications) { model.suppressChangeNotifications = true; model.listeners.onContentDidChange(model, options); model.suppressChangeNotifications = false; } } function placeholderDidChange(model, placeholderId) { var _a; if (typeof ((_a = model.listeners) === null || _a === void 0 ? void 0 : _a.onPlaceholderDidChange) === 'function' && !model.suppressChangeNotifications) { model.suppressChangeNotifications = true; model.listeners.onPlaceholderDidChange(model, placeholderId); model.suppressChangeNotifications = false; } } /* // type User = { name: string }; // interface UserEvents { // login(user: User): void; // logout(): string; // } type Filter<T, Cond, U extends keyof T = keyof T> = { [K in U]: T[K] extends Cond ? K : never; }[U]; type In<T> = T extends (...args: infer U) => any ? U : []; type Out<T> = T extends () => infer U ? U : never; // Extract an array type of valid event keys type EventKey<T> = Filter<T, Function> & string; // Extract the argument/return types of a valid event type Arguments<T> = T extends (...args: infer U) => any ? U : []; type Result<T> = T extends () => infer U ? U : never; type EventIn<T, K extends EventKey<T>> = In<T[K]>; type EventOut<T, K extends EventKey<T>> = Out<T[K]> | void; export type Listener<T, K extends EventKey<T> = EventKey<T>> = ( ...args: EventIn<T, K> ) => EventOut<T, K>; export type ListenerMap<T> = Partial<{ [K in EventKey<T>]: Listener<T, K> }>; interface Emitter<T> { on<K extends EventKey<T>>(key: K, fn: Listener<T, K>): typeof fn; } // import { EventEmitter } from 'events'; // const ee = (new EventEmitter() as unknown) as Emitter<UserEvents>; // ee.on('login', (user: string) => {}); // ee.on('logout', () => { // return 'done'; // }); */ class LatexModeEditor extends ModeEditor { constructor() { super('latex'); } createAtom(command, _style) { return new LatexAtom(command); } onPaste(mathfield, ev) { if (!ev.clipboardData) return false; let text = ev.clipboardData.getData('text/x-latex'); if (!text) text = ev.clipboardData.getData('text/plain'); if (text && contentWillChange(mathfield.model, { inputType: 'insertFromPaste', data: text, })) { mathfield.snapshot(); if (this.insert(mathfield.model, text)) { contentDidChange(mathfield.model, { inputType: 'insertFromPaste' }); requestUpdate(mathfield); } ev.preventDefault(); ev.stopPropagation(); return true; } return false; } insert(model, text, options) { if (!contentWillChange(model, { data: text, inputType: 'insertText' })) return false; if (!options) options = {}; if (!options.insertionMode) options.insertionMode = 'replaceSelection'; if (!options.selectionMode) options.selectionMode = 'placeholder'; const { suppressChangeNotifications } = model; if (options.suppressChangeNotifications) model.suppressChangeNotifications = true; const savedSuppressChangeNotifications = model.suppressChangeNotifications; model.suppressChangeNotifications = true; // Delete any selected items if (options.insertionMode === 'replaceSelection' && !model.selectionIsCollapsed) model.deleteAtoms(range(model.selection)); else if (options.insertionMode === 'replaceAll') { model.root.setChildren([], 'body'); model.position = 0; } else if (options.insertionMode === 'insertBefore') model.collapseSelection('backward'); else if (options.insertionMode === 'insertAfter') model.collapseSelection('forward'); // Short-circuit the tokenizer and parser when in LaTeX mode const newAtoms = []; for (const c of text) if (COMMAND_MODE_CHARACTERS.test(c)) newAtoms.push(new LatexAtom(c)); // // Insert the new atoms // let cursor = model.at(model.position); // In some cases (after a SelectAll command, for example), the cursor // can be positoned *after* the LatexGroup. In that case, adjust to be // the last atom inside the LatexGroup. if (cursor instanceof LatexGroupAtom) cursor = cursor.lastChild; // If there is no LatexGroup (for example, it was deleted, but we're still // in LaTeX mode), insert one. if (!(cursor.parent instanceof LatexGroupAtom)) { const group = new LatexGroupAtom(''); cursor.parent.addChildAfter(group, cursor); cursor = group.firstChild; } const lastNewAtom = cursor.parent.addChildrenAfter(newAtoms, cursor); // Prepare to dispatch notifications model.suppressChangeNotifications = savedSuppressChangeNotifications; if (options.selectionMode === 'before') ; else if (options.selectionMode === 'item') model.setSelection(model.anchor, model.offsetOf(lastNewAtom)); else if (lastNewAtom) model.position = model.offsetOf(lastNewAtom); contentDidChange(model, { data: text, inputType: 'insertText' }); model.suppressChangeNotifications = suppressChangeNotifications; return true; } } function getLatexGroup(model) { return model.atoms.find((x) => x instanceof LatexGroupAtom); } function getLatexGroupBody(model) { var _a, _b; const atom = model.atoms.find((x) => x instanceof LatexGroupAtom); if (!atom) return []; return ((_b = (_a = atom.body) === null || _a === void 0 ? void 0 : _a.filter((x) => x instanceof LatexAtom)) !== null && _b !== void 0 ? _b : []); } function getCommandSuggestionRange(model, options) { var _a; let start = 0; let found = false; const last = Number.isFinite(options === null || options === void 0 ? void 0 : options.before) ? (_a = options === null || options === void 0 ? void 0 : options.before) !== null && _a !== void 0 ? _a : 0 : model.lastOffset; while (start <= last && !found) { const atom = model.at(start); found = atom instanceof LatexAtom && atom.isSuggestion; if (!found) start++; } if (!found) return [undefined, undefined]; let end = start; let done = false; while (end <= last && !done) { const atom = model.at(end); done = !(atom instanceof LatexAtom && atom.isSuggestion); if (!done) end++; } return [start - 1, end - 1]; } new LatexModeEditor(); function removeSuggestion(mathfield) { const group = getLatexGroupBody(mathfield.model).filter((x) => x.isSuggestion); if (group.length === 0) return; mathfield.model.position = mathfield.model.offsetOf(group[0].leftSibling); for (const atom of group) atom.parent.removeChild(atom); } function updateAutocomplete(mathfield, options) { var _a; const { model } = mathfield; // Remove any error indicator and any suggestions removeSuggestion(mathfield); for (const atom of getLatexGroupBody(model)) atom.isError = false; if (!model.selectionIsCollapsed) { hidePopover(mathfield); return; } // The current command is the sequence of atoms around the insertion point // that ends on the left with a '\' and on the right with a non-command // character. const command = []; let atom = model.at(model.position); while (atom && atom instanceof LatexAtom && /^[a-zA-Z\*]$/.test(atom.value)) atom = atom.leftSibling; if (atom && atom instanceof LatexAtom && atom.value === '\\') { // We've found the start of a command. // Go forward and collect the potential atoms of the command command.push(atom); atom = atom.rightSibling; while (atom && atom instanceof LatexAtom && /^[a-zA-Z\*]$/.test(atom.value)) { command.push(atom); atom = atom.rightSibling; } } const commandString = command.map((x) => x.value).join(''); const suggestions = commandString ? suggest(mathfield, commandString) : []; if (suggestions.length === 0) { // This looks like a command name, but not a known one if (/^\\[a-zA-Z\*]+$/.test(commandString)) { command.forEach((x) => { x.isError = true; }); } hidePopover(mathfield); return; } mathfield.suggestionIndex = (_a = options === null || options === void 0 ? void 0 : options.atIndex) !== null && _a !== void 0 ? _a : 0; if (mathfield.suggestionIndex < 0) mathfield.suggestionIndex = suggestions.length - 1; const suggestion = suggestions[mathfield.suggestionIndex % suggestions.length]; if (suggestion !== commandString) { const lastAtom = command[command.length - 1]; lastAtom.parent.addChildrenAfter([...suggestion.slice(commandString.length - suggestion.length)].map((x) => new LatexAtom(x, { isSuggestion: true })), lastAtom); requestUpdate(mathfield); } showPopoverWithLatex(mathfield, suggestion, suggestions.length > 1); } function acceptCommandSuggestion(model) { const [from, to] = getCommandSuggestionRange(model, { before: model.position, }); if (from === undefined || to === undefined) return false; let result = false; model.getAtoms([from, to]).forEach((x) => { if (x.isSuggestion) { x.isSuggestion = false; result = true; } }); return result; } /** * When in LaTeX mode, insert the LaTeX being edited and leave latex mode * */ function complete(mathfield, completion = 'accept', options) { var _a, _b; hidePopover(mathfield); const latexGroup = getLatexGroup(mathfield.model); if (!latexGroup) return false; if (completion === 'accept-suggestion') { const suggestions = getLatexGroupBody(mathfield.model).filter((x) => x.isSuggestion); if (suggestions.length === 0) return false; for (const suggestion of suggestions) suggestion.isSuggestion = false; mathfield.model.position = mathfield.model.offsetOf(suggestions[suggestions.length - 1]); return true; } const body = getLatexGroupBody(mathfield.model).filter((x) => !x.isSuggestion); const latex = body.map((x) => x.value).join(''); const newPos = latexGroup.leftSibling; latexGroup.parent.removeChild(latexGroup); mathfield.model.position = mathfield.model.offsetOf(newPos); mathfield.mode = (_a = options === null || options === void 0 ? void 0 : options.mode) !== null && _a !== void 0 ? _a : 'math'; if (completion === 'reject') return true; ModeEditor.insert('math', mathfield.model, latex, { macros: mathfield.options.macros, selectionMode: ((_b = options === null || options === void 0 ? void 0 : options.selectItem) !== null && _b !== void 0 ? _b : false) ? 'item' : 'placeholder', format: 'latex', }); mathfield.snapshot(); mathfield.model.announce('replacement'); return true; } // @revisit: move to mathfield.vibrate() const HAPTIC_FEEDBACK_DURATION = 3; // In ms const COMMANDS = {}; /** * Register one or more selectors. * The selector function return true to request a render update of the expression. */ function register(commands, options) { options = options !== null && options !== void 0 ? options : { target: 'mathfield', canUndo: false }; for (const selector of Object.keys(commands)) { console.assert(!COMMANDS[selector], 'Selector already defined: ', selector); COMMANDS[selector] = { ...options, fn: commands[selector] }; } } function getCommandTarget(command) { var _a; let selector; selector = isArray(command) ? command[0] : command; // Convert kebab case (like-this) to camel case (likeThis). selector = selector.replace(/-\w/g, (m) => m[1].toUpperCase()); return (_a = COMMANDS[selector]) === null || _a === void 0 ? void 0 : _a.target; } function perform(mathfield, command) { var _a, _b, _c; if (!command) return false; let selector; let args = []; let handled = false; let dirty = false; if (isArray(command)) { selector = command[0]; args = command.slice(1); } else selector = command; // Convert kebab case (like-this) to camel case (likeThis). selector = selector.replace(/-\w/g, (m) => m[1].toUpperCase()); const commandTarget = (_a = COMMANDS[selector]) === null || _a === void 0 ? void 0 : _a.target; // TODO Refactor this method // Actually using commands by this way increase code complexity, // ideally all code must be moved under command code, maybe it is // a good idea to implement new Command API with additional hooks // and callbacks to make command code more transparent. Now logic of // commands are splitted between command function, registration options // and there. if (commandTarget === 'model') { // If in read-only mode, reject commands that would modify the // content. if (mathfield.options.readOnly && /^(paste|cut|insert|delete|transpose|add)/.test(selector)) { mathfield.model.announce('plonk'); return false; } if (/^(delete|transpose|add)/.test(selector) && selector !== 'deleteBackward') mathfield.resetKeystrokeBuffer(); if (/^(delete|transpose|add)/.test(selector) && mathfield.mode !== 'latex') { // Update the undo state to account for the current selection mathfield.popUndoStack(); mathfield.snapshot(); } if (mathfield.mode === 'latex' && !/^(complete)/.test(selector)) removeSuggestion(mathfield); COMMANDS[selector].fn(mathfield.model, ...args); if (mathfield.mode !== 'latex' && /^(delete|transpose|add)/.test(selector)) mathfield.snapshot(); if (mathfield.mode === 'latex') updateAutocomplete(mathfield); dirty = true; handled = true; } else if (commandTarget === 'virtual-keyboard') { dirty = (_c = (_b = mathfield.virtualKeyboard) === null || _b === void 0 ? void 0 : _b.executeCommand(command)) !== null && _c !== void 0 ? _c : false; handled = true; } else if (COMMANDS[selector]) { if (/^(undo|redo)/.test(selector)) mathfield.resetKeystrokeBuffer(); dirty = COMMANDS[selector].fn(mathfield, ...args); handled = true; } else throw new Error('Unknown command "' + selector + '"'); // Virtual keyboard commands do not update mathfield state if (commandTarget !== 'virtual-keyboard') { // If the command changed the selection so that it is no longer // collapsed, or if it was an editing command, reset the inline // shortcut buffer and the user style if (!mathfield.model.selectionIsCollapsed || /^(transpose|paste|complete|((moveToNextChar|moveToPreviousChar|extend).*))_$/.test(selector)) { mathfield.resetKeystrokeBuffer(); mathfield.style = {}; } } // Render the mathlist if (dirty) requestUpdate(mathfield); return handled; } /** * Perform a command, but: * * focus the mathfield * * provide haptic and audio feedback * This is used by the virtual keyboard when command keys (delete, arrows, etc..) * are pressed. */ function performWithFeedback(mathfield, selector) { var _a, _b, _c; // @revisit: have a registry of commands -> sound mathfield.focus(); if (mathfield.options.keypressVibration && canVibrate()) navigator.vibrate(HAPTIC_FEEDBACK_DURATION); // Convert kebab case to camel case. selector = selector.replace(/-\w/g, (m) => m[1].toUpperCase()); if (selector === 'moveToNextPlaceholder' || selector === 'moveToPreviousPlaceholder' || selector === 'complete') (_a = mathfield.returnKeypressSound) === null || _a === void 0 ? void 0 : _a.play().catch(console.warn); else if (selector === 'deleteBackward' || selector === 'deleteForward' || selector === 'deletePreviousWord' || selector === 'deleteNextWord' || selector === 'deleteToGroupStart' || selector === 'deleteToGroupEnd' || selector === 'deleteToMathFieldStart' || selector === 'deleteToMathFieldEnd') (_b = mathfield.deleteKeypressSound) === null || _b === void 0 ? void 0 : _b.play().catch(console.warn); else (_c = mathfield.keypressSound) === null || _c === void 0 ? void 0 : _c.play().catch(console.warn); const result = mathfield.executeCommand(selector); mathfield.scrollIntoView(); return result; } register({ performWithFeedback: (mathfield, command) => performWithFeedback(mathfield, command), }); function nextSuggestion(mathfield) { // The modulo of the suggestionIndex is used to determine which suggestion // to display, so no need to worry about rolling over. updateAutocomplete(mathfield, { atIndex: mathfield.suggestionIndex + 1 }); return false; } function previousSuggestion(mathfield) { updateAutocomplete(mathfield, { atIndex: mathfield.suggestionIndex - 1 }); return false; } register({ complete, nextSuggestion, previousSuggestion, }, { target: 'mathfield', category: 'autocomplete' }); class Scrim { /** * - If `options.preventOverlayClose` is false, the scrim is closed if the * user clicks on the scrim. That's the behavior for menus, for example. * When you need a fully modal situation until the user has made an * explicit choice (validating cookie usage, for example), set * `preventOverlayClose` to true. * - `onClose()` is called when the scrim is being closed * - */ constructor(options) { var _a, _b; this.preventOverlayClose = (_a = options === null || options === void 0 ? void 0 : options.preventOverlayClose) !== null && _a !== void 0 ? _a : false; this.translucent = (_b = options === null || options === void 0 ? void 0 : options.translucent) !== null && _b !== void 0 ? _b : false; this.state = 'closed'; } get element() { if (this._element) return this._element; const element = document.createElement('div'); element.setAttribute('role', 'presentation'); element.style.position = 'fixed'; element.style.contain = 'content'; element.style.top = '0'; element.style.left = '0'; element.style.right = '0'; element.style.bottom = '0'; element.style.zIndex = 'var(--scrim-zindex, 10099)'; // Bootstrap modals are at 10050 (see #1201) element.style.outline = 'none'; if (this.translucent) { element.style.background = 'rgba(255, 255, 255, .2)'; element.style['backdropFilter'] = 'contrast(40%)'; } else element.style.background = 'transparent'; this._element = element; return element; } open(options) { var _a; if (this.state !== 'closed') return; this.state = 'opening'; // Remember the previously focused element. We'll restore it when we close. this.savedActiveElement = deepActiveElement$1(); const { element } = this; ((_a = options === null || options === void 0 ? void 0 : options.root) !== null && _a !== void 0 ? _a : document.body).appendChild(element); element.addEventListener('click', this); document.addEventListener('touchmove', this, false); document.addEventListener('scroll', this, false); // Prevent (some) scrolling // (touch scrolling will still happen) const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth; this.savedMarginRight = document.body.style.marginRight; this.savedOverflow = document.body.style.overflow; document.body.style.overflow = 'hidden'; const marginRight = Number.parseFloat(getComputedStyle(document.body).marginRight); document.body.style.marginRight = `${marginRight + scrollbarWidth}px`; if (options === null || options === void 0 ? void 0 : options.child) element.append(options.child); this.state = 'open'; } close() { var _a, _b, _c, _d; if (this.state !== 'open') return; this.state = 'closing'; if (typeof this.onClose === 'function') this.onClose(); const { element } = this; element.removeEventListener('click', this); document.removeEventListener('touchmove', this, false); document.removeEventListener('scroll', this, false); element.remove(); // Restore body state document.body.style.overflow = (_a = this.savedOverflow) !== null && _a !== void 0 ? _a : ''; document.body.style.marginRight = (_b = this.savedMarginRight) !== null && _b !== void 0 ? _b : ''; // Restore the previously focused element if (deepActiveElement$1() !== this.savedActiveElement) (_d = (_c = this.savedActiveElement) === null || _c === void 0 ? void 0 : _c.focus) === null || _d === void 0 ? void 0 : _d.call(_c); // Remove all children element.innerHTML = ''; this.state = 'closed'; } handleEvent(ev) { if (!this.preventOverlayClose) { if (ev.target === this._element && ev.type === 'click') { this.close(); ev.preventDefault(); ev.stopPropagation(); } else if (ev.target === document && (ev.type === 'touchmove' || ev.type === 'scroll')) { // This is an attempt at scrolling on a touch-device this.close(); ev.preventDefault(); ev.stopPropagation(); } } } } function deepActiveElement$1() { var _a; let a = document.activeElement; while ((_a = a === null || a === void 0 ? void 0 : a.shadowRoot) === null || _a === void 0 ? void 0 : _a.activeElement) a = a.shadowRoot.activeElement; return a; } let VIRTUAL_KEYBOARD_STYLESHEET_HASH = undefined; function showAlternateKeys(keyboard, altKeysetId) { var _a; throwIfNotInBrowser(); const altKeys = ALT_KEYS[altKeysetId]; const altContainer = document.createElement('div'); altContainer.setAttribute('aria-hidden', 'true'); altContainer.className = 'ML__keyboard alternate-keys' + (keyboard.element.classList.contains('material') ? ' material' : ''); altContainer.id = 'mathlive-alternate-keys-panel'; if (altKeys.length >= 14) { // Width 5: 5 key wide altContainer.style.width = '236px'; } else if (altKeys.length >= 7) { // Width 4 altContainer.style.width = '286px'; } else if (altKeys.length === 4 || altKeys.length === 2) { // Width 2 altContainer.style.width = '146px'; } else if (altKeys.length === 1) { // Width 1 altContainer.style.width = '86px'; } else { // Width 3 altContainer.style.width = '146px'; } // Reset container height altContainer.style.height = 'auto'; let markup = ''; for (const altKey of altKeys) { markup += '<li'; if (typeof altKey === 'string') markup += ' data-latex="' + altKey.replace(/"/g, '"') + '"'; else { if (altKey.latex) markup += ' data-latex="' + altKey.latex.replace(/"/g, '"') + '"'; if (altKey.content) { markup += ' data-content="' + altKey.content.replace(/"/g, '"') + '"'; } if (altKey.insert) { markup += ' data-insert="' + altKey.insert.replace(/"/g, '"') + '"'; } if (altKey.command) { if (typeof altKey.command === 'string') markup += ` data-command="${altKey.command.replace(/"/g, '"')}"`; else { markup += " data-command='" + JSON.stringify(altKey.command).replace(/"/g, '"') + "'"; } } if (altKey.aside) markup += ` data-aside="${altKey.aside.replace(/"/g, '"')}"`; if (altKey.class) markup += ` data-classes="${altKey.class}"`; } markup += '>'; markup += typeof altKey === 'string' ? altKey : (_a = altKey.label) !== null && _a !== void 0 ? _a : ''; markup += '</li>'; } markup = '<ul>' + markup + '</ul>'; altContainer.innerHTML = keyboard.options.createHTML(markup); // // Associate a command which each of the alternate keycaps // makeKeycap(keyboard, [...altContainer.querySelectorAll('li')], 'performAlternateKeys'); // // Create the scrim and attach the alternate key panel to it // if (!Scrim.scrim) Scrim.scrim = new Scrim(); Scrim.scrim.open({ root: keyboard.options.virtualKeyboardContainer, child: altContainer, }); // // Position the alternate panel // const keycapElement = keyboard === null || keyboard === void 0 ? void 0 : keyboard.element.querySelector('div.keyboard-layer.is-visible div.rows ul li[data-alt-keys="' + altKeysetId + '"]'); const position = keycapElement === null || keycapElement === void 0 ? void 0 : keycapElement.getBoundingClientRect(); if (position) { if (position.top - altContainer.clientHeight < 0) { // AltContainer.style.maxWidth = '320px'; // Up to six columns altContainer.style.width = 'auto'; if (altKeys.length <= 6) altContainer.style.height = '56px'; // 1 row else if (altKeys.length <= 12) altContainer.style.height = '108px'; // 2 rows else if (altKeys.length <= 18) altContainer.style.height = '205px'; // 3 rows else altContainer.classList.add('compact'); } const top = (position.top - altContainer.clientHeight + 5).toString() + 'px'; const left = Math.max(0, Math.min(window.innerWidth - altContainer.offsetWidth, (position.left + position.right - altContainer.offsetWidth) / 2)) + 'px'; altContainer.style.transform = 'translate(' + left + ',' + top + ')'; altContainer.classList.add('is-visible'); } return false; } function hideAlternateKeys() { var _a; throwIfNotInBrowser(); const altContainer = document.querySelector('#mathlive-alternate-keys-panel'); if (altContainer) { altContainer.classList.remove('is-visible'); altContainer.innerHTML = ''; } (_a = Scrim.scrim) === null || _a === void 0 ? void 0 : _a.close(); return false; } class VirtualKeyboard { constructor(options, mathfield) { this.originalContainerBottomPadding = null; this.options = options; this.visible = false; this._mathfield = mathfield; this.coreStylesheet = null; this.virtualKeyboardStylesheet = null; } setOptions(options) { var _a, _b; let currentKeyboardName = ''; if (this._element) { const currentKeyboard = this._element.querySelector('div.keyboard-layer.is-visible'); if (currentKeyboard) currentKeyboardName = (_a = currentKeyboard.getAttribute('data-layer')) !== null && _a !== void 0 ? _a : ''; this._element.remove(); this._element = undefined; } this.options = options; if (this.visible) { this.buildAndAttachElement(options.virtualKeyboardTheme); // Restore the active keyboard const newActive = this.element.querySelector(`.keyboard-layer[data-layer="${currentKeyboardName}"]`); if (newActive) { (_b = this.element.querySelector('.keyboard-layer.is-visible')) === null || _b === void 0 ? void 0 : _b.classList.remove('is-visible'); newActive.classList.add('is-visible'); } // Show the keyboard panel this.element.classList.add('is-visible'); } } get element() { return this._element; } set element(val) { var _a; if (this._element === val) return; (_a = this._element) === null || _a === void 0 ? void 0 : _a.remove(); this._element = val; } get visible() { return this._visible; } set visible(val) { this._visible = val; } get height() { var _a, _b; return (_b = (_a = this.element) === null || _a === void 0 ? void 0 : _a.offsetHeight) !== null && _b !== void 0 ? _b : 0; } buildAndAttachElement(theme) { var _a; this.element = makeKeyboardElement(this, theme !== null && theme !== void 0 ? theme : ''); on(this.element, 'touchstart:passive mousedown', () => this.focusMathfield()); (_a = this.options.virtualKeyboardContainer) === null || _a === void 0 ? void 0 : _a.appendChild(this.element); } handleEvent(evt) { if (!this.element) return; switch (evt.type) { case 'mouseup': case 'blur': case 'touchend': case 'touchcancel': // Safari on iOS will aggressively attempt to select when there is a long // press. Restore the userSelect on mouse up document.body.style.userSelect = ''; unshiftKeyboardLayer(this); break; } } focusMathfield() { var _a, _b; (_b = (_a = this._mathfield) === null || _a === void 0 ? void 0 : _a.focus) === null || _b === void 0 ? void 0 : _b.call(_a); } blurMathfield() { var _a, _b; (_b = (_a = this._mathfield) === null || _a === void 0 ? void 0 : _a.blur) === null || _b === void 0 ? void 0 : _b.call(_a); } stateChanged() { var _a, _b; (_b = (_a = this._mathfield) === null || _a === void 0 ? void 0 : _a.element) === null || _b === void 0 ? void 0 : _b.dispatchEvent(new Event('virtual-keyboard-toggle', { bubbles: true, cancelable: false, composed: true, })); } executeCommand(command) { var _a, _b, _c; let selector; let args = []; if (isArray(command)) { selector = command[0]; args = command.slice(1); } else selector = command; // Convert kebab case (like-this) to camel case (likeThis). selector = selector.replace(/-\w/g, (m) => m[1].toUpperCase()); if (((_a = COMMANDS[selector]) === null || _a === void 0 ? void 0 : _a.target) === 'virtual-keyboard') return COMMANDS[selector].fn(this, ...args); return ((_c = (_b = this._mathfield) === null || _b === void 0 ? void 0 : _b.executeCommand(command)) !== null && _c !== void 0 ? _c : false); } create() { if (!VIRTUAL_KEYBOARD_STYLESHEET_HASH) { VIRTUAL_KEYBOARD_STYLESHEET_HASH = hashCode(css_248z$3).toString(36); } this.virtualKeyboardStylesheet = inject(null, css_248z$3, VIRTUAL_KEYBOARD_STYLESHEET_HASH); this.coreStylesheet = inject(null, css_248z$4, hashCode(css_248z$4).toString(36)); void loadFonts(this.options.fontsDirectory); } enable() { // Listen to know when the mouse has been released without being // captured to remove the alternate keys panel and the shifted state of the // keyboard. // Note that we need to listen on the window to capture events happening // outside the virtual keyboard. // @todo should use a scrim instead (to prevent elements underneat the alt // layer from reacting while the alt layer is up) if (isBrowser()) { window.addEventListener('mouseup', this); window.addEventListener('blur', this); window.addEventListener('touchend', this); window.addEventListener('touchcancel', this); } } disable() { if (isBrowser()) { window.removeEventListener('mouseup', this); window.removeEventListener('blur', this); window.removeEventListener('touchend', this); window.removeEventListener('touchcancel', this); } } dispose() { } } const KEYBOARDS = { numeric: { tooltip: 'keyboard.tooltip.numeric', layer: 'math', label: '123', layers: ['math'], }, roman: { tooltip: 'keyboard.tooltip.roman', layer: 'lower-roman', label: 'ABC', layers: ['lower-roman', 'upper-roman'], }, greek: { tooltip: 'keyboard.tooltip.greek', layer: 'lower-greek', label: 'αβγ', classes: 'tex-math', layers: ['lower-greek', 'upper-greek'], }, functions: { tooltip: 'keyboard.tooltip.functions', layer: 'functions', label: '<i>f</i> ()', classes: 'tex', layers: ['functions'], }, symbols: { tooltip: 'keyboard.tooltip.symbols', layer: 'symbols', label: '∞≠∈', classes: 'tex', layers: ['symbols'], }, latex: { tooltip: 'keyboard.tooltip.command', // For the latex keyboard, perform a command rather than // doing a simple layer switch, as we want to enter latex mode // when the keyboard is activated command: ['switchMode', 'latex'], label: `<svg class="svg-glyph"><use xlink:href='#svg-command' /></svg>`, layers: ['latex-lower', 'latex-upper', 'latex-symbols'], }, style: { tooltip: 'keyboard.tooltip.style', layer: 'style', label: '<b>b</b><i>i</i>𝔹', }, }; const SHIFTED_KEYS = { '\\varphi ': ['Φ', '\\Phi '], '\\varsigma ': ['Σ', '\\Sigma '], '\\epsilon ': ['Ɛ', '\\char"0190'], '\\rho ': ['Ρ', '\\char"3A1'], '\\tau ': ['Τ', '\\char"3A4'], '\\upsilon ': ['Υ', '\\Upsilon '], '\\theta ': ['Θ', '\\Theta '], '\\iota ': ['Ι', '\\char"399'], '\\omicron ': ['Ο', '\\char"39F'], '\\pi ': ['Π', '\\Pi '], '\\alpha ': ['Α', '\\char"391'], '\\sigma ': ['Σ', '\\Sigma '], '\\delta ': ['Δ', '\\Delta '], '\\phi ': ['Φ', '\\Phi '], '\\gamma ': ['Γ', '\\Gamma '], '\\eta ': ['Η', '\\char"397'], '\\xi ': ['Ξ', '\\Xi '], '\\kappa ': ['Κ', '\\char"39A'], '\\lambda ': ['Λ', '\\Lambda '], '\\zeta ': ['Ζ', '\\char"396'], '\\chi ': ['Χ', '\\char"3A7'], '\\psi ': ['Ψ', '\\Psi '], '\\omega ': ['Ω', '\\Omega '], '\\beta ': ['Β', '\\char"392'], '\\nu ': ['Ν', '\\char"39D'], '\\mu ': ['Μ', '\\char"39C'], }; const ALT_KEYS_BASE = { '0': [ '\\emptyset', '\\varnothing', '\\infty', { latex: '#?_0', insert: '#@_0' }, '\\circ', '\\bigcirc', '\\bullet', ], '2': ['\\frac{1}{2}', { latex: '#?^2', insert: '#@^2' }], '3': ['\\frac{1}{3}', { latex: '#?^3', insert: '#@^3' }], '.': [ '.', ',', ';', '\\colon', { latex: ':', aside: 'ratio' }, { latex: '\\cdotp', aside: 'center dot', class: 'box' }, { latex: '\\cdots', aside: 'center ellipsis', class: 'box' }, { latex: '\\ldotp', aside: 'low dot', class: 'box' }, { latex: '\\ldots', aside: 'low ellipsis', class: 'box' }, { latex: '\\vdots', aside: '', class: 'box' }, { latex: '\\ddots', aside: '', class: 'box' }, '\\odot', '\\oslash', '\\circledcirc', ], '*': [ '\\cdot', '\\ast', '\\star', '\\bigstar', '\\ltimes', '\\rtimes', '\\rightthreetimes', '\\leftthreetimes', '\\intercal', '\\prod', { latex: '\\prod_{n\\mathop=0}^{\\infty}', class: 'small' }, ], '+': [ '\\pm', '\\mp', '\\sum', { latex: '\\sum_{n\\mathop=0}^{\\infty}', class: 'small' }, '\\dotplus', '\\oplus', ], '-': ['\\pm', '\\mp', '\\ominus', '\\vert #0 \\vert'], '/': ['\\divideontimes', '/', '\\div', '\\%'], '(': [ '\\left( #0\\right)', '\\left[ #0\\right]', '\\left\\{ #0\\right\\}', '\\left\\langle #0\\right\\rangle', '\\lfloor', '\\llcorner', '(', '\\lbrack', '\\lvert', '\\lVert', '\\lgroup', '\\langle', '\\lceil', '\\ulcorner', '\\lmoustache', '\\lbrace', ], ')': [ '\\rfloor', '\\lrcorner', ')', '\\rbrack', '\\rvert', '\\rVert', '\\rgroup', '\\rangle', '\\rceil', '\\urcorner', '\\rmoustache', '\\rbrace', ], '=': [ '\\cong', '\\asymp', '\\equiv', '\\differencedelta', '\\varpropto', '\\thickapprox', '\\approxeq', '\\thicksim', '\\backsim', '\\eqsim', '\\simeq', '\\Bumpeq', '\\bumpeq', '\\doteq', '\\Doteq', '\\fallingdotseq', '\\risingdotseq', '\\coloneq', '\\eqcirc', '\\circeq', '\\triangleq', '\\between', ], '!=': ['\\neq', '\\ncong', '', '\\nsim'], '<': [ '\\leq', '\\leqq', '\\lneqq', '\\ll', '\\lessgtr', '\\nless', '\\nleq', '\\lesssim', '\\precsim', '\\prec', '\\nprec', '\\preccurlyeq', '\\lessdot', ], '>': [ '\\geq', '\\geqq', '\\gneqq', '\\gg', '\\gtrless', '\\ngtr', '\\ngeq', '\\gtrsim', '\\succsim', '\\succ', '\\nsucc', '\\succcurlyeq', '\\gtrdot', ], 'in': ['\\owns'], '!in': ['\\backepsilon'], 'subset': ['\\subseteq', '\\nsubset', '\\nsubseteq'], 'superset': ['\\supseteq', '\\nsupset', '\\nsupseteq'], 'infinity': ['\\aleph_0', '\\aleph_1', '\\omega', '\\mathfrak{m}'], 'numeric-pi': ['\\prod', '\\theta', '\\rho', '\\sin', '\\cos', '\\tan'], 'ee': ['\\times 10^{#?}', '\\ln', '\\ln_{10}', '\\log'], '^': ['_{#?}'], // Integrals 'int': [ { latex: '\\int_{#?}^{#?}', class: 'small' }, { latex: '\\int', class: 'small' }, { latex: '\\smallint', class: 'small' }, { latex: '\\iint', class: 'small' }, { latex: '\\iiint', class: 'small' }, { latex: '\\oint', class: 'small' }, { latex: '\\dfrac{\\rd}{\\rd x}', class: 'small' }, { latex: '\\frac{\\partial}{\\partial x}', class: 'small' }, '\\capitalDifferentialD', '\\rd', '\\partial', ], 'nabla': ['\\nabla\\times', '\\nabla\\cdot', '\\nabla^{2}'], '!': ['!!', '\\Gamma', '\\Pi'], 'accents': [ '\\bar{#@}', '\\vec{#@}', '\\hat{#@}', '\\check{#@}', '\\dot{#@}', '\\ddot{#@}', '\\mathring{#@}', '\\breve{#@}', '\\acute{#@}', '\\tilde{#@}', '\\grave{#@}', ], 'underline': [ '\\underbrace{#@}', '\\underlinesegment{#@}', '\\underleftrightarrow{#@}', '\\underrightarrow{#@}', '\\underleftarrow{#@}', '\\undergroup{#@}', ], 'overline': [ '\\overbrace{#@}', '\\overlinesegment{#@}', '\\overleftrightarrow{#@}', '\\overrightarrow{#@}', '\\overleftarrow{#@}', '\\overgroup{#@}', ], 'xleftarrows': [ '\\xlongequal{}', '\\xleftrightarrow{}', '\\xLeftrightarrow{}', '\\xleftrightharpoons{}', '\\xLeftarrow{}', '\\xleftharpoonup{}', '\\xleftharpoondown{}', '\\xtwoheadleftarrow{}', '\\xhookleftarrow{}', '\\xtofrom{}', '\\xleftequilibrium{}', '\\xrightleftarrows{}', // From mhchem.sty package ], 'xrightarrows': [ '\\xlongequal{}', '\\xleftrightarrow{}', '\\xLeftrightarrow{}', '\\xleftrightharpoons{}', '\\xRightarrow{}', '\\xrightharpoonup{}', '\\xrightharpoondown{}', '\\xtwoheadrightarrow{}', '\\xrightleftharpoons{}', '\\xhookrightarrow{}', '\\xmapsto{}', '\\xrightequilibrium{}', '\\xrightleftarrows{}', // From mhchem.sty package ], // 'absnorm': [{latex:'\\lVert #@ \\rVert', aside:'norm'}, // {latex:'\\lvert #@ \\rvert', aside:'determinant'}, // {latex:'\\begin{cardinality} #@ \\end{cardinality}', aside:'cardinality'}, // {latex:'\\lvert #@ \\rvert', aside:'length'}, // {latex:'\\lvert #@ \\rvert', aside:'order'}, // ], 'A': [ { latex: '\\aleph', aside: 'aleph' }, { latex: '\\forall', aside: 'for all' }, ], 'a': [ { latex: '\\aleph', aside: 'aleph' }, { latex: '\\forall', aside: 'for all' }, ], 'b': [{ latex: '\\beth', aside: 'beth' }], 'B': [{ latex: '\\beth', aside: 'beth' }], 'c': [{ latex: '\\C', aside: 'set of complex numbers' }], 'd': [{ latex: '\\daleth', aside: 'daleth' }], 'D': [{ latex: '\\daleth', aside: 'daleth' }], 'e': [ { latex: '\\exponentialE', aside: 'exponential e' }, { latex: '\\exists', aside: 'there is' }, { latex: '\\nexists', aside: 'there isn’t' }, ], 'g': [{ latex: '\\gimel', aside: 'gimel' }], 'G': [{ latex: '\\gimel', aside: 'gimel' }], 'h': [ { latex: '\\hbar', aside: 'h bar' }, { latex: '\\hslash', aside: 'h slash' }, ], 'i': [{ latex: '\\imaginaryI', aside: 'imaginary i' }], 'j': [{ latex: '\\imaginaryJ', aside: 'imaginary j' }], 'l': [{ latex: '\\ell', aside: 'ell' }], 'n': [{ latex: '\\mathbb{N}', aside: 'set of natural numbers' }], 'p': [{ latex: '\\mathbb{P}', aside: 'set of primes' }], 'q': [{ latex: '\\mathbb{Q}', aside: 'set of rational numbers' }], 'r': [{ latex: '\\mathbb{R}', aside: 'set of real numbers' }], 'z': [{ latex: '\\mathbb{Z}', aside: 'set of integers' }], 'x-var': [ 'y', 'z', 't', 'r', { latex: 'f(#?)', class: 'small' }, { latex: 'g(#?)', class: 'small' }, 'x^2', 'x^n', 'x_n', 'x_{n+1}', 'x_i', 'x_{i+1}', ], 'n-var': ['i', 'j', 'p', 'k', 'a', 'u'], 'ii': ['\\Re', '\\Im', '\\imaginaryJ', '\\Vert #0 \\Vert'], 'logic': [ { latex: '\\exists', aside: 'there is' }, { latex: '\\nexists', aside: 'there isn’t' }, { latex: '\\ni', aside: 'such that' }, { latex: '\\Colon', aside: 'such that' }, { latex: '\\implies', aside: 'implies' }, { latex: '\\impliedby', aside: 'implied by' }, { latex: '\\iff', aside: 'if and only if' }, { latex: '\\land', aside: 'and' }, { latex: '\\lor', aside: 'or' }, { latex: '\\oplus', aside: 'xor' }, { latex: '\\lnot', aside: 'not' }, { latex: '\\downarrow', aside: 'nor' }, { latex: '\\uparrow', aside: 'nand' }, { latex: '\\curlywedge', aside: 'nor' }, { latex: '\\bar\\curlywedge', aside: 'nand' }, // {latex:'\\barwedge', aside:'bar wedge'}, // {latex:'\\curlyvee', aside:'curly vee'}, // {latex:'\\veebar', aside:'vee bar'}, { latex: '\\therefore', aside: 'therefore' }, { latex: '\\because', aside: 'because' }, { latex: '^\\biconditional', aside: 'biconditional' }, '\\leftrightarrow', '\\Leftrightarrow', '\\to', '\\models', '\\vdash', '\\gets', '\\dashv', '\\roundimplies', ], 'set-operators': [ '\\cap', '\\cup', '\\setminus', '\\smallsetminus', '\\complement', ], 'set-relations': [ '\\in', '\\notin', '\\ni', '\\owns', '\\subset', '\\supset', '\\subseteq', '\\supseteq', '\\subsetneq', '\\supsetneq', '\\varsubsetneq', '\\subsetneqq', '\\nsubset', '\\nsupset', '\\nsubseteq', '\\nsupseteq', ], 'space': [ { latex: '\\char"203A\\!\\char"2039', insert: '\\!', aside: 'negative thin space<br>⁻³⧸₁₈ em', }, { latex: '\\unicode{"203A}\\,\\unicode{"2039}', insert: '\\,', aside: 'thin space<br>³⧸₁₈ em', }, { latex: '\\unicode{"203A}\\:\\unicode{"2039}', insert: '\\:', aside: 'medium space<br>⁴⧸₁₈ em', }, { latex: '\\unicode{"203A}\\;\\unicode{"2039}', insert: '\\;', aside: 'thick space<br>⁵⧸₁₈ em', }, { latex: '\\unicode{"203A}\\ \\unicode{"2039}', insert: '\\ ', aside: '⅓ em', }, { latex: '\\unicode{"203A}\\enspace\\unicode{"2039}', insert: '\\enspace', aside: '½ em', }, { latex: '\\unicode{"203A}\\quad\\unicode{"2039}', insert: '\\quad', aside: '1 em', }, { latex: '\\unicode{"203A}\\qquad\\unicode{"2039}', insert: '\\qquad', aside: '2 em', }, ], // @todo could also delete to end 'delete': [ { label: '<span class="warning"><svg class="svg-glyph"><use xlink:href="#svg-trash" /></svg></span>', command: 'deleteAll', }, ], // @todo Tab: could turn on speech, visible keyboard... '->|': [], }; let ALT_KEYS = {}; const LAYERS = { 'math': ` <div class='rows'> <ul> <li class='keycap tex' data-alt-keys='x-var'><i>x</i></li> <li class='keycap tex' data-alt-keys='n-var'><i>n</i></li> <li class='separator w5'></li> <row name='numpad-1'/> <li class='separator w5'></li> <li class='keycap tex' data-latex='\\exponentialE' data-alt-keys='ee'>e</li> <li class='keycap tex' data-latex='\\imaginaryI' data-alt-keys='ii'>i</li> <li class='keycap tex' data-latex='\\pi' data-alt-keys='numeric-pi'></li> </ul> <ul> <li class='keycap tex' data-key='<' data-alt-keys='<'><</li> <li class='keycap tex' data-key='>' data-alt-keys='>'>></li> <li class='separator w5'></li> <row name='numpad-2'/> <li class='separator w5'></li> <li class='keycap tex' data-latex='#@^{2}' data-latex='x^2'></li> <li class='keycap tex' data-alt-keys='^' data-insert='#@^{#?}' data-latex='x^\\placeholder'></li> <li class='keycap tex small' data-insert='\\sqrt{#0}' data-latex='\\sqrt{#0}'></li> </ul> <ul> <li class='keycap tex' data-alt-keys='(' >(</li> <li class='keycap tex' data-alt-keys=')' >)</li> <li class='separator w5'></li> <row name='numpad-3'/> <li class='separator w5'></li> <li class='keycap small' data-alt-keys='int' data-latex='\\int_0^\\infty'></li> <li class='keycap' data-latex='\\forall' data-alt-keys='logic' ></li> <li class='action font-glyph bottom right' data-alt-keys='delete' data-command='["performWithFeedback","deleteBackward"]'><svg class="svg-glyph"><use xlink:href="#svg-delete-backward" /></svg></li></ul> </ul> <ul> <li class='keycap' data-alt-keys='foreground-color' data-command='["applyStyle",{"color":"red"}]'><span style='border-radius: 50%;width:22px;height:22px; border: 3px solid #cc2428; box-sizing: border-box'></span></li> <li class='keycap' data-alt-keys='background-color' data-command='["applyStyle",{"backgroundColor":"yellow"}]'><span style='border-radius: 50%;width:22px;height:22px; background:#fff590; box-sizing: border-box'></span></li> <li class='separator w5'></li> <row name='numpad-4'/> <li class='separator w5'></li> <arrows/> </ul> </div> `, 'lower-roman': ` <div class='rows'> <ul> <row name='numpad-1' class='if-wide'/> <row name='lower-1' shift-layer='upper-roman'/> </ul> <ul> <row name='numpad-2' class='if-wide'/> <row name='lower-2' shift-layer='upper-roman''/> </ul> <ul> <row name='numpad-3' class='if-wide'/> <row name='lower-3' shift-layer='upper-roman''/> </ul> <ul> <row name='numpad-4' class='if-wide'/> <li class='keycap' >;</li> <li class='keycap' >,</li> <li class='keycap w50' data-key=' ' data-alt-keys='space'> </li> <arrows/> </ul> </div>`, 'upper-roman': ` <div class='rows'> <ul> <row name='numpad-1' class='if-wide'/> <row name='upper-1' shift-layer='lower-roman'/> </ul> <ul> <row name='numpad-2' class='if-wide'/> <row name='upper-2' shift-layer='lower-roman'/> </ul> <ul> <row name='numpad-3' class='if-wide'/> <row name='upper-3' shift-layer='lower-roman'/> </ul> <ul> <row name='numpad-4' class='if-wide'/> <li class='keycap' >;</li> <li class='keycap' data-alt-keys='.'>;</li> <li class='keycap w50' data-key=' '> </li> <arrows/> </ul> </div>`, 'symbols': ` <div class='rows'> <ul> <row name='numpad-1' class='if-wide'/> <li class='keycap tex' data-alt-keys='(' data-insert='\\lbrace '>{</li> <li class='keycap tex' data-alt-keys=')' data-insert='\\rbrace '>}</li> <li class='separator w5'></li> <li class='keycap tex' data-alt-keys='in' data-insert='\\in '>∈</li> <li class='keycap tex' data-alt-keys='!in' data-insert='\\notin '>∉</li> <li class='keycap tex' data-insert='\\Re '>ℜ<aside>Real</aside></li> <li class='keycap tex' data-insert='\\Im '>ℑ<aside>Imaginary</aside></li> <li class='keycap w15' data-insert='\\ulcorner#0\\urcorner '><span><sup>┌</sup><span><span style='color:#ddd'>o</span><sup>┐</sup></span><aside>ceil</aside></li> <li class='keycap tex' data-alt-keys='nabla' data-insert='\\nabla '>∇<aside>nabla</aside></li> <li class='keycap tex' data-alt-keys='infinity' data-insert='\\infty '>∞</li> </ul> <ul> <row name='numpad-2' class='if-wide'/> <li class='keycap tex' data-alt-keys='(' data-insert='\\lbrack '>[</li> <li class='keycap tex' data-alt-keys=')' data-insert='\\rbrack '>]</li> <li class='separator w5'></li> <li class='keycap tex' data-alt-keys='subset' data-insert='\\subset '>⊂</li> <li class='keycap tex' data-alt-keys='superset' data-insert='\\supset '>⊃</li> <li class='keycap tex' data-key='!' data-alt-keys='!'>!<aside>factorial</aside></li> <li class='keycap' data-latex='^{\\prime} '><span><sup><span><span style='color:#ddd'>o</span>′</sup></span><aside>prime</aside></li> <li class='keycap w15' data-insert='\\llcorner#0\\lrcorner '><span><sub>└</sub><span style='color:#ddd'>o</span><sub>┘</sub></span><aside>floor</aside></li> <li class='keycap tex' data-insert='\\partial '>∂<aside>partial<br>derivative</aside></li> <li class='keycap tex' data-insert='\\emptyset '>∅<aside>empty set</aside></li> </ul> <ul> <row name='numpad-3' class='if-wide'/> <li class='keycap tex' data-alt-keys='(' data-insert='\\langle '>⟨</li> <li class='keycap tex' data-alt-keys=')' data-insert='\\rangle '>⟩</li> <li class='separator w5'></li> <li class='keycap tex' data-alt-keys='overline' data-latex='\\overline{#@}' data-aside='overline'></li> <li class='keycap tex' data-alt-keys='underline' data-latex='\\underline{#@}' data-aside='underline'></li> <li class='keycap tex' data-alt-keys='accents' data-insert='\\vec{#@}' data-latex='\\vec{#?}' data-aside='vector'></li> <li class='keycap tex small' data-alt-keys='xleftarrows' data-latex='\\xleftarrow{}' ></li> <li class='keycap tex small' data-alt-keys='xrightarrows' data-latex='\\xrightarrow{}' ></li> <li class='keycap tex' data-alt-keys='absnorm' data-insert='\\left| #0 \\right|' data-latex='\\left| #? \\right|' data-aside='abs'></li> <li class='action font-glyph bottom right w15' data-shifted='<span class="warning"><svg class="svg-glyph"><use xlink:href="#svg-trash" /></svg></span>' data-shifted-command='"deleteAll"' data-alt-keys='delete' data-command='["performWithFeedback","deleteBackward"]' ><svg class="svg-glyph"><use xlink:href="#svg-delete-backward" /></svg></li> </ul> <ul> <row name='numpad-4' class='if-wide'/> <li class='keycap tex' data-insert=','>,</li> <li class='keycap tex' data-insert='\\cdot '>⋅<aside>centered dot</aside></li> <li class='keycap tex' data-insert='\\colon '>:<aside>colon</aside></li> <li class='keycap tex' data-insert='\\circ '>∘<aside>circle</aside></li> <li class='keycap tex' data-insert='\\approx '>≈<aside>approx.</aside></li> <li class='keycap tex' data-insert='\\ne '>≠</li> <li class='keycap tex' data-insert='\\pm '>±</li> <arrows/> </ul> </div>`, 'lower-greek': ` <div class='rows'> <ul><li class='keycap tex' data-insert='\\varphi '><i>φ</i><aside>phi var.</aside></li> <li class='keycap tex' data-insert='\\varsigma '><i>ς</i><aside>sigma var.</aside></li> <li class='keycap tex' data-insert='\\epsilon '><i>ϵ</i></li> <li class='keycap tex' data-insert='\\rho '><i>ρ</i></li> <li class='keycap tex' data-insert='\\tau '><i>τ</i></li> <li class='keycap tex' data-insert='\\upsilon '><i>υ</i></li> <li class='keycap tex' data-insert='\\theta '><i>θ</i></li> <li class='keycap tex' data-insert='\\iota '><i>ι</i></li> <li class='keycap tex' data-insert='\\omicron '>ο</i></li> <li class='keycap tex' data-insert='\\pi '><i>π</i></li> </ul> <ul><li class='keycap tex' data-insert='\\alpha ' data-shifted='Α' data-shifted-command='["insert","\\\\char\\"391"]'><i>α</i></li> <li class='keycap tex' data-insert='\\sigma '><i>σ</i></li> <li class='keycap tex' data-insert='\\delta '><i>δ</i></li> <li class='keycap tex' data-insert='\\phi '><i>ϕ</i></i></li> <li class='keycap tex' data-insert='\\gamma '><i>γ</i></li> <li class='keycap tex' data-insert='\\eta '><i>η</i></li> <li class='keycap tex' data-insert='\\xi '><i>ξ</i></li> <li class='keycap tex' data-insert='\\kappa '><i>κ</i></li> <li class='keycap tex' data-insert='\\lambda '><i>λ</i></li> </ul> <ul><li class='shift modifier font-glyph bottom left w15 layer-switch' data-layer='upper-greek'><svg class="svg-glyph"><use xlink:href="#svg-shift" /></svg></li> <li class='keycap tex' data-insert='\\zeta '><i>ζ</i></li> <li class='keycap tex' data-insert='\\chi '><i>χ</i></li> <li class='keycap tex' data-insert='\\psi '><i>ψ</i></li> <li class='keycap tex' data-insert='\\omega '><i>ω</i></li> <li class='keycap tex' data-insert='\\beta '><i>β</i></li> <li class='keycap tex' data-insert='\\nu '><i>ν</i></li> <li class='keycap tex' data-insert='\\mu '><i>μ</i></li> <li class='action font-glyph bottom right w15' data-shifted='<span class="warning"><svg class="svg-glyph"><use xlink:href="#svg-trash" /></svg></span>' data-shifted-command='"deleteAll"' data-alt-keys='delete' data-command='["performWithFeedback","deleteBackward"]' ><svg class="svg-glyph"><use xlink:href="#svg-delete-backward" /></svg></li> </ul> <ul> <li class='keycap ' data-key=' '> </li> <li class='keycap'>,</li> <li class='keycap tex' data-insert='\\varepsilon '><i>ε</i><aside>epsilon var.</aside></li> <li class='keycap tex' data-insert='\\vartheta '><i>ϑ</i><aside>theta var.</aside></li> <li class='keycap tex' data-insert='\\varkappa '><i>ϰ</i><aside>kappa var.</aside></li> <li class='keycap tex' data-insert='\\varpi '><i>ϖ<aside>pi var.</aside></i></li> <li class='keycap tex' data-insert='\\varrho '><i>ϱ</i><aside>rho var.</aside></li> <arrows/> </ul> </div>`, 'upper-greek': ` <div class='rows'> <ul><li class='keycap tex' data-insert='\\Phi '>Φ<aside>phi</aside></li> <li class='keycap tex' data-insert='\\Sigma '>Σ<aside>sigma</aside></li> <li class='keycap tex' data-insert='\\char"0190'>Ɛ<aside>epsilon</aside></li> <li class='keycap tex' data-insert='\\char"3A1'>Ρ<aside>rho</aside></li> <li class='keycap tex' data-insert='\\char"3A4'>Τ<aside>tau</aside></li> <li class='keycap tex' data-insert='\\Upsilon '>Υ<aside>upsilon</aside></li> <li class='keycap tex' data-insert='\\Theta '>Θ<aside>theta</aside></li> <li class='keycap tex' data-insert='\\char"399'>Ι<aside>iota</aside></li> <li class='keycap tex' data-insert='\\char"39F'>Ο<aside>omicron</aside></li> <li class='keycap tex' data-insert='\\Pi '>Π<aside>pi</aside></li></ul> <ul><li class='keycap tex' data-insert='\\char"391'>Α<aside>alpha</aside></li> <li class='keycap tex' data-insert='\\Sigma '>Σ<aside>sigma</aside></li> <li class='keycap tex' data-insert='\\Delta '>Δ<aside>delta</aside></li> <li class='keycap tex' data-insert='\\Phi '>Φ<aside>phi</aside></li> <li class='keycap tex' data-insert='\\Gamma '>Γ<aside>gamma</aside></li> <li class='keycap tex' data-insert='\\char"397'>Η<aside>eta</aside></li> <li class='keycap tex' data-insert='\\Xi '>Ξ<aside>xi</aside></li> <li class='keycap tex' data-insert='\\char"39A'>Κ<aside>kappa</aside></li> <li class='keycap tex' data-insert='\\Lambda '>Λ<aside>lambda</aside></li></ul> <ul><li class='shift modifier font-glyph bottom left selected w15 layer-switch' data-layer='lower-greek'><svg class="svg-glyph"><use xlink:href="#svg-shift" /></svg></li> <li class='keycap tex' data-insert='\\char"396'>Ζ<aside>zeta</aside></li> <li class='keycap tex' data-insert='\\char"3A7'>Χ<aside>chi</aside></li> <li class='keycap tex' data-insert='\\Psi '>Ψ<aside>psi</aside></li> <li class='keycap tex' data-insert='\\Omega '>Ω<aside>omega</aside></li> <li class='keycap tex' data-insert='\\char"392'>Β<aside>beta</aside></li> <li class='keycap tex' data-insert='\\char"39D'>Ν<aside>nu</aside></li> <li class='keycap tex' data-insert='\\char"39C'>Μ<aside>mu</aside></li> <li class='action font-glyph bottom right w15' data-command='["performWithFeedback","deleteBackward"]'><svg class="svg-glyph"><use xlink:href="#svg-delete-backward" /></svg></li></ul> <ul> <li class='separator w10'> </li> <li class='keycap'>.</li> <li class='keycap w50' data-key=' '> </li> <arrows/> </ul> </div>`, 'latex-lower': ` <div class='rows'> <ul><row name='lower-1' class='tt' shift-layer='latex-upper'/></ul> <ul><row name='lower-2' class='tt' shift-layer='latex-upper'/></ul> <ul><row name='lower-3' class='tt' shift-layer='latex-upper'/></ul> <ul> <li class='layer-switch font-glyph modifier bottom left' data-layer='latex-symbols'>01#</li> <li class='keycap tt' data-shifted='[' data-shifted-command='["insertAndUnshiftKeyboardLayer", "["]'>{</li> <li class='keycap tt' data-shifted=']' data-shifted-command='["insertAndUnshiftKeyboardLayer", "]"]'>}</li> <li class='keycap tt' data-shifted='(' data-shifted-command='["insertAndUnshiftKeyboardLayer", "("]'>^</li> <li class='keycap tt' data-shifted=')' data-shifted-command='["insertAndUnshiftKeyboardLayer", ")"]'>_</li> <li class='keycap w20' data-key=' '> </li> <arrows/> </ul> </div>`, 'latex-upper': ` <div class='rows'> <ul><row name='upper-1' class='tt' shift-layer='latex-lower'/></ul> <ul><row name='upper-2' class='tt' shift-layer='latex-lower'/></ul> <ul><row name='upper-3' class='tt' shift-layer='latex-lower'/></ul> <ul> <li class='layer-switch font-glyph modifier bottom left' data-layer='latex-symbols'01#</li> <li class='keycap tt'>[</li> <li class='keycap tt'>]</li> <li class='keycap tt'>(</li> <li class='keycap tt'>)</li> <li class='keycap w20' data-key=' '> </li> <arrows/> </ul> </div>`, 'latex-symbols': ` <div class='rows'> <ul><li class='keycap tt'>1</li><li class='keycap tt'>2</li><li class='keycap tt'>3</li><li class='keycap tt'>4</li><li class='keycap tt'>5</li><li class='keycap tt'>6</li><li class='keycap tt'>7</li><li class='keycap tt'>8</li><li class='keycap tt'>9</li><li class='keycap tt'>0</li></ul> <ul><li class='keycap tt'>!</li><li class='keycap tt'>@</li><li class='keycap tt'>#</li><li class='keycap tt'>$</li><li class='keycap tt'>%</li><li class='keycap tt'>^</li><li class='keycap tt'>&</li><li class='keycap tt'>*</li><li class='keycap tt'>+</li><li class='keycap tt'>=</li></ul> <ul> <li class='keycap tt'>\\</li> <li class='keycap tt'>|</li> <li class='keycap tt'>/</li> <li class='keycap tt'>\`</li> <li class='keycap tt'>;</li> <li class='keycap tt'>:</li> <li class='keycap tt'>?</li> <li class='keycap tt'>'</li> <li class='keycap tt'>"</li> <li class='action font-glyph bottom right' data-shifted='<span class="warning"><svg class="svg-glyph"><use xlink:href="#svg-trash" /></svg></span>' data-shifted-command='"deleteAll"' data-alt-keys='delete' data-command='["performWithFeedback","deleteBackward"]' ><svg class="svg-glyph"><use xlink:href="#svg-delete-backward" /></svg></li> </ul> <ul> <li class='layer-switch font-glyph modifier bottom left' data-layer='latex-lower'>abc</li> <li class='keycap tt'><</li> <li class='keycap tt'>></li> <li class='keycap tt'>~</li> <li class='keycap tt'>,</li> <li class='keycap tt'>.</li> <li class='keycap' data-key=' '> </li> <arrows/> </ul> </div>`, 'functions': ` <div class='rows'> <ul><li class='separator'></li> <li class='fnbutton' data-insert='\\sin'></li> <li class='fnbutton' data-insert='\\sin^{-1}'></li> <li class='fnbutton' data-insert='\\ln'></li> <li class='fnbutton' data-insert='\\exponentialE^{#?}'></li> <li class='bigfnbutton' data-insert='\\operatorname{lcm}(#?)' data-latex='\\operatorname{lcm}()'></li> <li class='bigfnbutton' data-insert='\\operatorname{ceil}(#?)' data-latex='\\operatorname{ceil}()'></li> <li class='bigfnbutton' data-insert='\\lim_{n\\to\\infty}'></li> <li class='bigfnbutton' data-insert='\\int'></li> <li class='bigfnbutton' data-insert='\\operatorname{abs}(#?)' data-latex='\\operatorname{abs}()'></li> </ul> <ul><li class='separator'></li> <li class='fnbutton' data-latex='\\cos'></li> <li class='fnbutton' data-latex='\\cos^{-1}'></li> <li class='fnbutton' data-latex='\\ln_{10}'></li> <li class='fnbutton' data-latex='10^{#?}'></li> <li class='bigfnbutton' data-latex='\\operatorname{gcd}(#?)' data-latex='\\operatorname{gcd}()'></li> <li class='bigfnbutton' data-latex='\\operatorname{floor}(#?)' data-latex='\\operatorname{floor}()'></li> <li class='bigfnbutton' data-latex='\\sum_{n\\mathop=0}^{\\infty}'></li> <li class='bigfnbutton' data-latex='\\int_{0}^{\\infty}'></li> <li class='bigfnbutton' data-latex='\\operatorname{sign}(#?)' data-latex='\\operatorname{sign}()'></li> </ul> <ul><li class='separator'></li> <li class='fnbutton' data-latex='\\tan'></li> <li class='fnbutton' data-latex='\\tan^{-1}'></li> <li class='fnbutton' data-latex='\\log_{#?}'></li> <li class='fnbutton' data-latex='\\sqrt[#?]{#0}'></li> <li class='bigfnbutton' data-insert='#0 \\mod' data-latex='\\mod'></li> <li class='bigfnbutton' data-insert='\\operatorname{round}(#?) ' data-latex='\\operatorname{round}()'></li> <li class='bigfnbutton' data-insert='\\prod_{n\\mathop=0}^{\\infty}' data-latex='{\\scriptstyle \\prod_{n=0}^{\\infty}}'></li> <li class='bigfnbutton' data-insert='\\frac{\\differentialD #0}{\\differentialD x}'></li> <li class='action font-glyph bottom right' data-command='["performWithFeedback","deleteBackward"]'><svg class="svg-glyph"><use xlink:href="#svg-delete-backward" /></svg></li></ul> <ul><li class='separator'></li> <li class='fnbutton'>(</li> <li class='fnbutton'>)</li> <li class='fnbutton' data-insert='^{#?}' data-latex='x^{#?}'></li> <li class='fnbutton' data-insert='_{#?}' data-latex='x_{#?}'></li> <li class='keycap w20 ' data-key=' '> </li> <arrows/> </ul> </div>`, 'style': ` <div class='rows'> <ul> <li class='keycap' data-alt-keys='foreground-color' data-command='["applyStyle",{"color":"red"}]'><span style='border-radius: 50%;width:22px;height:22px; border: 3px solid #cc2428'></span></li> <li class='keycap' data-alt-keys='background-color' data-command='["applyStyle",{"backgroundColor":"yellow"}]'><span style='border-radius: 50%;width:22px;height:22px; background:#fff590'></span></li> <li class='separator w5'></li> <li class='keycap' data-command='["applyStyle",{"size":"3"}]' data-latex='\\scriptsize\\text{small}'></li> <li class='keycap' data-command='["applyStyle",{"size":"5"}]' data-latex='\\scriptsize\\text{normal}'></li> <li class='keycap' data-command='["applyStyle",{"size":"9"}]' data-latex='\\huge\\text{big}'></li> <li class='separator w5'></li> <li class='keycap' data-latex='\\langle' data-command='["insert", "\\\\langle", {"smartFence":true}]'></li> </ul> <ul> <li class='keycap' data-command='["applyStyle",{"series":"l"}]' data-latex='\\fontseries{l}\\text{Ab}'></li> <li class='keycap' data-command='["applyStyle",{"series":"m"}]' data-latex='\\fontseries{m}\\text{Ab}'></li> <li class='keycap' data-command='["applyStyle",{"series":"b"}]' data-latex='\\fontseries{b}\\text{Ab}'></li> <li class='keycap' data-command='["applyStyle",{"series":"bx"}]' data-latex='\\fontseries{bx}\\text{Ab}'></li> <li class='keycap' data-command='["applyStyle",{"series":"sb"}]' data-latex='\\fontseries{sb}\\text{Ab}'></li> <li class='keycap' data-command='["applyStyle",{"series":"c"}]' data-latex='\\fontseries{c}\\text{Ab}'></li> </ul> <ul> <li class='keycap' data-command='["applyStyle",{"shape":"up"}]' data-latex='\\textup{Ab}'></li> <li class='keycap' data-command='["applyStyle",{"shape":"it"}]' data-latex='\\textit{Ab}'></li> <li class='keycap' data-command='["applyStyle",{"shape":"sl"}]' data-latex='\\textsl{Ab}'></li> <li class='keycap' data-command='["applyStyle",{"shape":"sc"}]' data-latex='\\textsc{Ab}'></li> <li class='separator w5'></li> <li class='keycap' data-insert='\\emph{#@} ' data-latex='\\text{\\emph{emph}}'></li> </ul> <ul> <li class='keycap' data-command='["applyStyle",{"fontFamily":"cmr"}]' data-latex='\\textrm{Az}'></li> <li class='keycap' data-command='["applyStyle",{"fontFamily":"cmtt"}]' data-latex='\\texttt{Az}'></li> <li class='keycap' data-command='["applyStyle",{"fontFamily":"cmss"}]' data-latex='\\textsf{Az}'></li> <li class='keycap' data-command='["applyStyle",{"fontFamily":"bb"}]' data-latex='\\mathbb{AZ}'></li> <li class='keycap' data-command='["applyStyle",{"fontFamily":"scr"}]' data-latex='\\mathscr{AZ}'></li> <li class='keycap' data-command='["applyStyle",{"fontFamily":"cal"}]' data-latex='\\mathcal{A1}'></li> <li class='keycap' data-command='["applyStyle",{"fontFamily":"frak"}]' data-latex='\\mathfrak{Az}'></li> </ul> </div>`, }; function latexToMarkup(latex, arg) { // Since we don't have preceding atoms, we'll interpret #@ as a placeholder latex = latex.replace(/(^|[^\\])#@/g, '$1#?'); const root = new Atom('root'); root.body = typeset(parseLatex(latex, { parseMode: 'math', args: arg, macros: getMacros(), registers: getDefaultRegisters(), })); const box = coalesce(adjustInterAtomSpacing(new Box(root.render(new Context({ registers: getDefaultRegisters(), smartFence: false, }, { fontSize: DEFAULT_FONT_SIZE, }, 'displaystyle')), { classes: 'ML__base' }))); return makeStruts(box, { classes: 'ML__mathlive' }).toMarkup(); } /** * Return a markup string for the keyboard toolbar for the specified layer. */ function makeKeyboardToolbar(options, keyboardIDs, currentKeyboard) { var _a, _b, _c; // The left hand side of the toolbar has a list of all the available keyboards let result = "<div class='left'>"; const keyboardList = keyboardIDs.replace(/\s+/g, ' ').split(' '); if (keyboardList.length > 1) { const keyboards = { ...KEYBOARDS, ...((_a = options.customVirtualKeyboards) !== null && _a !== void 0 ? _a : {}), }; for (const keyboard of keyboardList) { if (!keyboards[keyboard]) { console.error('Unknown virtual keyboard "', keyboard, '"'); break; } result += "<div class='"; if (keyboard === currentKeyboard) result += 'selected '; else if (keyboards[keyboard].command) result += 'action '; else result += 'layer-switch '; result += ((_b = keyboards[keyboard].classes) !== null && _b !== void 0 ? _b : '') + "'"; if (keyboards[keyboard].tooltip) { result += "data-tooltip='" + ((_c = localize(keyboards[keyboard].tooltip)) !== null && _c !== void 0 ? _c : keyboards[keyboard].tooltip) + "' "; } if (keyboard !== currentKeyboard) { if (typeof keyboards[keyboard].command === 'string') result += `data-command='"${keyboards[keyboard].command}"'`; else if (Array.isArray(keyboards[keyboard].command)) { result += `data-command='"${keyboards[keyboard].command.join('')}"'`; } if (keyboards[keyboard].layer) result += "data-layer='" + keyboards[keyboard].layer + "'"; } result += '>' + keyboards[keyboard].label + '</div>'; } } result += '</div>'; const toolbarOptions = options.virtualKeyboardToolbar; const availableActions = toolbarOptions === 'default' ? ['copyToClipboard', 'undo', 'redo'] : []; const actionsMarkup = { copyToClipboard: ` <div class='action' data-command='"copyToClipboard"' data-tooltip='${localize('tooltip.copy to clipboard')}'> <svg><use xlink:href='#svg-copy' /></svg> </div> `, undo: ` <div class='action disabled' data-command='"undo"' data-tooltip='${localize('tooltip.undo')}'> <svg><use xlink:href='#svg-undo' /></svg> </div> `, redo: ` <div class='action disabled' data-command='"redo"' data-tooltip='${localize('tooltip.redo')}'> <svg><use xlink:href='#svg-redo' /></svg> </div> `, }; // The right hand side of the toolbar, with the copy/undo/redo commands if (availableActions.length > 0) { result += ` <div class='right'> ${availableActions .map((action) => actionsMarkup[action]) .join('')} </div> `; } return "<div class='keyboard-toolbar' role='toolbar'>" + result + '</div>'; } function makeKeycap(keyboard, elementList, chainedCommand) { var _a, _b; for (const element of elementList) { let html = undefined; // Display if (element.getAttribute('data-latex')) { html = latexToMarkup(element.getAttribute('data-latex').replace(/"/g, '"'), () => '\\placeholder{}'); } else if (element.getAttribute('data-insert') && element.innerHTML === '') { html = latexToMarkup(element.getAttribute('data-insert').replace(/"/g, '"'), () => '\\placeholder{}'); } else if (element.getAttribute('data-content')) html = element.getAttribute('data-content').replace(/"/g, '"'); if (element.getAttribute('data-aside')) { html = (html !== null && html !== void 0 ? html : '') + '<aside>' + element.getAttribute('data-aside').replace(/"/g, '"') + '</aside>'; } if (html !== undefined) element.innerHTML = keyboard.options.createHTML(html); if (element.getAttribute('data-classes')) element.classList.add(element.getAttribute('data-classes')); const key = (_a = element.getAttribute('data-insert')) === null || _a === void 0 ? void 0 : _a.replace(/"/g, '"'); if (key && SHIFTED_KEYS[key]) { element.dataset.shifted = SHIFTED_KEYS[key][0]; element.dataset.shiftedCommand = JSON.stringify([ 'insertAndUnshiftKeyboardLayer', SHIFTED_KEYS[key][1], ]); } // Commands let selector = undefined; const command = element.getAttribute('data-command'); if (command) { if (/^[a-zA-Z]+$/.test(command)) selector = command; else { try { selector = JSON.parse(command); } catch (e) { } } } else if (element.getAttribute('data-insert')) { selector = [ 'insert', element.getAttribute('data-insert'), { focus: true, feedback: true, scrollIntoView: true, mode: 'math', format: 'latex', resetStyle: true, }, ]; } else if (element.getAttribute('data-latex')) { selector = [ 'insert', element.getAttribute('data-latex'), { focus: true, feedback: true, scrollIntoView: true, mode: 'math', format: 'latex', resetStyle: true, }, ]; } else { selector = [ 'typedText', (_b = element.getAttribute('data-key')) !== null && _b !== void 0 ? _b : element.textContent, { focus: true, feedback: true, simulateKeystroke: true }, ]; } if (selector) { if (chainedCommand) selector = [chainedCommand, selector]; let handlers = selector; const altKeysetId = element.getAttribute('data-alt-keys'); if (altKeysetId) { const altKeys = ALT_KEYS[altKeysetId]; if (altKeys) { handlers = { default: selector, pressAndHoldStart: ['showAlternateKeys', altKeysetId], pressAndHoldEnd: 'hideAlternateKeys', }; // } else { // console.warn(`Unknown alt key set: "${altKeysetId}"`); } } attachButtonHandlers((command) => keyboard.executeCommand(command), element, handlers); } } } /** * Expand the shortcut tags (e.g. <row>) inside a layer. */ function expandLayerMarkup(options, layer) { var _a, _b, _c, _d; const ROWS = { // First row should be 10 key wide // Second row should be 10 key wide // Third row should be 8.5 key wide // One row should have ^ (shift key) which is 1.5 key wide // One row should have ~ (delete key) which is .5 or 1.5 key wide qwerty: { 'lower-1': 'qwertyuiop', 'lower-2': ' asdfghjkl ', 'lower-3': '^zxcvbnm~', 'upper-1': 'QWERTYUIOP', 'upper-2': ' ASDFGHJKL ', 'upper-3': '^ZXCVBNM~', 'numpad-1': '789/', 'numpad-2': '456*', 'numpad-3': '123-', 'numpad-4': '0.=+', }, azerty: { 'lower-1': 'azertyuiop', 'lower-2': 'qsdfghjklm', 'lower-3': '^ wxcvbn ~', 'upper-1': 'AZERTYUIOP', 'upper-2': 'QSDFGHJKLM', 'upper-3': '^ WXCVBN ~', }, qwertz: { 'lower-1': 'qwertzuiop', 'lower-2': ' asdfghjkl ', 'lower-3': '^yxcvbnm~', 'upper-1': 'QWERTZUIOP', 'upper-2': ' ASDFGHJKL', 'upper-3': '^YXCVBNM~', }, dvorak: { 'lower-1': '^ pyfgcrl ', 'lower-2': 'aoeuidhtns', 'lower-3': 'qjkxbmwvz~', 'upper-1': '^ PYFGCRL ', 'upper-2': 'AOEUIDHTNS', 'upper-3': 'QJKXBMWVZ~', }, colemak: { 'lower-1': ' qwfpgjluy ', 'lower-2': 'arstdhneio', 'lower-3': '^zxcvbkm~', 'upper-1': ' QWFPGNLUY ', 'upper-2': 'ARSTDHNEIO', 'upper-3': '^ZXCVBKM~', }, }; // Determine the layout of the virtual keyboard based on a // detected physical keyboard layout, or the current locale let layoutName = options.virtualKeyboardLayout; if (layoutName === 'auto') { const activeLayout = getActiveKeyboardLayout(); if (activeLayout) layoutName = activeLayout.virtualLayout; if (!layoutName || layoutName === 'auto') { layoutName = (_a = { fr: 'azerty', be: 'azerty', al: 'qwertz', ba: 'qwertz', cz: 'qwertz', de: 'qwertz', hu: 'qwertz', sk: 'qwertz', ch: 'qwertz', }[l10n.locale.slice(0, 2)]) !== null && _a !== void 0 ? _a : 'qwerty'; } } const layout = (_b = ROWS[layoutName]) !== null && _b !== void 0 ? _b : ROWS.qwerty; let result = layer; let row; result = result.replace(/<arrows\/>/g, ` <li class='action' data-command='["performWithFeedback","moveToPreviousChar"]' data-shifted='<svg class="svg-glyph"><use xlink:href="#svg-angle-double-left" /></svg>' data-shifted-command='["performWithFeedback","extendToPreviousChar"]'> <svg class="svg-glyph"><use xlink:href='#svg-arrow-left' /></svg> </li> <li class='action' data-command='["performWithFeedback","moveToNextChar"]' data-shifted='<svg class="svg-glyph"><use xlink:href="#svg-angle-double-right" /></svg>' data-shifted-command='["performWithFeedback","extendToNextChar"]'> <svg class="svg-glyph"><use xlink:href='#svg-arrow-right' /></svg> </li> <li class='action' data-command='["performWithFeedback","commit"]'> <svg class="svg-glyph"><use xlink:href='#svg-commit' /></svg></li>`); let m = result.match(/(<row\s+)(.*)((?:<\/row|\/)>)/); while (m) { row = ''; const attributesArray = m[2].match(/[a-zA-Z][a-zA-Z\d-]*=(['"])(.*?)\1/g); const attributes = {}; if (attributesArray) { for (const attribute of attributesArray) { const m2 = attribute.match(/([a-zA-Z][a-zA-Z\d-]*)=(['"])(.*?)\2/); if (m2) attributes[m2[1]] = m2[3]; } } let keys = layout[attributes.name]; if (!keys) keys = ROWS.qwerty[attributes.name]; if (!keys) console.warn('Unknown roman keyboard row:', attributes.name); else { for (const c of keys) { let cls = (_c = attributes.class) !== null && _c !== void 0 ? _c : ''; if (cls) cls = ` ${cls}`; if (c === '~') { row += `<li class='action font-glyph bottom right `; row += keys.length - (keys.match(/ /g) || []).length / 2 === 10 ? 'w10' : 'w15'; row += `' data-shifted='<span class="warning"><svg class="svg-glyph"><use xlink:href="#svg-trash" /></svg></span>' data-shifted-command='"deleteAll"' data-alt-keys='delete' data-command='["performWithFeedback","deleteBackward"]' ><svg class="svg-glyph"><use xlink:href="#svg-delete-backward" /></svg></li>`; } else if (c === ' ') { // Separator row += "<li class='separator w5'></li>"; } else if (c === '^') { // Shift key row += `<li class='shift modifier font-glyph bottom left w15 layer-switch' data-layer='` + attributes['shift-layer'] + `'><svg class="svg-glyph"><use xlink:href="#svg-shift" /></svg></li>`; } else if (c === '/') { row += "<li class='keycap" + cls + "' data-alt-keys='/' data-insert='\\frac{#@}{#?}'>÷</li>"; } else if (c === '*') { row += "<li class='keycap" + cls + "' data-alt-keys='*' data-insert='\\times '>×</li>"; } else if (c === '-') { row += "<li class='keycap" + cls + "' data-alt-keys='-' data-key='-'>−</li>"; } else if (c === '.') { row += "<li class='keycap" + cls + "' data-alt-keys='.' data-command='\"insertDecimalSeparator\"'>" + ((_d = options['decimalSeparator']) !== null && _d !== void 0 ? _d : '.') + '</li>'; } else if (cls.includes('tt')) { row += `<li class='keycap${cls}' data-alt-keys='${c}' ` + `data-command='["typedText","${c}",{"mode":"command", "focus":true, "feedback":true}]'` + `>${c}</li>`; } else { row += "<li class='keycap" + cls + "' data-alt-keys='" + c + "'>" + c + '</li>'; } } } result = result.replace(new RegExp(m[1] + m[2] + m[3]), row); m = result.match(/(<row\s+)(.*)((?:<\/row|\/)>)/); } return result; } /** * Construct a virtual keyboard element based on the config options in the * mathfield and an optional theme. */ function makeKeyboardElement(keyboard, theme) { var _a, _b, _c, _d; throwIfNotInBrowser(); const svgIcons = `<svg xmlns="http://www.w3.org/2000/svg" style="display: none;"> <symbol id="svg-delete-backward" viewBox="0 0 576 512"> <path d="M432.1 208.1L385.9 256L432.1 303C442.3 312.4 442.3 327.6 432.1 336.1C423.6 346.3 408.4 346.3 399 336.1L352 289.9L304.1 336.1C295.6 346.3 280.4 346.3 271 336.1C261.7 327.6 261.7 312.4 271 303L318.1 256L271 208.1C261.7 199.6 261.7 184.4 271 175C280.4 165.7 295.6 165.7 304.1 175L352 222.1L399 175C408.4 165.7 423.6 165.7 432.1 175C442.3 184.4 442.3 199.6 432.1 208.1V208.1zM512 64C547.3 64 576 92.65 576 128V384C576 419.3 547.3 448 512 448H205.3C188.3 448 172 441.3 160 429.3L9.372 278.6C3.371 272.6 0 264.5 0 256C0 247.5 3.372 239.4 9.372 233.4L160 82.75C172 70.74 188.3 64 205.3 64L512 64zM528 128C528 119.2 520.8 112 512 112H205.3C201 112 196.9 113.7 193.9 116.7L54.63 256L193.9 395.3C196.9 398.3 201 400 205.3 400H512C520.8 400 528 392.8 528 384V128z"/> </symbol> <symbol id="svg-shift" viewBox="0 0 384 512"> <path d="M2.438 252.3C7.391 264.2 19.06 272 32 272h80v160c0 26.51 21.49 48 48 48h64C250.5 480 272 458.5 272 432v-160H352c12.94 0 24.61-7.797 29.56-19.75c4.953-11.97 2.219-25.72-6.938-34.88l-160-176C208.4 35.13 200.2 32 192 32S175.6 35.13 169.4 41.38l-160 176C.2188 226.5-2.516 240.3 2.438 252.3zM192 86.63L313.4 224H224v208H160V224H70.63L192 86.63z"/> </symbol> <symbol id="svg-commit" viewBox="0 0 512 512"> <path d="M135 432.1l-128-128C2.344 300.3 0 294.2 0 288s2.344-12.28 7.031-16.97l128-128c9.375-9.375 24.56-9.375 33.94 0s9.375 24.56 0 33.94L81.94 264H464v-208C464 42.75 474.8 32 488 32S512 42.75 512 56V288c0 13.25-10.75 24-24 24H81.94l87.03 87.03c9.375 9.375 9.375 24.56 0 33.94S144.4 442.3 135 432.1z"/> </symbol> <symbol id="svg-command" viewBox="0 0 640 512"> <path d="M34.495 36.465l211.051 211.05c4.686 4.686 4.686 12.284 0 16.971L34.495 475.535c-4.686 4.686-12.284 4.686-16.97 0l-7.071-7.07c-4.686-4.686-4.686-12.284 0-16.971L205.947 256 10.454 60.506c-4.686-4.686-4.686-12.284 0-16.971l7.071-7.07c4.686-4.687 12.284-4.687 16.97 0zM640 468v-10c0-6.627-5.373-12-12-12H300c-6.627 0-12 5.373-12 12v10c0 6.627 5.373 12 12 12h328c6.627 0 12-5.373 12-12z"/> </symbol> <symbol id="svg-undo" viewBox="0 0 512 512"> <path d="M20 8h10c6.627 0 12 5.373 12 12v110.625C85.196 57.047 165.239 7.715 256.793 8.001 393.18 8.428 504.213 120.009 504 256.396 503.786 393.181 392.834 504 256 504c-63.926 0-122.202-24.187-166.178-63.908-5.113-4.618-5.354-12.561-.482-17.433l7.069-7.069c4.503-4.503 11.749-4.714 16.482-.454C150.782 449.238 200.935 470 256 470c117.744 0 214-95.331 214-214 0-117.744-95.331-214-214-214-82.862 0-154.737 47.077-190.289 116H180c6.627 0 12 5.373 12 12v10c0 6.627-5.373 12-12 12H20c-6.627 0-12-5.373-12-12V20c0-6.627 5.373-12 12-12z"/> </symbol> <symbol id="svg-redo" viewBox="0 0 512 512"> <path d="M492 8h-10c-6.627 0-12 5.373-12 12v110.625C426.804 57.047 346.761 7.715 255.207 8.001 118.82 8.428 7.787 120.009 8 256.396 8.214 393.181 119.166 504 256 504c63.926 0 122.202-24.187 166.178-63.908 5.113-4.618 5.354-12.561.482-17.433l-7.069-7.069c-4.503-4.503-11.749-4.714-16.482-.454C361.218 449.238 311.065 470 256 470c-117.744 0-214-95.331-214-214 0-117.744 95.331-214 214-214 82.862 0 154.737 47.077 190.289 116H332c-6.627 0-12 5.373-12 12v10c0 6.627 5.373 12 12 12h160c6.627 0 12-5.373 12-12V20c0-6.627-5.373-12-12-12z"/> </symbol> <symbol id="svg-arrow-left" viewBox="0 0 320 512"> <path d="M206.7 464.6l-183.1-191.1C18.22 267.1 16 261.1 16 256s2.219-11.97 6.688-16.59l183.1-191.1c9.152-9.594 24.34-9.906 33.9-.7187c9.625 9.125 9.938 24.37 .7187 33.91L73.24 256l168 175.4c9.219 9.5 8.906 24.78-.7187 33.91C231 474.5 215.8 474.2 206.7 464.6z"/> </symbol> <symbol id="svg-arrow-right" viewBox="0 0 320 512"> <path d="M113.3 47.41l183.1 191.1c4.469 4.625 6.688 10.62 6.688 16.59s-2.219 11.97-6.688 16.59l-183.1 191.1c-9.152 9.594-24.34 9.906-33.9 .7187c-9.625-9.125-9.938-24.38-.7187-33.91l168-175.4L78.71 80.6c-9.219-9.5-8.906-24.78 .7187-33.91C88.99 37.5 104.2 37.82 113.3 47.41z"/> </symbol> <symbol id="svg-tab" viewBox="0 0 448 512"> <path d="M32 217.1c0-8.8 7.2-16 16-16h144v-93.9c0-7.1 8.6-10.7 13.6-5.7l143.5 143.1c6.3 6.3 6.3 16.4 0 22.7L205.6 410.4c-5 5-13.6 1.5-13.6-5.7v-93.9H48c-8.8 0-16-7.2-16-16v-77.7m-32 0v77.7c0 26.5 21.5 48 48 48h112v61.9c0 35.5 43 53.5 68.2 28.3l143.6-143c18.8-18.8 18.8-49.2 0-68L228.2 78.9c-25.1-25.1-68.2-7.3-68.2 28.3v61.9H48c-26.5 0-48 21.6-48 48zM436 64h-8c-6.6 0-12 5.4-12 12v360c0 6.6 5.4 12 12 12h8c6.6 0 12-5.4 12-12V76c0-6.6-5.4-12-12-12z"/> </symbol> <symbol id="svg-copy" viewBox="0 0 448 512"> <path d="M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM352 32.491a15.88 15.88 0 0 1 7.431 4.195l51.882 51.883A15.885 15.885 0 0 1 415.508 96H352V32.491zM288 464c0 8.822-7.178 16-16 16H48c-8.822 0-16-7.178-16-16V144c0-8.822 7.178-16 16-16h80v240c0 26.51 21.49 48 48 48h112v48zm128-96c0 8.822-7.178 16-16 16H176c-8.822 0-16-7.178-16-16V48c0-8.822 7.178-16 16-16h144v72c0 13.2 10.8 24 24 24h72v240z"/> </symbol> <symbol id="svg-angle-double-right" viewBox="0 0 320 512"> <path d="M166.9 264.5l-117.8 116c-4.7 4.7-12.3 4.7-17 0l-7.1-7.1c-4.7-4.7-4.7-12.3 0-17L127.3 256 25.1 155.6c-4.7-4.7-4.7-12.3 0-17l7.1-7.1c4.7-4.7 12.3-4.7 17 0l117.8 116c4.6 4.7 4.6 12.3-.1 17zm128-17l-117.8-116c-4.7-4.7-12.3-4.7-17 0l-7.1 7.1c-4.7 4.7-4.7 12.3 0 17L255.3 256 153.1 356.4c-4.7 4.7-4.7 12.3 0 17l7.1 7.1c4.7 4.7 12.3 4.7 17 0l117.8-116c4.6-4.7 4.6-12.3-.1-17z"/> </symbol> <symbol id="svg-angle-double-left" viewBox="0 0 320 512"> <path d="M153.1 247.5l117.8-116c4.7-4.7 12.3-4.7 17 0l7.1 7.1c4.7 4.7 4.7 12.3 0 17L192.7 256l102.2 100.4c4.7 4.7 4.7 12.3 0 17l-7.1 7.1c-4.7 4.7-12.3 4.7-17 0L153 264.5c-4.6-4.7-4.6-12.3.1-17zm-128 17l117.8 116c4.7 4.7 12.3 4.7 17 0l7.1-7.1c4.7-4.7 4.7-12.3 0-17L64.7 256l102.2-100.4c4.7-4.7 4.7-12.3 0-17l-7.1-7.1c-4.7-4.7-12.3-4.7-17 0L25 247.5c-4.6 4.7-4.6 12.3.1 17z"/> </symbol> <symbol id="svg-trash" viewBox="0 0 448 512"> <path d="M336 64l-33.6-44.8C293.3 7.1 279.1 0 264 0h-80c-15.1 0-29.3 7.1-38.4 19.2L112 64H24C10.7 64 0 74.7 0 88v2c0 3.3 2.7 6 6 6h26v368c0 26.5 21.5 48 48 48h288c26.5 0 48-21.5 48-48V96h26c3.3 0 6-2.7 6-6v-2c0-13.3-10.7-24-24-24h-88zM184 32h80c5 0 9.8 2.4 12.8 6.4L296 64H152l19.2-25.6c3-4 7.8-6.4 12.8-6.4zm200 432c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V96h320v368zm-176-44V156c0-6.6 5.4-12 12-12h8c6.6 0 12 5.4 12 12v264c0 6.6-5.4 12-12 12h-8c-6.6 0-12-5.4-12-12zm-80 0V156c0-6.6 5.4-12 12-12h8c6.6 0 12 5.4 12 12v264c0 6.6-5.4 12-12 12h-8c-6.6 0-12-5.4-12-12zm160 0V156c0-6.6 5.4-12 12-12h8c6.6 0 12 5.4 12 12v264c0 6.6-5.4 12-12 12h-8c-6.6 0-12-5.4-12-12z"/> </symbol> </svg> `; // <symbol id="svg-wikipedia" viewBox="0 0 640 512"> // <path d="M640 51.2l-.3 12.2c-28.1.8-45 15.8-55.8 40.3-25 57.8-103.3 240-155.3 358.6H415l-81.9-193.1c-32.5 63.6-68.3 130-99.2 193.1-.3.3-15 0-15-.3C172 352.3 122.8 243.4 75.8 133.4 64.4 106.7 26.4 63.4.2 63.7c0-3.1-.3-10-.3-14.2h161.9v13.9c-19.2 1.1-52.8 13.3-43.3 34.2 21.9 49.7 103.6 240.3 125.6 288.6 15-29.7 57.8-109.2 75.3-142.8-13.9-28.3-58.6-133.9-72.8-160-9.7-17.8-36.1-19.4-55.8-19.7V49.8l142.5.3v13.1c-19.4.6-38.1 7.8-29.4 26.1 18.9 40 30.6 68.1 48.1 104.7 5.6-10.8 34.7-69.4 48.1-100.8 8.9-20.6-3.9-28.6-38.6-29.4.3-3.6 0-10.3.3-13.6 44.4-.3 111.1-.3 123.1-.6v13.6c-22.5.8-45.8 12.8-58.1 31.7l-59.2 122.8c6.4 16.1 63.3 142.8 69.2 156.7L559.2 91.8c-8.6-23.1-36.4-28.1-47.2-28.3V49.6l127.8 1.1.2.5z"/> // </symbol> // <symbol id="svg-link" viewBox="0 0 512 512"> // <path d="M301.148 394.702l-79.2 79.19c-50.778 50.799-133.037 50.824-183.84 0-50.799-50.778-50.824-133.037 0-183.84l79.19-79.2a132.833 132.833 0 0 1 3.532-3.403c7.55-7.005 19.795-2.004 20.208 8.286.193 4.807.598 9.607 1.216 14.384.481 3.717-.746 7.447-3.397 10.096-16.48 16.469-75.142 75.128-75.3 75.286-36.738 36.759-36.731 96.188 0 132.94 36.759 36.738 96.188 36.731 132.94 0l79.2-79.2.36-.36c36.301-36.672 36.14-96.07-.37-132.58-8.214-8.214-17.577-14.58-27.585-19.109-4.566-2.066-7.426-6.667-7.134-11.67a62.197 62.197 0 0 1 2.826-15.259c2.103-6.601 9.531-9.961 15.919-7.28 15.073 6.324 29.187 15.62 41.435 27.868 50.688 50.689 50.679 133.17 0 183.851zm-90.296-93.554c12.248 12.248 26.362 21.544 41.435 27.868 6.388 2.68 13.816-.68 15.919-7.28a62.197 62.197 0 0 0 2.826-15.259c.292-5.003-2.569-9.604-7.134-11.67-10.008-4.528-19.371-10.894-27.585-19.109-36.51-36.51-36.671-95.908-.37-132.58l.36-.36 79.2-79.2c36.752-36.731 96.181-36.738 132.94 0 36.731 36.752 36.738 96.181 0 132.94-.157.157-58.819 58.817-75.3 75.286-2.651 2.65-3.878 6.379-3.397 10.096a163.156 163.156 0 0 1 1.216 14.384c.413 10.291 12.659 15.291 20.208 8.286a131.324 131.324 0 0 0 3.532-3.403l79.19-79.2c50.824-50.803 50.799-133.062 0-183.84-50.802-50.824-133.062-50.799-183.84 0l-79.2 79.19c-50.679 50.682-50.688 133.163 0 183.851z"/> // </symbol> // <symbol id="svg-external-link" viewBox="0 0 448 512"> // <path d="M400 32H48C21.49 32 0 53.49 0 80v352c0 26.51 21.49 48 48 48h352c26.51 0 48-21.49 48-48V80c0-26.51-21.49-48-48-48zm16 400c0 8.822-7.178 16-16 16H48c-8.822 0-16-7.178-16-16V80c0-8.822 7.178-16 16-16h352c8.822 0 16 7.178 16 16v352zM99.515 374.828c-4.686-4.686-4.686-12.284 0-16.971l195.15-195.15-.707-.707-89.958.342c-6.627 0-12-5.373-12-12v-9.999c0-6.628 5.372-12 12-12L340 128c6.627 0 12 5.372 12 12l-.343 136c0 6.627-5.373 12-12 12h-9.999c-6.627 0-12-5.373-12-12l.342-89.958-.707-.707-195.15 195.15c-4.686 4.686-12.284 4.686-16.971 0l-5.657-5.657z"/> // </symbol> // <symbol id="svg-external-link" viewBox="0 0 512 512"> // <path d="M256 40c118.621 0 216 96.075 216 216 0 119.291-96.61 216-216 216-119.244 0-216-96.562-216-216 0-119.203 96.602-216 216-216m0-32C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm-36 344h12V232h-12c-6.627 0-12-5.373-12-12v-8c0-6.627 5.373-12 12-12h48c6.627 0 12 5.373 12 12v140h12c6.627 0 12 5.373 12 12v8c0 6.627-5.373 12-12 12h-72c-6.627 0-12-5.373-12-12v-8c0-6.627 5.373-12 12-12zm36-240c-17.673 0-32 14.327-32 32s14.327 32 32 32 32-14.327 32-32-14.327-32-32-32z"/> // </symbol> let markup = svgIcons; keyboard.create(); // Auto-populate the ALT_KEYS table ALT_KEYS_BASE['foreground-color'] = []; for (const color of Object.keys(FOREGROUND_COLORS)) { ALT_KEYS_BASE['foreground-color'].push({ class: 'small-button', content: '<span style="border-radius:50%;width:32px;height:32px; box-sizing: border-box; border: 3px solid ' + FOREGROUND_COLORS[color] + '"></span>', command: ['applyStyle', { color }], }); } ALT_KEYS_BASE['background-color'] = []; for (const color of Object.keys(BACKGROUND_COLORS)) { ALT_KEYS_BASE['background-color'].push({ class: 'small-button', content: '<span style="border-radius:50%;width:32px;height:32px; background:' + BACKGROUND_COLORS[color] + '"></span>', command: ['applyStyle', { backgroundColor: color }], }); } ALT_KEYS = { ...ALT_KEYS_BASE }; for (const key of Object.keys(ALT_KEYS)) ALT_KEYS[key] = ALT_KEYS[key].slice(); const UPPER_ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; const LOWER_ALPHA = 'abcdefghijklmnopqrstuvwxyz'; const DIGITS = '0123456789'; // Define the alternate set for uppercase keys for (let i = 0; i < 26; i++) { const key = UPPER_ALPHA[i]; if (!ALT_KEYS[key]) ALT_KEYS[key] = []; ALT_KEYS[key].unshift({ latex: '\\mathbb{' + key + '}', aside: 'blackboard', insert: '\\mathbb{' + key + '}', }); ALT_KEYS[key].unshift({ latex: '\\mathbf{' + key + '}', aside: 'bold', insert: '\\mathbf{' + key + '}', }); ALT_KEYS[key].unshift({ latex: '\\mathsf{' + key + '}', aside: 'sans', insert: '\\mathsf{' + key + '}', }); ALT_KEYS[key].unshift({ latex: '\\mathtt{' + key + '}', aside: 'monospace', insert: '\\mathtt{' + key + '}', }); ALT_KEYS[key].unshift({ latex: '\\mathcal{' + key + '}', aside: 'calligraphy', insert: '\\mathcal{' + key + '}', }); ALT_KEYS[key].unshift({ latex: '\\mathfrak{' + key + '}', aside: 'fraktur', insert: '\\mathfrak{' + key + '}', }); } // Define the alternate set for lowercase keys for (let i = 0; i <= 26; i++) { const key = LOWER_ALPHA[i]; if (!ALT_KEYS[key]) ALT_KEYS[key] = []; ALT_KEYS[key].unshift({ latex: '\\mathsf{' + key + '}', aside: 'sans', insert: '\\mathsf{' + key + '}', }); ALT_KEYS[key].unshift({ latex: '\\mathbf{' + key + '}', aside: 'bold', insert: '\\mathbf{' + key + '}', }); ALT_KEYS[key].unshift({ latex: '\\mathtt{' + key + '}', aside: 'monospace', insert: '\\mathtt{' + key + '}', }); ALT_KEYS[key].unshift({ latex: '\\mathfrak{' + key + '}', aside: 'fraktur', insert: '\\mathfrak{' + key + '}', }); } for (let i = 0; i < 10; i++) { const key = DIGITS[i]; if (!ALT_KEYS[key]) ALT_KEYS[key] = []; // The mathbb font does not appear to include digits, // although it's supposed to. // ALT_KEYS[key].push({ // latex: '\\underset{\\textsf{\\footnotesize blackboard}}{\\mathbb{' + key + '}}', // insert: '\\mathbb{' + key + '}}'}); ALT_KEYS[key].unshift({ latex: '\\mathbf{' + key + '}', aside: 'bold', insert: '\\mathbf{' + key + '}', }); ALT_KEYS[key].unshift({ latex: '\\mathsf{' + key + '}', aside: 'sans', insert: '\\mathsf{' + key + '}', }); ALT_KEYS[key].unshift({ latex: '\\mathtt{' + key + '}', aside: 'monospace', insert: '\\mathtt{' + key + '}', }); ALT_KEYS[key].unshift({ latex: '\\mathcal{' + key + '}', aside: 'script', insert: '\\mathcal{' + key + '}', }); ALT_KEYS[key].unshift({ latex: '\\mathfrak{' + key + '}', aside: 'fraktur', insert: '\\mathfrak{' + key + '}', }); } let keyboardIDs = keyboard.options.virtualKeyboards; if (!keyboardIDs) keyboardIDs = 'all'; keyboardIDs = keyboardIDs.replace(/\ball\b/i, 'numeric functions symbols roman greek'); const layers = { ...LAYERS, ...((_a = keyboard.options.customVirtualKeyboardLayers) !== null && _a !== void 0 ? _a : {}), }; const keyboards = { ...KEYBOARDS, ...((_b = keyboard.options.customVirtualKeyboards) !== null && _b !== void 0 ? _b : {}), }; const keyboardList = keyboardIDs.replace(/\s+/g, ' ').split(' '); for (const keyboardName of keyboardList) { if (!keyboards[keyboardName]) { console.error('Unknown virtual keyboard "' + keyboardName + '"'); continue; } // Add the default layer to the list of layers, // and make sure the list of layers is uniquified. let keyboardLayers = (_c = keyboards[keyboardName].layers) !== null && _c !== void 0 ? _c : []; if (keyboards[keyboardName].layer) keyboardLayers.push(keyboards[keyboardName].layer); keyboardLayers = [...new Set(keyboardLayers)]; for (const layerName of keyboardLayers) { if (!layers[layerName]) { console.error('Unknown virtual keyboard layer: "', layerName, '"'); break; } if (typeof layers[layerName] === 'object') { const layer = layers[layerName]; // Process JSON layer to web element based layer. let layerMarkup = ''; if (typeof layer.styles === 'string') layerMarkup += `<style>${layer.styles}</style>`; else if (typeof layer.styles === 'object') layerMarkup += `<style>${jsonToCss(layer.styles)}</style>`; if (layer.backdrop) layerMarkup += `<div class='${layer.backdrop}'>`; if (layer.container) layerMarkup += `<div class='${layer.container}'>`; if (layer.rows) { layerMarkup += `<div class='rows'>`; for (const row of layer.rows) { layerMarkup += `<ul>`; for (const keycap of row) { layerMarkup += `<li`; if (keycap.class) { let cls = keycap.class; if (keycap.layer && !/layer-switch/.test(cls)) cls += ' layer-switch'; if (!/separator/.test(cls)) cls += ' keycap'; layerMarkup += ` class="${cls}"`; } else layerMarkup += ` class="keycap"`; if (keycap.key) layerMarkup += ` data-key="${keycap.key}"`; if (keycap.command) { if (typeof keycap.command === 'string') layerMarkup += ` data-command='"${keycap.command}"'`; else { layerMarkup += ` data-command='`; layerMarkup += JSON.stringify(keycap.command); layerMarkup += `'`; } } if (keycap.insert) layerMarkup += ` data-insert="${keycap.insert}"`; if (keycap.latex) layerMarkup += ` data-latex="${keycap.latex}"`; if (keycap.aside) layerMarkup += ` data-aside="${keycap.aside}"`; if (keycap.variants) { const keysetId = Date.now().toString(36).slice(-2) + Math.floor(Math.random() * 0x186a0).toString(36); ALT_KEYS[keysetId] = keycap.variants; layerMarkup += ` data-alt-keys="${keysetId}"`; } if (keycap.shifted) layerMarkup += ` data-shifted="${keycap.shifted}"`; if (keycap.shiftedCommand) layerMarkup += ` data-shifted-command="${keycap.shiftedCommand}"`; if (keycap.layer) layerMarkup += ` data-layer="${keycap.layer}"`; layerMarkup += `>${keycap.label ? keycap.label : ''}</li>`; } layerMarkup += `</ul>`; } layerMarkup += `</div>`; } if (layer.container) layerMarkup += '</div>'; if (layer.backdrop) layerMarkup += '</div>'; layers[layerName] = layerMarkup; } markup += `<div tabindex="-1" class='keyboard-layer' data-layer='${layerName}'>`; markup += makeKeyboardToolbar(keyboard.options, keyboardIDs, keyboardName); const layerMarkup = layers[layerName]; // A layer can contain 'shortcuts' (i.e. <row> tags) that need to // be expanded if (typeof layerMarkup === 'string') markup += expandLayerMarkup(keyboard.options, layerMarkup); markup += '</div>'; } } const result = document.createElement('div'); result.className = 'ML__keyboard'; if (theme) result.classList.add(theme); else if (keyboard.options.virtualKeyboardTheme) result.classList.add(keyboard.options.virtualKeyboardTheme); // We have a separate 'plate' element to support positioning the keyboard // inside custom `virtualKeyboardContainer` const plate = document.createElement('div'); plate.className = 'ML__keyboard--plate'; plate.innerHTML = keyboard.options.createHTML(markup); result.appendChild(plate); // Attach the element handlers const keycaps = result.querySelectorAll('.keycap, .action, .fnbutton, .bigfnbutton'); for (const keycap of keycaps) { keycap.id = 'ML__k' + Date.now().toString(36).slice(-2) + Math.floor(Math.random() * 0x186a0).toString(36); } makeKeycap(keyboard, [...keycaps]); const elementList = result.querySelectorAll('.layer-switch'); for (const element of elementList) { if (element.classList.contains('shift')) { // This is a potential press-and-hold layer switch attachButtonHandlers((command) => keyboard.executeCommand(command), element, { // When the modifier is initially pressed, we will shift the labels // (if available) pressed: 'shiftKeyboardLayer', // If the key is released before a delay, we switch to the target layer default: ['switchKeyboardLayer', element.getAttribute('data-layer')], // If the key is released after a longer delay, we restore the // shifted labels pressAndHoldEnd: 'unshiftKeyboardLayer', }); } else { // This is a simple layer switch attachButtonHandlers((command) => keyboard.executeCommand(command), element, { default: ['switchKeyboardLayer', element.getAttribute('data-layer')], }); } } // Select the first keyboard as the initial one. const layerElements = result.querySelectorAll('.keyboard-layer'); for (const x of layerElements) { x.addEventListener('mousedown', (evt) => { evt.preventDefault(); evt.stopPropagation(); }); x.addEventListener('touchstart', (evt) => { evt.preventDefault(); evt.stopPropagation(); }, { passive: false }); } console.assert(layerElements.length > 0, 'No virtual keyboards available'); (_d = layerElements[0]) === null || _d === void 0 ? void 0 : _d.classList.add('is-visible'); return result; } /* * Restore the key labels and commands to the state before a modifier key * was pressed. * */ function unshiftKeyboardLayer(keyboard) { hideAlternateKeys(); const keycaps = keyboard.element.querySelectorAll('div.keyboard-layer.is-visible .rows .keycap, div.keyboard-layer.is-visible .rows .action'); if (keycaps) { for (const keycap of keycaps) { const content = keycap.getAttribute('data-unshifted-content'); if (content) { keycap.innerHTML = keyboard.options.createHTML(content); keycap.dataset.unshiftedContent = ''; } const command = keycap.getAttribute('data-unshifted-command'); if (command) { keycap.dataset.command = command; keycap.dataset.unshiftedCommand = ''; } } } return false; } function onUndoStateChanged(keyboard, canUndoState, canRedoState) { var _a; const toolbar = (_a = keyboard.element) === null || _a === void 0 ? void 0 : _a.querySelector('.keyboard-toolbar'); if (!toolbar) return false; const undoButton = toolbar.querySelector('[data-command=\'"undo"\']'); const redoButton = toolbar.querySelector('[data-command=\'"redo"\']'); if (redoButton) { if (canRedoState) redoButton.classList.remove('disabled'); else redoButton.classList.add('disabled'); } if (undoButton) { if (canUndoState) undoButton.classList.remove('disabled'); else undoButton.classList.add('disabled'); } return false; } function jsonToCssProps(json) { if (typeof json === 'string') return json; return Object.entries(json) .map(([k, v]) => `${k}:${v} !important`) .join(';'); } function jsonToCss(json) { return Object.keys(json) .map((k) => { return `${k} {${jsonToCssProps(json[k])}}`; }) .join(''); } /* * Alternate options are displayed when a key on the virtual keyboard is pressed * and held. * */ register({ showAlternateKeys, }, { target: 'virtual-keyboard' }); function switchKeyboardLayer(keyboard, layer) { if (layer !== 'lower-command' && layer !== 'upper-command' && layer !== 'symbols-command') { // If we switch to a non-command keyboard layer, first exit command mode. keyboard.executeCommand('complete'); } showVirtualKeyboard(keyboard); // If the alternate keys panel was visible, hide it hideAlternateKeys(); // If we were in a temporarily shifted state (shift-key held down) // restore our state before switching to a new layer. unshiftKeyboardLayer(keyboard); const layers = keyboard === null || keyboard === void 0 ? void 0 : keyboard.element.querySelectorAll('.keyboard-layer'); // Search for the requested layer let found = false; for (const layer_ of layers) { if (layer_.dataset.layer === layer) { found = true; break; } } // We did find the layer, switch to it. // If we didn't find it, do nothing and keep the current layer if (found) { for (const layer_ of layers) { if (layer_.dataset.layer === layer) layer_.classList.add('is-visible'); else layer_.classList.remove('is-visible'); } } keyboard.focusMathfield(); return true; } function shiftKeyboardLayer(keyboard) { const keycaps = keyboard === null || keyboard === void 0 ? void 0 : keyboard.element.querySelectorAll('div.keyboard-layer.is-visible .rows .keycap, div.keyboard-layer.is-visible .rows .action'); if (keycaps) { for (const keycap of keycaps) { // If there's already an unshiftedContent attribute, we're already in // shifted mode. Don't do it twice. if (keycap.dataset.unshiftedContent) return false; let shiftedContent = keycap.getAttribute('data-shifted'); if (shiftedContent || /^[a-z]$/.test(keycap.innerHTML)) { keycap.dataset.unshiftedContent = keycap.innerHTML; if (!shiftedContent) shiftedContent = keycap.innerHTML.toUpperCase(); keycap.innerHTML = keyboard.options.createHTML(shiftedContent); const command = keycap.getAttribute('data-command'); if (command) { keycap.dataset.unshiftedCommand = command; const shiftedCommand = keycap.getAttribute('data-shifted-command'); if (shiftedCommand) keycap.dataset.command = shiftedCommand; else { const commandObject = JSON.parse(command); if (isArray(commandObject)) commandObject[1] = commandObject[1].toUpperCase(); keycap.dataset.command = JSON.stringify(commandObject); } } } } } return false; } /* * Temporarily change the labels and the command of the keys * (for example when a modifier key is held down.) */ register({ shiftKeyboardLayer, }, { target: 'virtual-keyboard' }); function performAlternateKeys(keyboard, command) { hideAlternateKeys(); return keyboard.executeCommand(command); } function insertAndUnshiftKeyboardLayer(keyboard, c) { keyboard.executeCommand(['insert', c]); unshiftKeyboardLayer(keyboard); return true; } register({ hideAlternateKeys: () => hideAlternateKeys(), /* * The command invoked when an alternate key is pressed. * We need to hide the Alternate Keys panel, then perform the * command. */ performAlternateKeys, switchKeyboardLayer: (keyboard, layer) => switchKeyboardLayer(keyboard, layer), unshiftKeyboardLayer: (keyboard) => unshiftKeyboardLayer(keyboard), insertAndUnshiftKeyboardLayer, }, { target: 'virtual-keyboard' }); function toggleVirtualKeyboardAlt(keyboard) { let hadAltTheme = false; if (keyboard === null || keyboard === void 0 ? void 0 : keyboard.element) { hadAltTheme = keyboard === null || keyboard === void 0 ? void 0 : keyboard.element.classList.contains('material'); keyboard.disable(); } showVirtualKeyboard(keyboard, hadAltTheme ? '' : 'material'); return false; } function toggleVirtualKeyboardShift(keyboard) { var _a, _b; keyboard.options.virtualKeyboardLayout = { qwerty: 'azerty', azerty: 'qwertz', qwertz: 'dvorak', dvorak: 'colemak', colemak: 'qwerty', }[keyboard.options.virtualKeyboardLayout]; const layer = (_b = (_a = keyboard === null || keyboard === void 0 ? void 0 : keyboard.element.querySelector('div.keyboard-layer.is-visible')) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : ''; if (keyboard) keyboard.disable(); showVirtualKeyboard(keyboard); if (layer) switchKeyboardLayer(keyboard, layer); return false; } register({ /* Toggle the virtual keyboard, but switch to the alternate theme if available */ toggleVirtualKeyboardAlt, /** Toggle the virtual keyboard, but switch another keyboard layout */ toggleVirtualKeyboardShift, }, { target: 'virtual-keyboard' }); function showVirtualKeyboard(keyboard, theme = '') { var _a, _b; const container = keyboard.options.virtualKeyboardContainer; if (!container) return false; if (keyboard.element) keyboard.element.classList.add('is-visible'); else keyboard.buildAndAttachElement(theme); if (!keyboard.visible) { if ((_a = window.mathlive) === null || _a === void 0 ? void 0 : _a.visibleVirtualKeyboard) hideVirtualKeyboard((_b = window.mathlive) === null || _b === void 0 ? void 0 : _b.visibleVirtualKeyboard); if (!window.mathlive) window.mathlive = {}; window.mathlive.visibleVirtualKeyboard = keyboard; const padding = container.style.paddingBottom; keyboard.originalContainerBottomPadding = padding; container.style.paddingBottom = padding ? `calc(${padding} + var(--keyboard-height, 276px) - 1px)` : 'calc(var(--keyboard-height, 276px) - 1px)'; } // For the transition effect to work, the property has to be changed // after the insertion in the DOM. Use setTimeout setTimeout(() => { var _a; (_a = keyboard.element) === null || _a === void 0 ? void 0 : _a.classList.add('is-visible'); keyboard.focusMathfield(); }, 1); keyboard.visible = true; keyboard.stateChanged(); return false; } function hideVirtualKeyboard(keyboard) { var _a, _b, _c; const container = keyboard.options.virtualKeyboardContainer; if (!container) return false; if (keyboard.element) { if (!window.mathlive) window.mathlive = {}; window.mathlive.visibleVirtualKeyboard = undefined; // Remove the element from the DOM keyboard.disable(); hideAlternateKeys(); keyboard.visible = false; (_a = keyboard.coreStylesheet) === null || _a === void 0 ? void 0 : _a.release(); keyboard.coreStylesheet = null; (_b = keyboard.virtualKeyboardStylesheet) === null || _b === void 0 ? void 0 : _b.release(); keyboard.virtualKeyboardStylesheet = null; (_c = keyboard._element) === null || _c === void 0 ? void 0 : _c.remove(); keyboard._element = undefined; if (keyboard.originalContainerBottomPadding !== null) container.style.paddingBottom = keyboard.originalContainerBottomPadding; } keyboard.visible = false; keyboard.stateChanged(); return false; } function toggleVirtualKeyboard(keyboard, theme) { if (keyboard.visible) hideVirtualKeyboard(keyboard); else showVirtualKeyboard(keyboard, theme); return false; } register({ toggleVirtualKeyboard: (keyboard, theme) => toggleVirtualKeyboard(keyboard, theme), hideVirtualKeyboard: (keyboard) => hideVirtualKeyboard(keyboard), showVirtualKeyboard: (keyboard, theme) => showVirtualKeyboard(keyboard, theme), onUndoStateChanged: (keyboard, canUndoState, canRedoState) => onUndoStateChanged(keyboard, canUndoState, canRedoState), }, { target: 'virtual-keyboard' }); const POST_MESSAGE_TYPE = 'mathlive#remote-virtual-keyboard-message'; /** * Must be used on frame with mathfield editor */ class VirtualKeyboardDelegate { /** * @param targetOrigin only virtual keyboards in a frame (or document) with this * origin will be able to receive messages. * Specify a value other than '*' to improve security and prevent malicious * sites from intercepting content. */ constructor(options) { var _a, _b, _c; this.targetOrigin = (_b = (_a = options.targetOrigin) !== null && _a !== void 0 ? _a : window.origin) !== null && _b !== void 0 ? _b : '*'; this.originValidator = (_c = options.originValidator) !== null && _c !== void 0 ? _c : 'same-origin'; this._mathfield = options.mathfield; } get visible() { var _a, _b, _c; return (_c = (_b = (_a = window.mathlive) === null || _a === void 0 ? void 0 : _a.sharedVirtualKeyboard) === null || _b === void 0 ? void 0 : _b.visible) !== null && _c !== void 0 ? _c : false; } set visible(value) { window.mathlive.sharedVirtualKeyboard.visible = value; } setOptions(options) { this.sendMessage('setOptions', { options: JSON.stringify(getValidOptions(options)), }); } create() { } dispose() { this.disable(); } enable() { if (!this.enabled) { this.enabled = true; window.addEventListener('message', this); } } disable() { if (this.enabled) { window.removeEventListener('message', this); this.enabled = false; } } executeCommand(command) { var _a, _b, _c; if (getCommandTarget(command) === 'virtual-keyboard') { if (this._mathfield) { this.setOptions(getValidOptions(this._mathfield.getOptions())); if (command === 'showVirtualKeyboard' || (command === 'toggleVirtualKeyboard' && this.visible === false)) (_b = (_a = this._mathfield).focus) === null || _b === void 0 ? void 0 : _b.call(_a); } this.sendMessage('executeCommand', { command }); return false; } return (_c = this._mathfield) === null || _c === void 0 ? void 0 : _c.executeCommand(command); } focusMathfield() { } blurMathfield() { } stateChanged() { } handleEvent(event) { var _a, _b, _c, _d; if (event.type === 'message' && event.data && event.data.type === POST_MESSAGE_TYPE) { if (!validateOrigin(event.origin, this.originValidator)) { throw new Error(`Message from unknown origin (${event.origin}) cannot be handled`); } const { action } = event.data; if (action === 'executeCommand') { // Avoid an infinite messages loop if within one window if (getCommandTarget(event.data.command) === 'virtual-keyboard' && window === window.parent) return; this.executeCommand(event.data.command); } else if (action === 'updateState') { this.visible = event.data.state.visible; this.height = event.data.state.height; } else if (action === 'focus') (_b = (_a = this._mathfield) === null || _a === void 0 ? void 0 : _a.focus) === null || _b === void 0 ? void 0 : _b.call(_a); else if (action === 'blur') (_d = (_c = this._mathfield) === null || _c === void 0 ? void 0 : _c.blur) === null || _d === void 0 ? void 0 : _d.call(_c); } } sendMessage(action, payload = {}) { if (window.parent) { window.parent.postMessage({ type: POST_MESSAGE_TYPE, action, ...payload, }, this.targetOrigin); return true; } return false; } } /** * Must be used on parent frame where virtual keyboard will be rendered */ class RemoteVirtualKeyboard extends VirtualKeyboard { constructor(options) { const validOptions = { ...RemoteVirtualKeyboard.defaultOptions, ...getValidOptions(options), }; if (options === null || options === void 0 ? void 0 : options.createHTML) validOptions.createHTML = options.createHTML; if (options === null || options === void 0 ? void 0 : options.virtualKeyboardContainer) validOptions.virtualKeyboardContainer = options.virtualKeyboardContainer; super(validOptions); window.addEventListener('message', this); document.body.addEventListener('focusin', (event) => { var _a; const target = event.target; if ((target === null || target === void 0 ? void 0 : target.isConnected) && ((_a = target.tagName) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === 'math-field') { const mf = target; if (mf.virtualKeyboardMode === 'onfocus' && mf.virtualKeyboardState === 'hidden') mf.virtualKeyboardState = 'visible'; } }); document.addEventListener('focusout', (event) => { var _a; const target = event.target; if ((target === null || target === void 0 ? void 0 : target.isConnected) && ((_a = target.tagName) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === 'math-field') { setTimeout(() => { var _a, _b; if (((_b = (_a = document.activeElement) === null || _a === void 0 ? void 0 : _a.tagName) === null || _b === void 0 ? void 0 : _b.toLowerCase()) !== 'math-field') { const mf = document.querySelector('math-field'); if (mf) mf.virtualKeyboardState = 'hidden'; } }, 300); } }); } static get defaultOptions() { var _a, _b; return { createHTML: (s) => s, fontsDirectory: './fonts', soundsDirectory: './sounds', targetOrigin: window.origin, originValidator: 'same-origin', virtualKeyboards: 'all', virtualKeyboardLayout: 'auto', customVirtualKeyboardLayers: {}, customVirtualKeyboards: {}, virtualKeyboardTheme: /macos|ios/.test(osPlatform()) ? 'apple' : 'material', keypressVibration: true, keypressSound: null, plonkSound: null, virtualKeyboardToolbar: 'default', virtualKeyboardContainer: (_b = (_a = globalThis.document) === null || _a === void 0 ? void 0 : _a.body) !== null && _b !== void 0 ? _b : null, }; } handleEvent(event) { if (event.type === 'message' && event.data && event.data.type === POST_MESSAGE_TYPE) { if (!validateOrigin(event.origin, this.options.originValidator)) { throw new Error(`Can not handle message from unknown origin (${event.origin}).`); } const { action } = event.data; if (action === 'executeCommand') { const { command } = event.data; this.sourceFrame = event.source; // Avoid an infinite messages loop if within one window const commandTarget = getCommandTarget(command); if (commandTarget !== 'virtual-keyboard' && window === window.parent) return; this.executeCommand(command); } else if (action === 'setOptions') { const currentOptions = JSON.stringify(getValidOptions(this.options)); if (currentOptions !== event.data.options) { const parsedOptions = getValidOptions(JSON.parse(event.data.options)); // We can't pass functions through and nor do we want to allow the // caller to change the keyboard container parsedOptions.createHTML = this.options.createHTML; parsedOptions.virtualKeyboardContainer = this.options.virtualKeyboardContainer; this.setOptions(parsedOptions); } } } } stateChanged() { var _a, _b; this.sendMessage('stateChanged', { state: { visible: this.visible, height: (_b = (_a = this.element) === null || _a === void 0 ? void 0 : _a.offsetHeight) !== null && _b !== void 0 ? _b : 0, }, }); } executeCommand(command) { const commandTarget = getCommandTarget(command); // Virtual keyboard commands must be handled at local window if (commandTarget === 'virtual-keyboard') return super.executeCommand(command); this.sendMessage('executeCommand', { command }); return false; } /** * @category Focus */ focus() { this.sendMessage('focus'); } /** * @category Focus */ blur() { this.sendMessage('blur'); } canUndo() { return this.canUndoState; } canRedo() { return this.canRedoState; } dispose() { window.removeEventListener('message', this); } sendMessage(action, payload = {}) { var _a; (_a = this.sourceFrame) === null || _a === void 0 ? void 0 : _a.postMessage({ type: POST_MESSAGE_TYPE, action, ...payload, }, this.options.targetOrigin); } } function getValidOptions(options) { if (typeof options !== 'object') return {}; const validOptions = {}; // Note: we explicitly exclude `virtualKeyboardContainer` and `createHhtml` (a function) // as valid options if (options.fontsDirectory) validOptions.fontsDirectory = options.fontsDirectory; if (options.soundsDirectory) validOptions.soundsDirectory = options.soundsDirectory; if (options.virtualKeyboards) validOptions.virtualKeyboards = options.virtualKeyboards; if (options.virtualKeyboardLayout) validOptions.virtualKeyboardLayout = options.virtualKeyboardLayout; if (options.customVirtualKeyboardLayers) { validOptions.customVirtualKeyboardLayers = options.customVirtualKeyboardLayers; } if (options.customVirtualKeyboards) validOptions.customVirtualKeyboards = options.customVirtualKeyboards; if (options.virtualKeyboardTheme) validOptions.virtualKeyboardTheme = options.virtualKeyboardTheme; if (options.keypressVibration) validOptions.keypressVibration = options.keypressVibration; if (options.keypressSound) validOptions.keypressSound = options.keypressSound; if (options.plonkSound) validOptions.plonkSound = options.plonkSound; if (options.virtualKeyboardToolbar) validOptions.virtualKeyboardToolbar = options.virtualKeyboardToolbar; if (options.targetOrigin) validOptions.targetOrigin = options.targetOrigin; if (options.originValidator) validOptions.originValidator = options.originValidator; return validOptions; } function speakableText(speechOptions, prefix, atoms) { const options = { ...speechOptions, textToSpeechMarkup: '', textToSpeechRulesOptions: { ...speechOptions.textToSpeechRulesOptions, markup: 'none', }, }; return prefix + atomToSpeakableText(atoms, options); } /** * * Speak some part of the expression, either with or without synchronized highlighting. * * @param speakOptions.withHighlighting - If true, synchronized * highlighting of speech will happen (if possible). Default is false. */ register({ speak: (mathfield, scope, options) => { return speak(mathfield, scope, options); }, }, { target: 'mathfield', category: 'speech' }); function speak(mathfield, scope, speakOptions) { var _a, _b; speakOptions = speakOptions !== null && speakOptions !== void 0 ? speakOptions : { withHighlighting: false }; const { model } = mathfield; function getAtoms(scope) { let result = null; switch (scope) { case 'all': result = model.root; break; case 'selection': result = model.getAtoms(model.selection); break; case 'left': { result = model.getAtoms(model.offsetOf(model.at(model.position).leftSibling), model.position); break; } case 'right': { result = model.getAtoms(model.position, model.offsetOf(model.at(model.position).rightSibling)); break; } case 'group': result = model.getAtoms(model.getSiblingsRange(model.position)); break; case 'parent': { const { parent } = model.at(model.position); if (parent && parent.type !== 'root') result = parent; else result = model.root; break; } default: result = model.root; } return result; } function getFailedSpeech(scope) { let result = ''; switch (scope) { case 'all': console.log('Internal failure: speak all failed'); break; case 'selection': result = 'no selection'; break; case 'left': result = 'at start'; break; case 'right': result = 'at end'; break; case 'group': console.log('Internal failure: speak group failed'); break; case 'parent': result = 'no parent'; break; default: console.log('unknown speak_ param value: "' + scope + '"'); break; } return result; } const atoms = getAtoms(scope); if (atoms === null) { (_b = (_a = mathfield.options).speakHook) === null || _b === void 0 ? void 0 : _b.call(_a, getFailedSpeech(scope), mathfield.options); return false; } const options = { ...mathfield.options }; if (speakOptions.withHighlighting || options.speechEngine === 'amazon') { options.textToSpeechMarkup = isBrowser() && window.sre && options.textToSpeechRules === 'sre' ? 'ssml_step' : 'ssml'; } const text = atomToSpeakableText(atoms, options); if (isBrowser() && speakOptions.withHighlighting) { window.mathlive.readAloudMathField = mathfield; render(mathfield, { forHighlighting: true }); if (mathfield.options.readAloudHook) { mathfield.options.readAloudHook(mathfield.field, text, mathfield.options); } } else if (mathfield.options.speakHook) mathfield.options.speakHook(text, options); return false; } function defaultSpeakHook(text, config) { var _a, _b; if (!config && isBrowser() && 'mathlive' in window) config = window.mathlive.config; config = config !== null && config !== void 0 ? config : {}; if (!config.speechEngine || config.speechEngine === 'local') { if (isBrowser()) { // On ChromeOS: chrome.accessibilityFeatures.spokenFeedback // See also https://developer.chrome.com/apps/tts const utterance = new SpeechSynthesisUtterance(text); window.speechSynthesis.speak(utterance); } else console.log('Speak:', text); } else if (config.speechEngine === 'amazon') { if (!isBrowser() || !('AWS' in window)) { console.warn('AWS SDK not loaded. See https://www.npmjs.com/package/aws-sdk'); } else { const polly = new window.AWS.Polly({ apiVersion: '2016-06-10' }); const parameters = { OutputFormat: 'mp3', VoiceId: (_a = config.speechEngineVoice) !== null && _a !== void 0 ? _a : 'Joanna', Engine: [ 'Amy', 'Emma', 'Brian', 'Ivy', 'Joanna', 'Kendra', 'Kimberly', 'Salli', 'Joey', 'Justin', 'Matthew', ].includes((_b = config.speechEngineVoice) !== null && _b !== void 0 ? _b : 'Joanna') ? 'neural' : 'standard', // SampleRate: '24000', Text: text, TextType: 'ssml', // SpeechMarkTypes: ['ssml]' }; // https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Polly.html#synthesizeSpeech-property polly.synthesizeSpeech(parameters, (err, data) => { if (err) console.warn('polly.synthesizeSpeech() error:', err, err.stack); // Announce('plonk'); else if (data === null || data === void 0 ? void 0 : data.AudioStream) { const uInt8Array = new Uint8Array(data.AudioStream); const blob = new Blob([uInt8Array.buffer], { type: 'audio/mpeg', }); const url = URL.createObjectURL(blob); const audioElement = new Audio(url); audioElement.play().catch((error) => console.log(error)); } else console.log('polly.synthesizeSpeech():', data); }); // Can call AWS.Request() on the result of synthesizeSpeech() } } else if (config.speechEngine === 'google') { console.warn('The Google speech engine is not supported yet. Please come again.'); // @todo: implement support for Google Text-to-Speech API, // using config.speechEngineToken, config.speechEngineVoice and // config.speechEngineAudioConfig // curl -H "Authorization: Bearer "$(gcloud auth application-default print-access-token) \ // -H "Content-Type: application/json; charset=utf-8" \ // --data "{ // 'input':{ // 'text':'Android is a mobile operating system developed by Google, // based on the Linux kernel and designed primarily for // touchscreen mobile devices such as smartphones and tablets.' // }, // 'voice':{ // 'languageCode':'en-gb', // 'name':'en-GB-Standard-A', // 'ssmlGender':'FEMALE' // }, // 'audioConfig':{ // 'audioEncoding':'MP3' // } // }" "https://texttospeech.googleapis.com/v1beta1/text:synthesize" > synthesize-text.txt } } // Import { atomsToMathML } from '../addons/math-ml'; /** * Given an atom, describe the relationship between the atom * and its siblings and their parent. */ function relationName(atom) { let result = undefined; if (atom.treeBranch === 'body') { result = { enclose: 'cross out', leftright: 'delimiter', surd: 'square root', root: 'math field', mop: 'operator', // E.g. `\operatorname`, a `mop` with a body }[atom.type]; } else if (atom.parent.type === 'genfrac') { if (atom.treeBranch === 'above') return 'numerator'; if (atom.treeBranch === 'below') return 'denominator'; } else if (atom.parent.type === 'surd') { if (atom.treeBranch === 'above') result = 'index'; } else if (atom.treeBranch === 'superscript') result = 'superscript'; else if (atom.treeBranch === 'subscript') result = 'subscript'; if (!result) console.log('unknown relationship'); return result !== null && result !== void 0 ? result : 'parent'; } /** * Announce a change in selection or content via the aria-live region. * * @param action The action that invoked the change. * @param previousPosition The position of the insertion point before the change */ function defaultAnnounceHook(mathfield, action, previousPosition, atoms) { var _a; //* * Fix: the focus is the end of the selection, so it is before where we want it let liveText = ''; // Const action = moveAmount > 0 ? "right" : "left"; if (action === 'plonk') { // Use this sound to indicate minor errors, for // example when an action has no effect. (_a = mathfield.plonkSound) === null || _a === void 0 ? void 0 : _a.play().catch((error) => console.warn(error)); // As a side effect, reset the keystroke buffer mathfield.resetKeystrokeBuffer(); } else if (action === 'delete') liveText = speakableText(mathfield.options, 'deleted: ', atoms); //* ** FIX: could also be moveUp or moveDown -- do something different like provide context??? else if (action === 'focus' || action.includes('move')) { //* ** FIX -- should be xxx selected/unselected */ liveText = getRelationshipAsSpokenText(mathfield.model, previousPosition) + (mathfield.model.selectionIsCollapsed ? '' : 'selected: ') + getNextAtomAsSpokenText(mathfield.model, mathfield.options); } else if (action === 'replacement') { // Announce the contents liveText = speakableText(mathfield.options, '', mathfield.model.at(mathfield.model.position)); } else if (action === 'line') { // Announce the current line -- currently that's everything // mathfield.accessibleNode.innerHTML = mathfield.options.createHTML( // '<math xmlns="http://www.w3.org/1998/Math/MathML">' + // atomsToMathML(mathfield.model.root, mathfield.options) + // '</math>' // ); liveText = speakableText(mathfield.options, '', mathfield.model.root); mathfield.keyboardDelegate.setAriaLabel('after: ' + liveText); /** * FIX -- testing hack for setting braille ***/ // mathfield.accessibleNode.focus(); // console.log("before sleep"); // sleep(1000).then(() => { // mathfield.textarea.focus(); // console.log("after sleep"); // }); } else { liveText = atoms ? speakableText(mathfield.options, action + ' ', atoms) : action; } // Aria-live regions are only spoken when it changes; force a change by // alternately using nonbreaking space or narrow nonbreaking space const ariaLiveChangeHack = mathfield.ariaLiveText.textContent.includes('\u00a0') ? ' \u202F ' : ' \u00A0 '; mathfield.ariaLiveText.textContent = liveText + ariaLiveChangeHack; // This.textarea.setAttribute('aria-label', liveText + ariaLiveChangeHack); } function getRelationshipAsSpokenText(model, previousOffset) { if (Number.isNaN(previousOffset)) return ''; const previous = model.at(previousOffset); if (!previous) return ''; if (previous.treeDepth <= model.at(model.position).treeDepth) return ''; let result = ''; let ancestor = previous.parent; const newParent = model.at(model.position).parent; while (ancestor !== model.root && ancestor !== newParent) { result += `out of ${relationName(ancestor)};`; ancestor = ancestor.parent; } return result; } /** * * Return the spoken text for the atom to the right of the current selection. * Take into consideration the position amongst siblings to include 'start of' * and 'end of' if applicable. */ function getNextAtomAsSpokenText(model, options) { if (!model.selectionIsCollapsed) return speakableText(options, '', model.getAtoms(model.selection)); let result = ''; // Announce start of denominator, etc const cursor = model.at(model.position); const relation = relationName(cursor); if (cursor.isFirstSibling) result = (relation ? 'start of ' + relation : 'unknown') + ': '; if (cursor.isLastSibling) { // Don't say both start and end if (!cursor.isFirstSibling) result += relation ? 'end of ' + relation : 'unknown'; } else result += speakableText(options, '', cursor); return result; } function removeHighlight(element) { element.classList.remove('ML__highlight'); if (element.children) for (const child of element.children) removeHighlight(child); } /** * Highlights the box corresponding to the specified atomID. * * This is used for text-to-speech with synchronized highlighting (read aloud) * * @category Read Aloud * @param {string} atomID * */ function highlightAtomID(element, atomID) { var _a; if (!atomID || ((_a = element.dataset) === null || _a === void 0 ? void 0 : _a.atomId) === atomID) { element.classList.add('ML__highlight'); if (element.children && element.children.length > 0) { [...element.children].forEach((x) => { if (x instanceof HTMLElement) highlightAtomID(x); }); } } else { element.classList.remove('ML__highlight'); if (element.children && element.children.length > 0) { [...element.children].forEach((x) => { if (x instanceof HTMLElement) highlightAtomID(x, atomID); }); } } } /** * "Read Aloud" is an asynchronous operation that reads the * reading with synchronized highlighting * * @param element - The DOM element to highlight * @param text - The text to speak */ function defaultReadAloudHook(element, text, config) { var _a, _b, _c; if (!isBrowser()) return; if (!config && window.mathlive) config = window.mathlive.config; if (config.speechEngine !== 'amazon') { console.warn('Use Amazon TTS Engine for synchronized highlighting'); if (config.speakHook) config.speakHook(text, config); return; } if (!window.AWS) { console.warn('AWS SDK not loaded. See https://www.npmjs.com/package/aws-sdk'); return; } const polly = new window.AWS.Polly({ apiVersion: '2016-06-10' }); const parameters = { OutputFormat: 'json', VoiceId: (_a = config.speechEngineVoice) !== null && _a !== void 0 ? _a : 'Joanna', Engine: 'standard', Text: text, TextType: 'ssml', SpeechMarkTypes: ['ssml'], }; window.mathlive = (_b = window.mathlive) !== null && _b !== void 0 ? _b : {}; window.mathlive.readAloudElement = element; const statusHook = (_c = config.onReadAloudStatus) !== null && _c !== void 0 ? _c : window.mathlive.onReadAloudStatus; // Request the mark points polly.synthesizeSpeech(parameters, (err, data) => { if (err) { console.warn('polly.synthesizeSpeech() error:', err, err.stack); return; } if (!data || !data.AudioStream) { console.log('polly.synthesizeSpeech():', data); return; } const response = new TextDecoder('utf-8').decode(new Uint8Array(data.AudioStream)); window.mathlive.readAloudMarks = response .split('\n') .map((x) => (x ? JSON.parse(x) : {})); window.mathlive.readAloudTokens = []; for (const mark of window.mathlive.readAloudMarks) if (mark.value) window.mathlive.readAloudTokens.push(mark.value); window.mathlive.readAloudCurrentMark = ''; // Request the audio parameters.OutputFormat = 'mp3'; parameters.SpeechMarkTypes = []; polly.synthesizeSpeech(parameters, (err, data) => { if (err) { console.warn('polly.synthesizeSpeech(', text, ') error:', err, err.stack); return; } if (!data || !data.AudioStream) return; const uInt8Array = new Uint8Array(data.AudioStream); const blob = new Blob([uInt8Array.buffer], { type: 'audio/mpeg', }); const url = URL.createObjectURL(blob); if (!window.mathlive.readAloudAudio) { window.mathlive.readAloudAudio = new Audio(); window.mathlive.readAloudAudio.addEventListener('ended', () => { const mathfield = window.mathlive.readAloudMathField; if (statusHook) statusHook(mathfield, 'ended'); if (mathfield) { render(mathfield); window.mathlive.readAloudElement = null; window.mathlive.readAloudMathField = null; window.mathlive.readAloudTokens = []; window.mathlive.readAloudMarks = []; window.mathlive.readAloudCurrentMark = ''; } else removeHighlight(window.mathlive.readAloudElement); }); window.mathlive.readAloudAudio.addEventListener('timeupdate', () => { let value = ''; // The target, the atom we're looking for, is the one matching the current audio // plus 100 ms. By anticipating it a little bit, it feels more natural, otherwise it // feels like the highlighting is trailing the audio. const target = window.mathlive.readAloudAudio.currentTime * 1000 + 100; // Find the smallest element which is bigger than the target time for (const mark of window.mathlive.readAloudMarks) if (mark.time < target) value = mark.value; if (window.mathlive.readAloudCurrentMark !== value) { window.mathlive.readAloudCurrentToken = value; if (value && value === window.mathlive.readAloudFinalToken) window.mathlive.readAloudAudio.pause(); else { window.mathlive.readAloudCurrentMark = value; highlightAtomID(window.mathlive.readAloudElement, window.mathlive.readAloudCurrentMark); } } }); } else window.mathlive.readAloudAudio.pause(); window.mathlive.readAloudAudio.src = url; if (statusHook) statusHook(window.mathlive.readAloudMathField, 'playing'); window.mathlive.readAloudAudio.play(); }); }); } const AUDIO_FEEDBACK_VOLUME = 0.5; // From 0.0 to 1.0 const NO_OP_LISTENER = () => { }; function loadSound(soundDirectory, sound) { if (sound === null || sound === undefined || sound === 'none' || sound === 'null') return null; if (sound instanceof HTMLAudioElement) { sound.load(); return sound; } if (typeof sound === 'string') { sound = sound.trim(); if (sound.length === 0) return null; const result = new Audio(); result.src = resolveRelativeUrl((soundDirectory === undefined || soundDirectory.length === 0 ? './sounds' : soundDirectory) + '/' + sound); // Note that on iOS the volume property is read-only result.volume = AUDIO_FEEDBACK_VOLUME; result.load(); return result; } return null; } function unloadSound(sound) { if (sound instanceof HTMLAudioElement) { sound.pause(); sound.removeAttribute('src'); // Important: to properly unload call `load()` after removing the // `src` attribute sound.load(); } } function update(current, updates) { var _a, _b, _c, _d, _e; const soundsDirectory = (_b = (_a = updates.soundsDirectory) !== null && _a !== void 0 ? _a : current.soundsDirectory) !== null && _b !== void 0 ? _b : './sounds'; const result = get(current, Object.keys(current)); for (const key of Object.keys(updates)) { switch (key) { case 'scriptDepth': if (isArray(updates.scriptDepth)) result.scriptDepth = [updates.scriptDepth[0], updates.scriptDepth[1]]; else if (typeof updates.scriptDepth === 'number') result.scriptDepth = [updates.scriptDepth, updates.scriptDepth]; else throw new TypeError('Unexpected value for scriptDepth'); break; case 'locale': if (updates.locale === 'auto') result.locale = isBrowser() ? navigator.language.slice(0, 5) : 'en'; else result.locale = updates.locale; l10n.locale = result.locale; break; case 'strings': l10n.merge(updates.strings); result.strings = l10n.strings; break; case 'virtualKeyboardLayout': result.virtualKeyboardLayout = updates.virtualKeyboardLayout; break; case 'virtualKeyboardMode': const keyboardMode = updates.virtualKeyboardMode.toLowerCase(); if (keyboardMode === 'auto') result.virtualKeyboardMode = isTouchCapable() ? 'onfocus' : 'off'; else result.virtualKeyboardMode = keyboardMode; break; case 'customVirtualKeyboardLayers': result.customVirtualKeyboardLayers = { ...result.customVirtualKeyboardLayers, ...updates.customVirtualKeyboardLayers, }; break; case 'customVirtualKeyboards': result.customVirtualKeyboards = { ...result.customVirtualKeyboards, ...updates.customVirtualKeyboards, }; break; case 'letterShapeStyle': if (updates.letterShapeStyle === 'auto') { // Letter shape style (locale dependent) if (l10n.locale.startsWith('fr')) result.letterShapeStyle = 'french'; else result.letterShapeStyle = 'tex'; } else result.letterShapeStyle = updates.letterShapeStyle; break; case 'plonkSound': if (result.plonkSound instanceof HTMLAudioElement) unloadSound(result.plonkSound); result.plonkSound = loadSound(soundsDirectory, updates.plonkSound); break; case 'keypressSound': if (typeof result.keypressSound === 'object' && result.keypressSound !== null && 'default' in result.keypressSound) { unloadSound(result.keypressSound.default); unloadSound(result.keypressSound.delete); unloadSound(result.keypressSound.return); unloadSound(result.keypressSound.spacebar); } if (updates.keypressSound === null) { result.keypressSound = { default: null, delete: null, return: null, spacebar: null, }; } else if (typeof updates.keypressSound === 'string') { const sound = loadSound(soundsDirectory, updates.keypressSound); result.keypressSound = { delete: sound, return: sound, spacebar: sound, default: sound, }; } else if (updates.keypressSound instanceof HTMLAudioElement) { result.keypressSound = { delete: updates.keypressSound, return: updates.keypressSound, spacebar: updates.keypressSound, default: updates.keypressSound, }; } else if (typeof updates.keypressSound === 'object' && 'default' in updates.keypressSound) { result.keypressSound = { ...updates.keypressSound }; result.keypressSound.default = loadSound(soundsDirectory, result.keypressSound.default); result.keypressSound.delete = (_c = loadSound(soundsDirectory, result.keypressSound.delete)) !== null && _c !== void 0 ? _c : updates.keypressSound.default; result.keypressSound.return = (_d = loadSound(soundsDirectory, result.keypressSound.return)) !== null && _d !== void 0 ? _d : updates.keypressSound.default; result.keypressSound.spacebar = (_e = loadSound(soundsDirectory, result.keypressSound.spacebar)) !== null && _e !== void 0 ? _e : updates.keypressSound.default; } break; case 'virtualKeyboardContainer': result.virtualKeyboardContainer = updates.virtualKeyboardContainer; break; case 'macros': result.macros = normalizeMacroDictionary(updates.macros); break; case 'onContentWillChange': if (updates[key] === null) result[key] = () => true; else if (typeof updates[key] !== 'function') throw new TypeError(key + ' must be a function or null'); result[key] = updates[key]; break; case 'onBlur': case 'onFocus': case 'onContentDidChange': case 'onSelectionWillChange': case 'onSelectionDidChange': case 'onUndoStateWillChange': case 'onUndoStateDidChange': case 'onModeChange': case 'onCommit': case 'onReadAloudStatus': case 'onError': if (updates[key] === null) result[key] = NO_OP_LISTENER; else if (typeof updates[key] !== 'function') throw new TypeError(key + ' must be a function or null'); result[key] = updates[key]; break; default: if (isArray(updates[key])) result[key] = [...updates[key]]; else if (typeof updates[key] === 'object') result[key] = { ...updates[key] }; else result[key] = updates[key]; } } return result; } function get(config, keys) { let resolvedKeys; if (typeof keys === 'string') resolvedKeys = [keys]; else if (keys === undefined) resolvedKeys = Object.keys(config); else resolvedKeys = keys; const result = {}; for (const x of resolvedKeys) { if (isArray(config[x])) result[x] = [...config[x]]; else if (config[x] instanceof HTMLElement) { //For 'plonksound', it's a AudioElement result[x] = config[x]; } else if (config[x] === null) result[x] = null; else if (typeof config[x] === 'object') { // Some object literal, make a copy (for keypressSound) result[x] = { ...config[x] }; } else result[x] = config[x]; } // If requested a single key, return its value if (typeof keys === 'string') return result[keys]; return result; } const DEFAULT_KEYBOARD_TOGGLE_GLYPH = `<span style="width: 21px; margin-top: 4px;"><svg style="width: 21px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M528 64H48C21.49 64 0 85.49 0 112v288c0 26.51 21.49 48 48 48h480c26.51 0 48-21.49 48-48V112c0-26.51-21.49-48-48-48zm16 336c0 8.823-7.177 16-16 16H48c-8.823 0-16-7.177-16-16V112c0-8.823 7.177-16 16-16h480c8.823 0 16 7.177 16 16v288zM168 268v-24c0-6.627-5.373-12-12-12h-24c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h24c6.627 0 12-5.373 12-12zm96 0v-24c0-6.627-5.373-12-12-12h-24c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h24c6.627 0 12-5.373 12-12zm96 0v-24c0-6.627-5.373-12-12-12h-24c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h24c6.627 0 12-5.373 12-12zm96 0v-24c0-6.627-5.373-12-12-12h-24c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h24c6.627 0 12-5.373 12-12zm-336 80v-24c0-6.627-5.373-12-12-12H84c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h24c6.627 0 12-5.373 12-12zm384 0v-24c0-6.627-5.373-12-12-12h-24c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h24c6.627 0 12-5.373 12-12zM120 188v-24c0-6.627-5.373-12-12-12H84c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h24c6.627 0 12-5.373 12-12zm96 0v-24c0-6.627-5.373-12-12-12h-24c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h24c6.627 0 12-5.373 12-12zm96 0v-24c0-6.627-5.373-12-12-12h-24c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h24c6.627 0 12-5.373 12-12zm96 0v-24c0-6.627-5.373-12-12-12h-24c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h24c6.627 0 12-5.373 12-12zm96 0v-24c0-6.627-5.373-12-12-12h-24c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h24c6.627 0 12-5.373 12-12zm-96 152v-8c0-6.627-5.373-12-12-12H180c-6.627 0-12 5.373-12 12v8c0 6.627 5.373 12 12 12h216c6.627 0 12-5.373 12-12z"/></svg></span>`; function getDefault() { var _a, _b, _c; return { readOnly: false, createHTML: (s) => s, fontsDirectory: './fonts', soundsDirectory: './sounds', defaultMode: 'math', macros: getMacros(), registers: { ...getDefaultRegisters() }, colorMap: defaultColorMap, backgroundColorMap: defaultBackgroundColorMap, horizontalSpacingScale: 1, letterShapeStyle: l10n.locale.startsWith('fr') ? 'french' : 'tex', smartMode: false, smartFence: true, smartSuperscript: true, scriptDepth: [Infinity, Infinity], removeExtraneousParentheses: true, mathModeSpace: '', decimalSeparator: '.', locale: l10n.locale, strings: l10n.strings, keybindings: DEFAULT_KEYBINDINGS, inlineShortcuts: INLINE_SHORTCUTS, inlineShortcutTimeout: 0, virtualKeyboardToggleGlyph: DEFAULT_KEYBOARD_TOGGLE_GLYPH, virtualKeyboardMode: 'auto', virtualKeyboards: 'all', virtualKeyboardLayout: 'auto', customVirtualKeyboardLayers: {}, customVirtualKeyboards: {}, virtualKeyboardTheme: isBrowser() && /android|cros/i.test(navigator.userAgent) ? 'material' : 'apple', keypressVibration: true, keypressSound: null, plonkSound: null, virtualKeyboardToolbar: 'default', virtualKeyboardContainer: (_b = (_a = globalThis.document) === null || _a === void 0 ? void 0 : _a.body) !== null && _b !== void 0 ? _b : null, useSharedVirtualKeyboard: false, sharedVirtualKeyboardTargetOrigin: (_c = globalThis.window) === null || _c === void 0 ? void 0 : _c.origin, originValidator: 'same-origin', textToSpeechRules: 'mathlive', textToSpeechMarkup: '', textToSpeechRulesOptions: {}, speechEngine: 'local', speechEngineVoice: 'Joanna', speechEngineRate: '100%', speakHook: defaultSpeakHook, readAloudHook: defaultReadAloudHook, onAnnounce: defaultAnnounceHook, onKeystroke: () => true, onMoveOutOf: () => true, onTabOutOf: () => true, onPlaceholderDidChange: () => { }, onMulticharSymbol: () => '', onBlur: NO_OP_LISTENER, onFocus: NO_OP_LISTENER, onContentWillChange: () => true, onContentDidChange: NO_OP_LISTENER, onSelectionWillChange: NO_OP_LISTENER, onSelectionDidChange: NO_OP_LISTENER, onUndoStateWillChange: NO_OP_LISTENER, onUndoStateDidChange: NO_OP_LISTENER, onModeChange: NO_OP_LISTENER, onReadAloudStatus: NO_OP_LISTENER, onCommit: NO_OP_LISTENER, onExport: defaultExportHook, onError: NO_OP_LISTENER, value: '', }; } function effectiveMode(options) { if (options.defaultMode === 'inline-math') return 'math'; return options.defaultMode; } function isOffset(value) { return typeof value === 'number' && !Number.isNaN(value); } function isRange(value) { return Array.isArray(value) && value.length === 2; } function isSelection(value) { return (value !== undefined && value !== null && typeof value === 'object' && 'ranges' in value && Array.isArray(value.ranges)); } /** @internal */ class ModelPrivate { constructor(options, listeners, hooks, target) { this.options = options; this.root = new Atom('root', { mode: options.mode }); this.root.body = []; this._selection = { ranges: [[0, 0]], direction: 'none' }; this._anchor = 0; this._position = 0; this.setListeners(listeners); this.setHooks(hooks); this.mathfield = target; this.suppressChangeNotifications = false; } get atoms() { return this.root.children; } /** * The selection, accounting for the common ancestors */ get selection() { return this._selection; } set selection(value) { this.setSelection(value); } setSelection(arg1, arg2) { return this.deferNotifications({ selection: true }, () => { // // 1/ Normalize the input // (account for offset < 0, etc...) // const value = this.normalizeSelection(arg1, arg2); if (value === undefined) throw new TypeError('Invalid selection'); // // 2/ Short-circuit a common case... // if (value.ranges.length === 1 && value.ranges[0][0] === value.ranges[0][1]) { const pos = value.ranges[0][0]; console.assert(pos >= 0 && pos <= this.lastOffset); this._position = pos; this._anchor = pos; this._selection = value; } else { // // 2b/ Determine the anchor and position // (smallest, largest offsets, oriented as per `direction`) // const selRange = range(value); if (value.direction === 'backward') [this._position, this._anchor] = selRange; else [this._anchor, this._position] = selRange; const first = this.at(selRange[0]); const last = this.at(selRange[1]); const commonAncestor = Atom.commonAncestor(first, last); if ((commonAncestor === null || commonAncestor === void 0 ? void 0 : commonAncestor.type) === 'array' && first.parent === commonAncestor && last.parent === commonAncestor) ; else { this._selection = { ranges: [[this.offsetOf(first), this.offsetOf(last)]], direction: value.direction, }; // 3b.3/ Adjust the position to match the selection if (value.direction === 'backward') this._position = this._selection.ranges[0][0]; else this._position = this._selection.ranges[0][1]; console.assert(this._position >= 0 && this._position <= this.lastOffset); } } }); } /** * The "focus" or "cursor" (i.e. not the anchor) a.k.a the insertion point * or caret: where things are going to be inserted next. * */ get position() { return this._position; } set position(value) { this.setSelection(value, value); } /** * The offset from which the selection is extended */ get anchor() { return this._anchor; } get selectionIsCollapsed() { return this._anchor === this._position; } get selectionIsPlaceholder() { if (Math.abs(this._anchor - this._position) === 1) { return (this.at(Math.max(this._anchor, this._position)).type === 'placeholder'); } return false; } collapseSelection(direction = 'forward') { if (this._anchor === this._position) return false; if (direction === 'backward') this.position = Math.min(this._anchor, this._position); else this.position = Math.max(this._anchor, this._position); return true; } get lastOffset() { return this.atoms.length - 1; } at(index) { return this.atoms[index]; } offsetOf(atom) { return this.atoms.indexOf(atom); } getSiblingsRange(offset) { const atom = this.at(offset); const { parent } = atom; if (!parent) return [0, this.lastOffset]; const branch = atom.parent.branch(atom.treeBranch); return [this.offsetOf(branch[0]), this.offsetOf(branch[branch.length - 1])]; } getBranchRange(offset, branchName) { const branch = this.at(offset).branch(branchName); return [this.offsetOf(branch[0]), this.offsetOf(branch[branch.length - 1])]; } getAtoms(arg1, arg2, arg3) { var _a, _b; let options = arg3 !== null && arg3 !== void 0 ? arg3 : {}; if (isSelection(arg1)) { options = (_a = arg2) !== null && _a !== void 0 ? _a : {}; if (arg1.ranges.length > 1) { return arg1.ranges.reduce((acc, range) => [...acc, ...this.getAtoms(range, options)], []); } arg1 = arg1.ranges[0]; } let start; let end; if (isOffset(arg1)) { start = arg1; if (!isOffset(arg2)) return []; end = arg2; } else { [start, end] = arg1; options = (_b = arg2) !== null && _b !== void 0 ? _b : {}; } if (!Number.isFinite(start)) return []; if (options.includeChildren === undefined) options.includeChildren = false; if (start < 0) start = this.lastOffset - start + 1; if (end < 0) end = this.lastOffset - end + 1; const first = Math.min(start, end) + 1; const last = Math.max(start, end); if (first === 1 && last === this.lastOffset) { // This is the entire selection, // return the root return [this.root]; } let result = []; for (let i = first; i <= last; i++) { const atom = this.atoms[i]; if (atomIsInRange(this, atom, first, last)) result.push(atom); } if (!options.includeChildren) { // Remove any atoms whose ancestor is also included result = result.filter((atom) => { let ancestorIncluded = false; let { parent } = atom; while (parent && !ancestorIncluded) { ancestorIncluded = atomIsInRange(this, parent, first, last); parent = parent.parent; } return !ancestorIncluded; }); } return result; } /** * Unlike `getAtoms()`, the argument here is an index * Return all the atoms, in order, starting at startingIndex * then looping back at the beginning */ getAllAtoms(startingIndex) { const result = []; const last = this.lastOffset; for (let i = startingIndex; i <= last; i++) result.push(this.atoms[i]); for (let i = 0; i < startingIndex; i++) result.push(this.atoms[i]); return result; } /** Remove the specified atoms from the tree. * **WARNING** upon return the selection may now be invalid */ extractAtoms(range) { let result = this.getAtoms(range); if (result.length === 1 && result[0].type === 'root') { // We're trying to delete the root. // Don't actually delete the root, delete all the children of the root. result = result[0].children; } for (const child of result) child.parent.removeChild(child); return result; } deleteAtoms(range) { this.extractAtoms(range); this.position = range[0]; } atomToString(atom, inFormat) { const format = inFormat !== null && inFormat !== void 0 ? inFormat : 'latex'; if (format.startsWith('latex')) { return Mode.serialize([atom], { expandMacro: format === 'latex-expanded', skipStyles: format === 'latex-unstyled', defaultMode: this.mathfield.options.defaultMode, }); } if (format === 'math-ml') return atomsToMathML(atom, this.mathfield.options); if (format === 'spoken') return atomToSpeakableText(atom, this.mathfield.options); if (format === 'spoken-text') { const saveTextToSpeechMarkup = this.mathfield.options.textToSpeechMarkup; this.mathfield.options.textToSpeechMarkup = ''; const result = atomToSpeakableText(atom, this.mathfield.options); this.mathfield.options.textToSpeechMarkup = saveTextToSpeechMarkup; return result; } if (format === 'spoken-ssml' || format === 'spoken-ssml-with-highlighting') { const saveTextToSpeechMarkup = this.mathfield.options.textToSpeechMarkup; // Const savedAtomIdsSettings = this.config.atomIdsSettings; // @revisit this.mathfield.options.textToSpeechMarkup = 'ssml'; // If (format === 'spoken-ssml-with-highlighting') { // @revisit // this.config.atomIdsSettings = { seed: 'random' }; // } const result = atomToSpeakableText(atom, this.mathfield.options); this.mathfield.options.textToSpeechMarkup = saveTextToSpeechMarkup; // This.config.atomIdsSettings = savedAtomIdsSettings; // @revisit return result; } if (format === 'math-json') { try { const expr = this.mathfield.computeEngine.parse(Atom.serialize(atom, { expandMacro: false, defaultMode: 'math' })); return JSON.stringify(expr.json); } catch (e) { return JSON.stringify(['Error', 'Nothing', `'${e.toString()}'`]); } } if (format === 'ascii-math') return atomToAsciiMath(atom); console.warn('Unknown format :', format); return ''; } getValue(arg1, arg2, arg3) { // GetValue() if (arg1 === undefined) return this.atomToString(this.root, 'latex'); // GetValue(format): Output format only if (typeof arg1 === 'string') return this.atomToString(this.root, arg1); let ranges; let format; if (isOffset(arg1) && isOffset(arg2)) { ranges = [this.normalizeRange([arg1, arg2])]; format = arg3 !== null && arg3 !== void 0 ? arg3 : 'latex'; } else if (isRange(arg1)) { ranges = [this.normalizeRange(arg1)]; format = arg2; } else if (isSelection(arg1)) { ranges = arg1.ranges; format = arg2; } else { ranges = []; format = 'latex'; } if (format.startsWith('latex')) { const options = { expandMacro: format === 'latex-expanded', skipStyles: format === 'latex-unstyled', defaultMode: this.mathfield.options.defaultMode, }; return joinLatex(ranges.map((range) => Atom.serialize(this.getAtoms(range), options))); } return ranges .map((range) => this.getAtoms(range) .map((atom) => this.atomToString(atom, format)) .join('')) .join(''); } /** * Method called in response to a user interaction */ extendSelection(direction) { let anchor = this._anchor; // Keep the anchor anchored, move the position forward or back if (direction === 'forward') { let pos = this._position; do { let atom = this.at(pos + 1); if (atom === null || atom === void 0 ? void 0 : atom.inCaptureSelection) { // When going forward, if in a capture selection, jump to // after while (!atom.captureSelection) atom = atom.parent; pos = this.offsetOf(atom === null || atom === void 0 ? void 0 : atom.lastChild) + 1; } else pos += 1; } while (pos <= this.lastOffset && this.at(pos).isFirstSibling); if (pos === anchor - 1 && this.at(anchor).type === 'first') pos = anchor; return this.extendSelectionTo(anchor, pos); } // // Extending backward // let pos = this._position - 1; if (pos < 0) return false; while (pos >= 0 && this.at(pos).isLastSibling) { let atom = this.at(pos); if (atom === null || atom === void 0 ? void 0 : atom.inCaptureSelection) { // When going backward, if in a capture selection, jump to // before while (!atom.captureSelection) atom = atom.parent; pos = this.offsetOf(atom.firstChild) - 1; } else pos -= 1; } if (pos < 0) pos = 0; if (pos === anchor + 1 && this.at(pos).type === 'first') anchor = pos; return this.extendSelectionTo(anchor, pos); } /** * Unlike `setSelection`, this method is intended to be used in response * to a user action, and it performs various adjustments to result * in a more intuitive selection. * For example: * - when all the children of an atom are selected, the atom * become selected. * - this method will *not* change the anchor, but may result * in a selection whose boundary is outside the anchor */ extendSelectionTo(anchor, position) { return this.deferNotifications({ selection: true }, () => { const range = this.normalizeRange([anchor, position]); let [start, end] = range; // Include the parent if all the children are selected let { parent } = this.at(end); if (parent) { if (parent.type === 'genfrac' || parent.type === 'msubsup') { while (parent !== this.root && childrenInRange(this, parent, [start, end])) { end = this.offsetOf(parent); parent = parent.parent; } } } parent = this.at(start).parent; while (parent !== this.root && childrenInRange(this, parent, [start, end])) { start = this.offsetOf(parent.leftSibling); parent = parent.parent; } // Now that the start has potentially changed, check again // if end needs to be updated parent = this.at(end).parent; if ((parent === null || parent === void 0 ? void 0 : parent.type) === 'genfrac') { while (parent !== this.root && childrenInRange(this, parent, [start, end])) { end = this.offsetOf(parent); console.assert(end >= 0); parent = parent.parent; } } this._position = this.normalizeOffset(position); this._selection = { ranges: [[start, end]], direction: 'none', }; }); } setListeners(listeners) { this.listeners = listeners; } setHooks(hooks) { this.hooks = { announce: (hooks === null || hooks === void 0 ? void 0 : hooks.announce) ? hooks.announce : (_target, _command, _previousPosition, _atoms) => { }, moveOut: (hooks === null || hooks === void 0 ? void 0 : hooks.moveOut) ? hooks.moveOut : () => true, tabOut: (hooks === null || hooks === void 0 ? void 0 : hooks.tabOut) ? hooks.tabOut : () => true, }; } /** * This method is called to provide feedback when using a screen reader * or other assistive device, for example when changing the selection or * moving the insertion point. * * It can also be used with the 'plonk' command to provide an audible * feedback when a command is not possible. * * This method should not be called from other methods of the model * (such as `setSelection`) as these methods can also be called * programmatically and a feedback in these case would be innapropriate, * however they should be called from functions called as a result of a user * action, such as the functions in `commands.ts` */ announce(command, previousPosition, atoms = []) { this.hooks.announce(this.mathfield, command, previousPosition, atoms); } // Suppress notification while scope is executed, // then notify of content change, and selection change (if actual change) deferNotifications(options, f) { const oldSelection = this._selection; const oldAnchor = this._anchor; const oldPosition = this._position; const saved = this.suppressChangeNotifications; this.suppressChangeNotifications = true; const previousCounter = this.root.changeCounter; f(); const contentChanged = this.root.changeCounter !== previousCounter; const selectionChanged = oldAnchor !== this._anchor || oldPosition !== this._position || compareSelection(this._selection, oldSelection) === 'different'; this.suppressChangeNotifications = saved; // Notify of content change, if requested if (options.content && contentChanged) contentDidChange(this, { data: options.data, inputType: options.type }); // If the selection has effectively changed, notify if (options.selection && selectionChanged) selectionDidChange(this); return contentChanged || selectionChanged; } normalizeOffset(value) { if (value > 0) value = Math.min(value, this.lastOffset); else if (value < 0) value = this.lastOffset + value + 1; return value; } /** * Ensure that the range is valid and canonical, i.e. * - start <= end * - collapsed = start === end * - start >= 0, end >=0 */ normalizeRange(range) { // 1. Normalize the offsets let [start, end] = range; start = this.normalizeOffset(start); end = this.normalizeOffset(end); return start < end ? [start, end] : [end, start]; } normalizeSelection(value, value2) { var _a; let result = undefined; if (isOffset(value)) { const offset = this.normalizeOffset(value); if (isOffset(value2)) { const offset2 = this.normalizeOffset(value2); result = offset <= offset2 ? { ranges: [[offset, offset2]], direction: 'none' } : { ranges: [[offset2, offset]], direction: 'backward', }; } else result = { ranges: [[offset, offset]], direction: 'none' }; } else if (isRange(value)) { const start = this.normalizeOffset(value[0]); const end = this.normalizeOffset(value[1]); result = start <= end ? { ranges: [[start, end]], direction: 'none' } : { ranges: [[end, start]], direction: 'backward' }; } else if (isSelection(value)) { result = { ranges: value.ranges.map((x) => this.normalizeRange(x)), direction: (_a = value.direction) !== null && _a !== void 0 ? _a : 'none', }; } console.assert(result !== undefined); return result; } } function atomIsInRange(model, atom, first, last) { const offset = model.offsetOf(atom); if (offset < first || offset > last) return false; if (!atom.hasChildren) return true; const firstOffset = model.offsetOf(atom.firstChild); if (firstOffset >= first && firstOffset <= last) { const lastOffset = model.offsetOf(atom.lastChild); if (lastOffset >= first && lastOffset <= last) return true; } return false; } function childrenInRange(model, atom, range) { if (!(atom === null || atom === void 0 ? void 0 : atom.hasChildren)) return false; const [start, end] = range; const first = model.offsetOf(atom.firstChild); const last = model.offsetOf(atom.lastChild); if (first >= start && first <= end && last >= first && last <= end) return true; return false; } // Import { getEnvironmentDefinition } from '../core/definitions'; function addRowAfter(model) { if (!contentWillChange(model, { inputType: 'insertText' })) return false; contentDidChange(model, { inputType: 'insertText' }); return true; } function addRowBefore(model) { if (!contentWillChange(model, { inputType: 'insertText' })) return false; contentDidChange(model, { inputType: 'insertText' }); return true; } function addColumnAfter(model) { if (!contentWillChange(model, { inputType: 'insertText' })) return false; contentDidChange(model, { inputType: 'insertText' }); return true; } function addColumnBefore(model) { if (!contentWillChange(model, { inputType: 'insertText' })) return false; contentDidChange(model, { inputType: 'insertText' }); return true; } register({ addRowAfter, addColumnAfter, addRowBefore, addColumnBefore, }, { target: 'model', category: 'array-edit' }); // Import { // arrayFirstCellByRow, // arrayColRow, // arrayAdjustRow, // arrayIndex, // arrayJoinColumns, // arrayRemoveRow, // arrayColumnCellCount, // arrayRemoveColumn, // arrayJoinRows, // } from './model-array'; // function deleteFirstSiblingInArray(model: ModelPrivate): boolean { // const contentWasChanging = model.suppressChangeNotifications; // model.suppressChangeNotifications = true; // const array = model.parent.array; // if (arrayFirstCellByRow(array) === model.relation) { // // (1) First cell: // // delete array, replace it with linearized content // const atoms = arrayJoinRows(array); // model.path.pop(); // model.siblings().splice(model.anchorOffset(), 1, ...atoms); // model._iter = null; // setSelectionOffset(model, model.anchorOffset() - 1, atoms.length); // } else { // const colRow = arrayColRow(array, model.relation); // if (colRow.col === 0) { // // (2) First (non-empty) column (but not first row): // // Move to the end of the last cell of the previous row // const dest = arrayAdjustRow(array, colRow, -1); // dest.col = array[dest.row].length - 1; // model.path[model.path.length - 1].relation = ('cell' + // arrayIndex(array, dest)) as Relation; // const destLength = array[dest.row][dest.col].length; // // (2.1) Linearize it and merge it with last cell of previous row // // (note that atoms could be empty if there are no non-empty // // cells left in the row) // const atoms = arrayJoinColumns(array[colRow.row]); // array[dest.row][dest.col] = array[dest.row][dest.col].concat(atoms); // setSelectionOffset(model, destLength - 1, atoms.length); // // (2.2) Remove row // arrayRemoveRow(array, colRow.row); // } else { // // (3) Non-first column // // (3.1) If column is empty, remove it // if (arrayColumnCellCount(array, colRow.col) === 0) { // arrayRemoveColumn(array, colRow.col); // colRow.col -= 1; // model.path[model.path.length - 1].relation = ('cell' + // arrayIndex(array, colRow)) as Relation; // const destCell = array[colRow.row][colRow.col]; // setSelectionOffset(model, destCell.length - 1, 0); // } // // (3.2) merge cell with cell in previous column // } // } // // Dispatch notifications // model.suppressChangeNotifications = contentWasChanging; // selectionDidChange(model); // contentDidChange(model); // return true; // } /** * Handle special cases when deleting an atom as per the table below * - deleting an empty numerator: demote fraction * - forward-deleting a square root: demote it * - delete last atom inside a square root: delete the square root * - delete last atom in a subsup: delete the subsup * - etc... * * * @param branch: if deleting inside an atom, the branch being delete * (always the first or last atom of the branch). If undefined, the atom * itself is about to be deleted. * * @return true if handled */ function onDelete(model, direction, atom, branch) { var _a, _b, _c, _d, _e, _f; const parent = atom.parent; if (atom instanceof LeftRightAtom) { // // 'leftright': \left\right // const atStart = (!branch && direction === 'forward') || (branch === 'body' && direction === 'backward'); const pos = atStart ? model.offsetOf(atom) - 1 : model.offsetOf(atom.lastChild); if (!atStart && atom.leftDelim !== '?' && atom.leftDelim !== '.') { // Insert open fence parent.addChildBefore(Mode.createAtom('math', atom.leftDelim), atom); } else if (atStart && atom.rightDelim !== '?' && atom.rightDelim !== '.') { // Insert closing fence parent.addChildAfter(Mode.createAtom('math', atom.rightDelim), atom); } // Hoist body parent.addChildrenAfter(atom.removeBranch('body'), atom); parent.removeChild(atom); model.position = pos; return true; } if (atom.type === 'surd') { // // 'surd': square root // if ((direction === 'forward' && !branch) || (direction === 'backward' && branch === 'body')) { // Before fwd or body 1st bwd: Demote body const pos = atom.leftSibling; if (atom.hasChildren) parent.addChildrenAfter(atom.removeBranch('body'), atom); parent.removeChild(atom); model.position = model.offsetOf(pos); } else if (direction === 'forward' && branch === 'body') { // Body last fwd: move to after model.position = model.offsetOf(atom); } else if (!branch && direction === 'backward') { // After bwd: move to last of body if (atom.hasChildren) model.position = model.offsetOf(atom.lastChild); else { model.position = Math.max(0, model.offsetOf(atom) - 1); parent.removeChild(atom); } } else if (branch === 'above') { if (atom.hasEmptyBranch('above')) atom.removeBranch('above'); if (direction === 'backward') { // Above 1st model.position = model.offsetOf(atom.leftSibling); } else { // Above last model.position = model.offsetOf(atom.body[0]); } } return true; } if (atom.type === 'box' || atom.type === 'enclose') { // // 'box': \boxed, \fbox 'enclose': \cancel // const pos = (branch && direction === 'backward') || (!branch && direction === 'forward') ? atom.leftSibling : atom.lastChild; parent.addChildrenAfter(atom.removeBranch('body'), atom); parent.removeChild(atom); model.position = model.offsetOf(pos); return true; } if (atom.type === 'genfrac' || atom.type === 'overunder') { // // 'genfrac': \frac, \choose, etc... // if (!branch) { // After or before atom if (atom.type === 'overunder' && atom.hasEmptyBranch('body')) return false; if (atom.type === 'genfrac' && atom.hasEmptyBranch('below') && atom.hasEmptyBranch('above')) return false; model.position = model.offsetOf(direction === 'forward' ? atom.firstChild : atom.lastChild); return true; } if ((direction === 'forward' && branch === 'above') || (direction === 'backward' && branch === 'below')) { // Above last or below first: hoist const above = atom.removeBranch('above'); const below = atom.removeBranch('below'); parent.addChildrenAfter([...above, ...below], atom); parent.removeChild(atom); model.position = model.offsetOf(above.length > 0 ? above[above.length - 1] : below[0]); return true; } if (direction === 'backward') { // Above first: move to before model.position = model.offsetOf(atom.leftSibling); return true; } // Below last: move to after model.position = model.offsetOf(atom); return true; } if (atom.isExtensibleSymbol || atom.type === 'msubsup') { // // Extensible operator: \sum, \int, etc... // Superscript/subscript carrier // if (!branch && direction === 'forward') return false; if (!branch) { if (atom.subscript || atom.superscript) { const pos = direction === 'forward' ? (_b = (_a = atom.superscript) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : (_c = atom.subscript) === null || _c === void 0 ? void 0 : _c[0] : (_e = (_d = atom.subscript) === null || _d === void 0 ? void 0 : _d[0].lastSibling) !== null && _e !== void 0 ? _e : (_f = atom.superscript) === null || _f === void 0 ? void 0 : _f[0].lastSibling; if (pos) model.position = model.offsetOf(pos); return true; } return false; } if (branch && atom.hasEmptyBranch(branch)) atom.removeBranch(branch); if (!atom.hasChildren) { // We've removed the last branch of a msubsup const pos = direction === 'forward' ? model.offsetOf(atom) : Math.max(0, model.offsetOf(atom) - 1); atom.parent.removeChild(atom); model.position = pos; return true; } if (branch === 'superscript') { if (direction === 'backward') { const pos = model.offsetOf(atom.firstChild) - 1; console.assert(pos >= 0); model.position = pos; } else if (atom.subscript) model.position = model.offsetOf(atom.subscript[0]); else model.position = model.offsetOf(atom); } else if (branch === 'subscript') { if (direction === 'backward' && atom.superscript) { // Subscript first: move to superscript end model.position = model.offsetOf(atom.superscript[0].lastSibling); } else if (direction === 'backward') { // Subscript first: move to before model.position = model.offsetOf(atom.firstChild) - 1; } else { // Subscript last: move after model.position = model.offsetOf(atom); } } return true; } return false; } /** * Delete the item at the current position */ function deleteBackward(model) { if (!contentWillChange(model, { inputType: 'deleteContentBackward' })) return false; if (!model.selectionIsCollapsed) return deleteRange(model, range(model.selection), 'deleteContentBackward'); return model.deferNotifications({ content: true, selection: true, type: 'deleteContentBackward' }, () => { let target = model.at(model.position); if (target && onDelete(model, 'backward', target)) return; if (target === null || target === void 0 ? void 0 : target.isFirstSibling) { if (onDelete(model, 'backward', target.parent, target.treeBranch)) return; target = null; } // At the first position: nothing to delete... if (!target) { model.announce('plonk'); return; } model.position = model.offsetOf(target.leftSibling); target.parent.removeChild(target); model.announce('delete', undefined, [target]); }); } /** * Delete the item forward of the current position, update the position and * send notifications */ function deleteForward(model) { if (!contentWillChange(model, { inputType: 'deleteContentForward' })) return false; if (!model.selectionIsCollapsed) return deleteRange(model, range(model.selection), 'deleteContentForward'); return model.deferNotifications({ content: true, selection: true, type: 'deleteContentForward' }, () => { var _a, _b; let target = model.at(model.position).rightSibling; if (target && onDelete(model, 'forward', target)) return; if (!target) { target = model.at(model.position); if (target.isLastSibling && onDelete(model, 'forward', target.parent, target.treeBranch)) return; target = null; } else if (model.at(model.position).isLastSibling && onDelete(model, 'forward', target.parent, target.treeBranch)) return; if (model.position === model.lastOffset || !target) { model.announce('plonk'); return; } target.parent.removeChild(target); let sibling = (_a = model.at(model.position)) === null || _a === void 0 ? void 0 : _a.rightSibling; while ((sibling === null || sibling === void 0 ? void 0 : sibling.type) === 'msubsup') { sibling.parent.removeChild(sibling); sibling = (_b = model.at(model.position)) === null || _b === void 0 ? void 0 : _b.rightSibling; } model.announce('delete', undefined, [target]); }); } /** * Delete the specified range, as a result of a user action: this will * account for special cases such as deleting empty denominator, and will * provide appropriate feedback to screen readers. * * Use model.deleteAtoms() for operations that are not a result of * user action. */ function deleteRange(model, range, type) { const result = model.getAtoms(range); if (result.length > 0 && result[0].parent) { let firstChild = result[0].parent.firstChild; if (firstChild.type === 'first') firstChild = firstChild.rightSibling; const lastChild = result[result.length - 1].parent.lastChild; let firstSelected = result[0]; if (firstSelected.type === 'first') firstSelected = firstSelected.rightSibling; const lastSelected = result[result.length - 1]; // If we're deleting all the children, also delete the parent // (for example for surd/\sqrt) if (firstSelected === firstChild && lastSelected === lastChild) { const parent = result[0].parent; if (parent.type !== 'root') { range = [ model.offsetOf(parent.leftSibling), model.offsetOf(parent.rightSibling), ]; } } } return model.deferNotifications({ content: true, selection: true, type }, () => model.deleteAtoms(range)); } /** * Create, remove or update a composition atom at the current location */ function updateComposition(model, s) { const cursor = model.at(model.position); // We're creating or updating a composition if (cursor.type === 'composition') { // Composition already in progress, update it cursor.value = s; } else { // No composition yet, create one // Remove previous caret const { caret } = cursor; cursor.caret = ''; // Create 'composition' atom, with caret const atom = new CompositionAtom(s, { mode: cursor.mode }); atom.caret = caret; cursor.parent.addChildAfter(atom, cursor); // Move cursor one past the composition zone model.position += 1; } } /** * Remove the composition zone */ function removeComposition(model) { const cursor = model.at(model.position); if (cursor.type === 'composition') { cursor.parent.removeChild(cursor); model.position -= 1; } } function getMode(model, offset) { const atom = model.at(offset); let result; if (atom) { result = atom.mode; let ancestor = atom.parent; while (!result && ancestor) { if (ancestor) result = ancestor.mode; ancestor = ancestor.parent; } } return result; } function applyStyleToUnstyledAtoms(atom, style) { if (!atom || !style) return; if (isArray(atom)) { // Apply styling options to each atom atom.forEach((x) => applyStyleToUnstyledAtoms(x, style)); } else if (typeof atom === 'object') { if (!atom.style.color && !atom.style.backgroundColor && !atom.style.fontFamily && !atom.style.fontShape && !atom.style.fontSeries && !atom.style.fontSize && !atom.style.variant && !atom.style.variantStyle) { atom.applyStyle(style); applyStyleToUnstyledAtoms(atom.body, style); applyStyleToUnstyledAtoms(atom.above, style); applyStyleToUnstyledAtoms(atom.below, style); applyStyleToUnstyledAtoms(atom.subscript, style); applyStyleToUnstyledAtoms(atom.superscript, style); } } } /** * Apply a style (color, background) to the selection. * * If the style is already applied to the selection, remove it. If the selection * has the style partially applied (i.e. only some sections), remove it from * those sections, and apply it to the entire selection. */ function applyStyle$1(model, range, style, options) { function everyStyle(property, value) { for (const atom of atoms) if (atom.style[property] !== value) return false; return true; } range = model.normalizeRange(range); if (range[0] === range[1]) return false; const atoms = model.getAtoms(range, { includeChildren: true }); if (options.operation === 'toggle') { if (style.color && everyStyle('color', style.color)) { // If the selection already has this color, turn it off style.color = 'none'; style.verbatimColor = undefined; } if (style.backgroundColor && everyStyle('backgroundColor', style.backgroundColor)) { // If the selection already has this color, turn it off style.backgroundColor = 'none'; style.verbatimBackgroundColor = undefined; } if (style.fontFamily && everyStyle('fontFamily', style.fontFamily)) { // If the selection already has this font family, turn it off style.fontFamily = 'none'; } // If (style.series) style.fontSeries = style.series; if (style.fontSeries && everyStyle('fontSeries', style.fontSeries)) { // If the selection already has this series (weight), turn it off style.fontSeries = 'auto'; } // If (style.shape) style.fontShape = style.shape; if (style.fontShape && everyStyle('fontShape', style.fontShape)) { // If the selection already has this shape (italic), turn it off style.fontShape = 'auto'; } // If (style.size) style.fontSize = style.size; if (style.fontSize && everyStyle('fontSize', style.fontSize)) { // If the selection already has this size, reset it to default size style.fontSize = DEFAULT_FONT_SIZE; } } for (const atom of atoms) atom.applyStyle(style); return true; } /* * Calculates the offset of the "next word". * This is inspired by the behavior of text editors on macOS, namely: blue yellow ^- ^------- * That is: * (1) If starts with an alphanumerical character, find the first alphanumerical * character which is followed by a non-alphanumerical character * * The behavior regarding non-alphanumeric characters is less consistent. * Here's the behavior we use: * * +=-()_:” blue * ^--------- * +=-()_:” blue * ^--------- * +=-()_:”blue * ^-------- * * (2) If starts in whitespace, skip whitespace, then find first non-whitespace* * followed by whitespace * (*) Pages actually uses the character class of the first non-whitespace * encountered. * * (3) If starts in a non-whitespace, non alphanumerical character, find the first * whitespace * */ function wordBoundaryOffset(model, offset, direction) { if (model.at(offset).mode !== 'text') return offset; const dir = direction === 'backward' ? -1 : +1; let result; if (LETTER_AND_DIGITS.test(model.at(offset).value)) { // (1) We start with an alphanumerical character let i = offset; let match; do { match = model.at(i).mode === 'text' && LETTER_AND_DIGITS.test(model.at(i).value); i += dir; } while (model.at(i) && match); result = model.at(i) ? i - 2 * dir : i - dir; } else if (/\s/.test(model.at(offset).value)) { // (2) We start with whitespace // Skip whitespace let i = offset; while (model.at(i) && model.at(i).mode === 'text' && /\s/.test(model.at(i).value)) i += dir; if (!model.at(i)) { // We've reached the end result = i - dir; } else { let match = true; do { match = model.at(i).mode === 'text' && !/\s/.test(model.at(i).value); i += dir; } while (model.at(i) && match); result = model.at(i) ? i - 2 * dir : i - dir; } } else { // (3) let i = offset; // Skip non-whitespace while (model.at(i) && model.at(i).mode === 'text' && !/\s/.test(model.at(i).value)) i += dir; result = model.at(i) ? i : i - dir; let match = true; while (model.at(i) && match) { match = model.at(i).mode === 'text' && /\s/.test(model.at(i).value); if (match) result = i; i += dir; } result = model.at(i) ? i - 2 * dir : i - dir; } return result - (dir > 0 ? 0 : 1); } /** * Keyboard navigation with alt/option: * Move the insertion point to the next/previous point of interest. * A point of interest is an atom of a different type (mbin, mord, etc...) * than the current focus. * If `extend` is true, the selection will be extended. Otherwise, it is * collapsed, then moved. * @todo array */ function skip(model, direction, options) { var _a, _b, _c, _d, _e, _f, _g; const previousPosition = model.position; if (!((_a = options === null || options === void 0 ? void 0 : options.extend) !== null && _a !== void 0 ? _a : false)) model.collapseSelection(direction); let atom = model.at(model.position); if (direction === 'forward') { if (atom.type === 'msubsup') { atom = atom.rightSibling; if (!atom) atom = model.at(model.position + 1); } else atom = model.at(model.position + 1); } if (!atom) { model.announce('plonk'); return false; } let offset = model.offsetOf(atom); if (atom instanceof TextAtom) { // // We're in a text zone, skip word by word // offset = wordBoundaryOffset(model, offset, direction); } else if (atom instanceof LatexAtom) { // // We're in a LaTeX mode zone, skip suggestion // if (atom.isSuggestion) { // Since suggestions are always at the end, this must be forward console.assert(direction === 'forward'); while (atom && atom instanceof LatexAtom) { atom.isSuggestion = false; offset = model.offsetOf(atom); atom = atom.rightSibling; } } else if (direction === 'forward') { atom = atom.rightSibling; if (!atom || !(atom instanceof LatexAtom)) { // At the end of the command model.announce('plonk'); return false; } while (atom && atom instanceof LatexAtom && /[a-zA-Z\*]/.test(atom.value)) { offset = model.offsetOf(atom); atom = atom.rightSibling; } } else { atom = atom.leftSibling; if (!atom || !(atom instanceof LatexAtom)) { // At the start of the command model.announce('plonk'); return false; } while (atom && atom instanceof LatexAtom && /[a-zA-Z\*]/.test(atom.value)) { offset = model.offsetOf(atom); atom = atom.leftSibling; } } } else if (direction === 'forward' && atom.type === 'mopen') { // // Right before a 'mopen', skip to the corresponding balanced fence // let level = 0; do { if (atom.type === 'mopen') level += 1; else if (atom.type === 'mclose') level -= 1; atom = atom.rightSibling; } while (!atom.isLastSibling && level !== 0); offset = model.offsetOf(atom.leftSibling); } else if (direction === 'backward' && atom.type === 'mclose') { // // Right after a 'mclose', skip to the corresponding balanced fence // let level = 0; do { if (atom.type === 'mopen') level += 1; else if (atom.type === 'mclose') level -= 1; atom = atom.leftSibling; } while (!atom.isFirstSibling && level !== 0); offset = model.offsetOf(atom); } else if (direction === 'backward') { // // We're in a regular math zone (not before/after a fence) // if (atom.type === 'first') { while (offset > 0 && atom.type === 'first') { offset -= 1; atom = model.at(offset); } } else { const type = atom.type; if (atom.type === 'msubsup') { // If we're after a 'msubsup', skip to its left sibling // (the base of the super/subscript) offset = model.offsetOf(model.at(offset).leftSibling); } offset -= 1; let nextType = (_b = model.at(offset)) === null || _b === void 0 ? void 0 : _b.type; // If (nextType === 'msubsup') { // offset = model.offsetOf(model.at(offset).leftSibling); // } while (offset >= 0 && nextType === type) { if (((_c = model.at(offset)) === null || _c === void 0 ? void 0 : _c.type) === 'msubsup') offset = model.offsetOf(model.at(offset).leftSibling); else offset -= 1; nextType = model.at(offset).type; } } } else { const { type } = atom; // If (atom.type === 'msubsup') { // offset = model.offsetOf(model.at(offset).rightSibling); // } let nextType = (_d = model.at(offset)) === null || _d === void 0 ? void 0 : _d.type; const { lastOffset } = model; while (offset <= lastOffset && (nextType === type || nextType === 'msubsup')) { while (((_e = model.at(offset).rightSibling) === null || _e === void 0 ? void 0 : _e.type) === 'msubsup') offset = model.offsetOf(model.at(offset).rightSibling); offset += 1; nextType = (_f = model.at(offset)) === null || _f === void 0 ? void 0 : _f.type; } offset -= 1; } if ((_g = options === null || options === void 0 ? void 0 : options.extend) !== null && _g !== void 0 ? _g : false) { if (!model.setSelection(model.anchor, offset)) { model.announce('plonk'); return false; } } else { if (offset === model.position) { model.announce('plonk'); return false; } model.position = offset; } model.announce('move', previousPosition); return true; } /** * Handle keyboard navigation (arrow keys) */ function move(model, direction, options) { var _a, _b, _c, _d; options = options !== null && options !== void 0 ? options : { extend: false }; if (direction !== 'forward') { const [from, to] = getCommandSuggestionRange(model); if (from !== undefined && to !== undefined) model.deleteAtoms([from, to]); } if (direction === 'upward') return moveUpward(model, options); if (direction === 'downward') return moveDownward(model, options); const previousPosition = model.position; if (options.extend) return model.extendSelection(direction); if (model.selectionIsPlaceholder) { model.collapseSelection(direction); return move(model, direction); } if (!model.collapseSelection(direction)) { let pos = model.position + (direction === 'forward' ? +1 : -1); // // 1. Handle `captureSelection` and `skipBoundary` // if (pos >= 0 && pos <= model.lastOffset) { if (direction === 'forward') { let atom = model.at(pos); if (atom.inCaptureSelection) { // If in a capture selection, while going forward jump to // after while (!atom.captureSelection) atom = atom.parent; pos = model.offsetOf(atom); } else if (!atom.isFirstSibling && atom.isLastSibling && ((_a = atom.parent) === null || _a === void 0 ? void 0 : _a.skipBoundary)) { // When going forward if next is skipboundary, move 2 if (pos + 1 === model.lastOffset) pos = pos + 1; else { model.position = pos + 1; return move(model, 'forward', options); } } else if (atom instanceof LatexAtom && atom.isSuggestion) atom.isSuggestion = false; } else if (direction === 'backward') { let atom = model.at(pos); if ((_b = atom.parent) === null || _b === void 0 ? void 0 : _b.inCaptureSelection) { // If in a capture selection while going backward, jump to // before while (!atom.captureSelection) atom = atom.parent; pos = Math.max(0, model.offsetOf(atom.leftSibling)); } else if (!atom.isLastSibling && atom.isFirstSibling && ((_c = atom.parent) === null || _c === void 0 ? void 0 : _c.skipBoundary)) { // When going backward, if land on first of group and previous is // skipbounday, move -2 pos = Math.max(0, pos - 1); } } } // // 2. Handle out of bounds // if (pos < 0 || pos > model.lastOffset) { // We're going out of bounds let result = true; // True => perform default handling if (!model.suppressChangeNotifications) result = (_d = model.hooks) === null || _d === void 0 ? void 0 : _d.moveOut(model, direction); if (result) model.announce('plonk'); return result; } // // 3. Handle placeholder // setPositionHandlingPlaceholder(model, pos); } model.announce('move', previousPosition); return true; } function setPositionHandlingPlaceholder(model, pos) { var _a, _b, _c; if (((_a = model.at(pos)) === null || _a === void 0 ? void 0 : _a.type) === 'placeholder') { // We're going right of a placeholder: select it model.setSelection(pos - 1, pos); } else if (((_c = (_b = model.at(pos)) === null || _b === void 0 ? void 0 : _b.rightSibling) === null || _c === void 0 ? void 0 : _c.type) === 'placeholder') { // We're going left of a placeholder: select it model.setSelection(pos, pos + 1); } else model.position = pos; } function moveUpward(model, options) { var _a, _b, _c; const extend = (_a = options === null || options === void 0 ? void 0 : options.extend) !== null && _a !== void 0 ? _a : false; if (!extend) model.collapseSelection('backward'); // Find a target branch // This is to handle the case: `\frac{x}{\sqrt{y}}`. If we're at `y` // we'd expect to move to `x`, even though `\sqrt` doesn't have an 'above' // branch, but one of its ancestor does. let atom = model.at(model.position); while (atom && atom.treeBranch !== 'below' && !(Array.isArray(atom.treeBranch) && atom.parent instanceof ArrayAtom)) atom = atom.parent; // handle navigating through matrices and such if (Array.isArray(atom === null || atom === void 0 ? void 0 : atom.treeBranch) && atom.parent instanceof ArrayAtom) { const arrayAtom = atom.parent; const currentIndex = arrayAtom.array[atom.treeBranch[0]][atom.treeBranch[1]].indexOf(atom); const rowAbove = Math.max(0, atom.treeBranch[0] - 1); const cell = arrayAtom.array[rowAbove][atom.treeBranch[1]]; const targetIndex = Math.min(cell.length - 1, currentIndex); const targetSelection = model.offsetOf(cell[targetIndex]); if (extend) { const [left, right] = model.selection.ranges[0]; // doesn't highlight let newSelection; if (targetSelection < left) { // extending selection upwards newSelection = { ranges: [[targetSelection, right]], direction: 'backward', }; } else { // reducing selection upwards newSelection = { ranges: [[left, targetSelection]], direction: 'forward', }; } model.setSelection(newSelection); // does highlight, has direction error // model.extendSelectionTo( // targetSelection < left ? right : left, // targetSelection // ); // doesn't highlight, doesn't continue selection // model.extendSelectionTo( // targetSelection, // targetSelection < left ? right : left // ); } else { // move cursor to row above setPositionHandlingPlaceholder(model, targetSelection); } model.announce('move up'); } else if (atom) { if (extend) { model.setSelection(model.offsetOf(atom.parent.leftSibling), model.offsetOf(atom.parent)); } else { // If branch doesn't exist, create it const branch = (_b = atom.parent.branch('above')) !== null && _b !== void 0 ? _b : atom.parent.createBranch('above'); // Move to the last atom of the branch setPositionHandlingPlaceholder(model, model.offsetOf(branch[branch.length - 1])); } model.announce('move up'); } else { let result = true; // True => perform default handling if (!model.suppressChangeNotifications) result = (_c = model.hooks) === null || _c === void 0 ? void 0 : _c.moveOut(model, 'upward'); model.announce(result ? 'plonk' : 'line'); return result; } return true; } function moveDownward(model, options) { var _a, _b, _c; const extend = (_a = options === null || options === void 0 ? void 0 : options.extend) !== null && _a !== void 0 ? _a : false; if (!extend) model.collapseSelection('forward'); // Find a target branch // This is to handle the case: `\frac{\sqrt{x}}{y}`. If we're at `x` // we'd expect to move to `y`, even though `\sqrt` doesn't have a 'below' // branch, but one of its ancestor does. let atom = model.at(model.position); while (atom && atom.treeBranch !== 'above' && !(Array.isArray(atom.treeBranch) && atom.parent instanceof ArrayAtom)) atom = atom.parent; // handle navigating through matrices and such if (Array.isArray(atom === null || atom === void 0 ? void 0 : atom.treeBranch) && atom.parent instanceof ArrayAtom) { const arrayAtom = atom.parent; const currentIndex = arrayAtom.array[atom.treeBranch[0]][atom.treeBranch[1]].indexOf(atom); const rowBelow = Math.min(arrayAtom.array.length - 1, atom.treeBranch[0] + 1); const cell = arrayAtom.array[rowBelow][atom.treeBranch[1]]; const targetIndex = Math.min(cell.length - 1, currentIndex); const targetSelection = model.offsetOf(cell[targetIndex]); if (extend) { const [left, right] = model.selection.ranges[0]; let newSelection; if (targetSelection < right) { // reducing selection downwards newSelection = { ranges: [[targetSelection, right]], direction: 'backward', }; } else { // extending selection downwards newSelection = { ranges: [[left, targetSelection]], direction: 'forward', }; } // @todo: setSelection doesn't correctly handle this model.setSelection(newSelection); } else { // move cursor to row below setPositionHandlingPlaceholder(model, targetSelection); } model.announce('move down'); } else if (atom) { if (extend) { model.setSelection(model.offsetOf(atom.parent.leftSibling), model.offsetOf(atom.parent)); } else { // If branch doesn't exist, create it const branch = (_b = atom.parent.branch('below')) !== null && _b !== void 0 ? _b : atom.parent.createBranch('below'); // Move to the last atom of the branch setPositionHandlingPlaceholder(model, model.offsetOf(branch[branch.length - 1])); } model.announce('move down'); } else { let result = true; // True => perform default handling if (!model.suppressChangeNotifications) result = (_c = model.hooks) === null || _c === void 0 ? void 0 : _c.moveOut(model, 'downward'); model.announce(result ? 'plonk' : 'line'); return result; } return true; } register({ deleteAll: (model) => contentWillChange(model, { inputType: 'deleteContent' }) && deleteRange(model, [0, -1], 'deleteContent'), deleteForward: (model) => deleteForward(model), deleteBackward: (model) => deleteBackward(model), deleteNextWord: (model) => contentWillChange(model, { inputType: 'deleteWordForward' }) && deleteRange(model, [model.anchor, wordBoundaryOffset(model, model.position, 'forward')], 'deleteWordForward'), deletePreviousWord: (model) => contentWillChange(model, { inputType: 'deleteWordBackward' }) && deleteRange(model, [model.anchor, wordBoundaryOffset(model, model.position, 'backward')], 'deleteWordBackward'), deleteToGroupStart: (model) => contentWillChange(model, { inputType: 'deleteSoftLineBackward' }) && deleteRange(model, [model.anchor, model.offsetOf(model.at(model.position).firstSibling)], 'deleteSoftLineBackward'), deleteToGroupEnd: (model) => contentWillChange(model, { inputType: 'deleteSoftLineForward' }) && deleteRange(model, [model.anchor, model.offsetOf(model.at(model.position).lastSibling)], 'deleteSoftLineForward'), deleteToMathFieldStart: (model) => contentWillChange(model, { inputType: 'deleteHardLineBackward' }) && deleteRange(model, [model.anchor, 0], 'deleteHardLineBackward'), deleteToMathFieldEnd: (model) => contentWillChange(model, { inputType: 'deleteHardLineForward' }) && deleteRange(model, [model.anchor, -1], 'deleteHardLineForward'), }, { target: 'model', category: 'delete' }); function moveAfterParent(model) { const previousPosition = model.position; if (!model.at(previousPosition).parent) { model.announce('plonk'); return false; } model.position = model.offsetOf(model.at(model.position).parent); model.announce('move', previousPosition); return true; } function superscriptDepth(model) { let result = 0; let atom = model.at(model.position); let wasSuperscript = false; while (atom) { if (!atom.hasEmptyBranch('superscript') || !atom.hasEmptyBranch('subscript')) result += 1; if (!atom.hasEmptyBranch('superscript')) wasSuperscript = true; else if (!atom.hasEmptyBranch('subscript')) wasSuperscript = false; atom = atom.parent; } return wasSuperscript ? result : 0; } function subscriptDepth(model) { let result = 0; let atom = model.at(model.position); let wasSubscript = false; while (atom) { if (!atom.hasEmptyBranch('superscript') || !atom.hasEmptyBranch('subscript')) result += 1; if (!atom.hasEmptyBranch('superscript')) wasSubscript = false; else if (!atom.hasEmptyBranch('subscript')) wasSubscript = true; atom = atom.parent; } return wasSubscript ? result : 0; } /** * Switch the cursor to the superscript and select it. If there is no subscript * yet, create one. */ function moveToSuperscript(model) { var _a; model.collapseSelection(); if (superscriptDepth(model) >= model.mathfield.options.scriptDepth[1]) { model.announce('plonk'); return false; } let target = model.at(model.position); if (target.subsupPlacement === undefined) { // This atom can't have a superscript/subscript: // add an adjacent `msubsup` atom instead. if (((_a = target.rightSibling) === null || _a === void 0 ? void 0 : _a.type) !== 'msubsup') { target.parent.addChildAfter(new SubsupAtom({ style: target.computedStyle }), target); } target = target.rightSibling; } // Ensure there is a superscript branch target.createBranch('superscript'); model.setSelection(model.getSiblingsRange(model.offsetOf(target.superscript[0]))); return true; } /** * Switch the cursor to the subscript and select it. If there is no subscript * yet, create one. */ function moveToSubscript(model) { var _a; model.collapseSelection(); if (subscriptDepth(model) >= model.mathfield.options.scriptDepth[0]) { model.announce('plonk'); return false; } let target = model.at(model.position); if (target.subsupPlacement === undefined) { // This atom can't have a superscript/subscript: // add an adjacent `msubsup` atom instead. if (((_a = model.at(model.position + 1)) === null || _a === void 0 ? void 0 : _a.type) !== 'msubsup') { target.parent.addChildAfter(new SubsupAtom({ style: model.at(model.position).computedStyle, }), target); } target = model.at(model.position + 1); } // Ensure there is a subscript branch target.createBranch('subscript'); model.setSelection(model.getSiblingsRange(model.offsetOf(target.subscript[0]))); return true; } /** * Return an array of tabbable elements, approximately in the order a browser * would (the browsers are inconsistent), which is first by accounting * for non-null tabIndex, then null tabIndex, then document order of focusable * elements. */ function getTabbableElements() { function tabbable(element) { const regularTabbables = []; const orderedTabbables = []; const candidates = [ ...element.querySelectorAll(`input, select, textarea, a[href], button, [tabindex], audio[controls], video[controls], [contenteditable]:not([contenteditable="false"]), details>summary`), ].filter(isNodeMatchingSelectorTabbable); candidates.forEach((candidate, i) => { const candidateTabindex = getTabindex(candidate); if (candidateTabindex === 0) regularTabbables.push(candidate); else { orderedTabbables.push({ documentOrder: i, tabIndex: candidateTabindex, node: candidate, }); } }); return orderedTabbables .sort((a, b) => a.tabIndex === b.tabIndex ? a.documentOrder - b.documentOrder : a.tabIndex - b.tabIndex) .map((a) => a.node) .concat(regularTabbables); } function isNodeMatchingSelectorTabbable(element) { if (!isNodeMatchingSelectorFocusable(element) || isNonTabbableRadio(element) || getTabindex(element) < 0) return false; return true; } function isNodeMatchingSelectorFocusable(node) { if (node.disabled || (node.type === 'hidden' && node.tagName.toUpperCase() === 'INPUT') || isHidden(node)) return false; return true; } function getTabindex(node) { var _a; const tabindexAttr = Number.parseInt((_a = node.getAttribute('tabindex')) !== null && _a !== void 0 ? _a : 'NaN', 10); if (!Number.isNaN(tabindexAttr)) return tabindexAttr; // Browsers do not return `tabIndex` correctly for contentEditable nodes; // so if they don't have a tabindex attribute specifically set, assume it's 0. if (node.contentEditable === 'true') return 0; // In Chrome, <audio controls/> and <video controls/> elements get a default // `tabIndex` of -1 when the 'tabindex' attribute isn't specified in the DOM, // yet they are still part of the regular tab order; in FF, they get a default // `tabIndex` of 0; since Chrome still puts those elements in the regular tab // order, consider their tab index to be 0 if ((node.nodeName === 'AUDIO' || node.nodeName === 'VIDEO') && node.getAttribute('tabindex') === null) return 0; return node.tabIndex; } function isNonTabbableRadio(node) { return (node.tagName.toUpperCase() === 'INPUT' && node.type === 'radio' && !isTabbableRadio(node)); } function getCheckedRadio(nodes, form) { for (const node of nodes) if (node.checked && node.form === form) return node; return null; } function isTabbableRadio(node) { var _a; if (!node.name) return true; const radioScope = (_a = node.form) !== null && _a !== void 0 ? _a : node.ownerDocument; const radioSet = radioScope.querySelectorAll('input[type="radio"][name="' + node.name + '"]'); const checked = getCheckedRadio(radioSet, node.form); return !checked || checked === node; } function isHidden(element) { if (!isBrowser() || element === document.activeElement || element.contains(document.activeElement)) return false; if (getComputedStyle(element).visibility === 'hidden') return true; // Note that browsers generally don't consider the bounding rect // as a criteria to determine if an item is focusable, but we want // to exclude the invisible textareas used to capture keyoard input. const bounds = element.getBoundingClientRect(); if (bounds.width === 0 || bounds.height === 0) return true; while (element) { if (getComputedStyle(element).display === 'none') return true; element = element.parentElement; } return false; } if (!isBrowser()) return []; return tabbable(document.body); } /** * Move to the next/previous placeholder or empty child list. * @return False if no placeholder found and did not move */ function leap(model, dir, callHooks = true) { var _a, _b; const dist = dir === 'forward' ? 1 : -1; if (model.at(model.anchor).type === 'placeholder') { // If we're already at a placeholder, move by one more (the placeholder // is right after the insertion point) move(model, dir); } // Candidate placeholders are atom of type 'placeholder' // or empty children list (except for the root: if the root is empty, // it is not a valid placeholder) const atoms = model.getAllAtoms(Math.max(model.position + dist, 0)); if (dir === 'backward') atoms.reverse(); const placeholders = atoms.filter((atom) => atom.type === 'placeholder' || (atom.treeDepth > 2 && atom.isFirstSibling && atom.isLastSibling)); // If no placeholders were found, call handler or move to the next focusable // element in the document if (placeholders.length === 0) { const handled = !callHooks || !((_b = (_a = model.hooks).tabOut) === null || _b === void 0 ? void 0 : _b.call(_a, model, dir)); if (handled) { model.announce('plonk'); return false; } const tabbable = getTabbableElements(); if (!document.activeElement || tabbable.length === 1) { model.announce('plonk'); return false; } let index = tabbable.indexOf(document.activeElement) + dist; if (document.activeElement instanceof MathfieldElement && moveToNextNestedMathfield(document.activeElement, dir, dist)) return true; if (index < 0) index = tabbable.length - 1; if (index >= tabbable.length) index = 0; if (tabbable[index] instanceof MathfieldElement && moveToNextNestedMathfield(tabbable[index], dir, dist)) return true; tabbable[index].focus(); if (index === 0) { model.announce('plonk'); return false; } return true; } /** * */ function moveToNextNestedMathfield(element, dir, dist) { var _a, _b, _c; const nestedMathfield = [ ...((_b = (_a = element.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('math-field')) !== null && _b !== void 0 ? _b : []), ]; if (nestedMathfield.length) { const activeMathfield = (_c = element.shadowRoot) === null || _c === void 0 ? void 0 : _c.activeElement; console.log(activeMathfield); const activeIndex = nestedMathfield.indexOf(activeMathfield); let newMathfieldIndex = activeIndex + dist; console.log(activeIndex); if (activeIndex < 0 && dir === 'backward') newMathfieldIndex = nestedMathfield.length - 1; if (newMathfieldIndex >= 0 && newMathfieldIndex < nestedMathfield.length) { console.log(newMathfieldIndex, nestedMathfield, 'forward'); nestedMathfield[newMathfieldIndex].focus(); return true; } } return false; } // Set the selection to the next placeholder const previousPosition = model.position; const newPosition = model.offsetOf(placeholders[0]); if (placeholders[0].type === 'placeholder') model.setSelection(newPosition - 1, newPosition); else model.position = newPosition; model.announce('move', previousPosition); return true; } /** * If cursor is currently in: * - superscript: move to subscript, creating it if necessary * - subscript: move to superscript, creating it if necessary * - numerator: move to denominator * - denominator: move to numerator * - otherwise: move to superscript */ register({ moveToOpposite: (model) => { const OPPOSITE_RELATIONS = { superscript: 'subscript', subscript: 'superscript', above: 'below', below: 'above', }; const cursor = model.at(model.position); const { parent } = cursor; if (!parent) { model.announce('plonk'); return false; } const relation = cursor.treeBranch; let oppositeRelation; if (typeof relation === 'string') oppositeRelation = OPPOSITE_RELATIONS[relation]; if (!oppositeRelation) { if (!cursor.subsupPlacement) return moveToSuperscript(model); return moveToSubscript(model); } if (!parent.branch(oppositeRelation)) { // Don't have children of the opposite relation yet // Add them parent.createBranch(oppositeRelation); } return model.setSelection(model.getBranchRange(model.offsetOf(parent), oppositeRelation)); }, moveBeforeParent: (model) => { const { parent } = model.at(model.position); if (!parent) { model.announce('plonk'); return false; } model.position = model.offsetOf(parent); return true; }, moveAfterParent: (model) => moveAfterParent(model), moveToNextPlaceholder: (model) => leap(model, 'forward'), moveToPreviousPlaceholder: (model) => leap(model, 'backward'), moveToNextChar: (model) => move(model, 'forward'), moveToPreviousChar: (model) => move(model, 'backward'), moveUp: (model) => move(model, 'upward'), moveDown: (model) => move(model, 'downward'), moveToNextWord: (model) => skip(model, 'forward'), moveToPreviousWord: (model) => skip(model, 'backward'), moveToGroupStart: (model) => { const pos = model.offsetOf(model.at(model.position).firstSibling); if (pos === model.position) { model.announce('plonk'); return false; } model.position = pos; return true; }, moveToGroupEnd: (model) => { const pos = model.offsetOf(model.at(model.position).lastSibling); if (pos === model.position) { model.announce('plonk'); return false; } model.position = pos; return true; }, moveToMathFieldStart: (model) => { if (model.position === 0) { model.announce('plonk'); return false; } model.position = 0; return true; }, moveToMathFieldEnd: (model) => { if (model.position === model.lastOffset) { model.announce('plonk'); return false; } model.position = model.lastOffset; return true; }, moveToSuperscript: (model) => moveToSuperscript(model), moveToSubscript: (model) => moveToSubscript(model), }, { target: 'model', category: 'selection-anchor' }); /** * Select all the atoms in the current group, that is all the siblings. * When the selection is in a numerator, the group is the numerator. When * the selection is a superscript or subscript, the group is the supsub. * When the selection is in a text zone, the "group" is a word. */ function selectGroup(model) { var _a, _b, _c, _d, _e, _f; if (getMode(model, model.position) === 'text') { let start = Math.min(model.anchor, model.position); let end = Math.max(model.anchor, model.position); // let done = false; while (!done && start > 0) { const atom = model.at(start); if (atom.mode === 'text' && LETTER_AND_DIGITS.test(atom.value)) start -= 1; else done = true; } done = false; while (!done && end <= model.lastOffset) { const atom = model.at(end); if (atom.mode === 'text' && LETTER_AND_DIGITS.test(atom.value)) end += 1; else done = true; } if (done) end -= 1; if (start >= end) { // No word found. Select a single character model.setSelection(end - 1, end); return true; } model.setSelection(start, end); } else { const atom = model.at(model.position); // In a math zone, select all the sibling nodes if (atom.isDigit()) { // In a number, select all the digits let start = Math.min(model.anchor, model.position); let end = Math.max(model.anchor, model.position); // while ((_a = model.at(start)) === null || _a === void 0 ? void 0 : _a.isDigit()) start -= 1; while ((_b = model.at(end)) === null || _b === void 0 ? void 0 : _b.isDigit()) end += 1; model.setSelection(start, end - 1); } else { if (atom.style.variant || atom.style.variantStyle) { let start = Math.min(model.anchor, model.position); let end = Math.max(model.anchor, model.position); let x = (_c = model.at(start)) === null || _c === void 0 ? void 0 : _c.style; while (x && x.variant === atom.style.variant && x.variantStyle === atom.style.variantStyle) { start -= 1; x = (_d = model.at(start)) === null || _d === void 0 ? void 0 : _d.style; } x = (_e = model.at(end)) === null || _e === void 0 ? void 0 : _e.style; while (x && x.variant === atom.style.variant && x.variantStyle === atom.style.variantStyle) { end += 1; x = (_f = model.at(end)) === null || _f === void 0 ? void 0 : _f.style; } model.setSelection(start, end - 1); } else { model.setSelection(model.offsetOf(atom.firstSibling), model.offsetOf(atom.lastSibling)); } } } return true; } register({ selectGroup: (model) => { const result = selectGroup(model); if (!result) model.announce('plonk'); return result; }, selectAll: (model) => model.setSelection(0, model.lastOffset), extendSelectionForward: (model) => { const result = model.extendSelection('forward'); if (!result) model.announce('plonk'); return result; }, extendSelectionBackward: (model) => { const result = model.extendSelection('backward'); if (!result) model.announce('plonk'); return result; }, extendToNextWord: (model) => skip(model, 'forward', { extend: true }), extendToPreviousWord: (model) => skip(model, 'backward', { extend: true }), extendSelectionUpward: (model) => move(model, 'upward', { extend: true }), extendSelectionDownward: (model) => move(model, 'downward', { extend: true }), /** * Extend the selection until the next boundary is reached. A boundary * is defined by an atom of a different type (mbin, mord, etc...) * than the current focus. For example, in "1234+x=y", if the focus is between * "1" and "2", invoking `extendToNextBoundary_` would extend the selection * to "234". */ extendToNextBoundary: (model) => skip(model, 'forward', { extend: true }), /** * Extend the selection until the previous boundary is reached. A boundary * is defined by an atom of a different type (mbin, mord, etc...) * than the current focus. For example, in "1+23456", if the focus is between * "5" and "6", invoking `extendToPreviousBoundary` would extend the selection * to "2345". */ extendToPreviousBoundary: (model) => skip(model, 'backward', { extend: true }), extendToGroupStart: (model) => { const result = model.setSelection(model.anchor, model.offsetOf(model.at(model.position).firstSibling)); if (!result) model.announce('plonk'); return result; }, extendToGroupEnd: (model) => { const result = model.setSelection(model.anchor, model.offsetOf(model.at(model.position).lastSibling)); if (!result) model.announce('plonk'); return result; }, extendToMathFieldStart: (model) => { const result = model.setSelection(model.anchor, 0); if (!result) model.announce('plonk'); return result; }, extendToMathFieldEnd: (model) => { const result = model.setSelection(model.anchor, model.lastOffset); if (!result) model.announce('plonk'); return result; }, }, { target: 'model', category: 'selection-extend' }); /** * This modules handles low-level keyboard events and normalize them across * browsers. * * See https://dvcs.w3.org/hg/d4e/raw-file/tip/key-event-test.html * (also at https://w3c.github.io/uievents/tools/key-event-viewer.html) * * * - **KeyboardEvent.key** (the printable value associated with the key or a string * for special keys) * https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values * https://www.w3.org/TR/uievents-key/ * * - **KeyboardEvent.code** (the physical key being pressed. On an AZERTY keyboard * the key labelled "A" is KeyQ) * https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code/code_values * https://www.w3.org/TR/uievents-code/ * * Note: * - `charCode`, `keyCode` and `which` are deprecated * * For background, see this info regarding keybinding in VSCode: * - https://github.com/microsoft/vscode/tree/master/src/vs/workbench/services/keybinding * - https://github.com/microsoft/vscode/wiki/Keybinding-Issues */ const PRINTABLE_KEYCODE = new Set([ 'Backquote', 'Digit0', 'Digit1', 'Digit2', 'Digit3', 'Digit4', 'Digit5', 'Digit6', 'Digit7', 'Digit8', 'Digit9', 'Minus', 'Equal', 'IntlYen', 'KeyQ', 'KeyW', 'KeyE', 'KeyR', 'KeyT', 'KeyY', 'KeyU', 'KeyI', 'KeyO', 'KeyP', 'BracketLeft', 'BracketRight', 'Backslash', 'KeyA', 'KeyS', 'KeyD', 'KeyF', 'KeyG', 'KeyH', 'KeyJ', 'KeyK', 'KeyL', 'Semicolon', 'Quote', 'IntlBackslash', 'KeyZ', 'KeyX', 'KeyC', 'KeyV', 'KeyB', 'KeyN', 'KeyM', 'Comma', 'Period', 'Slash', 'IntlRo', 'Space', 'Numpad0', 'Numpad1', 'Numpad2', 'Numpad3', 'Numpad4', 'Numpad5', 'Numpad6', 'Numpad7', 'Numpad8', 'Numpad9', 'NumpadAdd', 'NumpadComma', 'NumpadDecimal', 'NumpadDivide', 'NumpadEqual', 'NumpadHash', 'NumpadMultiply', 'NumpadParenLeft', 'NumpadParenRight', 'NumpadStar', 'NumpadSubstract', ]); function mightProducePrintableCharacter(evt) { if (evt.ctrlKey || evt.metaKey) { // ignore ctrl/cmd-combination but not shift/alt-combinations return false; } // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values if (evt.key === 'Dead') return false; // When issued via a composition, the `code` field is empty if (evt.code === '') return true; return PRINTABLE_KEYCODE.has(evt.code); } /** * Create a normalized representation of a keyboard event, * i.e., key code and modifier keys. For example: * - `ctrl+Shift+alt+[KeyF]` * * Note: the key code corresponds to a physical key, e.g. 'KeyQ' is * the key labeled 'A' on a French keyboard * */ function keyboardEventToString(evt) { evt = normalizeKeyboardEvent(evt); const modifiers = []; if (evt.ctrlKey) modifiers.push('ctrl'); if (evt.metaKey) modifiers.push('meta'); if (evt.altKey) modifiers.push('alt'); if (evt.shiftKey) modifiers.push('shift'); // If no modifiers, simply return the key name if (modifiers.length === 0) return '[' + evt.code + ']'; modifiers.push('[' + evt.code + ']'); return modifiers.join('+'); } /** * Setup to capture the keyboard events from a `TextArea` and redispatch them to * handlers. * * In general, commands (arrows, delete, etc..) should be handled * in the `keystroke()` handler while text input should be handled in * `typedtext()`. * * @param {HTMLElement} textarea A `TextArea` element that will capture the keyboard * events. While this element will usually be a `TextArea`, it could be any * element that is focusable and can receive keyboard events. * @param {Object.<string, any>} handlers * @param {function} handlers.keystroke invoked on a key down event, including * for special keys such as ESC, arrow keys, tab, etc... and their variants * with modifiers. * @param {function} handlers.typedtext invoked on a keypress or other events * when a key corresponding to a character has been pressed. This include `a-z`, * `0-9`, `{}`, `^_()`, etc... * This does not include arrow keys, tab, etc... but does include 'space' * When a 'character' key is pressed, both `keystroke()` and `typedtext()` will * be invoked. When a control/function key is pressed, only `keystroke()` will * be invoked. In some cases, for example when using input methods or entering * emoji, only `typedtext()` will be invoked. */ function delegateKeyboardEvents(textarea, element, handlers) { let keydownEvent = null; let keypressEvent = null; let compositionInProgress = false; let focusInProgress = false; let blurInProgress = false; // This callback is invoked after a keyboard event has been processed // by the textarea let callbackTimeoutID; function defer(cb) { clearTimeout(callbackTimeoutID); callbackTimeoutID = setTimeout(() => { clearTimeout(callbackTimeoutID); cb(); }); } function handleTypedText() { // Some browsers (Firefox, Opera) fire a keypress event for commands // such as cmd+C where there might be a non-empty selection. // We need to ignore these. if (textarea.selectionStart !== textarea.selectionEnd) return; const text = textarea.value; textarea.value = ''; if (text.length > 0) handlers.typedText(text); } const target = textarea; target.addEventListener('keydown', (event) => { // "Process" key indicates commit of IME session (on Firefox) // It's handled with compositionEnd so it can be safely ignored if (compositionInProgress || event.key === 'Process' || event.code === 'CapsLock' || /(Control|Meta|Alt|Shift)(Left|Right)/.test(event.code)) { keydownEvent = null; return; } keydownEvent = event; keypressEvent = null; if (!handlers.keystroke(keyboardEventToString(event), event)) { keydownEvent = null; textarea.value = ''; } else if (isTouchCapable()) { // If we did not create a text-area because we're on a mobile // device and we don't want to use the OS virtual keyboard, capture // the key events possibly coming from an attached hardware keyboard handlers.typedText(event.key); } }, true); target.addEventListener('keypress', (event) => { if (compositionInProgress) return; // If this is not the first keypress after a keydown, that is, // if this is a repeated keystroke, call the keystroke handler. if (!compositionInProgress) { if (keydownEvent && keypressEvent) handlers.keystroke(keyboardEventToString(keydownEvent), keydownEvent); keypressEvent = event; defer(handleTypedText); } }, true); target.addEventListener('keyup', () => { if (compositionInProgress) return; // If we've received a keydown, but no keypress, check what's in the // textarea field. if (keydownEvent && !keypressEvent) handleTypedText(); }, true); target.addEventListener('paste', (event) => { // In some cases (Linux browsers), the text area might not be focused // when doing a middle-click paste command. textarea.focus(); textarea.value = ''; handlers.paste(event); }, true); target.addEventListener('cut', (ev) => handlers.cut(ev), true); target.addEventListener('copy', (ev) => handlers.copy(ev), true); target.addEventListener('blur', (event) => { var _a, _b, _c; // If we're attempting to focus the mathfield (which can happen on iOS if // clicking right on the border of the mathfield) ignore it // (preventDefault on the event doesn't work) if (((_b = (_a = event['relatedTarget']) === null || _a === void 0 ? void 0 : _a['_mathfield']) === null || _b === void 0 ? void 0 : _b['element']) === element) { textarea.focus(); return; } // If the scrim is up, ignore blur (while the alternate key panel is up) const scrimState = (_c = Scrim.scrim) === null || _c === void 0 ? void 0 : _c.state; if (scrimState === 'open' || scrimState === 'opening') { event.preventDefault(); event.stopPropagation(); return; } // If the relatedTarget (the element that is gaining the focus) // is contained in our shadow host, ignore the blur event if (event.relatedTarget === event.target.getRootNode().host) { event.preventDefault(); event.stopPropagation(); return; } if (blurInProgress || focusInProgress) return; blurInProgress = true; keydownEvent = null; keypressEvent = null; if (handlers.blur) handlers.blur(); blurInProgress = false; }, true); target.addEventListener('focus', (_ev) => { if (blurInProgress || focusInProgress) return; focusInProgress = true; if (handlers.focus) handlers.focus(); focusInProgress = false; }, true); target.addEventListener('compositionstart', (event) => { compositionInProgress = true; textarea.value = ''; if (handlers.compositionStart) handlers.compositionStart(event.data); }, true); target.addEventListener('compositionupdate', (ev) => { if (!compositionInProgress) return; if (handlers.compositionUpdate) handlers.compositionUpdate(ev.data); }, true); target.addEventListener('compositionend', (ev) => { textarea.value = ''; if (!compositionInProgress) return; compositionInProgress = false; if (handlers.compositionEnd) handlers.compositionEnd(ev.data); }, true); target.addEventListener('beforeinput', (ev) => { ev.stopImmediatePropagation(); }); // The `input` handler gets called when the field is changed, // but no other relevant events have been triggered // for example with emoji input... target.addEventListener('input', (ev) => { if (compositionInProgress) return; // If this was an `input` event sent as a result of a commit of // IME, ignore it. // (This is what FireFox does, even though the spec says it shouldn't happen) // See https://github.com/w3c/uievents/issues/202 if (ev.inputType === 'insertCompositionText') return; // Paste is handled in paste handler if (ev.inputType === 'insertFromPaste') { ev.preventDefault(); ev.stopPropagation(); return; } defer(handleTypedText); // Do not propagate the event (it crosses the shadow dom barrier) ev.preventDefault(); ev.stopPropagation(); }); return { cancelComposition: () => { const savedBlur = handlers.blur; const savedFocus = handlers.focus; handlers.blur = null; handlers.focus = null; textarea.blur(); textarea.focus(); handlers.blur = savedBlur; handlers.focus = savedFocus; }, blur: () => { if (typeof textarea.blur === 'function') textarea.blur(); }, focus: () => { if (typeof textarea.focus === 'function') textarea.focus(); }, hasFocus: () => { return deepActiveElement() === textarea; }, setValue: (value) => { if (value) { textarea.value = value; // The textarea may be a span (on mobile, for example), so check that // it has a select() before calling it. if (deepActiveElement() === textarea && typeof textarea.select === 'function') textarea.select(); } else { textarea.value = ''; textarea.setAttribute('aria-label', ''); } }, setAriaLabel: (value) => { textarea.setAttribute('aria-label', 'after: ' + value); }, moveTo: (x, y) => { textarea.style.top = `${y}px`; textarea.style.left = `${x}px`; }, }; } function deepActiveElement() { var _a; if (!isBrowser()) return null; let a = document.activeElement; while ((_a = a === null || a === void 0 ? void 0 : a.shadowRoot) === null || _a === void 0 ? void 0 : _a.activeElement) a = a.shadowRoot.activeElement; return a; } function eventToChar(evt) { var _a; if (!evt) return ''; let result; if (evt.key === 'Unidentified') { // On Android, the evt.key seems to always be 'Unidentified'. // Get the value entered in the event target if (evt.target) result = evt.target.value; } result = (_a = result !== null && result !== void 0 ? result : evt.key) !== null && _a !== void 0 ? _a : evt.code; if (/^(Dead|Return|Enter|Tab|Escape|Delete|PageUp|PageDown|Home|End|Help|ArrowLeft|ArrowRight|ArrowUp|ArrowDown)$/.test(result)) result = ''; return result; } class UndoManager { constructor(model) { this.recording = false; this.canCoalesce = false; this.model = model; this.reset(); } // Maximum number of undo/redo states get maximumDepth() { return 1000; } reset() { this.stack = []; this.index = -1; } startRecording() { this.recording = true; } canUndo() { return this.index > 0; } canRedo() { return this.index !== this.stack.length - 1; } undo(options) { if (!this.canUndo()) return; if (typeof (options === null || options === void 0 ? void 0 : options.onUndoStateWillChange) === 'function') options.onUndoStateWillChange(this.model.mathfield, 'undo'); this.restore(this.stack[this.index - 1], { ...options, type: 'undo' }); this.index -= 1; if (options && typeof options.onUndoStateDidChange === 'function') options.onUndoStateDidChange(this.model.mathfield, 'undo'); this.canCoalesce = false; } redo(options) { if (!this.canRedo()) return; if (typeof (options === null || options === void 0 ? void 0 : options.onUndoStateWillChange) === 'function') options.onUndoStateWillChange(this.model.mathfield, 'redo'); this.index += 1; this.restore(this.stack[this.index], { ...options, type: 'redo' }); if (options && typeof options.onUndoStateDidChange === 'function') options.onUndoStateDidChange(this.model.mathfield, 'redo'); this.canCoalesce = false; } pop() { if (!this.canUndo()) return; this.index -= 1; this.stack.pop(); } /** * Push a snapshot of the content and selection of the mathfield onto the * undo stack so that it can potentially be reverted to later. */ snapshot(options) { if (!this.recording) return; if (typeof (options === null || options === void 0 ? void 0 : options.onUndoStateWillChange) === 'function') options.onUndoStateWillChange(this.model.mathfield, 'snapshot'); // Drop any entries that are part of the redo stack this.stack.splice(this.index + 1, this.stack.length - this.index - 1); // Add a new entry this.stack.push({ content: this.model.root.toJson(), selection: this.model.selection, }); this.index++; // If we've reached the maximum number of undo operations, forget the // oldest one. if (this.stack.length > this.maximumDepth) this.stack.shift(); if (typeof (options === null || options === void 0 ? void 0 : options.onUndoStateDidChange) === 'function') options.onUndoStateDidChange(this.model.mathfield, 'snapshot'); this.canCoalesce = false; } snapshotAndCoalesce(options) { if (!this.recording) return; if (this.canCoalesce) this.pop(); this.snapshot(options); this.canCoalesce = true; } /** * Return an object capturing the state of the content and selection of the * mathfield. Pass this object to restore() to reset the value of the math * field to this saved value. This does not affect the undo stack. */ save() { return { content: this.model.root.toJson(), selection: this.model.selection, }; } /** * Set the content and selection of the mathfield to a value previously * captured with save() or stored in the undo stack. * This does not affect the undo stack. */ restore(state, options) { const wasSuppressing = this.model.suppressChangeNotifications; if (options.suppressChangeNotifications !== undefined) { this.model.suppressChangeNotifications = options.suppressChangeNotifications; } let changeOption = {}; if (options.type === 'undo') changeOption = { inputType: 'historyUndo' }; if (options.type === 'redo') changeOption = { inputType: 'historyRedo' }; if (contentWillChange(this.model, changeOption)) { // Restore the content and selection this.model.root = fromJson(state.content); this.model.selection = state.selection; contentDidChange(this.model, changeOption); } this.model.suppressChangeNotifications = wasSuppressing; } } /** * Convert the atoms before the anchor to 'text' mode * @param count - how many atoms back to look at * @param {function} until - callback to indicate when to stop * @private */ function convertLastAtomsToText(model, count, until) { if (typeof count === 'function') { until = count; count = Infinity; } if (count === undefined) count = Infinity; let i = model.position; let done = false; let text = ''; while (!done) { const atom = model.at(i); done = count === 0 || atom === undefined || atom.mode !== 'math' || !(/mord|textord|mpunct/.test(atom.type) || (atom.type === 'mop' && /[a-zA-Z]+/.test(atom.value))) || !atom.hasEmptyBranch('superscript') || !atom.hasEmptyBranch('subscript') || (typeof until === 'function' && !until(atom)); if (!done) { atom.mode = 'text'; atom.command = atom.value; atom.verbatimLatex = undefined; text += atom.value; } i -= 1; count -= 1; } contentDidChange(model, { data: text, inputType: 'insertText' }); } /** * Convert the atoms before the anchor to 'math' mode 'mord' * @param {number} count - how many atoms back to look at * @param {function} until - callback to indicate when to stop * @private */ function convertLastAtomsToMath(model, count, until) { if (typeof count === 'function') { until = count; count = Infinity; } if (count === undefined) count = Infinity; let i = model.position; let done = false; const data = []; while (!done) { const atom = model.at(i); done = count === 0 || !atom || atom.isFirstSibling || atom.mode !== 'text' || atom.value === ' ' || (until && !until(atom)); if (!done) { data.push(atom.serialize({ defaultMode: 'math' })); atom.mode = 'math'; } i -= 1; count -= 1; } removeIsolatedSpace(model); contentDidChange(model, { data: joinLatex(data), inputType: 'insertText' }); } // Export function applyMode( // _mathfield: MathfieldPrivate, // _range: Range, // _mode: ParseMode // ): boolean { // const model = mathfield.model; // There's a mode ('text', 'math', 'command') change // if (model.selectionIsCollapsed) { // // Nothing selected // mathfield.switchMode(mode as ParseMode); // return // } // Convert the selection from one mode to another // const previousMode = mathfield.mode; // const targetMode = // (getMode(model, model.position) ?? // mathfield.options.defaultMode) === 'math' // ? 'text' // : 'math'; // let convertedSelection = mathfield.getValue( // mathfield.selection, // 'ascii-math' // ); // if (targetMode === 'math' && /^"[^"]+"$/.test(convertedSelection)) { // convertedSelection = convertedSelection.slice(1, -1); // } // mathfield.insert(convertedSelection, { // mode: targetMode, // selectionMode: 'item', // format: targetMode === 'text' ? 'text' : 'ascii-math', // }); // mathfield.mode = targetMode; // const [groupStart, groupEnd] = model.getSiblingsRange( // model.position // ); // const first = Math.min(model.anchor, model.position); // const last = Math.max(model.anchor, model.position); // if ( // groupStart >= first && // groupStart <= last && // groupEnd >= first && // groupEnd <= last // ) { // // The entire group was selected. Adjust parent mode if // // appropriate // const parent = model.at(model.position).parent; // if ( // parent && // (parent.type === 'group' || parent.type === 'root') // ) { // parent.mode = targetMode; // } // } // // Notify of mode change // if ( // mathfield.mode !== previousMode && // typeof mathfield.options.onModeChange === 'function' // ) { // mathfield.options.onModeChange(mathfield, mathfield.mode); // } // } // return false; // } /** * Going backwards from the anchor, if a text zone consisting of a single * space character is found (i.e. it is surrounded by math zone), * remove it. */ function removeIsolatedSpace(model) { var _a; let i = model.position - 1; while (i >= 0 && ((_a = model.at(i)) === null || _a === void 0 ? void 0 : _a.mode) === 'math') i -= 1; if (i < 0) return; // If the atom before the last one converted is a // text mode space, preceded by a math mode atom, // remove the space if (model.at(i).mode === 'text' && model.at(i).value === ' ' && model.at(i - 1).mode === 'math') { model.at(i - 1).parent.removeChild(model.at(i - 1)); // We need to adjust the selection after doing some surgery on the atoms list // But we don't want to receive selection notification changes // which could have a side effect of changing the mode :( const save = model.suppressChangeNotifications; model.suppressChangeNotifications = true; model.position -= 1; model.suppressChangeNotifications = save; contentDidChange(model, { inputType: 'deleteContent' }); } } /** * Return the characters before the insertion point that could potentially be * turned into text mode. * This excludes things like 'mop' (e.g. \sin) */ function getTextBeforePosition(model) { // Going backwards, accumulate let result = ''; let i = model.position; let done = false; while (!done) { const atom = model.at(i); done = !(atom && (atom.mode === 'text' || (atom.mode === 'math' && /mord|textord|mpunct/.test(atom.type)))); if (!done) result = atom.value + result; i -= 1; } return result; } /** * Consider whether to switch mode give the content before the insertion point * and the character being input * * @param keystroke * @param evt - a Event corresponding to the keystroke * @return true if the mode should change */ function smartMode(mathfield, keystroke, evt) { if (mathfield.smartModeSuppressed) return false; const { model } = mathfield; // Are we at the end of a group? if (!model.at(model.position).isLastSibling) return false; // Is there an event that would produce a printable char? // (i.e. not an arrow key, etc...) if (!evt || !mightProducePrintableCharacter(evt)) return false; const c = eventToChar(evt); if (!model.selectionIsCollapsed) { // There is a selection if (mathfield.mode === 'text') { // If the character is '/' or '_' or '^', switch to 'math' if (/[/_^]/.test(c)) return true; } return false; } const context = getTextBeforePosition(model) + c; if (mathfield.mode === 'text') { // We're in text mode. Should we switch to math? if (keystroke === 'Esc' || /[/\\]/.test(c)) { // If this is a command for a fraction, // or the '\' command mode key // switch to 'math' return true; } if (/[\^_]/.test(c)) { // If this is a superscript or subscript // switch to 'math' if (/(^|\s)[a-zA-Z][^_]$/.test(context)) { // If left hand context is a single letter, // convert it to math convertLastAtomsToMath(model, 1); } return true; } // If this is a closing matching fence // switch to 'math' mode const lFence = { ')': '(', '}': '{', ']': '[' }[c]; const { parent } = model.at(model.position); if (lFence && parent instanceof LeftRightAtom && parent.leftDelim === lFence) return true; if (/(^|[^a-zA-Z])(a|I) $/.test(context)) { // Single letters that are valid words in the current language // Do nothing. @todo: localization return false; } if (/[$€£₤₺¥¤฿¢₡₧₨₹₩₱]/u.test(c)) { // A currency symbol. // Switch to math mode return true; } if (/(^|[^a-zA-Z'’])[a-zA-Z] $/.test(context)) { // An isolated letter, followed by a space: // Convert the letter to math, stay in text mode. convertLastAtomsToMath(model, 1); return false; } if (/\D\.[^\d\s]$/.test(context)) { // A period followed by something other than space or a digit // and not preceded by a digit. // We thought this was a text period, but turns out it's not // Turn it into a \cdot convertLastAtomsToMath(model, 1); const atom = model.at(model.position); atom.value = '⋅'; // Centered dot atom.style.variant = 'normal'; // @revisit. Was 'auto'. Check for proper conversion. atom.command = '\\cdot'; atom.verbatimLatex = undefined; contentDidChange(model, { data: '\\cdot', inputType: 'insertText' }); return true; } if (/(^|\s)[a-zA-Z][^a-zA-Z]$/.test(context)) { // Single letter (x), followed by a non-letter (>, =...) convertLastAtomsToMath(model, 1); return true; } if (/\.\d$/.test(context)) { // If the new character is a digit, // and it was preceded by a dot (which may have been converted // to text) // turn the dot back into 'math' convertLastAtomsToMath(model, 1); return true; } if (/\([\d+\-.]$/.test(context)) { // An open paren followed by a number // Turn the paren back to math and switch. convertLastAtomsToMath(model, 1); return true; } if (/\([a-z][,;]$/.test(context)) { // An open paren followed by a single letter, then a "," or ";" // Turn the paren back and letter to math and switch. convertLastAtomsToMath(model, 2); return true; } // The tests above can look behind and change what had previously // been entered. Now, let's just look at the typed character. if (/[\d+\-=><*|]$/.test(c)) { // If this new character looks like a number, // or a relational operator (=, <, >) // or a "*" or "|" // (note that <=, >=, etc... are handled separately as shortcuts) // switch to 'math' removeIsolatedSpace(model); return true; } } else { // We're in math mode. Should we switch to text? if (keystroke === '[Space]') { convertLastAtomsToText(model, undefined, (a) => /[a-z][:,;.]$/.test(a.value)); return true; } if (/[a-zA-Z]{3,}$/.test(context) && !/(dxd|abc|xyz|uvw)$/.test(context)) { // A sequence of three characters // (except for some exceptions) // Convert them to text. convertLastAtomsToText(model, undefined, (a) => /[a-zA-Z]/.test(a.value)); return true; } if (/(^|\W)(if)$/i.test(context)) { // @todo localization convertLastAtomsToText(model, 1); return true; } if (/(\u0393|\u0394|\u0398|\u039B|\u039E|\u03A0|\u03A3|\u03A5|\u03A6|\u03A8|\u03A9|[\u03B1-\u03C9]|\u03D1|\u03D5|\u03D6|\u03F1|\u03F5){3,}$/u.test(context) && !/(αβγ)$/.test(context)) { // A sequence of three *greek* characters // (except for one exception) // Convert them to text. convertLastAtomsToText(model, undefined, (a) => /(:|,|;|.|\u0393|\u0394|\u0398|\u039B|\u039E|\u03A0|\u03A3|\u03A5|\u03A6|\u03A8|\u03A9|[\u03B1-\u03C9]|\u03D1|\u03D5|\u03D6|\u03F1|\u03F5)/u.test(a.value)); return true; } if (c === '?') { // If the last character is a question mark, // turn it to 'text' return true; } if (c === '.' && !/[\d-+]\.$/.test(context)) { // A period after something other than a digit (or minus) return true; } } return false; } /* eslint-disable no-new */ class MathModeEditor extends ModeEditor { constructor() { super('math'); } onPaste(mathfield, ev) { if (!ev.clipboardData) return false; if (!contentWillChange(mathfield.model, { dataTransfer: ev.clipboardData, inputType: 'insertFromPaste', })) return false; let text = ''; let format = 'auto'; // // 1/ Try to get serialized atoms // try { const atomJson = JSON.parse(ev.clipboardData.getData('application/json+mathlive')); if (atomJson && Array.isArray(atomJson)) { mathfield.snapshot(); const atoms = fromJson(atomJson); const { model } = mathfield; if (!model.selectionIsCollapsed) model.deleteAtoms(range(model.selection)); const cursor = model.at(model.position); cursor.parent.addChildrenAfter(atoms, cursor); model.position = model.offsetOf(atoms[atoms.length - 1]); contentDidChange(model, { inputType: 'insertFromPaste' }); requestUpdate(mathfield); ev.preventDefault(); ev.stopPropagation(); return true; } } catch { } // // 2/ Try to get a MathJSON data type // const json = ev.clipboardData.getData('application/json'); if (json) { const expr = JSON.parse(json); if (typeof expr === 'object' && 'latex' in expr && expr.latex) text = expr.latex; if (!text) { try { const box = mathfield.computeEngine.box(expr); if (!box.has('Error')) text = box.latex; } catch { text = ''; } } if (!text) format = 'latex'; } // // 3/ Try to get raw LaTeX // if (!text) { text = ev.clipboardData.getData('application/x-latex'); if (text) format = 'latex'; } // // 4/ If that didn't work, try some plain text // (could be LaTeX, could be MathASCII) // if (!text) text = ev.clipboardData.getData('text/plain'); if (text) { mathfield.snapshot(); let wasLatex; [wasLatex, text] = trimModeShiftCommand(text); if (format === 'auto' && wasLatex) format = 'latex'; if (this.insert(mathfield.model, text, { smartFence: mathfield.options.smartFence, colorMap: mathfield.colorMap, backgroundColorMap: mathfield.backgroundColorMap, format, })) requestUpdate(mathfield); ev.preventDefault(); ev.stopPropagation(); return true; } return false; } insert(model, input, options) { var _a, _b; const data = typeof input === 'string' ? input : model.mathfield.computeEngine.box(input).latex; if (!contentWillChange(model, { data, inputType: 'insertText' })) return false; if (!options.insertionMode) options.insertionMode = 'replaceSelection'; if (!options.selectionMode) options.selectionMode = 'placeholder'; if (!options.format) options.format = 'auto'; options.macros = (_a = options.macros) !== null && _a !== void 0 ? _a : model.options.macros; // // Try to insert a smart fence. // if (!((_b = options.smartFence) !== null && _b !== void 0 ? _b : false)) { // When smartFence is turned off, only do a "smart" fence insert // if we're inside a `leftright`, at the last char if (options.insertionMode !== 'replaceAll') { const { parent } = model.at(model.position); if (parent instanceof LeftRightAtom && parent.rightDelim === '?' && model.at(model.position).isLastSibling && typeof input === 'string' && /^[)}\]|]$/.test(input)) { parent.isDirty = true; parent.rightDelim = input; model.position += 1; selectionDidChange(model); contentDidChange(model, { data, inputType: 'insertText' }); return true; } } } else if (model.selectionIsCollapsed && typeof input === 'string' && insertSmartFence(model, input, options.style)) return true; const { suppressChangeNotifications } = model; if (options.suppressChangeNotifications) model.suppressChangeNotifications = true; const contentWasChanging = model.suppressChangeNotifications; model.suppressChangeNotifications = true; // // Save the content of the selection, if any // const args = {}; args[0] = model.getValue(model.selection, 'latex-unstyled'); args['?'] = '\\placeholder{}'; args['@'] = args['?']; // // Delete any selected items // if (options.insertionMode === 'replaceSelection' && !model.selectionIsCollapsed) model.deleteAtoms(range(model.selection)); else if (options.insertionMode === 'replaceAll') { model.root.setChildren([], 'body'); model.position = 0; } else if (options.insertionMode === 'insertBefore') model.collapseSelection('backward'); else if (options.insertionMode === 'insertAfter') model.collapseSelection('forward'); // // Delete any placeholders before or after the insertion point // if (!model.at(model.position).isLastSibling && model.at(model.position + 1).type === 'placeholder') { // Before a `placeholder` model.deleteAtoms([model.position, model.position + 1]); } else if (model.at(model.position).type === 'placeholder') { // After a `placeholder` model.deleteAtoms([model.position - 1, model.position]); } // // Calculate the implicit argument (#@) // if (args[0]) { // There was a selection, we'll use it for #@ args['@'] = args[0]; } else if (typeof input === 'string' && /(^|[^\\])#@/.test(input)) { // We'll use the preceding `mord`s or text mode atoms for it (implicit argument) const offset = getImplicitArgOffset(model); if (offset >= 0) { args['@'] = model.getValue(offset, model.position, 'latex-unstyled'); model.deleteAtoms([offset, model.position]); } } if (!args[0]) args[0] = args['?']; let usedArg = false; const argFunction = (arg) => { usedArg = true; return args[arg]; }; const [format, newAtoms] = convertStringToAtoms$1(model, input, argFunction, options); if (!newAtoms) return false; const placeholdersFound = findPlaceholders(newAtoms); const fillInTheBlankPlaceholders = placeholdersFound.filter((atom) => atom.placeholderId && !model.mathfield._placeholders.has(atom.placeholderId)); // Remove placeholders that have a matching placeholder ID // (those are placeholders used for "fill-in-the-blank") const idsFound = placeholdersFound.map((atom) => atom.placeholderId); [...model.mathfield._placeholders.keys()] .filter((placeholderId) => !idsFound.includes(placeholderId)) .forEach((placeholderId) => { var _a; if (model.mathfield._placeholders.has(placeholderId)) { (_a = model.mathfield._placeholders.get(placeholderId)) === null || _a === void 0 ? void 0 : _a.field.remove(); model.mathfield._placeholders.delete(placeholderId); } }); fillInTheBlankPlaceholders.forEach((placeholder) => { var _a, _b; console.assert(!!placeholder.placeholderId && !model.mathfield._placeholders.has(placeholder.placeholderId)); const element = new MathfieldElement({ virtualKeyboardMode: 'onfocus', readOnly: false, fontsDirectory: model.mathfield.options.fontsDirectory, }); const container = (_a = model.mathfield.element) === null || _a === void 0 ? void 0 : _a.querySelector('.ML__placeholdercontainer'); element.value = ((_b = placeholder.defaultValue) === null || _b === void 0 ? void 0 : _b.length) ? Atom.serialize(placeholder.defaultValue, { defaultMode: 'text' }) : ''; element.classList.add('nested-mathfield'); element.style.display = 'inline-block'; element.style.zIndex = '1001'; element.style.position = 'absolute'; element.style.minWidth = '30px'; const style = document.createElement('style'); style.textContent = `.nested-mathfield { border: 1px solid black; } .ML__container{ min-height:auto !important; } `; element.appendChild(style); element.addEventListener('input', () => { placeholderDidChange(model, placeholder.placeholderId); // this timeout gives some time for a placeholder to render properly // before rendering the main field. setTimeout(() => requestUpdate(model.mathfield)); }); container === null || container === void 0 ? void 0 : container.appendChild(element); model.mathfield._placeholders.set(placeholder.placeholderId, { atom: placeholder, field: element, }); }); // // Insert the new atoms // const { parent } = model.at(model.position); // Are we inserting a fraction inside a lefright? if (format !== 'latex' && model.options.removeExtraneousParentheses && parent instanceof LeftRightAtom && parent.leftDelim === '(' && parent.hasEmptyBranch('body') && newAtoms.length === 1 && newAtoms[0].type === 'genfrac') { // Remove the leftright // i.e. `\left(\frac{}{}\right))` -> `\frac{}{}` const newParent = parent.parent; const branch = parent.treeBranch; newParent.removeChild(parent); newParent.setChildren(newAtoms, branch); } const hadEmptyBody = parent.hasEmptyBranch('body'); const cursor = model.at(model.position); cursor.parent.addChildrenAfter(newAtoms, cursor); if (format === 'latex' && typeof input === 'string') { // If we are given a latex string with no arguments, store it as // "verbatim latex". // Caution: we can only do this if the `serialize()` for this parent // would return an empty string. If the latex is generated using other // properties than parent.body, for example by adding '\left.' and // '\right.' with a 'leftright' type, we can't use this shortcut. if (parent.type === 'root' && hadEmptyBody && !usedArg) parent.verbatimLatex = input; } // Prepare to dispatch notifications // (for selection changes, then content change) model.suppressChangeNotifications = contentWasChanging; const lastNewAtom = newAtoms[newAtoms.length - 1]; // Update the anchor's location if (options.selectionMode === 'placeholder') { // Move to the next placeholder const newPlaceholders = newAtoms.reduce((acc, atom) => [ ...acc, ...atom.children.filter((x) => x.type === 'placeholder'), ], []); if (newPlaceholders.length > 0) { const placeholderOffset = model.offsetOf(newPlaceholders[0]); model.setSelection(placeholderOffset - 1, placeholderOffset); model.announce('move'); // Should have placeholder selected } else if (lastNewAtom) { // No placeholder found, move to right after what we just inserted model.position = model.offsetOf(lastNewAtom); } } else if (options.selectionMode === 'before') ; else if (options.selectionMode === 'after') { if (lastNewAtom) model.position = model.offsetOf(lastNewAtom); } else if (options.selectionMode === 'item') model.setSelection(model.anchor, model.offsetOf(lastNewAtom)); contentDidChange(model, { data, inputType: 'insertText' }); model.suppressChangeNotifications = suppressChangeNotifications; return true; } } function convertStringToAtoms$1(model, s, args, options) { var _a, _b; let format = undefined; let result = []; if (typeof s !== 'string' || options.format === 'math-json') { [format, s] = [ 'latex', model.mathfield.computeEngine.box(s).latex, ]; result = parseLatex(s, { parseMode: 'math', macros: options === null || options === void 0 ? void 0 : options.macros, onError: model.listeners.onError, colorMap: options.colorMap, backgroundColorMap: options.backgroundColorMap, }); } else if (typeof s === 'string' && options.format === 'ascii-math') { [format, s] = parseMathString(s, { format: 'ascii-math', inlineShortcuts: model.mathfield.options.inlineShortcuts, }); result = parseLatex(s, { parseMode: 'math', macros: options === null || options === void 0 ? void 0 : options.macros, onError: model.listeners.onError, colorMap: options.colorMap, backgroundColorMap: options.backgroundColorMap, }); // Simplify result. if (format !== 'latex' && model.options.removeExtraneousParentheses) simplifyParen(result); } else if (options.format === 'auto' || ((_a = options.format) === null || _a === void 0 ? void 0 : _a.startsWith('latex'))) { if (options.format === 'auto') { [format, s] = parseMathString(s, { format: 'auto', inlineShortcuts: model.mathfield.options.inlineShortcuts, }); } // If the whole string is bracketed by a mode shift command, remove it if (options.format === 'latex') [, s] = trimModeShiftCommand(s); result = parseLatex(s, { parseMode: 'math', args: args, macros: { ...model.options.macros, ...((_b = options.macros) !== null && _b !== void 0 ? _b : {}) }, smartFence: options.smartFence, onError: model.listeners.onError, colorMap: options.colorMap, backgroundColorMap: options.backgroundColorMap, }); // Simplify result. if (options.format !== 'latex' && model.options.removeExtraneousParentheses) simplifyParen(result); } // // Some atoms may already have a style (for example if there was an // argument, i.e. the selection, that this was applied to). // So, don't apply style to atoms that are already styled, but *do* // apply it to newly created atoms that have no style yet. // applyStyleToUnstyledAtoms(result, options.style); return [format !== null && format !== void 0 ? format : 'latex', result]; } function removeParen(atoms) { if (!atoms) return null; console.assert(atoms[0].type === 'first'); if (atoms.length > 1) return null; const atom = atoms[0]; if (atom instanceof LeftRightAtom && atom.leftDelim === '(' && atom.rightDelim === ')') return atom.removeBranch('body'); return null; } /** * If it's a fraction with a parenthesized numerator or denominator * remove the parentheses * @revisit: don't need model, only need to know if removeExtraneousParentheses * Check at callsites. */ function simplifyParen(atoms) { if (!atoms) return; for (let i = 0; atoms[i]; i++) { const atom = atoms[i]; if (atom instanceof LeftRightAtom && atom.leftDelim === '(') { let genFracCount = 0; let genFracIndex = 0; let nonGenFracCount = 0; for (let j = 0; atom.body[j]; j++) { if (atom.body[j].type === 'genfrac') { genFracCount++; genFracIndex = j; } nonGenFracCount++; } if (nonGenFracCount === 0 && genFracCount === 1) { // This is a single frac inside a leftright: remove the leftright atoms[i] = atom.body[genFracIndex]; } } } for (const atom of atoms) { for (const branch of atom.branches) { if (!atom.hasEmptyBranch(branch)) { simplifyParen(atom.branch(branch)); const newChildren = removeParen(atom.branch(branch)); if (newChildren) atom.setChildren(newChildren, branch); } } if (atom instanceof ArrayAtom) for (const x of atom.cells) simplifyParen(x); } } function findPlaceholders(atoms) { if (!atoms) return []; const result = []; for (const atom of atoms) { for (const branch of atom.branches) { if (!atom.hasEmptyBranch(branch)) { const branchPlaceholder = findPlaceholders(atom.branch(branch)); result.push(...branchPlaceholder); } } if (atom instanceof PlaceholderAtom) result.push(atom); } return result; } /** * Locate the offset before the insertion point that would indicate * a good place to select as an implicit argument. * * For example with '1+\sin(x)', if the insertion point is at the * end, the implicit arg offset would be after the plus. As a result, * inserting a fraction after the sin would yield: '1+\frac{\sin(c)}{\placeholder{}}' */ function getImplicitArgOffset(model) { let atom = model.at(model.position); if (atom.mode === 'text') { while (!atom.isFirstSibling && atom.mode === 'text') atom = atom.leftSibling; return model.offsetOf(atom); } // Find the first 'mrel', 'mbin', etc... to the left of the insertion point // until the first sibling. // Terms inside of delimiters (parens, brackets, etc) are grouped and kept together. const atomAtCursor = atom; const delimiterStack = []; while (!atom.isFirstSibling && (isImplicitArg(atom) || delimiterStack.length > 0)) { if (atom.type === 'mclose') delimiterStack.unshift(atom.value); if (atom.type === 'mopen' && delimiterStack.length > 0 && atom.value === LEFT_DELIM[delimiterStack[0]]) delimiterStack.shift(); atom = atom.leftSibling; } if (atomAtCursor === atom) return -1; return model.offsetOf(atom); } /** * * Predicate returns true if the atom should be considered an implicit argument. * * Used for example when typing "/" to insert a fraction: all the atoms to * the left of insertion point that return true for `isImplicitArg()` will * be included as the numerator */ function isImplicitArg(atom) { if (/^(mord|surd|msubsup|leftright|mop|mclose)$/.test(atom.type)) { // Exclude `\int`, \`sum`, etc... if (atom.isExtensibleSymbol) return false; // Exclude trig functions (they can be written as `\sin \frac\pi3` without parens) if (atom.isFunction) return false; return true; } return false; } /** * Insert a smart fence '(', '{', '[', etc... * If not handled (because `fence` wasn't a fence), return false. */ function insertSmartFence(model, fence, style) { console.assert(model.selectionIsCollapsed); const atom = model.at(model.position); const { parent } = atom; let delims = parent instanceof LeftRightAtom ? parent.leftDelim + parent.rightDelim : ''; if (delims === '\\lbrace\\rbrace') delims = '{}'; if (delims === '\\{\\}') delims = '{}'; if (delims === '\\lparen\\rparen') delims = '()'; // // 1. Are we inserting a middle fence? // ...as in {...|...} // if (delims === '{}' && /\||\\vert|\\Vert|\\mvert|\\mid/.test(fence)) { ModeEditor.insert('math', model, '\\,\\middle' + fence + '\\, ', { format: 'latex', style, }); return true; } // Normalize some fences. // Note that '{' and '}' are not valid braces. // They should be '\{' or '\lbrace' and '\}' or '\rbrace' if (fence === '{' || fence === '\\{') fence = '\\lbrace'; if (fence === '}' || fence === '\\}') fence = '\\rbrace'; if (fence === '[') fence = '\\lbrack'; if (fence === ']') fence = '\\rbrack'; // // 2. Is it an open fence? // const rDelim = RIGHT_DELIM[fence]; if (rDelim && !(parent instanceof LeftRightAtom && parent.leftDelim === '|')) { // We have a valid open fence as input ModeEditor.insert('math', model, `\\left${fence}\\right?`, { format: 'latex', style, }); // If there is content after the anchor, move it into the `leftright` atom if (atom.lastSibling.type !== 'first') { const lastSiblingOffset = model.offsetOf(atom.lastSibling); const content = model.extractAtoms([model.position, lastSiblingOffset]); model.at(model.position).body = content; model.position -= 1; } return true; } // // 3. Is it a close fence? // let lDelim = ''; Object.keys(RIGHT_DELIM).forEach((delim) => { if (fence === RIGHT_DELIM[delim]) lDelim = delim; }); if (lDelim) { // We found a matching open fence, so it was a valid close fence. // Note that `lDelim` may not match `fence`. That's OK. // If we're the last atom inside a 'leftright', // update the parent if (parent instanceof LeftRightAtom && atom.isLastSibling) { parent.isDirty = true; parent.rightDelim = fence; model.position += 1; contentDidChange(model, { data: fence, inputType: 'insertText' }); return true; } // If we have a `leftright` sibling to our left // with an indeterminate right fence, // move what's between us and the `leftright` inside the `leftright` const firstSibling = model.offsetOf(atom.firstSibling); let i; for (i = model.position; i >= firstSibling; i--) { const atom = model.at(i); if (atom instanceof LeftRightAtom && atom.rightDelim === '?') break; } const match = model.at(i); if (i >= firstSibling && match instanceof LeftRightAtom) { match.rightDelim = fence; match.addChildren(model.extractAtoms([i, model.position]), atom.treeBranch); model.position = i; contentDidChange(model, { data: fence, inputType: 'insertText' }); return true; } // If we're inside a `leftright`, but not the last atom, // and the `leftright` right delim is indeterminate // adjust the body (put everything after the insertion point outside) if (parent instanceof LeftRightAtom && parent.rightDelim === '?') { parent.isDirty = true; parent.rightDelim = fence; parent.parent.addChildren(model.extractAtoms([model.position, model.offsetOf(atom.lastSibling)]), parent.treeBranch); model.position = model.offsetOf(parent); contentDidChange(model, { data: fence, inputType: 'insertText' }); return true; } // Is our grand-parent a 'leftright'? // If `\left(\frac{1}{x|}\right?` with the cursor at `|` // go up to the 'leftright' and apply it there instead const grandparent = parent.parent; if (grandparent instanceof LeftRightAtom && grandparent.rightDelim === '?' && model.at(model.position).isLastSibling) { model.position = model.offsetOf(grandparent); return insertSmartFence(model, fence, style); } // Meh... We couldn't find a matching open fence. Just insert the // closing fence as a regular character return false; } return false; } new MathModeEditor(); var css_248z$1 = "#mathlive-keystroke-caption-panel{--keystroke:#fff;--on-keystroke:#555;--keystroke-border:#f7f7f7;background:var(--secondary,hsl(var(--hue,212),19%,26%));border-color:var(--secondary-border,#e8e8e8);border-radius:6px;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23);display:flex;flex-direction:row-reverse;justify-content:center;padding:16px;position:absolute;text-align:center;visibility:hidden;z-index:1}@media (prefers-color-scheme:dark){body:not([theme=light]) #mathlive-keystroke-caption-panel{--keystroke:hsl(var(--hue,212),50%,30%);--on-keystroke:#fafafa;--keystroke-border:hsl(var(--hue,212),50%,25%)}}body[theme=dark] #mathlive-keystroke-caption-panel{--keystroke:hsl(var(--hue,212),50%,30%);--on-keystroke:#fafafa;--keystroke-border:hsl(var(--hue,212),50%,25%)}#mathlive-keystroke-caption-panel>span{fill:currentColor;background-color:var(--keystroke);border:2px solid var(--keystroke-border);border-radius:6px;color:var(--on-keystroke);font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;font-size:1em;margin:0 8px 0 0;min-width:14px;padding:4px}"; let KEYSTROKE_CAPTION_STYLESHEET_HASH = undefined; let gKeystrokeCaptionStylesheet = null; let gCoreStylesheet = null; function showKeystroke(mathfield, keystroke) { if (mathfield.options.readOnly || !mathfield.keystrokeCaptionVisible) return; const vb = createKeystrokeCaption(mathfield); const bounds = mathfield.element.getBoundingClientRect(); vb.style.left = `${bounds.left}px`; vb.style.top = `${bounds.top - 64}px`; vb.innerHTML = mathfield.options.createHTML('<span>' + (getKeybindingMarkup(keystroke) || keystroke) + '</span>' + vb.innerHTML); vb.style.visibility = 'visible'; setTimeout(() => { if (vb.childNodes.length > 0) vb.childNodes[vb.childNodes.length - 1].remove(); if (vb.childNodes.length === 0) vb.style.visibility = 'hidden'; }, 3000); } function toggleKeystrokeCaption(mathfield) { mathfield.keystrokeCaptionVisible = !mathfield.keystrokeCaptionVisible; if (!mathfield.keystrokeCaptionVisible) { if (mathfield.keystrokeCaption) mathfield.keystrokeCaption.style.visibility = 'hidden'; } else { mathfield.keystrokeCaption = createKeystrokeCaption(mathfield); mathfield.keystrokeCaption.innerHTML = ''; } return false; } function createKeystrokeCaption(mf) { if (mf.keystrokeCaption) return mf.keystrokeCaption; mf.keystrokeCaption = getSharedElement('mathlive-keystroke-caption-panel'); if (KEYSTROKE_CAPTION_STYLESHEET_HASH === undefined) { KEYSTROKE_CAPTION_STYLESHEET_HASH = hashCode(css_248z$1).toString(36); } gKeystrokeCaptionStylesheet = inject(null, css_248z$1, KEYSTROKE_CAPTION_STYLESHEET_HASH); gCoreStylesheet = inject(null, css_248z$4, hashCode(css_248z$4).toString(36)); return mf.keystrokeCaption; } function disposeKeystrokeCaption(mf) { releaseSharedElement(mf.keystrokeCaption); if (gKeystrokeCaptionStylesheet) gKeystrokeCaptionStylesheet.release(); if (gCoreStylesheet) gCoreStylesheet.release(); delete mf.keystrokeCaption; } /** * Handler in response to a keystroke event. * * Return `false` if the event has been handled as a shortcut or command and * need no further processing. Return `true` if the event should be handled as * a regular textual input. * * * Theory of Operation * * When the user type on the keyboard, printable keys (i.e. not arrows, shift, * escape, etc...) are captured in a `keystrokeBuffer`. * * The buffer is used to determine if the user intended to type an * inline shortcut (e.g. "pi" for `\pi`), a multichar symbol, or some keybinding * (i.e.g "command+a" to "seletc all"). * * Characters are added to this buffer while the user type printable characters * consecutively. If the user change selection (with the mouse, or by * navigating with the keyboard), if an unambiguous match for the buffer is * found, the buffer is cleared. * */ function onKeystroke(mathfield, keystroke, evt) { var _a; const { model } = mathfield; // 1. Update the keybindings according to the current keyboard layout // 1.1 Possibly update the current keyboard layout based on this event validateKeyboardLayout(evt); const activeLayout = getActiveKeyboardLayout(); if (mathfield.keyboardLayout !== activeLayout.id) { mathfield.keyboardLayout = activeLayout.id; // If we changed keyboard layout, we'll have to recache the keybindings mathfield._keybindings = undefined; } // 2. Display the keystroke in the keystroke panel (if visible) showKeystroke(mathfield, keystroke); // 3. Reset the timer for the keystroke buffer reset clearTimeout(mathfield.keystrokeBufferResetTimer); // 4. Give a chance to the custom keystroke handler to intercept the event // (note that in readonly mode, while you can't modify the content, you // can use navigation keys) if (!mathfield.options.readOnly && mathfield.options.onKeystroke && !mathfield.options.onKeystroke(mathfield, keystroke, evt)) { if (evt.preventDefault) { evt.preventDefault(); evt.stopPropagation(); } return false; } // 5. Let's try to find a matching inline shortcut or keybinding let shortcut; let selector = ''; let stateIndex; let resetKeystrokeBuffer = false; // 5.1 Check if the keystroke, prefixed with the previously typed keystrokes, // would match a long shortcut (i.e. '~~') // Ignore the key if Command or Control is pressed (it may be a keybinding, // see 5.3) if (mathfield.mode !== 'latex' && !evt.ctrlKey && !evt.metaKey) { if (keystroke === '[Backspace]') { // Special case for backspace to correctly handle undoing mathfield.keystrokeBuffer = mathfield.keystrokeBuffer.slice(0, -1); mathfield.keystrokeBufferStates.pop(); mathfield.resetKeystrokeBuffer({ defer: true }); } else if (!mightProducePrintableCharacter(evt)) { // It was a non-alpha character (PageUp, End, etc...) mathfield.resetKeystrokeBuffer(); mathfield.snapshot(); } else { const c = eventToChar(evt); // Determine the context to interpret the shortcut, i.e. // the set of atoms to the left of the insertion point const context = getLeftSiblings(mathfield); let multicharSymbol = false; // Find the longest substring that matches a shortcut const candidate = mathfield.keystrokeBuffer + c; // Loop over possible candidates, from the longest possible, to the shortest let i = 0; while (!shortcut && i < candidate.length) { // At this length (i), what are the left siblings? const leftSiblings = getLeftSiblings(mathfield, mathfield.keystrokeBufferStates.length - (candidate.length - i)); // Is this an inline shortcut? shortcut = getInlineShortcut(leftSiblings, candidate.slice(i), mathfield.options.inlineShortcuts); // If not a shortcut, could this be interpreted as a multichar symbol? if (!shortcut && mathfield.mode === 'math') { if (context.every((x) => x.type === 'mord')) { shortcut = mathfield.options.onMulticharSymbol(mathfield, candidate.slice(i)); multicharSymbol = !!shortcut; } } i += 1; } stateIndex = mathfield.keystrokeBufferStates.length - (candidate.length - i); mathfield.keystrokeBuffer += c; mathfield.keystrokeBufferStates.push(mathfield.getUndoRecord()); if (!multicharSymbol && countInlineShortcutsStartingWith(candidate, mathfield.options) <= 1) { // There's only a single shortcut matching this sequence. // We can confidently reset the keystroke buffer\ // console.log( // candidate, // mathfield.keystrokeBuffer, // mathfield.keystrokeBufferStates // ); resetKeystrokeBuffer = true; } else { // There are several potential shortcuts matching this sequence. // // Don't reset the keystroke buffer yet, but schedule a defered reset, // in case some keys typed later disambiguate the desired shortcut. // // This handles the case with two shortcuts for "sin" and "sinh", to // avoid the detecting of the "sin" shortcut from preventing the "sinh" // shortcut from ever being triggered. mathfield.resetKeystrokeBuffer({ defer: true }); } } } // // 5.2. Should we switch mode? // // Need to check this before determing if there's a valid shortcut // since if we switch to math mode, we may want to apply the shortcut // e.g. "slope = rise/run" if (mathfield.options.smartMode) { const previousMode = mathfield.mode; if (shortcut) { // If we found a shortcut (e.g. "alpha"), // switch to math mode and insert it mathfield.mode = 'math'; } else if (smartMode(mathfield, keystroke, evt)) { mathfield.mode = { math: 'text', text: 'math' }[mathfield.mode]; selector = ''; } // Notify of mode change if (mathfield.mode !== previousMode && typeof mathfield.options.onModeChange === 'function') mathfield.options.onModeChange(mathfield, mathfield.mode); } // 5.3 Check if this matches a keybinding. // // Need to check this **after** checking for inline shortcuts because // Shift+Backquote is a keybinding that inserts "\~"", but "~~" is a // shortcut for "\approx" and needs to have priority over Shift+Backquote if (!shortcut) { if (!selector) { selector = getCommandForKeybinding(mathfield.keybindings, mathfield.mode, keystroke); } // 5.4 Handle the return/enter key if (!selector && (keystroke === '[Enter]' || keystroke === '[Return]')) { let result = true; if (contentWillChange(mathfield.model, { inputType: 'insertLineBreak' })) { // No matching keybinding: trigger a commit if (typeof mathfield.options.onCommit === 'function') { mathfield.options.onCommit(mathfield); if (evt.preventDefault) { evt.preventDefault(); evt.stopPropagation(); result = false; } } // Dispatch an 'input' event matching the behavior of `<textarea>` contentDidChange(mathfield.model, { inputType: 'insertLineBreak' }); } return result; } if (mathfield.mode === 'math') { // // 5.5 If this is the Space bar and we're just before or right after // a text zone, or if `mathModeSpace` is enabled, insert the space // if (keystroke === '[Space]') { if (mathfield.options.mathModeSpace) { mathfield.snapshot(); ModeEditor.insert('math', model, mathfield.options.mathModeSpace, { format: 'latex', }); selector = ''; mathfield.dirty = true; mathfield.scrollIntoView(); if (evt.preventDefault) { evt.preventDefault(); evt.stopPropagation(); } return true; } const nextSibling = model.at(model.position + 1); const previousSibling = model.at(model.position - 1); if ((nextSibling === null || nextSibling === void 0 ? void 0 : nextSibling.mode) === 'text' || (previousSibling === null || previousSibling === void 0 ? void 0 : previousSibling.mode) === 'text') { mathfield.snapshot(); ModeEditor.insert('text', model, ' '); mathfield.dirty = true; } } // // 5.6 Handle the decimal separator // if (((_a = model.at(model.position).leftSibling) === null || _a === void 0 ? void 0 : _a.isDigit()) && mathfield.options.decimalSeparator === ',' && eventToChar(evt) === ',') selector = 'insertDecimalSeparator'; } } // No shortcut, no selector. We're done. if (!shortcut && !selector) return true; // // 6. Perform the action matching this selector or insert the shortcut // // // 6.1 If we have a `moveAfterParent` selector (usually triggered with // `spacebar), and we're at the end of a smart fence, close the fence with // an empty (.) right delimiter // const child = model.at(Math.max(model.position, model.anchor)); const { parent } = child; if (selector === 'moveAfterParent' && (parent === null || parent === void 0 ? void 0 : parent.type) === 'leftright' && child.isLastSibling && mathfield.options.smartFence && insertSmartFence(model, '.', mathfield.style)) { // Pressing the space bar (moveAfterParent selector) when at the end // of a potential smartFence will close it as a semi-open fence selector = ''; requestUpdate(mathfield); // Re-render the closed smartFence } // // 6.2 If there's a selector, perform it. // if (selector) mathfield.executeCommand(selector); else if (shortcut) { // // 6.3 Cancel the (upcoming) composition // This is to prevent starting a composition when the keyboard event // has already been handled. // Example: alt+U -> \cup, but could also be diaeresis deak key (¨) which // starts a composition // mathfield.keyboardDelegate.cancelComposition(); // // 6.4 Insert the shortcut // // If the shortcut is a mandatory escape sequence (\}, etc...) // don't make it undoable, this would result in syntactically incorrect // formulas // const style = { ...model.at(model.position).computedStyle, ...mathfield.style, }; if (!/^\\({|}|\[|]|@|#|\$|%|&|\^|_|backslash)$/.test(shortcut)) { // To enable the substitution to be undoable, // insert the character before applying the substitution const saveMode = mathfield.mode; ModeEditor.insert(mathfield.mode, model, eventToChar(evt), { suppressChangeNotifications: true, style, }); // Create a snapshot with the inserted character mathfield.snapshot(); // Revert to the state before the beginning of the shortcut // (restore doesn't change the undo stack) mathfield.restoreToUndoRecord(mathfield.keystrokeBufferStates[stateIndex]); mathfield.mode = saveMode; } model.deferNotifications({ content: true, selection: true, data: shortcut !== null && shortcut !== void 0 ? shortcut : null, type: 'insertText', }, () => { // Insert the substitute, possibly as a smart fence ModeEditor.insert(mathfield.mode, model, shortcut, { format: 'latex', style, smartFence: true, }); // Check if as a result of the substitution there is now an isolated // (text mode) space (surrounded by math). In which case, remove it. removeIsolatedSpace(mathfield.model); // Switch (back) to text mode if the shortcut ended with a space if (shortcut.endsWith(' ')) { mathfield.mode = 'text'; ModeEditor.insert('text', model, ' ', { style }); } return true; // Content changed }); mathfield.snapshot(); mathfield.dirty = true; // Mark the field as dirty. It will get rendered in scrollIntoView() model.announce('replacement'); // If we're done with the shortcuts (found a unique one), reset it. if (resetKeystrokeBuffer) mathfield.resetKeystrokeBuffer(); } // // 7. Make sure the mathfield and the insertion point is scrolled into view // mathfield.scrollIntoView(); // // 8. Keystroke has been handled, if it wasn't caught in the default // case, so prevent propagation // if (evt.preventDefault) { evt.preventDefault(); evt.stopPropagation(); } return false; } /** * This handler is invoked when text has been typed, pasted in or input with * an input method. As a result, `text` can be a sequence of characters to * be inserted. * @param {object} options * @param {boolean} options.focus - If true, the mathfield will be focused * @param {boolean} options.feedback - If true, provide audio and haptic feedback * @param {boolean} options.simulateKeystroke - If true, generate some synthetic * keystrokes (useful to trigger inline shortcuts, for example) * @private */ function onTypedText(mathfield, text, options) { var _a; const { model } = mathfield; if (mathfield.options.readOnly) { model.announce('plonk'); return; } options = options !== null && options !== void 0 ? options : {}; // // 1/ Focus (and scroll into view), then provide audio and haptic feedback // if (options.focus) mathfield.focus(); if (options.feedback) { if (mathfield.options.keypressVibration && canVibrate()) navigator.vibrate(HAPTIC_FEEDBACK_DURATION); (_a = mathfield.keypressSound) === null || _a === void 0 ? void 0 : _a.play().catch(console.warn); } // // 2/ Switch mode if requested // if (typeof options.mode === 'string' && mathfield.mode !== options.mode) mathfield.switchMode(options.mode); // // 3/ Simulate keystroke, if requested // if (options.simulateKeystroke) { // For (const c of text) { const c = text.charAt(0); const ev = new KeyboardEvent('keypress', { key: c }); if (!onKeystroke(mathfield, c, ev)) return; // } } // // 4/ Insert the specified text at the current insertion point. // If the selection is not collapsed, the content will be deleted first. // const atom = model.at(model.position); const style = { ...atom.computedStyle, ...mathfield.style, }; if (!model.selectionIsCollapsed) { model.deleteAtoms(range(model.selection)); mathfield.snapshot(); } // Decompose the string into an array of graphemes. // This is necessary to correctly process what is displayed as a single // glyph (a grapheme) but which is composed of multiple Unicode // codepoints. This is the case in particular for some emojis, such as // those with a skin tone modifier, the country flags emojis or // compound emojis such as the professional emojis, including the // David Bowie emoji: 👨🏻🎤 const graphemes = splitGraphemes(text); if (mathfield.mode === 'latex') { model.deferNotifications({ content: true, selection: true, data: text, type: 'insertText' }, () => { removeSuggestion(mathfield); for (const c of graphemes) ModeEditor.insert('latex', model, c); updateAutocomplete(mathfield); }); } else if (mathfield.mode === 'text') for (const c of graphemes) ModeEditor.insert('text', model, c, { style }); else if (mathfield.mode === 'math') { for (const c of graphemes) { // Some characters are mapped to commands. Handle them here. // This is important to handle synthetic text input and // non-US keyboards, on which, fop example, the '^' key is // not mapped to 'Shift-Digit6'. let selector = { '^': 'moveToSuperscript', '_': 'moveToSubscript', ' ': 'moveAfterParent', }[c]; if (c === ' ' && mathfield.options.mathModeSpace) selector = ['insert', mathfield.options.mathModeSpace]; if (selector) mathfield.executeCommand(selector); else if (/\d/.test(c) && mathfield.options.smartSuperscript && atom.treeBranch === 'superscript' && atom.hasNoSiblings) { // We are inserting a digit into an empty superscript // If smartSuperscript is on, insert the digit, and // exit the superscript. ModeEditor.insert('math', model, c, { style }); moveAfterParent(model); } else { // If adding an alphabetic character, and the leftmost atom is an // ordinary character, use the same variant/variantStyle (\mathit, \mathrm...) if (atom.type === 'mord' && /[a-zA-Z]/.test(atom.value) && /[a-zA-Z]/.test(c)) { if (atom.style.variant) style.variant = atom.style.variant; if (atom.style.variantStyle) style.variantStyle = atom.style.variantStyle; } // General purpose character insertion ModeEditor.insert('math', model, c, { style, smartFence: mathfield.options.smartFence, }); } } } // // 5/ Take a snapshot for undo stack // mathfield.snapshotAndCoalesce(); // // 6/ Render the mathfield // mathfield.dirty = true; // Render and make sure the mathfield and the insertion point is visible mathfield.scrollIntoView(); } function getLeftSiblings(mf, index = -1) { const model = mf.model; const savedState = mf.getUndoRecord(); if (index >= 0) mf.restoreToUndoRecord(mf.keystrokeBufferStates[index]); let atom = model.at(Math.min(model.position, model.anchor)); const result = []; while (atom.type !== 'first') { result.push(atom); atom = atom.leftSibling; } if (index >= 0) mf.restoreToUndoRecord(savedState); return result; } register({ undo: (mathfield) => { complete(mathfield, 'accept'); // Undo to the previous state mathfield.undo(); return true; }, redo: (mathfield) => { complete(mathfield, 'accept'); mathfield.redo(); return true; }, scrollIntoView: (mathfield) => { mathfield.scrollIntoView(); return true; }, scrollToStart: (mathfield) => { mathfield.field.scroll(0, 0); return true; }, scrollToEnd: (mathfield) => { const fieldBounds = mathfield.field.getBoundingClientRect(); mathfield.field.scroll(fieldBounds.left - window.scrollX, 0); return true; }, enterLatexMode: (mathfield) => { mathfield.switchMode('latex'); return true; }, toggleKeystrokeCaption: toggleKeystrokeCaption, switchMode: (mathfield, mode, prefix, suffix) => { mathfield.switchMode(mode, prefix, suffix); return true; }, insert: (mathfield, s, options) => mathfield.insert(s, options), typedText: (mathfield, text, options) => { onTypedText(mathfield, text, options); return true; }, insertDecimalSeparator: (mathfield) => { if (mathfield.mode === 'math' && mathfield.options.decimalSeparator === ',') { const model = mathfield.model; const child = model.at(Math.max(model.position, model.anchor)); if (child.isDigit()) { mathfield.snapshot(); mathfield.insert('{,}', { format: 'latex' }); return true; } } mathfield.insert('.'); return true; }, commit: (mathfield) => { if (typeof mathfield.options.onCommit === 'function') mathfield.options.onCommit(mathfield); return true; }, }); register({ copyToClipboard: (mathfield) => { mathfield.focus(); // If the selection is empty, select the entire field before // copying it. if (mathfield.model.selectionIsCollapsed) mathfield.select(); document.execCommand('copy'); return false; }, cutToClipboard: (mathfield) => { mathfield.focus(); document.execCommand('cut'); return true; }, pasteFromClipboard: (mathfield) => { mathfield.focus(); document.execCommand('paste'); return true; }, }, { target: 'mathfield', category: 'clipboard' }); function applyStyle(mathfield, inStyle) { mathfield.resetKeystrokeBuffer(); const style = validateStyle(mathfield, inStyle); const { model } = mathfield; if (model.selectionIsCollapsed) { // No selection, let's update the 'current' style if (mathfield.style.fontSeries && style.fontSeries === mathfield.style.fontSeries) style.fontSeries = 'auto'; if (style.fontShape && style.fontShape === mathfield.style.fontShape) style.fontShape = 'auto'; if (style.color && style.color === mathfield.style.color) style.color = 'none'; if (style.backgroundColor && style.backgroundColor === mathfield.style.backgroundColor) style.backgroundColor = 'none'; if (style.fontSize && style.fontSize === mathfield.style.fontSize) style.fontSize = 'auto'; // This global style will be used the next time an atom is inserted mathfield.style = { ...mathfield.style, ...style }; } else { mathfield.model.deferNotifications({ content: true, type: 'insertText' }, () => { // Change the style of the selection model.selection.ranges.forEach((range) => applyStyle$1(model, range, style, { operation: 'toggle' })); mathfield.snapshot(); }); } return true; } register({ applyStyle }, { target: 'mathfield' }); /** * Validate a style specification object */ function validateStyle(mathfield, style) { var _a, _b, _c, _d, _e, _f; const result = {}; if (typeof style.color === 'string') { result.verbatimColor = style.color; result.color = (_a = mathfield.colorMap(style.color)) !== null && _a !== void 0 ? _a : 'none'; } if (typeof style.backgroundColor === 'string') { result.verbatimBackgroundColor = style.backgroundColor; result.backgroundColor = (_b = mathfield.backgroundColorMap(style.backgroundColor)) !== null && _b !== void 0 ? _b : 'none'; } if (typeof style.fontFamily === 'string') result.fontFamily = style.fontFamily; if (typeof style.series === 'string') result.fontSeries = style.series; if (typeof style.fontSeries === 'string') result.fontSeries = style.fontSeries.toLowerCase(); if (result.fontSeries) { result.fontSeries = (_c = { bold: 'b', medium: 'm', normal: 'm', }[result.fontSeries]) !== null && _c !== void 0 ? _c : result.fontSeries; } if (typeof style.shape === 'string') result.fontShape = style.shape; if (typeof style.fontShape === 'string') result.fontShape = style.fontShape.toLowerCase(); if (result.fontShape) { result.fontShape = (_d = { italic: 'it', up: 'n', upright: 'n', normal: 'n', }[result.fontShape]) !== null && _d !== void 0 ? _d : result.fontShape; } const size = (_e = style.size) !== null && _e !== void 0 ? _e : style.fontSize; if (typeof size === 'number') result.fontSize = Math.max(1, Math.min(10, size)); else if (typeof size === 'string') { result.fontSize = (_f = { size1: 1, size2: 2, size3: 3, size4: 4, size5: 5, size6: 6, size7: 7, size8: 8, size9: 9, size10: 10, }[size.toLowerCase()]) !== null && _f !== void 0 ? _f : { tiny: 1, scriptsize: 2, footnotesize: 3, small: 4, normal: 5, normalsize: 5, large: 6, Large: 7, LARGE: 8, huge: 9, Huge: 10, }[size]; } return result; } let gLastTap = null; let gTapCount = 0; function isTouchEvent(evt) { return isBrowser() && 'TouchEvent' in globalThis && evt instanceof TouchEvent; } function isPointerEvent(evt) { return (isBrowser() && 'PointerEvent' in globalThis && evt instanceof PointerEvent); } function onPointerDown(mathfield, evt) { var _a; //Reset the atom bounds cache mathfield._atomBoundsCache = new Map(); const that = mathfield; let anchor; let trackingPointer = false; let dirty = 'none'; // If a mouse button other than the main one was pressed, return. // On iOS 12.4 Safari and Firefox on Android (which do not support // PointerEvent) the touchstart event is sent with event.buttons = 0 // which for a mouse event would normally be an invalid button. // Accept this button 0. if (isPointerEvent(evt) && evt.buttons > 1) return; let scrollLeft = false; let scrollRight = false; // Note: evt['touches'] is for touchstart (when PointerEvent is not supported) const anchorX = isTouchEvent(evt) ? evt.touches[0].clientX : evt.clientX; const anchorY = isTouchEvent(evt) ? evt.touches[0].clientY : evt.clientY; const anchorTime = Date.now(); const field = that.field; const scrollInterval = setInterval(() => { if (scrollLeft) field.scroll({ top: 0, left: field.scrollLeft - 16 }); else if (scrollRight) field.scroll({ top: 0, left: field.scrollLeft + 16 }); }, 32); function endPointerTracking(evt) { if (!isBrowser()) return; if ('PointerEvent' in window) { off(field, 'pointermove', onPointerMove); off(field, 'pointerup pointercancel', endPointerTracking); if (evt instanceof PointerEvent) field.releasePointerCapture(evt.pointerId); } else { off(field, 'touchmove', onPointerMove); off(field, 'touchcancel touchend', endPointerTracking); off(window, 'mousemove', onPointerMove); off(window, 'mouseup blur', endPointerTracking); } trackingPointer = false; clearInterval(scrollInterval); mathfield.element.classList.remove('tracking'); if (evt) evt.preventDefault(); } function onPointerMove(evt) { // If we've somehow lost focus, end tracking if (!that.hasFocus()) { endPointerTracking(null); return; } const x = isTouchEvent(evt) ? evt.touches[0].clientX : evt.clientX; const y = isTouchEvent(evt) ? evt.touches[0].clientY : evt.clientY; // Ignore events that are within small spatial and temporal bounds // of the pointer down const hysteresis = isTouchEvent(evt) || evt.pointerType === 'touch' ? 20 : 5; if (Date.now() < anchorTime + 500 && Math.abs(anchorX - x) < hysteresis && Math.abs(anchorY - y) < hysteresis) { evt.preventDefault(); evt.stopPropagation(); return; } const fieldBounds = field.getBoundingClientRect(); scrollRight = x > fieldBounds.right; scrollLeft = x < fieldBounds.left; let actualAnchor = anchor; if (isPointerEvent(evt)) { if (!evt.isPrimary) { actualAnchor = offsetFromPoint(that, evt.clientX, evt.clientY, { bias: 0, }); } } else if (evt.touches && evt.touches.length === 2) { actualAnchor = offsetFromPoint(that, evt.touches[1].clientX, evt.touches[1].clientY, { bias: 0 }); } const focus = offsetFromPoint(that, x, y, { bias: x <= anchorX ? (x === anchorX ? 0 : -1) : +1, }); if (actualAnchor >= 0 && focus >= 0) { that.model.extendSelectionTo(actualAnchor, focus); requestUpdate(mathfield); } // Prevent synthetic mouseMove event when this is a touch event evt.preventDefault(); evt.stopPropagation(); } // Calculate the tap count if (gLastTap && Math.abs(gLastTap.x - anchorX) < 5 && Math.abs(gLastTap.y - anchorY) < 5 && Date.now() < gLastTap.time + 500) { gTapCount += 1; gLastTap.time = anchorTime; } else { gLastTap = { x: anchorX, y: anchorY, time: anchorTime, }; gTapCount = 1; } const bounds = field.getBoundingClientRect(); if (anchorX >= bounds.left && anchorX <= bounds.right && anchorY >= bounds.top && anchorY <= bounds.bottom) { // Focus the mathfield if (!mathfield.hasFocus()) { dirty = 'all'; mathfield.focus({ scrollIntoView: false }); } // Clicking or tapping the field resets the keystroke buffer and // smart mode mathfield.resetKeystrokeBuffer(); mathfield.smartModeSuppressed = false; anchor = offsetFromPoint(mathfield, anchorX, anchorY, { bias: 0, }); if (anchor >= 0) { // Set a `tracking` class to avoid triggering the hover of the virtual // keyboard toggle, for example mathfield.element.classList.add('tracking'); if (evt.shiftKey) { // If the Shift key is down, extend the selection // (in that case, 'anchor' is actually the focus const wasCollapsed = mathfield.model.selectionIsCollapsed; mathfield.model.extendSelectionTo(mathfield.model.anchor, anchor); if (acceptCommandSuggestion(mathfield.model) || wasCollapsed) dirty = 'all'; else dirty = 'selection'; } else if (mathfield.model.at(anchor).type === 'placeholder') { mathfield.model.setSelection(anchor - 1, anchor); dirty = 'selection'; } else if (((_a = mathfield.model.at(anchor).rightSibling) === null || _a === void 0 ? void 0 : _a.type) === 'placeholder') { mathfield.model.setSelection(anchor, anchor + 1); dirty = 'selection'; } else { mathfield.model.position = anchor; if (acceptCommandSuggestion(mathfield.model)) dirty = 'all'; else dirty = 'selection'; } // Reset any user-specified style mathfield.style = {}; // `evt.detail` contains the number of consecutive clicks // for double-click, triple-click, etc... // (note that `evt.detail` is not set when using pointerEvent) if (evt.detail === 3 || gTapCount > 2) { endPointerTracking(evt); if (evt.detail === 3 || gTapCount === 3) { // This is a triple-click mathfield.model.selection = { ranges: [[0, mathfield.model.lastOffset]], }; dirty = 'all'; } } else if (!trackingPointer) { trackingPointer = true; if (isBrowser() && 'PointerEvent' in window) { on(field, 'pointermove', onPointerMove); on(field, 'pointerup pointercancel', endPointerTracking); if (evt instanceof PointerEvent) field.setPointerCapture(evt.pointerId); } else { on(window, 'blur', endPointerTracking); if (isTouchEvent(evt) && evt.touches) { // This is a touchstart event (and PointerEvent is not supported) // To receive the subsequent touchmove/touch, need to // listen to this evt.target. // This was a touch event on(evt.target, 'touchmove', onPointerMove); on(evt.target, 'touchcancel touchend', endPointerTracking); } else { on(window, 'mousemove', onPointerMove); on(window, 'mouseup', endPointerTracking); } } if (evt.detail === 2 || gTapCount === 2) { selectGroup(mathfield.model); dirty = 'all'; } } } } else gLastTap = null; if (dirty !== 'none') { if (mathfield.model.selectionIsCollapsed) dirty = 'all'; requestUpdate(mathfield); } // Prevent the browser from handling. In particular when this is a // touch event, prevent the synthetic mouseDown event from being generated evt.preventDefault(); } function distance(x, y, r) { const dx = x - (r.left + r.right) / 2; const dy = y - (r.top + r.bottom) / 2; return dx * dx + dy * dy; } function nearestAtomFromPointRecursive(mathfield, cache, atom, x, y) { if (!atom.id) return [Infinity, null]; if (cache.has(atom.id)) return cache.get(atom.id); const bounds = getAtomBounds(mathfield, atom); if (!bounds) return [Infinity, null]; let result = [ atom.type === 'group' ? Infinity : distance(x, y, bounds), atom, ]; // // 1. Consider any children within the horizontal bounds // if (!atom.captureSelection && x >= bounds.left && x <= bounds.right && atom.hasChildren) { for (const child of atom.children) { const r = nearestAtomFromPointRecursive(mathfield, cache, child, x, y); if (r[0] < result[0]) result = r; } } // // 2. If no children matched, this atom matches // if (!result[1]) result = [distance(x, y, bounds), atom]; cache.set(atom.id, result); return result; } function nearestAtomFromPoint(mathfield, x, y) { const [, atom] = nearestAtomFromPointRecursive(mathfield, new Map(), mathfield.model.root, x, y); return atom; } /** * @param options.bias if 0, the midpoint of the bounding box * is considered to return the sibling. If <0, the left sibling is * favored, if >0, the right sibling */ function offsetFromPoint(mathfield, x, y, options) { var _a; // // 1/ Check if we're inside the mathfield bounding box // const bounds = mathfield.fieldContent.getBoundingClientRect(); if (x > bounds.right || y > bounds.bottom + 8) return mathfield.model.lastOffset; if (x < bounds.left || y < bounds.top - 8) return 0; options = options !== null && options !== void 0 ? options : {}; options.bias = (_a = options.bias) !== null && _a !== void 0 ? _a : 0; // // 2/ Find the deepest element that is near the point that was // clicked on (the point could be outside of the element) // let atom = nearestAtomFromPoint(mathfield, x, y); // // 3/ Find the first parent from root that doesn't have a `captureSelection` // flag // const parents = []; let parent = atom; while (parent) { parents.unshift(parent); parent = parent.parent; } for (const x of parents) { if (x.captureSelection) { atom = x; break; } } let result = mathfield.model.offsetOf(atom); if (result < 0) return -1; // // 4/ Account for the desired biad // if (atom.leftSibling) { if (options.bias === 0 && atom.type !== 'placeholder') { // If the point clicked is to the left of the vertical midline, // adjust the offset to *before* the atom (i.e. after the // preceding atom) const bounds = getAtomBounds(mathfield, atom); if (bounds && x < (bounds.left + bounds.right) / 2) result = mathfield.model.offsetOf(atom.leftSibling); } else if (options.bias < 0) result = mathfield.model.offsetOf(atom.leftSibling); } return result; } var css_248z = "@keyframes ML__caret-blink{0%,to{opacity:1}50%{opacity:0}}.ML__caret:after{animation:ML__caret-blink 1.05s step-end infinite forwards;border:none;border-radius:2px;border-right:2px solid var(--caret,var(--ML__caret));content:\"\";left:-1px;margin-right:-2px;position:relative}.ML__text-caret:after{animation:ML__caret-blink 1.05s step-end infinite forwards;border:none;border-radius:1px;border-right:1px solid var(--caret,var(--ML__caret));content:\"\";left:0;margin-right:-1px;position:relative}.ML__latex-caret:after{animation:ML__caret-blink 1.05s step-end infinite forwards;border:none;color:var(--caret,var(--ML__caret));content:\"_\";margin-right:calc(-1ex - 2px);position:relative}.ML__container{--ML__highlight:hsl(var(--hue,212),97%,85%);--ML__highlight-text:hsla(var(--hue,212),40%,50%,0.1);--ML__contains-highlight:hsl(var(--hue,212),40%,95%);--ML__highlight-inactive:#ccc;--ML__on-highlight:currentColor;--ML__caret:hsl(var(--hue,212),40%,49%);--ML__smart-fence-color:currentColor;--ML__latex-color:var(--primary,hsl(var(--hue,212),40%,50%));align-items:flex-end;display:flex;flex-flow:row;isolation:isolate;justify-content:space-between;min-height:39px;touch-action:none;width:100%}@media (prefers-color-scheme:dark){.ML__container{--ML__highlight:hsl(var(--hue,212),25%,45%);--ML__highlight-text:hsla(var(--hue,212),40%,50%,0.2);--ML__contains-highlight:hsl(var(--hue,212),5%,34%);--ML__highlight-inactive:#334;--ML__on-highlight:currentColor;--ML__caret:hsl(var(--hue,212),60%,69%);--ML__smart-fence-color:currentColor;--ML__latex-color:var(--primary,hsl(var(--hue,212),40%,50%))}}.ML__content{align-items:center;align-self:center;display:flex;overflow:hidden;padding:2px 0 2px 1px;position:relative;width:100%}.ML__virtual-keyboard-toggle{box-sizing:border-box;display:none}.ML__virtual-keyboard-toggle>span{align-items:center;align-self:center;display:flex}.ML__virtual-keyboard-toggle.is-visible{fill:currentColor;align-items:center;align-self:center;background:transparent;border:1px solid transparent;border-radius:8px;color:var(--primary,hsl(var(--hue,212),40%,50%));cursor:pointer;display:flex;flex-direction:column;flex-shrink:0;height:34px;justify-content:center;margin-right:4px;padding:0;transition:background .2s cubic-bezier(.64,.09,.08,1);width:34px}.ML__virtual-keyboard-toggle.is-visible:hover{fill:currentColor;background:hsla(0,0%,70%,.5);border-radius:8px;color:#333}.ML__textarea__textarea{clip:rect(0 0 0 0);border:none;display:inline-block;font-family:KaTeX_Main;font-size:1em;height:1px;outline:none;position:absolute;resize:none;transform:scale(0);width:1px}.ML__focused .ML__text{background:var(--highlight-text,var(--ML__highlight-text))}.ML__smart-fence__close{color:var(--smart-fence-color,var(--ML__smart-fence-color));opacity:var(--smart-fence-opacity,.5)}.ML__selection{background:var(--highlight-inactive,var(--ML__highlight-inactive));box-sizing:border-box}.ML__focused .ML__selection{background:var(--highlight,var(--ML__highlight))!important;color:var(--on-highlight,var(--ML_on-highlight))}.ML__contains-caret .ML__sqrt-line,.ML__contains-caret .ML__sqrt-sign,.ML__contains-caret.ML__close,.ML__contains-caret.ML__open,.ML__contains-caret>.ML__close,.ML__contains-caret>.ML__open{color:var(--caret,var(--ML__caret))}.ML__contains-highlight{background:var(--contains-highlight,var(--ML__contains-highlight));box-sizing:border-box}.ML__latex{color:var(--latex-color,var(--ML__latex-color));font-family:IBM Plex Mono,Source Code Pro,Consolas,Roboto Mono,Menlo,Bitstream Vera Sans Mono,DejaVu Sans Mono,Monaco,Courier,monospace;font-weight:400}.ML__suggestion{opacity:.5}.ML__virtual-keyboard-toggle.is-visible.is-pressed:hover{fill:currentColor;background:hsl(var(--hue,212),25%,35%);color:#fafafa}.ML__virtual-keyboard-toggle:focus{border:2px solid var(--primary,hsl(var(--hue,212),40%,50%));border-radius:8px;outline:none}.ML__virtual-keyboard-toggle.is-active,.ML__virtual-keyboard-toggle.is-active:hover,.ML__virtual-keyboard-toggle.is-pressed{fill:currentColor;background:hsl(var(--hue,212),25%,35%);color:#fafafa}[data-ML__tooltip]{position:relative}[data-ML__tooltip][data-placement=top]:after{bottom:100%;top:inherit}[data-ML__tooltip]:after{background:#616161;border-radius:2px;box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2);color:#fff;content:attr(data-ML__tooltip);display:none;font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;font-size:12px;font-weight:400;max-width:200px;opacity:0;padding:8px;position:absolute;right:110%;text-align:center;transform:scale(.5);transition:all .15s cubic-bezier(.4,0,1,1);width:max-content;z-index:2}@media only screen and (max-width:767px){[data-ML__tooltip]:after{font-size:16px;padding:8px 16px}}:not(.tracking) [data-ML__tooltip]:hover{position:relative}:not(.tracking) [data-ML__tooltip]:hover:after{display:inline-table;opacity:1;transform:scale(1);visibility:visible}[data-ML__tooltip][data-delay]:after{transition-delay:0s}[data-ML__tooltip][data-delay]:hover:after{transition-delay:1s}"; class TextModeEditor extends ModeEditor { constructor() { super('text'); } onPaste(mathfield, ev) { if (!ev.clipboardData) return false; const text = ev.clipboardData.getData('text/plain'); if (text && contentWillChange(mathfield.model, { inputType: 'insertFromPaste', data: text, })) { mathfield.snapshot(); if (this.insert(mathfield.model, text)) { contentDidChange(mathfield.model, { inputType: 'insertFromPaste' }); requestUpdate(mathfield); } ev.preventDefault(); ev.stopPropagation(); return true; } return false; } insert(model, text, options = {}) { var _a; if (!contentWillChange(model, { data: text, inputType: 'insertText' })) return false; if (!options.insertionMode) options.insertionMode = 'replaceSelection'; if (!options.selectionMode) options.selectionMode = 'placeholder'; if (!options.format) options.format = 'auto'; options.macros = (_a = options.macros) !== null && _a !== void 0 ? _a : model.options.macros; const { suppressChangeNotifications } = model; if (options.suppressChangeNotifications) model.suppressChangeNotifications = true; const contentWasChanging = model.suppressChangeNotifications; model.suppressChangeNotifications = true; // // Delete any selected items // if (options.insertionMode === 'replaceSelection' && !model.selectionIsCollapsed) model.deleteAtoms(range(model.selection)); else if (options.insertionMode === 'replaceAll') { model.root.setChildren([], 'body'); model.position = 0; } else if (options.insertionMode === 'insertBefore') model.collapseSelection('backward'); else if (options.insertionMode === 'insertAfter') model.collapseSelection('forward'); const newAtoms = convertStringToAtoms(text); // Some atoms may already have a style (for example if there was an // argument, i.e. the selection, that this was applied to). // So, don't apply style to atoms that are already styled, but *do* // apply it to newly created atoms that have no style yet. applyStyleToUnstyledAtoms(newAtoms, options.style); if (!newAtoms) return false; const cursor = model.at(model.position); const lastNewAtom = cursor.parent.addChildrenAfter(newAtoms, cursor); // Prepare to dispatch notifications // (for selection changes, then content change) model.suppressChangeNotifications = contentWasChanging; if (options.selectionMode === 'before') ; else if (options.selectionMode === 'item') model.setSelection(model.anchor, model.offsetOf(lastNewAtom)); else if (lastNewAtom) model.position = model.offsetOf(lastNewAtom); contentDidChange(model, { data: text, inputType: 'insertText' }); model.suppressChangeNotifications = suppressChangeNotifications; return true; } } function convertStringToAtoms(s) { // Map special TeX characters to alternatives // Must do this one first, since other replacements include backslash s = s.replace(/\\/g, '\\textbackslash '); s = s.replace(/#/g, '\\#'); s = s.replace(/\$/g, '\\$'); s = s.replace(/%/g, '\\%'); s = s.replace(/&/g, '\\&'); // S = s.replace(/:/g, '\\colon'); // text colon? // s = s.replace(/\[/g, '\\lbrack'); // s = s.replace(/]/g, '\\rbrack'); s = s.replace(/_/g, '\\_'); s = s.replace(/{/g, '\\textbraceleft '); s = s.replace(/}/g, '\\textbraceright '); s = s.replace(/\^/g, '\\textasciicircum '); s = s.replace(/~/g, '\\textasciitilde '); s = s.replace(/£/g, '\\textsterling '); return parseLatex(s, { parseMode: 'text', registers: {} }); } new TextModeEditor(); /** CortexJS Compute Engine 0.6.0 */ function e(e) { return !("kind" in e) || "symbol" === e.kind; } function t(e) { return "kind" in e && "matchfix" === e.kind; } function i(e) { return "kind" in e && "infix" === e.kind; } function n(e) { return "kind" in e && "prefix" === e.kind; } function r(e) { return "kind" in e && "postfix" === e.kind; } function s(e) { return "kind" in e && "environment" === e.kind; } var a, o = "undefined" != typeof globalThis ? globalThis : "undefined" != typeof window ? window : "undefined" != typeof global ? global : "undefined" != typeof self ? self : {}, l = { exports: {} }; a = l, function (e) { var t, i, n, r, s = 9e15, o = 1e9, l = "0123456789abcdef", u = "2.3025850929940456840179914546843642076011014886287729760333279009675726096773524802359972050895982983419677840422862486334095254650828067566662873690987816894829072083255546808437998948262331985283935053089653777326288461633662222876982198867465436674744042432743651550489343149393914796194044002221051017141748003688084012647080685567743216228355220114804663715659121373450747856947683463616792101806445070648000277502684916746550586856935673420670581136429224554405758925724208241314695689016758940256776311356919292033376587141660230105703089634572075440370847469940168269282808481184289314848524948644871927809676271275775397027668605952496716674183485704422507197965004714951050492214776567636938662976979522110718264549734772662425709429322582798502585509785265383207606726317164309505995087807523710333101197857547331541421808427543863591778117054309827482385045648019095610299291824318237525357709750539565187697510374970888692180205189339507238539205144634197265287286965110862571492198849978748873771345686209167058", c = "3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989380952572010654858632789", h = { precision: 20, rounding: 4, modulo: 1, toExpNeg: -7, toExpPos: 21, minE: -s, maxE: s, crypto: !1 }, m = !0, f = "[DecimalError] ", g = f + "Invalid argument: ", p = f + "Precision limit exceeded", d = f + "crypto unavailable", v = "[object Decimal]", b = Math.floor, y = Math.pow, N = /^0b([01]+(\.[01]*)?|\.[01]+)(p[+-]?\d+)?$/i, x = /^0x([0-9a-f]+(\.[0-9a-f]*)?|\.[0-9a-f]+)(p[+-]?\d+)?$/i, _ = /^0o([0-7]+(\.[0-7]*)?|\.[0-7]+)(p[+-]?\d+)?$/i, S = /^(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i, w = 1e7, E = u.length - 1, I = c.length - 1, k = { toStringTag: v }; function A(e) { var t, i, n, r = e.length - 1, s = "", a = e[0]; if (r > 0) { for (s += a, t = 1; t < r; t++) (i = 7 - (n = e[t] + "").length) && (s += R(i)), s += n; (i = 7 - (n = (a = e[t]) + "").length) && (s += R(i)); } else if (0 === a) return "0"; for (; a % 10 == 0;) a /= 10; return s + a; } function M(e, t, i) { if (e !== ~~e || e < t || e > i) throw Error(g + e); } function F(e, t, i, n) { var r, s, a, o; for (s = e[0]; s >= 10; s /= 10) --t; return --t < 0 ? (t += 7, r = 0) : (r = Math.ceil((t + 1) / 7), t %= 7), s = y(10, 7 - t), o = e[r] % s | 0, null == n ? t < 3 ? (0 == t ? o = o / 100 | 0 : 1 == t && (o = o / 10 | 0), a = i < 4 && 99999 == o || i > 3 && 49999 == o || 5e4 == o || 0 == o) : a = (i < 4 && o + 1 == s || i > 3 && o + 1 == s / 2) && (e[r + 1] / s / 100 | 0) == y(10, t - 2) - 1 || (o == s / 2 || 0 == o) && 0 == (e[r + 1] / s / 100 | 0) : t < 4 ? (0 == t ? o = o / 1e3 | 0 : 1 == t ? o = o / 100 | 0 : 2 == t && (o = o / 10 | 0), a = (n || i < 4) && 9999 == o || !n && i > 3 && 4999 == o) : a = ((n || i < 4) && o + 1 == s || !n && i > 3 && o + 1 == s / 2) && (e[r + 1] / s / 1e3 | 0) == y(10, t - 3) - 1, a; } function O(e, t, i) { for (var n, r, s = [0], a = 0, o = e.length; a < o;) { for (r = s.length; r--;) s[r] *= t; for (s[0] += l.indexOf(e.charAt(a++)), n = 0; n < s.length; n++) s[n] > i - 1 && (void 0 === s[n + 1] && (s[n + 1] = 0), s[n + 1] += s[n] / i | 0, s[n] %= i); } return s.reverse(); } k.absoluteValue = k.abs = function () { var e = new this.constructor(this); return e.s < 0 && (e.s = 1), P(e); }, k.ceil = function () { return P(new this.constructor(this), this.e + 1, 2); }, k.clampedTo = k.clamp = function (e, t) { var i = this, n = i.constructor; if (e = new n(e), t = new n(t), !e.s || !t.s) return new n(NaN); if (e.gt(t)) throw Error(g + t); return i.cmp(e) < 0 ? e : i.cmp(t) > 0 ? t : new n(i); }, k.comparedTo = k.cmp = function (e) { var t, i, n, r, s = this, a = s.d, o = (e = new s.constructor(e)).d, l = s.s, u = e.s; if (!a || !o) return l && u ? l !== u ? l : a === o ? 0 : !a ^ l < 0 ? 1 : -1 : NaN; if (!a[0] || !o[0]) return a[0] ? l : o[0] ? -u : 0; if (l !== u) return l; if (s.e !== e.e) return s.e > e.e ^ l < 0 ? 1 : -1; for (t = 0, i = (n = a.length) < (r = o.length) ? n : r; t < i; ++t) if (a[t] !== o[t]) return a[t] > o[t] ^ l < 0 ? 1 : -1; return n === r ? 0 : n > r ^ l < 0 ? 1 : -1; }, k.cosine = k.cos = function () { var e, t, i = this, n = i.constructor; return i.d ? i.d[0] ? (e = n.precision, t = n.rounding, n.precision = e + Math.max(i.e, i.sd()) + 7, n.rounding = 1, i = function (e, t) { var i, n, r; if (t.isZero()) return t; (n = t.d.length) < 32 ? r = "" + 1 / U(4, i = Math.ceil(n / 3)) : (i = 16, r = "2.3283064365386962890625e-10"), e.precision += i, t = Q(e, 1, t.times(r), new e(1)); for (var s = i; s--;) { var a = t.times(t); t = a.times(a).minus(a).times(8).plus(1); } return e.precision -= i, t; }(n, K(n, i)), n.precision = e, n.rounding = t, P(2 == r || 3 == r ? i.neg() : i, e, t, !0)) : new n(1) : new n(NaN); }, k.cubeRoot = k.cbrt = function () { var e, t, i, n, r, s, a, o, l, u, c = this, h = c.constructor; if (!c.isFinite() || c.isZero()) return new h(c); for (m = !1, (s = c.s * y(c.s * c, 1 / 3)) && Math.abs(s) != 1 / 0 ? n = new h(s.toString()) : (i = A(c.d), (s = ((e = c.e) - i.length + 1) % 3) && (i += 1 == s || -2 == s ? "0" : "00"), s = y(i, 1 / 3), e = b((e + 1) / 3) - (e % 3 == (e < 0 ? -1 : 2)), (n = new h(i = s == 1 / 0 ? "5e" + e : (i = s.toExponential()).slice(0, i.indexOf("e") + 1) + e)).s = c.s), a = (e = h.precision) + 3;;) if (u = (l = (o = n).times(o).times(o)).plus(c), n = q(u.plus(c).times(o), u.plus(l), a + 2, 1), A(o.d).slice(0, a) === (i = A(n.d)).slice(0, a)) { if ("9999" != (i = i.slice(a - 3, a + 1)) && (r || "4999" != i)) { +i && (+i.slice(1) || "5" != i.charAt(0)) || (P(n, e + 1, 1), t = !n.times(n).times(n).eq(c)); break; } if (!r && (P(o, e + 1, 0), o.times(o).times(o).eq(c))) { n = o; break; } a += 4, r = 1; } return m = !0, P(n, e, h.rounding, t); }, k.decimalPlaces = k.dp = function () { var e, t = this.d, i = NaN; if (t) { if (i = 7 * ((e = t.length - 1) - b(this.e / 7)), e = t[e]) for (; e % 10 == 0; e /= 10) i--; i < 0 && (i = 0); } return i; }, k.dividedBy = k.div = function (e) { return q(this, new this.constructor(e)); }, k.dividedToIntegerBy = k.divToInt = function (e) { var t = this.constructor; return P(q(this, new t(e), 0, 1, 1), t.precision, t.rounding); }, k.equals = k.eq = function (e) { return 0 === this.cmp(e); }, k.floor = function () { return P(new this.constructor(this), this.e + 1, 3); }, k.greaterThan = k.gt = function (e) { return this.cmp(e) > 0; }, k.greaterThanOrEqualTo = k.gte = function (e) { var t = this.cmp(e); return 1 == t || 0 === t; }, k.hyperbolicCosine = k.cosh = function () { var e, t, i, n, r, s = this, a = s.constructor, o = new a(1); if (!s.isFinite()) return new a(s.s ? 1 / 0 : NaN); if (s.isZero()) return o; i = a.precision, n = a.rounding, a.precision = i + Math.max(s.e, s.sd()) + 4, a.rounding = 1, (r = s.d.length) < 32 ? t = "" + 1 / U(4, e = Math.ceil(r / 3)) : (e = 16, t = "2.3283064365386962890625e-10"), s = Q(a, 1, s.times(t), new a(1), !0); for (var l, u = e, c = new a(8); u--;) l = s.times(s), s = o.minus(l.times(c.minus(l.times(c)))); return P(s, a.precision = i, a.rounding = n, !0); }, k.hyperbolicSine = k.sinh = function () { var e, t, i, n, r = this, s = r.constructor; if (!r.isFinite() || r.isZero()) return new s(r); if (t = s.precision, i = s.rounding, s.precision = t + Math.max(r.e, r.sd()) + 4, s.rounding = 1, (n = r.d.length) < 3) r = Q(s, 2, r, r, !0); else { e = (e = 1.4 * Math.sqrt(n)) > 16 ? 16 : 0 | e, r = Q(s, 2, r = r.times(1 / U(5, e)), r, !0); for (var a, o = new s(5), l = new s(16), u = new s(20); e--;) a = r.times(r), r = r.times(o.plus(a.times(l.times(a).plus(u)))); } return s.precision = t, s.rounding = i, P(r, t, i, !0); }, k.hyperbolicTangent = k.tanh = function () { var e, t, i = this, n = i.constructor; return i.isFinite() ? i.isZero() ? new n(i) : (e = n.precision, t = n.rounding, n.precision = e + 7, n.rounding = 1, q(i.sinh(), i.cosh(), n.precision = e, n.rounding = t)) : new n(i.s); }, k.inverseCosine = k.acos = function () { var e, t = this, i = t.constructor, n = t.abs().cmp(1), r = i.precision, s = i.rounding; return -1 !== n ? 0 === n ? t.isNeg() ? L(i, r, s) : new i(0) : new i(NaN) : t.isZero() ? L(i, r + 4, s).times(.5) : (i.precision = r + 6, i.rounding = 1, t = t.asin(), e = L(i, r + 4, s).times(.5), i.precision = r, i.rounding = s, e.minus(t)); }, k.inverseHyperbolicCosine = k.acosh = function () { var e, t, i = this, n = i.constructor; return i.lte(1) ? new n(i.eq(1) ? 0 : NaN) : i.isFinite() ? (e = n.precision, t = n.rounding, n.precision = e + Math.max(Math.abs(i.e), i.sd()) + 4, n.rounding = 1, m = !1, i = i.times(i).minus(1).sqrt().plus(i), m = !0, n.precision = e, n.rounding = t, i.ln()) : new n(i); }, k.inverseHyperbolicSine = k.asinh = function () { var e, t, i = this, n = i.constructor; return !i.isFinite() || i.isZero() ? new n(i) : (e = n.precision, t = n.rounding, n.precision = e + 2 * Math.max(Math.abs(i.e), i.sd()) + 6, n.rounding = 1, m = !1, i = i.times(i).plus(1).sqrt().plus(i), m = !0, n.precision = e, n.rounding = t, i.ln()); }, k.inverseHyperbolicTangent = k.atanh = function () { var e, t, i, n, r = this, s = r.constructor; return r.isFinite() ? r.e >= 0 ? new s(r.abs().eq(1) ? r.s / 0 : r.isZero() ? r : NaN) : (e = s.precision, t = s.rounding, n = r.sd(), Math.max(n, e) < 2 * -r.e - 1 ? P(new s(r), e, t, !0) : (s.precision = i = n - r.e, r = q(r.plus(1), new s(1).minus(r), i + e, 1), s.precision = e + 4, s.rounding = 1, r = r.ln(), s.precision = e, s.rounding = t, r.times(.5))) : new s(NaN); }, k.inverseSine = k.asin = function () { var e, t, i, n, r = this, s = r.constructor; return r.isZero() ? new s(r) : (t = r.abs().cmp(1), i = s.precision, n = s.rounding, -1 !== t ? 0 === t ? ((e = L(s, i + 4, n).times(.5)).s = r.s, e) : new s(NaN) : (s.precision = i + 6, s.rounding = 1, r = r.div(new s(1).minus(r.times(r)).sqrt().plus(1)).atan(), s.precision = i, s.rounding = n, r.times(2))); }, k.inverseTangent = k.atan = function () { var e, t, i, n, r, s, a, o, l, u = this, c = u.constructor, h = c.precision, f = c.rounding; if (u.isFinite()) { if (u.isZero()) return new c(u); if (u.abs().eq(1) && h + 4 <= I) return (a = L(c, h + 4, f).times(.25)).s = u.s, a; } else { if (!u.s) return new c(NaN); if (h + 4 <= I) return (a = L(c, h + 4, f).times(.5)).s = u.s, a; } for (c.precision = o = h + 10, c.rounding = 1, e = i = Math.min(28, o / 7 + 2 | 0); e; --e) u = u.div(u.times(u).plus(1).sqrt().plus(1)); for (m = !1, t = Math.ceil(o / 7), n = 1, l = u.times(u), a = new c(u), r = u; -1 !== e;) if (r = r.times(l), s = a.minus(r.div(n += 2)), r = r.times(l), void 0 !== (a = s.plus(r.div(n += 2))).d[t]) for (e = t; a.d[e] === s.d[e] && e--;) ; return i && (a = a.times(2 << i - 1)), m = !0, P(a, c.precision = h, c.rounding = f, !0); }, k.isFinite = function () { return !!this.d; }, k.isInteger = k.isInt = function () { return !!this.d && b(this.e / 7) > this.d.length - 2; }, k.isNaN = function () { return !this.s; }, k.isNegative = k.isNeg = function () { return this.s < 0; }, k.isPositive = k.isPos = function () { return this.s > 0; }, k.isZero = function () { return !!this.d && 0 === this.d[0]; }, k.lessThan = k.lt = function (e) { return this.cmp(e) < 0; }, k.lessThanOrEqualTo = k.lte = function (e) { return this.cmp(e) < 1; }, k.logarithm = k.log = function (e) { var t, i, n, r, s, a, o, l, u = this, c = u.constructor, h = c.precision, f = c.rounding; if (null == e) e = new c(10), t = !0; else { if (i = (e = new c(e)).d, e.s < 0 || !i || !i[0] || e.eq(1)) return new c(NaN); t = e.eq(10); } if (i = u.d, u.s < 0 || !i || !i[0] || u.eq(1)) return new c(i && !i[0] ? -1 / 0 : 1 != u.s ? NaN : i ? 0 : 1 / 0); if (t) if (i.length > 1) s = !0; else { for (r = i[0]; r % 10 == 0;) r /= 10; s = 1 !== r; } if (m = !1, a = G(u, o = h + 5), n = t ? D(c, o + 10) : G(e, o), F((l = q(a, n, o, 1)).d, r = h, f)) do { if (a = G(u, o += 10), n = t ? D(c, o + 10) : G(e, o), l = q(a, n, o, 1), !s) { +A(l.d).slice(r + 1, r + 15) + 1 == 1e14 && (l = P(l, h + 1, 0)); break; } } while (F(l.d, r += 10, f)); return m = !0, P(l, h, f); }, k.minus = k.sub = function (e) { var t, i, n, r, s, a, o, l, u, c, h, f, g = this, p = g.constructor; if (e = new p(e), !g.d || !e.d) return g.s && e.s ? g.d ? e.s = -e.s : e = new p(e.d || g.s !== e.s ? g : NaN) : e = new p(NaN), e; if (g.s != e.s) return e.s = -e.s, g.plus(e); if (u = g.d, f = e.d, o = p.precision, l = p.rounding, !u[0] || !f[0]) { if (f[0]) e.s = -e.s; else { if (!u[0]) return new p(3 === l ? -0 : 0); e = new p(g); } return m ? P(e, o, l) : e; } if (i = b(e.e / 7), c = b(g.e / 7), u = u.slice(), s = c - i) { for ((h = s < 0) ? (t = u, s = -s, a = f.length) : (t = f, i = c, a = u.length), s > (n = Math.max(Math.ceil(o / 7), a) + 2) && (s = n, t.length = 1), t.reverse(), n = s; n--;) t.push(0); t.reverse(); } else { for ((h = (n = u.length) < (a = f.length)) && (a = n), n = 0; n < a; n++) if (u[n] != f[n]) { h = u[n] < f[n]; break; } s = 0; } for (h && (t = u, u = f, f = t, e.s = -e.s), a = u.length, n = f.length - a; n > 0; --n) u[a++] = 0; for (n = f.length; n > s;) { if (u[--n] < f[n]) { for (r = n; r && 0 === u[--r];) u[r] = w - 1; --u[r], u[n] += w; } u[n] -= f[n]; } for (; 0 === u[--a];) u.pop(); for (; 0 === u[0]; u.shift()) --i; return u[0] ? (e.d = u, e.e = C(u, i), m ? P(e, o, l) : e) : new p(3 === l ? -0 : 0); }, k.modulo = k.mod = function (e) { var t, i = this, n = i.constructor; return e = new n(e), !i.d || !e.s || e.d && !e.d[0] ? new n(NaN) : !e.d || i.d && !i.d[0] ? P(new n(i), n.precision, n.rounding) : (m = !1, 9 == n.modulo ? (t = q(i, e.abs(), 0, 3, 1)).s *= e.s : t = q(i, e, 0, n.modulo, 1), t = t.times(e), m = !0, i.minus(t)); }, k.naturalExponential = k.exp = function () { return j(this); }, k.naturalLogarithm = k.ln = function () { return G(this); }, k.negated = k.neg = function () { var e = new this.constructor(this); return e.s = -e.s, P(e); }, k.plus = k.add = function (e) { var t, i, n, r, s, a, o, l, u, c, h = this, f = h.constructor; if (e = new f(e), !h.d || !e.d) return h.s && e.s ? h.d || (e = new f(e.d || h.s === e.s ? h : NaN)) : e = new f(NaN), e; if (h.s != e.s) return e.s = -e.s, h.minus(e); if (u = h.d, c = e.d, o = f.precision, l = f.rounding, !u[0] || !c[0]) return c[0] || (e = new f(h)), m ? P(e, o, l) : e; if (s = b(h.e / 7), n = b(e.e / 7), u = u.slice(), r = s - n) { for (r < 0 ? (i = u, r = -r, a = c.length) : (i = c, n = s, a = u.length), r > (a = (s = Math.ceil(o / 7)) > a ? s + 1 : a + 1) && (r = a, i.length = 1), i.reverse(); r--;) i.push(0); i.reverse(); } for ((a = u.length) - (r = c.length) < 0 && (r = a, i = c, c = u, u = i), t = 0; r;) t = (u[--r] = u[r] + c[r] + t) / w | 0, u[r] %= w; for (t && (u.unshift(t), ++n), a = u.length; 0 == u[--a];) u.pop(); return e.d = u, e.e = C(u, n), m ? P(e, o, l) : e; }, k.precision = k.sd = function (e) { var t, i = this; if (void 0 !== e && e !== !!e && 1 !== e && 0 !== e) throw Error(g + e); return i.d ? (t = T(i.d), e && i.e + 1 > t && (t = i.e + 1)) : t = NaN, t; }, k.round = function () { var e = this, t = e.constructor; return P(new t(e), e.e + 1, t.rounding); }, k.sine = k.sin = function () { var e, t, i = this, n = i.constructor; return i.isFinite() ? i.isZero() ? new n(i) : (e = n.precision, t = n.rounding, n.precision = e + Math.max(i.e, i.sd()) + 7, n.rounding = 1, i = function (e, t) { var i, n = t.d.length; if (n < 3) return t.isZero() ? t : Q(e, 2, t, t); i = (i = 1.4 * Math.sqrt(n)) > 16 ? 16 : 0 | i, t = Q(e, 2, t = t.times(1 / U(5, i)), t); for (var r, s = new e(5), a = new e(16), o = new e(20); i--;) r = t.times(t), t = t.times(s.plus(r.times(a.times(r).minus(o)))); return t; }(n, K(n, i)), n.precision = e, n.rounding = t, P(r > 2 ? i.neg() : i, e, t, !0)) : new n(NaN); }, k.squareRoot = k.sqrt = function () { var e, t, i, n, r, s, a = this, o = a.d, l = a.e, u = a.s, c = a.constructor; if (1 !== u || !o || !o[0]) return new c(!u || u < 0 && (!o || o[0]) ? NaN : o ? a : 1 / 0); for (m = !1, 0 == (u = Math.sqrt(+a)) || u == 1 / 0 ? (((t = A(o)).length + l) % 2 == 0 && (t += "0"), u = Math.sqrt(t), l = b((l + 1) / 2) - (l < 0 || l % 2), n = new c(t = u == 1 / 0 ? "5e" + l : (t = u.toExponential()).slice(0, t.indexOf("e") + 1) + l)) : n = new c(u.toString()), i = (l = c.precision) + 3;;) if (n = (s = n).plus(q(a, s, i + 2, 1)).times(.5), A(s.d).slice(0, i) === (t = A(n.d)).slice(0, i)) { if ("9999" != (t = t.slice(i - 3, i + 1)) && (r || "4999" != t)) { +t && (+t.slice(1) || "5" != t.charAt(0)) || (P(n, l + 1, 1), e = !n.times(n).eq(a)); break; } if (!r && (P(s, l + 1, 0), s.times(s).eq(a))) { n = s; break; } i += 4, r = 1; } return m = !0, P(n, l, c.rounding, e); }, k.tangent = k.tan = function () { var e, t, i = this, n = i.constructor; return i.isFinite() ? i.isZero() ? new n(i) : (e = n.precision, t = n.rounding, n.precision = e + 10, n.rounding = 1, (i = i.sin()).s = 1, i = q(i, new n(1).minus(i.times(i)).sqrt(), e + 10, 0), n.precision = e, n.rounding = t, P(2 == r || 4 == r ? i.neg() : i, e, t, !0)) : new n(NaN); }, k.times = k.mul = function (e) { var t, i, n, r, s, a, o, l, u, c = this, h = c.constructor, f = c.d, g = (e = new h(e)).d; if (e.s *= c.s, !(f && f[0] && g && g[0])) return new h(!e.s || f && !f[0] && !g || g && !g[0] && !f ? NaN : f && g ? 0 * e.s : e.s / 0); for (i = b(c.e / 7) + b(e.e / 7), (l = f.length) < (u = g.length) && (s = f, f = g, g = s, a = l, l = u, u = a), s = [], n = a = l + u; n--;) s.push(0); for (n = u; --n >= 0;) { for (t = 0, r = l + n; r > n;) o = s[r] + g[n] * f[r - n - 1] + t, s[r--] = o % w | 0, t = o / w | 0; s[r] = (s[r] + t) % w | 0; } for (; !s[--a];) s.pop(); return t ? ++i : s.shift(), e.d = s, e.e = C(s, i), m ? P(e, h.precision, h.rounding) : e; }, k.toBinary = function (e, t) { return Y(this, 2, e, t); }, k.toDecimalPlaces = k.toDP = function (e, t) { var i = this, n = i.constructor; return i = new n(i), void 0 === e ? i : (M(e, 0, o), void 0 === t ? t = n.rounding : M(t, 0, 8), P(i, e + i.e + 1, t)); }, k.toExponential = function (e, t) { var i, n = this, r = n.constructor; return void 0 === e ? i = V(n, !0) : (M(e, 0, o), void 0 === t ? t = r.rounding : M(t, 0, 8), i = V(n = P(new r(n), e + 1, t), !0, e + 1)), n.isNeg() && !n.isZero() ? "-" + i : i; }, k.toFixed = function (e, t) { var i, n, r = this, s = r.constructor; return void 0 === e ? i = V(r) : (M(e, 0, o), void 0 === t ? t = s.rounding : M(t, 0, 8), i = V(n = P(new s(r), e + r.e + 1, t), !1, e + n.e + 1)), r.isNeg() && !r.isZero() ? "-" + i : i; }, k.toFraction = function (e) { var t, i, n, r, s, a, o, l, u, c, h, f, p = this, d = p.d, v = p.constructor; if (!d) return new v(p); if (u = i = new v(1), n = l = new v(0), a = (s = (t = new v(n)).e = T(d) - p.e - 1) % 7, t.d[0] = y(10, a < 0 ? 7 + a : a), null == e) e = s > 0 ? t : u; else { if (!(o = new v(e)).isInt() || o.lt(u)) throw Error(g + o); e = o.gt(t) ? s > 0 ? t : u : o; } for (m = !1, o = new v(A(d)), c = v.precision, v.precision = s = 7 * d.length * 2; h = q(o, t, 0, 1, 1), 1 != (r = i.plus(h.times(n))).cmp(e);) i = n, n = r, r = u, u = l.plus(h.times(r)), l = r, r = t, t = o.minus(h.times(r)), o = r; return r = q(e.minus(i), n, 0, 1, 1), l = l.plus(r.times(u)), i = i.plus(r.times(n)), l.s = u.s = p.s, f = q(u, n, s, 1).minus(p).abs().cmp(q(l, i, s, 1).minus(p).abs()) < 1 ? [u, n] : [l, i], v.precision = c, m = !0, f; }, k.toHexadecimal = k.toHex = function (e, t) { return Y(this, 16, e, t); }, k.toNearest = function (e, t) { var i = this, n = i.constructor; if (i = new n(i), null == e) { if (!i.d) return i; e = new n(1), t = n.rounding; } else { if (e = new n(e), void 0 === t ? t = n.rounding : M(t, 0, 8), !i.d) return e.s ? i : e; if (!e.d) return e.s && (e.s = i.s), e; } return e.d[0] ? (m = !1, i = q(i, e, 0, t, 1).times(e), m = !0, P(i)) : (e.s = i.s, i = e), i; }, k.toNumber = function () { return +this; }, k.toOctal = function (e, t) { return Y(this, 8, e, t); }, k.toPower = k.pow = function (e) { var t, i, n, r, s, a, o = this, l = o.constructor, u = +(e = new l(e)); if (!(o.d && e.d && o.d[0] && e.d[0])) return new l(y(+o, u)); if ((o = new l(o)).eq(1)) return o; if (n = l.precision, s = l.rounding, e.eq(1)) return P(o, n, s); if ((t = b(e.e / 7)) >= e.d.length - 1 && (i = u < 0 ? -u : u) <= 9007199254740991) return r = z(l, o, i, n), e.s < 0 ? new l(1).div(r) : P(r, n, s); if ((a = o.s) < 0) { if (t < e.d.length - 1) return new l(NaN); if (0 == (1 & e.d[t]) && (a = 1), 0 == o.e && 1 == o.d[0] && 1 == o.d.length) return o.s = a, o; } return (t = 0 != (i = y(+o, u)) && isFinite(i) ? new l(i + "").e : b(u * (Math.log("0." + A(o.d)) / Math.LN10 + o.e + 1))) > l.maxE + 1 || t < l.minE - 1 ? new l(t > 0 ? a / 0 : 0) : (m = !1, l.rounding = o.s = 1, i = Math.min(12, (t + "").length), (r = j(e.times(G(o, n + i)), n)).d && F((r = P(r, n + 5, 1)).d, n, s) && (t = n + 10, +A((r = P(j(e.times(G(o, t + i)), t), t + 5, 1)).d).slice(n + 1, n + 15) + 1 == 1e14 && (r = P(r, n + 1, 0))), r.s = a, m = !0, l.rounding = s, P(r, n, s)); }, k.toPrecision = function (e, t) { var i, n = this, r = n.constructor; return void 0 === e ? i = V(n, n.e <= r.toExpNeg || n.e >= r.toExpPos) : (M(e, 1, o), void 0 === t ? t = r.rounding : M(t, 0, 8), i = V(n = P(new r(n), e, t), e <= n.e || n.e <= r.toExpNeg, e)), n.isNeg() && !n.isZero() ? "-" + i : i; }, k.toSignificantDigits = k.toSD = function (e, t) { var i = this.constructor; return void 0 === e ? (e = i.precision, t = i.rounding) : (M(e, 1, o), void 0 === t ? t = i.rounding : M(t, 0, 8)), P(new i(this), e, t); }, k.toString = function () { var e = this, t = e.constructor, i = V(e, e.e <= t.toExpNeg || e.e >= t.toExpPos); return e.isNeg() && !e.isZero() ? "-" + i : i; }, k.truncated = k.trunc = function () { return P(new this.constructor(this), this.e + 1, 1); }, k.valueOf = k.toJSON = function () { var e = this, t = e.constructor, i = V(e, e.e <= t.toExpNeg || e.e >= t.toExpPos); return e.isNeg() ? "-" + i : i; }; var q = function () { function e(e, t, i) { var n, r = 0, s = e.length; for (e = e.slice(); s--;) n = e[s] * t + r, e[s] = n % i | 0, r = n / i | 0; return r && e.unshift(r), e; } function t(e, t, i, n) { var r, s; if (i != n) s = i > n ? 1 : -1; else for (r = s = 0; r < i; r++) if (e[r] != t[r]) { s = e[r] > t[r] ? 1 : -1; break; } return s; } function n(e, t, i, n) { for (var r = 0; i--;) e[i] -= r, r = e[i] < t[i] ? 1 : 0, e[i] = r * n + e[i] - t[i]; for (; !e[0] && e.length > 1;) e.shift(); } return function (r, s, a, o, l, u) { var c, h, m, f, g, p, d, v, y, N, x, _, S, E, I, k, A, M, F, O, q = r.constructor, V = r.s == s.s ? 1 : -1, C = r.d, D = s.d; if (!(C && C[0] && D && D[0])) return new q(r.s && s.s && (C ? !D || C[0] != D[0] : D) ? C && 0 == C[0] || !D ? 0 * V : V / 0 : NaN); for (u ? (g = 1, h = r.e - s.e) : (u = w, g = 7, h = b(r.e / g) - b(s.e / g)), F = D.length, A = C.length, N = (y = new q(V)).d = [], m = 0; D[m] == (C[m] || 0); m++) ; if (D[m] > (C[m] || 0) && h--, null == a ? (E = a = q.precision, o = q.rounding) : E = l ? a + (r.e - s.e) + 1 : a, E < 0) N.push(1), p = !0; else { if (E = E / g + 2 | 0, m = 0, 1 == F) { for (f = 0, D = D[0], E++; (m < A || f) && E--; m++) I = f * u + (C[m] || 0), N[m] = I / D | 0, f = I % D | 0; p = f || m < A; } else { for ((f = u / (D[0] + 1) | 0) > 1 && (D = e(D, f, u), C = e(C, f, u), F = D.length, A = C.length), k = F, _ = (x = C.slice(0, F)).length; _ < F;) x[_++] = 0; (O = D.slice()).unshift(0), M = D[0], D[1] >= u / 2 && ++M; do { f = 0, (c = t(D, x, F, _)) < 0 ? (S = x[0], F != _ && (S = S * u + (x[1] || 0)), (f = S / M | 0) > 1 ? (f >= u && (f = u - 1), 1 == (c = t(d = e(D, f, u), x, v = d.length, _ = x.length)) && (f--, n(d, F < v ? O : D, v, u))) : (0 == f && (c = f = 1), d = D.slice()), (v = d.length) < _ && d.unshift(0), n(x, d, _, u), -1 == c && (c = t(D, x, F, _ = x.length)) < 1 && (f++, n(x, F < _ ? O : D, _, u)), _ = x.length) : 0 === c && (f++, x = [0]), N[m++] = f, c && x[0] ? x[_++] = C[k] || 0 : (x = [C[k]], _ = 1); } while ((k++ < A || void 0 !== x[0]) && E--); p = void 0 !== x[0]; } N[0] || N.shift(); } if (1 == g) y.e = h, i = p; else { for (m = 1, f = N[0]; f >= 10; f /= 10) m++; y.e = m + h * g - 1, P(y, l ? a + y.e + 1 : a, o, p); } return y; }; }(); function P(e, t, i, n) { var r, s, a, o, l, u, c, h, f, g = e.constructor; e: if (null != t) { if (!(h = e.d)) return e; for (r = 1, o = h[0]; o >= 10; o /= 10) r++; if ((s = t - r) < 0) s += 7, a = t, l = (c = h[f = 0]) / y(10, r - a - 1) % 10 | 0; else if ((f = Math.ceil((s + 1) / 7)) >= (o = h.length)) { if (!n) break e; for (; o++ <= f;) h.push(0); c = l = 0, r = 1, a = (s %= 7) - 7 + 1; } else { for (c = o = h[f], r = 1; o >= 10; o /= 10) r++; l = (a = (s %= 7) - 7 + r) < 0 ? 0 : c / y(10, r - a - 1) % 10 | 0; } if (n = n || t < 0 || void 0 !== h[f + 1] || (a < 0 ? c : c % y(10, r - a - 1)), u = i < 4 ? (l || n) && (0 == i || i == (e.s < 0 ? 3 : 2)) : l > 5 || 5 == l && (4 == i || n || 6 == i && (s > 0 ? a > 0 ? c / y(10, r - a) : 0 : h[f - 1]) % 10 & 1 || i == (e.s < 0 ? 8 : 7)), t < 1 || !h[0]) return h.length = 0, u ? (t -= e.e + 1, h[0] = y(10, (7 - t % 7) % 7), e.e = -t || 0) : h[0] = e.e = 0, e; if (0 == s ? (h.length = f, o = 1, f--) : (h.length = f + 1, o = y(10, 7 - s), h[f] = a > 0 ? (c / y(10, r - a) % y(10, a) | 0) * o : 0), u) for (;;) { if (0 == f) { for (s = 1, a = h[0]; a >= 10; a /= 10) s++; for (a = h[0] += o, o = 1; a >= 10; a /= 10) o++; s != o && (e.e++, h[0] == w && (h[0] = 1)); break; } if (h[f] += o, h[f] != w) break; h[f--] = 0, o = 1; } for (s = h.length; 0 === h[--s];) h.pop(); } return m && (e.e > g.maxE ? (e.d = null, e.e = NaN) : e.e < g.minE && (e.e = 0, e.d = [0])), e; } function V(e, t, i) { if (!e.isFinite()) return H(e); var n, r = e.e, s = A(e.d), a = s.length; return t ? (i && (n = i - a) > 0 ? s = s.charAt(0) + "." + s.slice(1) + R(n) : a > 1 && (s = s.charAt(0) + "." + s.slice(1)), s = s + (e.e < 0 ? "e" : "e+") + e.e) : r < 0 ? (s = "0." + R(-r - 1) + s, i && (n = i - a) > 0 && (s += R(n))) : r >= a ? (s += R(r + 1 - a), i && (n = i - r - 1) > 0 && (s = s + "." + R(n))) : ((n = r + 1) < a && (s = s.slice(0, n) + "." + s.slice(n)), i && (n = i - a) > 0 && (r + 1 === a && (s += "."), s += R(n))), s; } function C(e, t) { var i = e[0]; for (t *= 7; i >= 10; i /= 10) t++; return t; } function D(e, t, i) { if (t > E) throw m = !0, i && (e.precision = i), Error(p); return P(new e(u), t, 1, !0); } function L(e, t, i) { if (t > I) throw Error(p); return P(new e(c), t, i, !0); } function T(e) { var t = e.length - 1, i = 7 * t + 1; if (t = e[t]) { for (; t % 10 == 0; t /= 10) i--; for (t = e[0]; t >= 10; t /= 10) i++; } return i; } function R(e) { for (var t = ""; e--;) t += "0"; return t; } function z(e, t, i, n) { var r, s = new e(1), a = Math.ceil(n / 7 + 4); for (m = !1;;) { if (i % 2 && J((s = s.times(t)).d, a) && (r = !0), 0 === (i = b(i / 2))) { i = s.d.length - 1, r && 0 === s.d[i] && ++s.d[i]; break; } J((t = t.times(t)).d, a); } return m = !0, s; } function $(e) { return 1 & e.d[e.d.length - 1]; } function Z(e, t, i) { for (var n, r = new e(t[0]), s = 0; ++s < t.length;) { if (!(n = new e(t[s])).s) { r = n; break; } r[i](n) && (r = n); } return r; } function j(e, t) { var i, n, r, s, a, o, l, u = 0, c = 0, h = 0, f = e.constructor, g = f.rounding, p = f.precision; if (!e.d || !e.d[0] || e.e > 17) return new f(e.d ? e.d[0] ? e.s < 0 ? 0 : 1 / 0 : 1 : e.s ? e.s < 0 ? 0 : e : NaN); for (null == t ? (m = !1, l = p) : l = t, o = new f(.03125); e.e > -2;) e = e.times(o), h += 5; for (l += n = Math.log(y(2, h)) / Math.LN10 * 2 + 5 | 0, i = s = a = new f(1), f.precision = l;;) { if (s = P(s.times(e), l, 1), i = i.times(++c), A((o = a.plus(q(s, i, l, 1))).d).slice(0, l) === A(a.d).slice(0, l)) { for (r = h; r--;) a = P(a.times(a), l, 1); if (null != t) return f.precision = p, a; if (!(u < 3 && F(a.d, l - n, g, u))) return P(a, f.precision = p, g, m = !0); f.precision = l += 10, i = s = o = new f(1), c = 0, u++; } a = o; } } function G(e, t) { var i, n, r, s, a, o, l, u, c, h, f, g = 1, p = e, d = p.d, v = p.constructor, b = v.rounding, y = v.precision; if (p.s < 0 || !d || !d[0] || !p.e && 1 == d[0] && 1 == d.length) return new v(d && !d[0] ? -1 / 0 : 1 != p.s ? NaN : d ? 0 : p); if (null == t ? (m = !1, c = y) : c = t, v.precision = c += 10, n = (i = A(d)).charAt(0), !(Math.abs(s = p.e) < 15e14)) return u = D(v, c + 2, y).times(s + ""), p = G(new v(n + "." + i.slice(1)), c - 10).plus(u), v.precision = y, null == t ? P(p, y, b, m = !0) : p; for (; n < 7 && 1 != n || 1 == n && i.charAt(1) > 3;) n = (i = A((p = p.times(e)).d)).charAt(0), g++; for (s = p.e, n > 1 ? (p = new v("0." + i), s++) : p = new v(n + "." + i.slice(1)), h = p, l = a = p = q(p.minus(1), p.plus(1), c, 1), f = P(p.times(p), c, 1), r = 3;;) { if (a = P(a.times(f), c, 1), A((u = l.plus(q(a, new v(r), c, 1))).d).slice(0, c) === A(l.d).slice(0, c)) { if (l = l.times(2), 0 !== s && (l = l.plus(D(v, c + 2, y).times(s + ""))), l = q(l, new v(g), c, 1), null != t) return v.precision = y, l; if (!F(l.d, c - 10, b, o)) return P(l, v.precision = y, b, m = !0); v.precision = c += 10, u = a = p = q(h.minus(1), h.plus(1), c, 1), f = P(p.times(p), c, 1), r = o = 1; } l = u, r += 2; } } function H(e) { return e.s * e.s / 0 + ""; } function W(e, t) { var i, n, r; for ((i = t.indexOf(".")) > -1 && (t = t.replace(".", "")), (n = t.search(/e/i)) > 0 ? (i < 0 && (i = n), i += +t.slice(n + 1), t = t.substring(0, n)) : i < 0 && (i = t.length), n = 0; 48 === t.charCodeAt(n); n++) ; for (r = t.length; 48 === t.charCodeAt(r - 1); --r) ; if (t = t.slice(n, r)) { if (r -= n, e.e = i = i - n - 1, e.d = [], n = (i + 1) % 7, i < 0 && (n += 7), n < r) { for (n && e.d.push(+t.slice(0, n)), r -= 7; n < r;) e.d.push(+t.slice(n, n += 7)); n = 7 - (t = t.slice(n)).length; } else n -= r; for (; n--;) t += "0"; e.d.push(+t), m && (e.e > e.constructor.maxE ? (e.d = null, e.e = NaN) : e.e < e.constructor.minE && (e.e = 0, e.d = [0])); } else e.e = 0, e.d = [0]; return e; } function B(e, i) { var n, r, s, a, o, l, u, c, h; if (i.indexOf("_") > -1) { if (i = i.replace(/(\d)_(?=\d)/g, "$1"), S.test(i)) return W(e, i); } else if ("Infinity" === i || "NaN" === i) return +i || (e.s = NaN), e.e = NaN, e.d = null, e; if (x.test(i)) n = 16, i = i.toLowerCase(); else if (N.test(i)) n = 2; else { if (!_.test(i)) throw Error(g + i); n = 8; } for ((a = i.search(/p/i)) > 0 ? (u = +i.slice(a + 1), i = i.substring(2, a)) : i = i.slice(2), o = (a = i.indexOf(".")) >= 0, r = e.constructor, o && (a = (l = (i = i.replace(".", "")).length) - a, s = z(r, new r(n), a, 2 * a)), a = h = (c = O(i, n, w)).length - 1; 0 === c[a]; --a) c.pop(); return a < 0 ? new r(0 * e.s) : (e.e = C(c, h), e.d = c, m = !1, o && (e = q(e, s, 4 * l)), u && (e = e.times(Math.abs(u) < 54 ? y(2, u) : t.pow(2, u))), m = !0, e); } function Q(e, t, i, n, r) { var s, a, o, l, u = e.precision, c = Math.ceil(u / 7); for (m = !1, l = i.times(i), o = new e(n);;) { if (a = q(o.times(l), new e(t++ * t++), u, 1), o = r ? n.plus(a) : n.minus(a), n = q(a.times(l), new e(t++ * t++), u, 1), void 0 !== (a = o.plus(n)).d[c]) { for (s = c; a.d[s] === o.d[s] && s--;) ; if (-1 == s) break; } s = o, o = n, n = a, a = s; } return m = !0, a.d.length = c + 1, a; } function U(e, t) { for (var i = e; --t;) i *= e; return i; } function K(e, t) { var i, n = t.s < 0, s = L(e, e.precision, 1), a = s.times(.5); if ((t = t.abs()).lte(a)) return r = n ? 4 : 1, t; if ((i = t.divToInt(s)).isZero()) r = n ? 3 : 2; else { if ((t = t.minus(i.times(s))).lte(a)) return r = $(i) ? n ? 2 : 3 : n ? 4 : 1, t; r = $(i) ? n ? 1 : 4 : n ? 3 : 2; } return t.minus(s).abs(); } function Y(e, t, n, r) { var s, a, u, c, h, m, f, g, p, d = e.constructor, v = void 0 !== n; if (v ? (M(n, 1, o), void 0 === r ? r = d.rounding : M(r, 0, 8)) : (n = d.precision, r = d.rounding), e.isFinite()) { for (v ? (s = 2, 16 == t ? n = 4 * n - 3 : 8 == t && (n = 3 * n - 2)) : s = t, (u = (f = V(e)).indexOf(".")) >= 0 && (f = f.replace(".", ""), (p = new d(1)).e = f.length - u, p.d = O(V(p), 10, s), p.e = p.d.length), a = h = (g = O(f, 10, s)).length; 0 == g[--h];) g.pop(); if (g[0]) { if (u < 0 ? a-- : ((e = new d(e)).d = g, e.e = a, g = (e = q(e, p, n, r, 0, s)).d, a = e.e, m = i), u = g[n], c = s / 2, m = m || void 0 !== g[n + 1], m = r < 4 ? (void 0 !== u || m) && (0 === r || r === (e.s < 0 ? 3 : 2)) : u > c || u === c && (4 === r || m || 6 === r && 1 & g[n - 1] || r === (e.s < 0 ? 8 : 7)), g.length = n, m) for (; ++g[--n] > s - 1;) g[n] = 0, n || (++a, g.unshift(1)); for (h = g.length; !g[h - 1]; --h) ; for (u = 0, f = ""; u < h; u++) f += l.charAt(g[u]); if (v) { if (h > 1) if (16 == t || 8 == t) { for (u = 16 == t ? 4 : 3, --h; h % u; h++) f += "0"; for (h = (g = O(f, s, t)).length; !g[h - 1]; --h) ; for (u = 1, f = "1."; u < h; u++) f += l.charAt(g[u]); } else f = f.charAt(0) + "." + f.slice(1); f = f + (a < 0 ? "p" : "p+") + a; } else if (a < 0) { for (; ++a;) f = "0" + f; f = "0." + f; } else if (++a > h) for (a -= h; a--;) f += "0"; else a < h && (f = f.slice(0, a) + "." + f.slice(a)); } else f = v ? "0p+0" : "0"; f = (16 == t ? "0x" : 2 == t ? "0b" : 8 == t ? "0o" : "") + f; } else f = H(e); return e.s < 0 ? "-" + f : f; } function J(e, t) { if (e.length > t) return e.length = t, !0; } function X(e) { return new this(e).abs(); } function ee(e) { return new this(e).acos(); } function te(e) { return new this(e).acosh(); } function ie(e, t) { return new this(e).plus(t); } function ne(e) { return new this(e).asin(); } function re(e) { return new this(e).asinh(); } function se(e) { return new this(e).atan(); } function ae(e) { return new this(e).atanh(); } function oe(e, t) { e = new this(e), t = new this(t); var i, n = this.precision, r = this.rounding, s = n + 4; return e.s && t.s ? e.d || t.d ? !t.d || e.isZero() ? (i = t.s < 0 ? L(this, n, r) : new this(0)).s = e.s : !e.d || t.isZero() ? (i = L(this, s, 1).times(.5)).s = e.s : t.s < 0 ? (this.precision = s, this.rounding = 1, i = this.atan(q(e, t, s, 1)), t = L(this, s, 1), this.precision = n, this.rounding = r, i = e.s < 0 ? i.minus(t) : i.plus(t)) : i = this.atan(q(e, t, s, 1)) : (i = L(this, s, 1).times(t.s > 0 ? .25 : .75)).s = e.s : i = new this(NaN), i; } function le(e) { return new this(e).cbrt(); } function ue(e) { return P(e = new this(e), e.e + 1, 2); } function ce(e, t, i) { return new this(e).clamp(t, i); } function he(e) { if (!e || "object" != typeof e) throw Error(f + "Object expected"); var t, i, n, r = !0 === e.defaults, a = ["precision", 1, o, "rounding", 0, 8, "toExpNeg", -s, 0, "toExpPos", 0, s, "maxE", 0, s, "minE", -s, 0, "modulo", 0, 9]; for (t = 0; t < a.length; t += 3) if (i = a[t], r && (this[i] = h[i]), void 0 !== (n = e[i])) { if (!(b(n) === n && n >= a[t + 1] && n <= a[t + 2])) throw Error(g + i + ": " + n); this[i] = n; } if (i = "crypto", r && (this[i] = h[i]), void 0 !== (n = e[i])) { if (!0 !== n && !1 !== n && 0 !== n && 1 !== n) throw Error(g + i + ": " + n); if (n) { if ("undefined" == typeof crypto || !crypto || !crypto.getRandomValues && !crypto.randomBytes) throw Error(d); this[i] = !0; } else this[i] = !1; } return this; } function me(e) { return new this(e).cos(); } function fe(e) { return new this(e).cosh(); } function ge(e, t) { return new this(e).div(t); } function pe(e) { return new this(e).exp(); } function de(e) { return P(e = new this(e), e.e + 1, 3); } function ve() { var e, t, i = new this(0); for (m = !1, e = 0; e < arguments.length;) if ((t = new this(arguments[e++])).d) i.d && (i = i.plus(t.times(t))); else { if (t.s) return m = !0, new this(1 / 0); i = t; } return m = !0, i.sqrt(); } function be(e) { return e instanceof t || e && e.toStringTag === v || !1; } function ye(e) { return new this(e).ln(); } function Ne(e, t) { return new this(e).log(t); } function xe(e) { return new this(e).log(2); } function _e(e) { return new this(e).log(10); } function Se() { return Z(this, arguments, "lt"); } function we() { return Z(this, arguments, "gt"); } function Ee(e, t) { return new this(e).mod(t); } function Ie(e, t) { return new this(e).mul(t); } function ke(e, t) { return new this(e).pow(t); } function Ae(e) { var t, i, n, r, s = 0, a = new this(1), l = []; if (void 0 === e ? e = this.precision : M(e, 1, o), n = Math.ceil(e / 7), this.crypto) if (crypto.getRandomValues) for (t = crypto.getRandomValues(new Uint32Array(n)); s < n;) (r = t[s]) >= 429e7 ? t[s] = crypto.getRandomValues(new Uint32Array(1))[0] : l[s++] = r % 1e7; else { if (!crypto.randomBytes) throw Error(d); for (t = crypto.randomBytes(n *= 4); s < n;) (r = t[s] + (t[s + 1] << 8) + (t[s + 2] << 16) + ((127 & t[s + 3]) << 24)) >= 214e7 ? crypto.randomBytes(4).copy(t, s) : (l.push(r % 1e7), s += 4); s = n / 4; } else for (; s < n;) l[s++] = 1e7 * Math.random() | 0; for (e %= 7, (n = l[--s]) && e && (r = y(10, 7 - e), l[s] = (n / r | 0) * r); 0 === l[s]; s--) l.pop(); if (s < 0) i = 0, l = [0]; else { for (i = -1; 0 === l[0]; i -= 7) l.shift(); for (n = 1, r = l[0]; r >= 10; r /= 10) n++; n < 7 && (i -= 7 - n); } return a.e = i, a.d = l, a; } function Me(e) { return P(e = new this(e), e.e + 1, this.rounding); } function Fe(e) { return (e = new this(e)).d ? e.d[0] ? e.s : 0 * e.s : e.s || NaN; } function Oe(e) { return new this(e).sin(); } function qe(e) { return new this(e).sinh(); } function Pe(e) { return new this(e).sqrt(); } function Ve(e, t) { return new this(e).sub(t); } function Ce() { var e = 0, t = arguments, i = new this(t[e]); for (m = !1; i.s && ++e < t.length;) i = i.plus(t[e]); return m = !0, P(i, this.precision, this.rounding); } function De(e) { return new this(e).tan(); } function Le(e) { return new this(e).tanh(); } function Te(e) { return P(e = new this(e), e.e + 1, 1); } (t = function e(t) { var i, n, r; function s(e) { var t, i, n, r = this; if (!(r instanceof s)) return new s(e); if (r.constructor = s, be(e)) return r.s = e.s, void (m ? !e.d || e.e > s.maxE ? (r.e = NaN, r.d = null) : e.e < s.minE ? (r.e = 0, r.d = [0]) : (r.e = e.e, r.d = e.d.slice()) : (r.e = e.e, r.d = e.d ? e.d.slice() : e.d)); if ("number" == (n = typeof e)) { if (0 === e) return r.s = 1 / e < 0 ? -1 : 1, r.e = 0, void (r.d = [0]); if (e < 0 ? (e = -e, r.s = -1) : r.s = 1, e === ~~e && e < 1e7) { for (t = 0, i = e; i >= 10; i /= 10) t++; return void (m ? t > s.maxE ? (r.e = NaN, r.d = null) : t < s.minE ? (r.e = 0, r.d = [0]) : (r.e = t, r.d = [e]) : (r.e = t, r.d = [e])); } return 0 * e != 0 ? (e || (r.s = NaN), r.e = NaN, void (r.d = null)) : W(r, e.toString()); } if ("string" !== n) throw Error(g + e); return 45 === (i = e.charCodeAt(0)) ? (e = e.slice(1), r.s = -1) : (43 === i && (e = e.slice(1)), r.s = 1), S.test(e) ? W(r, e) : B(r, e); } if (s.prototype = k, s.ROUND_UP = 0, s.ROUND_DOWN = 1, s.ROUND_CEIL = 2, s.ROUND_FLOOR = 3, s.ROUND_HALF_UP = 4, s.ROUND_HALF_DOWN = 5, s.ROUND_HALF_EVEN = 6, s.ROUND_HALF_CEIL = 7, s.ROUND_HALF_FLOOR = 8, s.EUCLID = 9, s.config = s.set = he, s.clone = e, s.isDecimal = be, s.abs = X, s.acos = ee, s.acosh = te, s.add = ie, s.asin = ne, s.asinh = re, s.atan = se, s.atanh = ae, s.atan2 = oe, s.cbrt = le, s.ceil = ue, s.clamp = ce, s.cos = me, s.cosh = fe, s.div = ge, s.exp = pe, s.floor = de, s.hypot = ve, s.ln = ye, s.log = Ne, s.log10 = _e, s.log2 = xe, s.max = Se, s.min = we, s.mod = Ee, s.mul = Ie, s.pow = ke, s.random = Ae, s.round = Me, s.sign = Fe, s.sin = Oe, s.sinh = qe, s.sqrt = Pe, s.sub = Ve, s.sum = Ce, s.tan = De, s.tanh = Le, s.trunc = Te, void 0 === t && (t = {}), t && !0 !== t.defaults) for (r = ["precision", "rounding", "toExpNeg", "toExpPos", "maxE", "minE", "modulo", "crypto"], i = 0; i < r.length;) t.hasOwnProperty(n = r[i++]) || (t[n] = this[n]); return s.config(t), s; }(h)).prototype.constructor = t, t.default = t.Decimal = t, u = new t(u), c = new t(c), a.exports ? ("function" == typeof Symbol && "symbol" == typeof Symbol.iterator && (k[Symbol.for("nodejs.util.inspect.custom")] = k.toString, k[Symbol.toStringTag] = "Decimal"), a.exports = t) : (e || (e = "undefined" != typeof self && self && self.self == self ? self : window), n = e.Decimal, t.noConflict = function () { return e.Decimal = n, t; }, e.Decimal = t); }(o); var u = l.exports, c = { exports: {} }; function h(e) { return 8205 === e || 65038 === e || 65039 === e || e >= 127995 && e <= 128e3 || e >= 129456 && e <= 129460 || e >= 917536 && e <= 917632; } function m(e) { return e >= 127462 && e <= 127487; } !function (e, t) { !function (t) { var i = function (e) { return .5 * (Math.exp(e) + Math.exp(-e)); }, n = function (e) { return .5 * (Math.exp(e) - Math.exp(-e)); }, r = function () { throw SyntaxError("Invalid Param"); }; function s(e, t) { var i = Math.abs(e), n = Math.abs(t); return 0 === e ? Math.log(n) : 0 === t ? Math.log(i) : i < 3e3 && n < 3e3 ? .5 * Math.log(e * e + t * t) : .5 * Math.log((e /= 2) * e + (t /= 2) * t) + Math.LN2; } function a(e, t) { if (!(this instanceof a)) return new a(e, t); var i = function (e, t) { var i = { re: 0, im: 0 }; if (null == e) i.re = i.im = 0; else if (void 0 !== t) i.re = e, i.im = t; else switch (typeof e) { case "object": if ("im" in e && "re" in e) i.re = e.re, i.im = e.im; else if ("abs" in e && "arg" in e) { if (!Number.isFinite(e.abs) && Number.isFinite(e.arg)) return a.INFINITY; i.re = e.abs * Math.cos(e.arg), i.im = e.abs * Math.sin(e.arg); } else if ("r" in e && "phi" in e) { if (!Number.isFinite(e.r) && Number.isFinite(e.phi)) return a.INFINITY; i.re = e.r * Math.cos(e.phi), i.im = e.r * Math.sin(e.phi); } else 2 === e.length ? (i.re = e[0], i.im = e[1]) : r(); break; case "string": i.im = i.re = 0; var n = e.match(/\d+\.?\d*e[+-]?\d+|\d+\.?\d*|\.\d+|./g), s = 1, o = 0; null === n && r(); for (var l = 0; l < n.length; l++) { var u = n[l]; " " === u || "\t" === u || "\n" === u || ("+" === u ? s++ : "-" === u ? o++ : "i" === u || "I" === u ? (s + o === 0 && r(), " " === n[l + 1] || isNaN(n[l + 1]) ? i.im += parseFloat((o % 2 ? "-" : "") + "1") : (i.im += parseFloat((o % 2 ? "-" : "") + n[l + 1]), l++), s = o = 0) : ((s + o === 0 || isNaN(u)) && r(), "i" === n[l + 1] || "I" === n[l + 1] ? (i.im += parseFloat((o % 2 ? "-" : "") + u), l++) : i.re += parseFloat((o % 2 ? "-" : "") + u), s = o = 0)); } s + o > 0 && r(); break; case "number": i.im = 0, i.re = e; break; default: r(); } return isNaN(i.re) || i.im, i; }(e, t); this.re = i.re, this.im = i.im; } a.prototype = { re: 0, im: 0, sign: function () { var e = this.abs(); return new a(this.re / e, this.im / e); }, add: function (e, t) { var i = new a(e, t); return this.isInfinite() && i.isInfinite() ? a.NAN : this.isInfinite() || i.isInfinite() ? a.INFINITY : new a(this.re + i.re, this.im + i.im); }, sub: function (e, t) { var i = new a(e, t); return this.isInfinite() && i.isInfinite() ? a.NAN : this.isInfinite() || i.isInfinite() ? a.INFINITY : new a(this.re - i.re, this.im - i.im); }, mul: function (e, t) { var i = new a(e, t); return this.isInfinite() && i.isZero() || this.isZero() && i.isInfinite() ? a.NAN : this.isInfinite() || i.isInfinite() ? a.INFINITY : 0 === i.im && 0 === this.im ? new a(this.re * i.re, 0) : new a(this.re * i.re - this.im * i.im, this.re * i.im + this.im * i.re); }, div: function (e, t) { var i = new a(e, t); if (this.isZero() && i.isZero() || this.isInfinite() && i.isInfinite()) return a.NAN; if (this.isInfinite() || i.isZero()) return a.INFINITY; if (this.isZero() || i.isInfinite()) return a.ZERO; e = this.re, t = this.im; var n, r, s = i.re, o = i.im; return 0 === o ? new a(e / s, t / s) : Math.abs(s) < Math.abs(o) ? new a((e * (r = s / o) + t) / (n = s * r + o), (t * r - e) / n) : new a((e + t * (r = o / s)) / (n = o * r + s), (t - e * r) / n); }, pow: function (e, t) { var i = new a(e, t); if (e = this.re, t = this.im, i.isZero()) return a.ONE; if (0 === i.im) { if (0 === t && e > 0) return new a(Math.pow(e, i.re), 0); if (0 === e) switch ((i.re % 4 + 4) % 4) { case 0: return new a(Math.pow(t, i.re), 0); case 1: return new a(0, Math.pow(t, i.re)); case 2: return new a(-Math.pow(t, i.re), 0); case 3: return new a(0, -Math.pow(t, i.re)); } } if (0 === e && 0 === t && i.re > 0 && i.im >= 0) return a.ZERO; var n = Math.atan2(t, e), r = s(e, t); return e = Math.exp(i.re * r - i.im * n), t = i.im * r + i.re * n, new a(e * Math.cos(t), e * Math.sin(t)); }, sqrt: function () { var e, t, i = this.re, n = this.im, r = this.abs(); if (i >= 0) { if (0 === n) return new a(Math.sqrt(i), 0); e = .5 * Math.sqrt(2 * (r + i)); } else e = Math.abs(n) / Math.sqrt(2 * (r - i)); return t = i <= 0 ? .5 * Math.sqrt(2 * (r - i)) : Math.abs(n) / Math.sqrt(2 * (r + i)), new a(e, n < 0 ? -t : t); }, exp: function () { var e = Math.exp(this.re); return this.im, new a(e * Math.cos(this.im), e * Math.sin(this.im)); }, expm1: function () { var e = this.re, t = this.im; return new a(Math.expm1(e) * Math.cos(t) + function (e) { var t = Math.PI / 4; if (-t > e || e > t) return Math.cos(e) - 1; var i = e * e; return i * (i * (i * (i * (i * (i * (i * (i / 20922789888e3 - 1 / 87178291200) + 1 / 479001600) - 1 / 3628800) + 1 / 40320) - 1 / 720) + 1 / 24) - .5); }(t), Math.exp(e) * Math.sin(t)); }, log: function () { var e = this.re, t = this.im; return new a(s(e, t), Math.atan2(t, e)); }, abs: function () { return e = this.re, t = this.im, i = Math.abs(e), n = Math.abs(t), i < 3e3 && n < 3e3 ? Math.sqrt(i * i + n * n) : (i < n ? (i = n, n = e / t) : n = t / e, i * Math.sqrt(1 + n * n)); var e, t, i, n; }, arg: function () { return Math.atan2(this.im, this.re); }, sin: function () { var e = this.re, t = this.im; return new a(Math.sin(e) * i(t), Math.cos(e) * n(t)); }, cos: function () { var e = this.re, t = this.im; return new a(Math.cos(e) * i(t), -Math.sin(e) * n(t)); }, tan: function () { var e = 2 * this.re, t = 2 * this.im, r = Math.cos(e) + i(t); return new a(Math.sin(e) / r, n(t) / r); }, cot: function () { var e = 2 * this.re, t = 2 * this.im, r = Math.cos(e) - i(t); return new a(-Math.sin(e) / r, n(t) / r); }, sec: function () { var e = this.re, t = this.im, r = .5 * i(2 * t) + .5 * Math.cos(2 * e); return new a(Math.cos(e) * i(t) / r, Math.sin(e) * n(t) / r); }, csc: function () { var e = this.re, t = this.im, r = .5 * i(2 * t) - .5 * Math.cos(2 * e); return new a(Math.sin(e) * i(t) / r, -Math.cos(e) * n(t) / r); }, asin: function () { var e = this.re, t = this.im, i = new a(t * t - e * e + 1, -2 * e * t).sqrt(), n = new a(i.re - t, i.im + e).log(); return new a(n.im, -n.re); }, acos: function () { var e = this.re, t = this.im, i = new a(t * t - e * e + 1, -2 * e * t).sqrt(), n = new a(i.re - t, i.im + e).log(); return new a(Math.PI / 2 - n.im, n.re); }, atan: function () { var e = this.re, t = this.im; if (0 === e) { if (1 === t) return new a(0, 1 / 0); if (-1 === t) return new a(0, -1 / 0); } var i = e * e + (1 - t) * (1 - t), n = new a((1 - t * t - e * e) / i, -2 * e / i).log(); return new a(-.5 * n.im, .5 * n.re); }, acot: function () { var e = this.re, t = this.im; if (0 === t) return new a(Math.atan2(1, e), 0); var i = e * e + t * t; return 0 !== i ? new a(e / i, -t / i).atan() : new a(0 !== e ? e / 0 : 0, 0 !== t ? -t / 0 : 0).atan(); }, asec: function () { var e = this.re, t = this.im; if (0 === e && 0 === t) return new a(0, 1 / 0); var i = e * e + t * t; return 0 !== i ? new a(e / i, -t / i).acos() : new a(0 !== e ? e / 0 : 0, 0 !== t ? -t / 0 : 0).acos(); }, acsc: function () { var e = this.re, t = this.im; if (0 === e && 0 === t) return new a(Math.PI / 2, 1 / 0); var i = e * e + t * t; return 0 !== i ? new a(e / i, -t / i).asin() : new a(0 !== e ? e / 0 : 0, 0 !== t ? -t / 0 : 0).asin(); }, sinh: function () { var e = this.re, t = this.im; return new a(n(e) * Math.cos(t), i(e) * Math.sin(t)); }, cosh: function () { var e = this.re, t = this.im; return new a(i(e) * Math.cos(t), n(e) * Math.sin(t)); }, tanh: function () { var e = 2 * this.re, t = 2 * this.im, r = i(e) + Math.cos(t); return new a(n(e) / r, Math.sin(t) / r); }, coth: function () { var e = 2 * this.re, t = 2 * this.im, r = i(e) - Math.cos(t); return new a(n(e) / r, -Math.sin(t) / r); }, csch: function () { var e = this.re, t = this.im, r = Math.cos(2 * t) - i(2 * e); return new a(-2 * n(e) * Math.cos(t) / r, 2 * i(e) * Math.sin(t) / r); }, sech: function () { var e = this.re, t = this.im, r = Math.cos(2 * t) + i(2 * e); return new a(2 * i(e) * Math.cos(t) / r, -2 * n(e) * Math.sin(t) / r); }, asinh: function () { var e = this.im; this.im = -this.re, this.re = e; var t = this.asin(); return this.re = -this.im, this.im = e, e = t.re, t.re = -t.im, t.im = e, t; }, acosh: function () { var e = this.acos(); if (e.im <= 0) { var t = e.re; e.re = -e.im, e.im = t; } else t = e.im, e.im = -e.re, e.re = t; return e; }, atanh: function () { var e = this.re, t = this.im, i = e > 1 && 0 === t, n = 1 - e, r = 1 + e, o = n * n + t * t, l = 0 !== o ? new a((r * n - t * t) / o, (t * n + r * t) / o) : new a(-1 !== e ? e / 0 : 0, 0 !== t ? t / 0 : 0), u = l.re; return l.re = s(l.re, l.im) / 2, l.im = Math.atan2(l.im, u) / 2, i && (l.im = -l.im), l; }, acoth: function () { var e = this.re, t = this.im; if (0 === e && 0 === t) return new a(0, Math.PI / 2); var i = e * e + t * t; return 0 !== i ? new a(e / i, -t / i).atanh() : new a(0 !== e ? e / 0 : 0, 0 !== t ? -t / 0 : 0).atanh(); }, acsch: function () { var e = this.re, t = this.im; if (0 === t) return new a(0 !== e ? Math.log(e + Math.sqrt(e * e + 1)) : 1 / 0, 0); var i = e * e + t * t; return 0 !== i ? new a(e / i, -t / i).asinh() : new a(0 !== e ? e / 0 : 0, 0 !== t ? -t / 0 : 0).asinh(); }, asech: function () { var e = this.re, t = this.im; if (this.isZero()) return a.INFINITY; var i = e * e + t * t; return 0 !== i ? new a(e / i, -t / i).acosh() : new a(0 !== e ? e / 0 : 0, 0 !== t ? -t / 0 : 0).acosh(); }, inverse: function () { if (this.isZero()) return a.INFINITY; if (this.isInfinite()) return a.ZERO; var e = this.re, t = this.im, i = e * e + t * t; return new a(e / i, -t / i); }, conjugate: function () { return new a(this.re, -this.im); }, neg: function () { return new a(-this.re, -this.im); }, ceil: function (e) { return e = Math.pow(10, e || 0), new a(Math.ceil(this.re * e) / e, Math.ceil(this.im * e) / e); }, floor: function (e) { return e = Math.pow(10, e || 0), new a(Math.floor(this.re * e) / e, Math.floor(this.im * e) / e); }, round: function (e) { return e = Math.pow(10, e || 0), new a(Math.round(this.re * e) / e, Math.round(this.im * e) / e); }, equals: function (e, t) { var i = new a(e, t); return Math.abs(i.re - this.re) <= a.EPSILON && Math.abs(i.im - this.im) <= a.EPSILON; }, clone: function () { return new a(this.re, this.im); }, toString: function () { var e = this.re, t = this.im, i = ""; return this.isNaN() ? "NaN" : this.isInfinite() ? "Infinity" : (Math.abs(e) < a.EPSILON && (e = 0), Math.abs(t) < a.EPSILON && (t = 0), 0 === t ? i + e : (0 !== e ? (i += e, i += " ", t < 0 ? (t = -t, i += "-") : i += "+", i += " ") : t < 0 && (t = -t, i += "-"), 1 !== t && (i += t), i + "i")); }, toVector: function () { return [this.re, this.im]; }, valueOf: function () { return 0 === this.im ? this.re : null; }, isNaN: function () { return isNaN(this.re) || isNaN(this.im); }, isZero: function () { return 0 === this.im && 0 === this.re; }, isFinite: function () { return isFinite(this.re) && isFinite(this.im); }, isInfinite: function () { return !(this.isNaN() || this.isFinite()); } }, a.ZERO = new a(0, 0), a.ONE = new a(1, 0), a.I = new a(0, 1), a.PI = new a(Math.PI, 0), a.E = new a(Math.E, 0), a.INFINITY = new a(1 / 0, 1 / 0), a.NAN = new a(NaN, NaN), a.EPSILON = 1e-15, Object.defineProperty(a, "__esModule", { value: !0 }), a.default = a, a.Complex = a, e.exports = a; }(); }(c); class f { constructor(e) { this.obeyspaces = !1, this.s = function (e) { if (/^[\u0020-\u00FF]*$/.test(e)) return e; const t = [], i = function (e) { const t = []; for (let i = 0; i < e.length; i++) { let n = e.charCodeAt(i); if (n >= 55296 && n <= 56319) { const t = e.charCodeAt(i + 1); t >= 56320 && t <= 57343 && (n = 65536 + 1024 * (n - 55296) + (t - 56320), i++); } t.push(n); } return t; }(e); let n = 0; for (; n < i.length;) { const e = i[n++], r = i[n]; if (8205 === r) { const e = n - 1; for (n += 2; 8205 === i[n];) n += 2; t.push(String.fromCodePoint(...i.slice(e, 2 * n - e + 1))); } else if (h(r)) { const e = n - 1; for (; h(i[n]);) n += 8205 === i[n] ? 2 : 1; t.push(String.fromCodePoint(...i.slice(e, 2 * n - e - 1))); } else m(e) ? (n += 1, t.push(String.fromCodePoint(...i.slice(n - 2, 2)))) : t.push(String.fromCodePoint(e)); } return t; }(e), this.pos = 0; } end() { return this.pos >= this.s.length; } get() { return this.pos < this.s.length ? this.s[this.pos++] : ""; } peek() { return this.s[this.pos]; } match(e) { let t; return t = "string" == typeof this.s ? e.exec(this.s.slice(this.pos)) : e.exec(this.s.slice(this.pos).join("")), (t === null || t === void 0 ? void 0 : t[0]) ? (this.pos += t[0].length, t[0]) : null; } next() { if (this.end()) return null; if (!this.obeyspaces && this.match(/^[ \f\n\r\t\v\xA0\u2028\u2029]+/)) return "<space>"; if (this.obeyspaces && this.match(/^[ \f\n\r\t\v\xA0\u2028\u2029]/)) return "<space>"; const e = this.get(); if ("\\" === e) { if (!this.end()) { let e = this.match(/^[a-zA-Z*]+/); if (e) this.match(/^[ \f\n\r\t\v\xA0\u2028\u2029]*/); else if (e = this.get(), " " === e) return "<space>"; return "\\" + e; } } else { if ("{" === e) return "<{>"; if ("}" === e) return "<}>"; if ("^" === e) { if ("^" === this.peek()) { this.get(); const e = this.match(/^(\^(\^(\^(\^[0-9a-f])?[0-9a-f])?[0-9a-f])?[0-9a-f])?[0-9a-f][0-9a-f]/); if (e) return String.fromCodePoint(parseInt(e.slice(e.lastIndexOf("^") + 1), 16)); } return e; } if ("#" === e) { if (!this.end()) { let e = !1; if (/[0-9?]/.test(this.peek()) && (e = !0, this.pos + 1 < this.s.length)) { const t = this.s[this.pos + 1]; e = /[^0-9A-Za-z]/.test(t); } return e ? "#" + this.get() : "#"; } } else if ("$" === e) return "$" === this.peek() ? (this.get(), "<$$>") : "<$>"; } return e; } } function g(e, t) { var _a, _b, _c, _d; let i = [], n = e.next(); if (n) if ("\\relax" === n) ; else if ("\\noexpand" === n) n = e.next(), n && i.push(n); else if ("\\obeyspaces" === n) e.obeyspaces = !0; else if ("\\space" === n || "~" === n) i.push("<space>"); else if ("\\bgroup" === n) i.push("<{>"); else if ("\\egroup" === n) i.push("<}>"); else if ("\\string" === n) n = e.next(), n && ("\\" === n[0] ? Array.from(n).forEach((e => i.push("\\" === e ? "\\backslash" : e))) : "<{>" === n ? i.push("\\{") : "<space>" === n ? i.push("~") : "<}>" === n && i.push("\\}")); else if ("\\csname" === n) { for (; "<space>" === e.peek();) e.next(); let r = "", s = !1, a = []; do { 0 === a.length && (/^#[0-9?]$/.test(e.peek()) ? (a = p((_b = (_a = t === null || t === void 0 ? void 0 : t[e.get().slice(1)]) !== null && _a !== void 0 ? _a : t === null || t === void 0 ? void 0 : t["?"]) !== null && _b !== void 0 ? _b : "\\placeholder{}", t), n = a[0]) : (n = e.next(), a = n ? [n] : [])), s = 0 === a.length, s || "\\endcsname" !== n || (s = !0, a.shift()), s || (s = "<$>" === n || "<$$>" === n || "<{>" === n || "<}>" === n || !!n && n.length > 1 && "\\" === n[0]), s || (r += a.shift()); } while (!s); r && i.push("\\" + r), i = i.concat(a); } else if ("\\endcsname" === n) ; else if (n.length > 1 && "#" === n[0]) { const e = n.slice(1); i = i.concat(p((_d = (_c = t === null || t === void 0 ? void 0 : t[e]) !== null && _c !== void 0 ? _c : t === null || t === void 0 ? void 0 : t["?"]) !== null && _d !== void 0 ? _d : "\\placeholder{}", t)); } else i.push(n); return i; } function p(e, t) { const i = e.toString().split(/\r?\n/); let n = "", r = ""; for (const e of i) { n += r, r = " "; const t = e.match(/((?:\\%)|[^%])*/); null !== t && (n += t[0]); } const s = new f(n), a = []; do { a.push(...g(s, t)); } while (!s.end()); return a; } function d(e) { let t = "", i = ""; for (const n of e) n && (/[a-zA-Z*]/.test(n[0]) && (i += t), t = /\\[a-zA-Z]+\*?$/.test(n) ? " " : "", i += n); return i; } function v(e) { let t = []; if (Array.isArray(e)) for (const i of e) Array.isArray(i) ? t = [...t, ...i] : t.push(i); else t = [e]; return d(t.map((e => { var _a; return ((_a = { "<space>": " ", "<$$>": "$$", "<$>": "$", "<{>": "{", "<}>": "}" }[e]) !== null && _a !== void 0 ? _a : e); }))); } function b(e) { return null !== e && "object" == typeof e && "num" in e; } function y(e) { return null !== e && "object" == typeof e && "sym" in e; } function N(e) { return null !== e && "object" == typeof e && "fn" in e; } function x(e) { return null === e ? null : "object" == typeof e && "str" in e ? e.str : "string" != typeof e || e.length < 2 || "'" !== e[0] || "'" !== e[e.length - 1] ? null : e.substring(1, e.length - 1); } function _(e) { return null === e ? null : Array.isArray(e) ? e[0] : N(e) ? e.fn[0] : null; } function S(e) { const t = _(e); return "string" == typeof t ? t : ""; } function w(e, t) { var _a, _b; return null === e ? null : Array.isArray(e) ? (_a = e[t]) !== null && _a !== void 0 ? _a : null : N(e) ? (_b = e.fn[t]) !== null && _b !== void 0 ? _b : null : null; } function E(e) { return Array.isArray(e) ? Math.max(0, e.length - 1) : N(e) ? Math.max(0, e.fn.length - 1) : 0; } function I(e) { if (null === e) return null; const t = y(e) ? e.sym : e; return "string" != typeof t || t.length >= 2 && "'" === t[0] && "'" === t[t.length - 1] ? null : t; } function k(e) { if (null === e) return null; const t = y(e) ? e.sym : e; return "string" != typeof t || t.length < 2 || "'" !== t[0] || "'" !== t[t.length - 1] ? null : t.slice(1, -1); } function A(e) { var _a; const t = _(e); if ("KeyValuePair" === t || "Tuple" === t || "Pair" === t) { const t = k(w(e, 1)); return t ? [t, (_a = w(e, 2)) !== null && _a !== void 0 ? _a : "Nothing"] : null; } return null; } function M(e) { if (null === e) return null; if ("object" == typeof e && "dict" in e) return e.dict; const t = A(e); if (t) return { [t[0]]: t[1] }; const i = _(e); if ("List" === i || "Dictionary" === i) { const t = {}; for (let i = 1; i < E(e); i++) { const n = A(w(e, i)); n && (t[n[0]] = n[1]); } return t; } return null; } function F(e) { if (null === e) return null; if ("number" == typeof e) return e; if (b(e)) return parseFloat(e.num); const t = I(e); return "NaN" === t ? NaN : "+Infinity" === t ? 1 / 0 : "-Infinity" === t ? -1 / 0 : null; } function O(e) { var _a, _b, _c, _d, _f, _g, _h, _j, _k, _l, _m; if (function (e) { return null === e || !Array.isArray(e) && ("object" != typeof e || !("fn" in e || "dic" in e)); }(e)) return [null, null]; const t = _(e); if (!t) return [null, null]; let i = null, n = null; if ("Negate" === t && ([i, n] = O((_a = w(e, 1)) !== null && _a !== void 0 ? _a : "Missing"), null !== i && null !== n)) return [-i, n]; if ("Rational" === t) return [(_c = F((_b = w(e, 1)) !== null && _b !== void 0 ? _b : NaN)) !== null && _c !== void 0 ? _c : NaN, (_f = F((_d = w(e, 2)) !== null && _d !== void 0 ? _d : NaN)) !== null && _f !== void 0 ? _f : NaN]; if ("Power" === t) { const t = F(w(e, 2)); 1 === t ? (i = (_g = F(w(e, 1))) !== null && _g !== void 0 ? _g : null, n = 1) : -1 === t && (i = 1, n = (_h = F(w(e, 1))) !== null && _h !== void 0 ? _h : null); } return "Divide" === t && (i = (_j = F(w(e, 1))) !== null && _j !== void 0 ? _j : null, n = (_k = F(w(e, 2))) !== null && _k !== void 0 ? _k : null), "Multiply" === t && "Power" === _(w(e, 2)) && -1 === F(w(w(e, 2), 2)) && (i = (_l = F(w(e, 1))) !== null && _l !== void 0 ? _l : null, n = (_m = F(w(w(e, 2), 1))) !== null && _m !== void 0 ? _m : null), null === i || null === n ? [null, null] : Number.isInteger(i) && Number.isInteger(n) ? [i, n] : [null, null]; } function q(e) { return Array.isArray(e) ? e.slice(1) : N(e) ? e.fn.slice(1) : []; } function P(e, t) { let i = null; if (Array.isArray(e) && (i = e), N(e) && (i = e.fn), null === i) return []; let n = 1; const r = []; for (; n < i.length;) r.push(t(i[n])), n += 1; return r; } function V(e, t, i, n = "both") { var _a, _b, _c, _d, _f, _g; if ("non" === n) return [e, t, i]; const r = _(t), s = _(i); return "left" === n ? r === e ? [e, ...(_a = q(t)) !== null && _a !== void 0 ? _a : [], i] : [e, t, i] : "right" === n ? s === e ? [e, t, ...(_b = q(i)) !== null && _b !== void 0 ? _b : []] : [e, t, i] : r === e && s === e ? [e, ...(_c = q(t)) !== null && _c !== void 0 ? _c : [], ...(_d = q(i)) !== null && _d !== void 0 ? _d : []] : r === e ? [e, ...(_f = q(t)) !== null && _f !== void 0 ? _f : [], i] : s === e ? [e, t, ...(_g = q(i)) !== null && _g !== void 0 ? _g : []] : [e, t, i]; } function C(e) { var _a, _b; const t = _(e); return null === e ? null : ("Delimiter" === t && (e = (_a = w(e, 1)) !== null && _a !== void 0 ? _a : null), null === e ? null : "Sequence" === t ? (_b = q(e)) !== null && _b !== void 0 ? _b : [] : null); } function D(e, t) { return t > 2 ? "solidus" : "radical"; } function L(e, t) { return t > 3 ? "inline-solidus" : "quotient"; } function T(e, t, i, n) { return null === i ? "\\sqrt{}" : (n = n !== null && n !== void 0 ? n : 2, "solidus" === t ? e.wrapShort(i) + "^{1\\/" + e.serialize(n) + "}" : "quotient" === t ? e.wrapShort(i) + "^{\\frac{1}{" + e.serialize(n) + "}}" : 2 === F(n) ? "\\sqrt{" + e.serialize(i) + "}" : "\\sqrt[" + e.serialize(n) + "]{" + e.serialize(i) + "}"); } function R(e, t) { var _a, _b; if (null === t) return ""; const i = (_a = w(t, 1)) !== null && _a !== void 0 ? _a : "Missing", n = (_b = w(t, 2)) !== null && _b !== void 0 ? _b : "Missing"; if (1 === E(t)) return e.serialize(i); const r = L(0, e.level); if ("inline-solidus" === r || "nice-solidus" === r) { const t = e.wrapShort(i), s = e.wrapShort(n); return "inline-solidus" === r ? `${t}\\/${s}` : `^{${t}}\\!\\!/\\!_{${s}}`; } return "reciprocal" === r ? e.wrap(i) + e.wrap(n) + "^{-1}" : "factor" === r ? "\\frac{1}{" + e.serialize(n) + "}" + e.wrap(i) : `\\frac{${e.serialize(i)}}{${e.serialize(n)}}`; } function z(e, t) { var _a, _b, _c; const i = _(t), n = (_a = w(t, 1)) !== null && _a !== void 0 ? _a : "Missing", r = (_b = w(t, 2)) !== null && _b !== void 0 ? _b : "Missing"; if ("Sqrt" === i) return T(e, D(0, e.level), n, 2); if ("Root" === i) return T(e, D(0, e.level), n, r); const s = (_c = F(r)) !== null && _c !== void 0 ? _c : 1; if (-1 === s) return e.serialize(["Divide", "1", n]); if (s < 0) return e.serialize(["Divide", "1", ["Power", n, -s]]); if ("Divide" === _(r) || "Rational" === _(r)) { if (1 === F(w(r, 1))) return T(e, D(0, e.level), n, w(r, 2)); } else if ("Power" === _(r) && -1 === F(w(r, 2))) return T(e, D(0, e.level), n, w(r, 1)); return e.wrapShort(n) + "^{" + e.serialize(r) + "}"; } const $ = [{ name: "CatalanConstant", serialize: "G" }, { name: "GoldenRatio", serialize: "\\varphi" }, { name: "EulerGamma", serialize: "\\gamma" }, { name: "Degrees", serialize: "\\frac{\\pi}{180}" }, { trigger: ["\\infty"], parse: { num: "+Infinity" } }, { name: "ComplexInfinity", trigger: ["\\tilde", "\\infty"], serialize: "\\tilde\\infty" }, { trigger: ["\\tilde", "<{>", "\\infty", "<}>"], parse: "ComplexInfinity" }, { name: "Pi", trigger: ["\\pi"] }, { trigger: ["\u03c0"], parse: "Pi" }, { name: "ExponentialE", trigger: ["e"], serialize: "e" }, { trigger: ["\\mathrm", "<{>", "e", "<}>"], parse: "ExponentialE" }, { trigger: ["\\exponentialE"], parse: "ExponentialE" }, { name: "ImaginaryUnit", trigger: ["\\imaginaryI"] }, { trigger: ["i"], parse: "ImaginaryUnit" }, { trigger: ["\\mathrm", "<{>", "i", "<}>"], parse: "ImaginaryUnit" }, { name: "Add", trigger: ["+"], kind: "infix", associativity: "both", precedence: 275, parse: (e, t, i) => { if (275 < t.minPrec) return null; const n = e.matchExpression({ ...t, minPrec: 275 }); return null === n ? null : V("Add", i, n); }, serialize: function (e, t) { var _a, _b; e.level -= 1; const i = _(t); let n = "", r = w(t, 1), s = !Number.isNaN((_a = F(r)) !== null && _a !== void 0 ? _a : NaN); if ("Negate" === i) n = "-" + e.wrap(r, 276); else if ("Add" === i) { n = e.serialize(r); const i = E(t) + 1; for (let a = 2; a < i; a++) { r = w(t, a); const i = (_b = F(r)) !== null && _b !== void 0 ? _b : NaN, o = !Number.isNaN(i); let l = !1; if (null !== r && s) { const [t, i] = O(r); null !== t && null !== i && isFinite(t) && isFinite(i) && 1 !== i && (n += e.options.invisiblePlus + e.serialize(r), l = !0); } if (!l) if (i < 0) n += e.serialize(r); else if ("Negate" === _(r)) n += e.wrap(r, 275); else { const t = e.wrap(r, 275); "-" === t[0] || "+" === t[0] ? n += t : n = n + "+" + t; } s = o; } } else if ("Subtract" === i) { const i = w(t, 2); n = null !== i ? e.wrap(r, 275) + "-" + e.wrap(i, 275) : e.wrap(r, 275); } return e.level += 1, n; } }, { kind: "prefix", trigger: ["+"], precedence: 275, parse: (e, t) => 275 < t.minPrec ? null : e.matchExpression({ ...t, minPrec: 400 }) }, { name: "Complex", precedence: 274, serialize: (e, t) => { const i = F(w(t, 1)), n = F(w(t, 2)); if (0 === n) return e.serialize(w(t, 1)); const r = 1 === n ? "\\imaginaryI" : -1 === n ? "-\\imaginaryI" : d([e.serialize(w(t, 2)), "\\imaginaryI"]); return 0 === i ? r : d(null !== n && n < 0 ? [e.serialize(w(t, 1)), r] : [e.serialize(w(t, 1)), "+", r]); } }, { name: "Divide", trigger: ["\\frac"], requiredLatexArg: 2, precedence: 660, parse: function (e) { var _a, _b, _c, _d; const t = (_a = e.matchRequiredLatexArgument()) !== null && _a !== void 0 ? _a : "Missing", i = (_b = e.matchRequiredLatexArgument()) !== null && _b !== void 0 ? _b : "Missing"; if ("PartialDerivative" === _(t) && ("PartialDerivative" === _(i) || "Multiply" === _(i) && "PartialDerivative" === _(w(i, 1)))) { const n = (_c = w(t, 3)) !== null && _c !== void 0 ? _c : "Missing"; let r = w(t, 1); null !== r && "Missing" !== r || (r = (_d = e.matchExpression()) !== null && _d !== void 0 ? _d : "Missing"); let s = []; if ("Multiply" === _(i)) { for (const e of q(i)) if ("PartialDerivative" === _(e)) { const t = w(e, 2); t && s.push(t); } } else { const e = w(i, 2); e && s.push(e); } return s.length > 1 && (s = ["List", ...s]), ["PartialDerivative", r, ...s, "Missing" === n ? 1 : n]; } return ["Divide", t, i]; }, serialize: R }, { trigger: ["\\/"], kind: "infix", associativity: "non", precedence: 660, parse: "Divide" }, { trigger: ["/"], kind: "infix", associativity: "non", precedence: 660, parse: "Divide" }, { trigger: ["\\div"], kind: "infix", associativity: "non", precedence: 660, parse: "Divide" }, { name: "Exp", serialize: (e, t) => { var _a; return d(["\\exponentialE^{", e.serialize((_a = w(t, 1)) !== null && _a !== void 0 ? _a : "Missing"), "}"]); } }, { name: "Factorial", trigger: ["!"], kind: "postfix", precedence: 810 }, { name: "Factorial2", trigger: ["!", "!"], kind: "postfix", precedence: 810 }, { trigger: "\\operatorname{floor}", parse: e => { const t = e.matchArguments("enclosure"); return null === t ? null : ["Floor", ...t]; } }, { name: "Gcd", trigger: "\\operatorname{gcd}", parse: e => { const t = e.matchArguments("enclosure"); return null === t ? null : ["Gcd", ...t]; }, serialize: (e, t) => d(["\\operatorname{gcd}", "\\left(", e.serialize(t), "\\right)"]) }, { name: "Half", serialize: "\\frac12" }, { name: "Lcm", trigger: "\\operatorname{lcm}" }, { name: "MinusPlus", trigger: ["\\mp"], kind: "infix", associativity: "both", precedence: 270 }, { name: "Multiply", trigger: ["\\times"], kind: "infix", associativity: "both", precedence: 390, serialize: function e(t, i) { var _a, _b; if (null === i) return ""; t.level -= 1; let n = ""; const [r, s] = function (e) { var _a, _b, _c, _d, _f; if ("Multiply" !== _(e)) return [[], []]; const t = [], i = [], n = q(e); for (const e of n) if ("Power" === _(e)) if ("Negate" === _(w(e, 2))) { const t = (_a = w(e, 1)) !== null && _a !== void 0 ? _a : "Missing", n = (_b = w(w(e, 2), 1)) !== null && _b !== void 0 ? _b : "Missing"; i.push(["Power", t, n]); } else { const n = (_c = F(w(e, 2))) !== null && _c !== void 0 ? _c : NaN; -1 === n ? i.push((_d = w(e, 1)) !== null && _d !== void 0 ? _d : "Missing") : n < 0 ? i.push(["Power", (_f = w(e, 1)) !== null && _f !== void 0 ? _f : "Missing", -n]) : t.push(e); } else t.push(e); return [t, i]; }(i); if (s.length > 0 && (n = 1 === s.length && 1 === s[0] ? 0 === r.length ? "1" : 1 === r.length ? t.serialize(r[0]) : e(t, ["Multiply", ...r]) : t.serialize(["Divide", 1 === r.length ? r[0] : ["Multiply", ...r], 1 === s.length ? s[0] : ["Multiply", ...s]])), n) return t.level += 1, n; let a = !1, o = null; const l = E(i) + 1; let u = !1; for (let e = 1; e < l; e++) { if (o = w(i, e), null === o) continue; let r; if ("number" == typeof o || b(o)) r = t.serialize(o), "-1" !== r || n ? ("-" === r[0] && (r = r.slice(1), a = !a), n = n ? d([n, t.options.multiply, r]) : r) : n = "-", u = !0; else { if ("Power" === _(o)) { const [e, i] = O((_a = w(o, 2)) !== null && _a !== void 0 ? _a : NaN); if (1 === e && null !== i) { n += T(t, D(0, t.level), w(o, 1), i), u = !1; continue; } } if ("Power" !== _(o) || isNaN((_b = F(w(o, 1))) !== null && _b !== void 0 ? _b : NaN)) { if ("Negate" === _(o) && (o = w(o, 1), a = !a), r = t.wrap(o, 390), n) { const e = _(o); n = !u || "Divide" !== e && "Rational" !== e ? t.options.invisibleMultiply ? d([n, t.options.invisibleMultiply, r]) : d([n, r]) : d([n, t.options.multiply, r]); } else n = r; u = !1; } else r = t.serialize(o), n = n ? d([n, t.options.multiply, r]) : r, u = !0; } } return t.level += 1, a ? "-" + n : n; } }, { trigger: ["\\cdot"], kind: "infix", associativity: "both", precedence: 390, parse: (e, t, i) => { if (391 < t.minPrec) return null; const n = e.matchExpression({ ...t, minPrec: 392 }); return null === n ? null : V("Multiply", i, n); } }, { trigger: ["*"], kind: "infix", associativity: "both", precedence: 390, parse: (e, t, i) => { if (391 < t.minPrec) return null; const n = e.matchExpression({ ...t, minPrec: 392 }); return null === n ? null : ["Multiply", i, n]; } }, { name: "Negate", trigger: ["-"], kind: "prefix", parse: (e, t) => { if (276 < t.minPrec) return null; const i = e.matchExpression({ ...t, minPrec: 400 }); return null === i ? null : ["Negate", i]; }, precedence: 275 }, { name: "PlusMinus", trigger: ["\\pm"], kind: "infix", associativity: "both", precedence: 270 }, { name: "Power", trigger: ["^"], kind: "infix", serialize: z }, { name: "Rational", precedence: 660, serialize: R }, { name: "Root", serialize: z }, { name: "Round", trigger: "\\operatorname{round}" }, { name: "Square", precedence: 720, serialize: (e, t) => e.wrapShort(w(t, 1)) + "^2" }, { name: "Sign", trigger: "\\operatorname{sgn}" }, { name: "Sqrt", trigger: ["\\sqrt"], optionalLatexArg: 1, requiredLatexArg: 1, parse: function (e) { const t = e.matchOptionalLatexArgument(), i = e.matchRequiredLatexArgument(); return null === i ? null !== t ? ["Root", "Missing", t] : ["Sqrt", "Missing"] : null !== t ? ["Root", i, t] : ["Sqrt", i]; }, serialize: z }, { name: "Subtract", trigger: ["-"], kind: "infix", associativity: "both", precedence: 275, parse: (e, t, i) => { if (276 < t.minPrec) return null; const n = e.matchExpression({ ...t, minPrec: 277 }); return null === n ? null : ["Subtract", i, n]; } }]; function Z(e = "") { return (t, i) => q(i).map((e => t.serialize(e))).join(e); } function j(e, t) { if (null === t) return ""; const i = _(t); if ("LatexString" === i) return B(e, t); if ("LatexTokens" === i) return W(e, t); const n = x(t); if (null !== n) return `\\text{${n}}`; const r = F(t); return null !== r ? r.toString() : `\\text{${JSON.stringify(t)}}`; } const G = [{ name: "Missing", trigger: ["\\placeholder"], requiredLatexArg: 1, serialize: e => { var _a; return (_a = e.options.missingSymbol) !== null && _a !== void 0 ? _a : "\\placeholder{}"; } }, { name: "BaseForm", serialize: (e, t) => { var _a, _b; const i = (_a = F(w(t, 2))) !== null && _a !== void 0 ? _a : NaN; if (isFinite(i) && i >= 2 && i <= 36) { const e = (_b = F(w(t, 1))) !== null && _b !== void 0 ? _b : NaN; if (isFinite(e)) { let t = Number(e).toString(i), n = 0; if (2 === i || 10 === i ? n = 4 : 16 === i ? n = 2 : i > 16 && (n = 4), n > 0) { const e = t; t = ""; for (let i = 0; i < e.length; i++) i > 0 && i % n == 0 && (t = "\\, " + t), t = e[e.length - i - 1] + t; } return `(\\text{${t}}_{${i}}`; } } return "\\operatorname{BaseForm}(" + e.serialize(w(t, 1)) + ", " + e.serialize(w(t, 2)) + ")"; } }, { name: "Delimiter", serialize: (e, t) => { var _a, _b, _c, _d, _f, _g; const i = E(t); if (0 === i) return ""; if (1 === i) return `\\left( ${e.serialize(w(t, 1))} \\right)`; let n = "", r = "\\left(", s = "\\left)"; 2 === i ? n = (_a = j(e, w(t, 2))) !== null && _a !== void 0 ? _a : "" : 3 === i ? (r = (_b = j(e, w(t, 2))) !== null && _b !== void 0 ? _b : "", s = (_c = j(e, w(t, 3))) !== null && _c !== void 0 ? _c : "") : (r = (_d = j(e, w(t, 2))) !== null && _d !== void 0 ? _d : "", n = (_f = j(e, w(t, 3))) !== null && _f !== void 0 ? _f : "", s = (_g = j(e, w(t, 4))) !== null && _g !== void 0 ? _g : ""); const a = w(t, 1); return n && "Sequence" === _(a) ? `${r} ${Z(n)(e, a)} ${s}` : `${r} ${e.serialize(a)} ${s}`; } }, { name: "Error", serialize: (e, t) => { const i = w(t, 1), n = "Nothing" === I(i) ? "" : e.serialize(i); if (E(t) >= 3) { const e = w(t, 3); if (e && "LatexForm" === _(e)) { const t = Q(x(w(e, 1))); if (t) return `${n !== null && n !== void 0 ? n : ""}\\texttt{\\textcolor{red}{${t}}}`; } } return n !== null && n !== void 0 ? n : ""; } }, { name: "FromLatex", serialize: (e, t) => `\\texttt{${Q(x(w(t, 1)))}}` }, { name: "LatexForm", serialize: B }, { name: "LatexTokens", serialize: W }, { kind: "matchfix", openDelimiter: "(", closeDelimiter: ")", parse: (e, t) => null === t ? null : "Sequence" === _(t) ? 0 === E(t) ? ["Delimiter", "Nothing"] : ["Delimiter", ...q(t)] : ["Delimiter", t] }, { name: "Sequence", trigger: [","], kind: "infix", precedence: 20, parse: (e, t, i) => { if (t.minPrec >= 20) return null; "Missing" === i && (i = "Nothing"); const n = ["Sequence", i]; let r = !1; for (; !r;) { for (r = !0, e.skipSpace(); e.match(",");) n.push("Nothing"), e.skipSpace(); if (e.atTerminator(t)) n.push("Nothing"); else { const i = e.matchExpression({ ...t, minPrec: 20 }); n.push(i !== null && i !== void 0 ? i : "Nothing"), r = null === i; } r || (e.skipSpace(), r = !e.match(",")); } return n; }, serialize: Z() }, { trigger: [";"], kind: "infix", precedence: 19, parse: (e, t, i) => { var _a, _b; if (t.minPrec >= 19) return null; "Missing" === i && (i = "Nothing"); const n = ["Sequence", ...(_a = C(i)) !== null && _a !== void 0 ? _a : ["Sequence", i]]; for (;;) { for (e.skipSpace(); e.match(",");) n.push("Nothing"), e.skipSpace(); if (e.atEnd) { n.push("Nothing"); break; } const i = e.matchExpression({ ...t, minPrec: 19 }); if (null === i) { n.push("Nothing"); break; } if (n.push(...(_b = C(i)) !== null && _b !== void 0 ? _b : ["Sequence", i]), e.skipSpace(), !e.match(",")) break; } return n; } }, { name: "String", trigger: ["\\text"], parse: e => H(e), serialize: (e, t) => { const i = q(t); return null === i || 0 === i.length ? "\\text{}" : d(["\\text{", i.map((t => e.serialize(t))).join(""), "}"]); } }, { name: "Subscript", trigger: ["_"], kind: "infix", serialize: (e, t) => 2 === E(t) ? e.serialize(w(t, 1)) + "_{" + e.serialize(w(t, 2)) + "}" : "_{" + e.serialize(w(t, 1)) + "}" }, { name: "Superplus", trigger: ["^", "+"], kind: "postfix" }, { name: "Subplus", trigger: ["_", "+"], kind: "postfix" }, { name: "Superminus", trigger: ["^", "-"], kind: "postfix" }, { name: "Subminus", trigger: ["_", "-"], kind: "postfix" }, { trigger: ["^", "*"], kind: "postfix", parse: (e, t) => ["Superstar", t] }, { name: "Superstar", trigger: ["^", "\\star"], kind: "postfix" }, { trigger: ["_", "*"], kind: "postfix", parse: (e, t) => ["Substar", t] }, { name: "Substar", trigger: ["_", "\\star"], kind: "postfix" }, { name: "Superdagger", trigger: ["^", "\\dagger"], kind: "postfix" }, { trigger: ["^", "\\dag"], kind: "postfix", parse: (e, t) => ["Superdagger", t] }, { name: "Prime", trigger: ["^", "\\prime"], kind: "postfix" }, { trigger: ["^", "\\doubleprime"], kind: "postfix", parse: (e, t) => ["Prime", t !== null && t !== void 0 ? t : "Nothing", 2] }, { name: "Derivative", serialize: (e, t) => { var _a; const i = (_a = F(w(t, 1))) !== null && _a !== void 0 ? _a : NaN; if (!isFinite(i)) return ""; const n = e.serialize(w(t, 2)); return 1 === i ? n + "^{\\prime}" : 2 === i ? n + "^{\\doubleprime}" : n + "^{(" + Number(i).toString() + ")}"; } }, { name: "Piecewise", trigger: "cases", kind: "environment", parse: e => { var _a; return ["Piecewise", (_a = e.matchTabular("cases")) !== null && _a !== void 0 ? _a : "Nothing"]; }, serialize: (e, t) => { if ("List" !== _(w(t, 1))) return ""; const i = q(w(t, 1)); let n = "", r = ""; for (const t of i) { n += r; const i = w(t, 1); if (null !== i) { n += e.serialize(i); const r = w(t, 2); null !== r && (n += "&" + e.serialize(r)); } r = "\\\\"; } return "\\begin{cases}" + n + "\\end{cases}"; } }]; function H(e, t) { var _a, _b, _c; if (!e.match("<{>")) return "Nothing"; const i = []; let n = "", r = null; for (; !e.atEnd && !e.match("<}>");) if ("<{>" === e.peek) i.push(H(e)); else if (e.match("\\textbf") && e.match("<{>")) i.push(H(e, { "font-weight": "bold" })); else if (e.match("\\color") && e.match("<{>")) { const t = e.matchColor(); t && e.match("<}>") && (null !== r && n ? i.push(["Style", n, { dict: r }]) : n && i.push(["String", n]), n = "", r = { color: t }); } else if (e.match("<space>")) n += " "; else if (e.match("<$>")) { const t = e.index, r = (_a = e.matchExpression()) !== null && _a !== void 0 ? _a : "Nothing"; e.skipSpace(), e.match("<$>") ? i.push(r) : (n += "$", e.index = t); } else if (e.match("<$$>")) { const t = e.index, r = (_b = e.matchExpression()) !== null && _b !== void 0 ? _b : "Nothing"; e.skipSpace(), e.match("<$$>") ? i.push(r) : (n += "$$", e.index = t); } else n += (_c = e.matchChar()) !== null && _c !== void 0 ? _c : ""; return null !== r && n ? i.push(["Style", n, { dict: r }]) : n && i.push(["String", n]), t ? ["Style", ["String", ...i], { dict: t }] : ["String", ...i]; } function W(e, t) { return null === t ? "" : d(P(t, (t => { const i = x(t); return null === i ? e.serialize(t) : "<{>" === i ? "{" : "<}>" === i ? "}" : "<$>" === i ? "$" : "<$$>" === i ? "$$" : "<space>" === i ? " " : i; }))); } function B(e, t) { return null === t ? "" : d(P(t, (t => { var _a; return (_a = x(t)) !== null && _a !== void 0 ? _a : e.serialize(t); }))); } function Q(e) { return null === e ? "" : e.replace(/[{}\[\]\\:\-\$%]/g, (e => { var _a; return ((_a = { "{": "\\lbrace ", "}": "\\rbrace ", "[": "\\lbrack ", "]": "\\rbrack ", ":": "\\colon ", "\\": "\\backslash " }[e]) !== null && _a !== void 0 ? _a : "\\" + e); })); } const U = [{ name: "Overscript", trigger: ["\\overset"], kind: "infix", precedence: 700 }, { name: "Underscript", trigger: ["\\underset"], kind: "infix", precedence: 700 }, { name: "Increment", trigger: ["+", "+"], kind: "postfix", precedence: 880 }, { name: "Decrement", trigger: ["-", "-"], kind: "postfix", precedence: 880 }, { name: "PreIncrement", trigger: ["+", "+"], kind: "prefix", precedence: 880 }, { name: "PreDecrement", trigger: ["-", "-"], kind: "prefix", precedence: 880 }, { name: "Ring", trigger: ["\\circ"], kind: "infix", precedence: 265 }, { name: "Transpose", trigger: ["^", "T"], kind: "infix" }, { name: "ConjugateTranspose", trigger: ["^", "H"], kind: "infix" }, { name: "StringJoin", trigger: ["\\lt", "\\gt"], kind: "infix", precedence: 780 }, { name: "Starstar", trigger: ["\\star", "\\star"], kind: "infix", precedence: 780 }, { name: "PartialDerivative", trigger: ["\\partial"], kind: "prefix", parse: e => { var _a, _b; let t = !1, i = "Nothing", n = "Nothing"; for (; !t;) e.skipSpace(), e.match("_") ? n = e.matchRequiredLatexArgument() : e.match("^") ? i = e.matchRequiredLatexArgument() : t = !0; const r = C(n); if (r && (n = ["List", ...r]), !n || !i) return null; let s = (_a = e.matchRequiredLatexArgument()) !== null && _a !== void 0 ? _a : "Nothing"; return "Nothing" !== s && (s = [s, ...(_b = e.matchArguments("enclosure")) !== null && _b !== void 0 ? _b : "Nothing"]), ["PartialDerivative", s, n, i]; }, serialize: (e, t) => { let i = "\\partial"; const n = w(t, 1), r = w(t, 2), s = w(t, 3); return null !== r && "Nothing" !== r && ("List" === _(r) ? i += "_{" + e.serialize(["Sequence", ...q(r)]) + "}" : i += "_{" + e.serialize(r) + "}"), null !== s && "Nothing" !== s && (i += "^{" + e.serialize(s) + "}"), null !== n && "Nothing" !== n && (i += e.serialize(n)), i; }, precedence: 740 }, { name: "OverBar", trigger: ["\\overline"], requiredLatexArg: 1 }, { name: "UnderBar", trigger: ["\\underline"], requiredLatexArg: 1 }, { name: "OverVector", trigger: ["\\vec"], requiredLatexArg: 1 }, { name: "OverTilde", trigger: ["\\tilde"], requiredLatexArg: 1 }, { name: "OverHat", trigger: ["\\hat"], requiredLatexArg: 1 }, { name: "OverRightArrow", trigger: ["\\overrightarrow"], requiredLatexArg: 1 }, { name: "OverLeftArrow", trigger: ["\\overleftarrow"], requiredLatexArg: 1 }, { name: "OverRightDoubleArrow", trigger: ["\\Overrightarrow"], requiredLatexArg: 1 }, { name: "OverLeftHarpoon", trigger: ["\\overleftharpoon"], requiredLatexArg: 1 }, { name: "OverRightHarpoon", trigger: ["\\overrightharpoon"], requiredLatexArg: 1 }, { name: "OverLeftRightArrow", trigger: ["\\overleftrightarrow"], requiredLatexArg: 1 }, { name: "OverBrace", trigger: ["\\overbrace"], requiredLatexArg: 1 }, { name: "OverLineSegment", trigger: ["\\overlinesegment"], requiredLatexArg: 1 }, { name: "OverGroup", trigger: ["\\overgroup"], requiredLatexArg: 1 }, { trigger: ["\\displaystyle"], parse: e => { const t = e.matchArguments("group"); return null === t ? null : ["Style", ...t, ["KeyValuePair", "'display'", "'block'"]]; } }, { trigger: ["\\textstyle"], parse: e => { const t = e.matchArguments("group"); return null === t ? null : ["Style", ...t, ["KeyValuePair", "'display'", "'inline'"]]; } }, { trigger: ["\\scriptstyle"], parse: e => { const t = e.matchArguments("group"); return null === t ? null : ["Style", ...t, ["KeyValuePair", "'display'", "'script'"]]; } }, { trigger: ["\\scriptscriptstyle"], parse: e => { const t = e.matchArguments("group"); return null === t ? null : ["Style", ...t, ["KeyValuePair", "'display'", "'scriptscript'"]]; } }, { trigger: ["\\tiny"], parse: e => { const t = e.matchArguments("group"); return null === t ? null : ["Style", ...t, ["KeyValuePair", "'size'", 1]]; } }, { trigger: ["\\scriptsize"], parse: e => { const t = e.matchArguments("group"); return null === t ? null : ["Style", ...t, ["KeyValuePair", "'size'", 2]]; } }, { trigger: ["\\footnotesize"], parse: e => { const t = e.matchArguments("group"); return null === t ? null : ["Style", ...t, ["KeyValuePair", "'size'", 3]]; } }, { trigger: ["\\small"], parse: e => { const t = e.matchArguments("group"); return null === t ? null : ["Style", ...t, ["KeyValuePair", "'size'", 4]]; } }, { trigger: ["\\normalsize"], parse: e => { const t = e.matchArguments("group"); return null === t ? null : ["Style", ...t, ["KeyValuePair", "'size'", 5]]; } }, { trigger: ["\\large"], parse: e => { const t = e.matchArguments("group"); return null === t ? null : ["Style", ...t, ["KeyValuePair", "'size'", 6]]; } }, { trigger: ["\\Large"], parse: e => { const t = e.matchArguments("group"); return null === t ? null : ["Style", ...t, ["KeyValuePair", "'size'", 7]]; } }, { trigger: ["\\LARGE"], parse: e => { const t = e.matchArguments("group"); return null === t ? null : ["Style", ...t, ["KeyValuePair", "'size'", 8]]; } }, { trigger: ["\\huge"], parse: e => { const t = e.matchArguments("group"); return null === t ? null : ["Style", ...t, ["KeyValuePair", "'size'", 9]]; } }, { trigger: ["\\Huge"], parse: e => { const t = e.matchArguments("group"); return null === t ? null : ["Style", ...t, ["KeyValuePair", "'size'", 10]]; } }, { name: "Style", serialize: (e, t) => { let i = e.serialize(w(t, 1)); const n = M(w(t, 2)); if (null === n) return i; "block" === k(n.display) ? i = d(["{\\displaystyle", i, "}"]) : "inline" === k(n.display) ? i = d(["{\\textstyle", i, "}"]) : "script" === k(n.display) ? i = d(["{\\scriptstyle", i, "}"]) : "scriptscript" === k(n.display) && (i = d(["{\\scriptscriptstyle", i, "}"])); const r = F(n.size); return null !== r && r >= 1 && r <= 10 && (i = d(["{", { 1: "\\tiny", 2: "\\scriptsize", 3: "\\footnotesize", 4: "\\small", 5: "\\normalsize", 6: "\\large", 7: "\\Large", 8: "\\LARGE", 9: "\\huge", 10: "\\Huge" }[r], i, "}"])), i; } }, { trigger: ["\\!"], parse: () => ["HorizontalSpacing", "Nothing", -3] }, { trigger: ["\\ "], parse: () => ["HorizontalSpacing", "Nothing", 6] }, { trigger: ["\\:"], parse: () => ["HorizontalSpacing", "Nothing", 4] }, { trigger: ["\\enskip"], parse: () => ["HorizontalSpacing", "Nothing", 9] }, { trigger: ["\\quad"], parse: () => ["HorizontalSpacing", "Nothing", 18] }, { trigger: ["\\qquad"], parse: () => ["HorizontalSpacing", "Nothing", 36] }, { trigger: ["\\,"], parse: () => ["HorizontalSpacing", "Nothing", 3] }, { trigger: ["\\;"], parse: () => ["HorizontalSpacing", "Nothing", 5] }, { trigger: ["\\enspace"], parse: () => ["HorizontalSpacing", "Nothing", 9] }, { name: "HorizontalSpacing", serialize: (e, t) => { var _a; if ("Nothing" === I(w(t, 1))) { const e = F(w(t, 2)); return null === e ? "" : (_a = { "-3": "\\!", 6: "\\ ", 3: "\\,", 4: "\\:", 5: "\\;", 9: "\\enspace", 18: "\\quad", 36: "\\qquad" }[e]) !== null && _a !== void 0 ? _a : ""; } return ""; } }]; function K(e) { return t => { var _a, _b; let i = !1, n = 0; if (t.skipSpace(), t.match("^")) { if (t.skipSpace(), t.match("<{>")) { t.skipSpace(), t.match("-") && t.match("1") && (i = !0); do { t.match("\\doubleprime") && (n += 2), t.match("\\prime") && (n += 1), t.match("'") && (n += 1); } while (!t.match("<}>") && !t.atEnd); } let e = !1; for (; !e;) t.skipSpace(), t.match("\\doubleprime") ? n += 2 : t.match("\\prime") || t.match("'") ? n += 1 : e = !0; } let r = (_b = (_a = { "\\arcsin": "Arcsin", "\\arccos": "Arccos", "\\arctan": "Arctan", "\\arctg": "Arctan", "\\arcctg": "Arctan", "\\arcsec": "Arcsec", "\\arccsc": " Arccsc", "\\arsinh": "Arsinh", "\\arcosh": "Arcosh", "\\artanh": "Artanh", "\\arcsech": "Arcsech", "\\arccsch": "Arcsch", "\\ch": "Cosh", "\\cos": "Cos", "\\cosec": "Csc", "\\cosh": "Csch", "\\cot": "Cot", "\\cotg": "Cot", "\\coth": "Coth", "\\csc": "Csc", "\\ctg": "Cot", "\\cth": "Coth", "\\sec": "Sec", "\\sin": "Sin", "\\sinh": "Sinh", "\\sh": "Sinh", "\\tan": "Tan", "\\tanh": "Tanh", "\\tg": "Tan", "\\th": "Tanh" }[e !== null && e !== void 0 ? e : ""]) !== null && _a !== void 0 ? _a : e) !== null && _b !== void 0 ? _b : ""; i && (r = ["InverseFunction", r]), n >= 1 && (r = ["Derivative", n, r]); const s = t.matchArguments("implicit"); return null === s ? [r, "Nothing"] : [r, ...s]; }; } const Y = [{ name: "Arcsin", trigger: ["\\arcsin"], parse: K("Arcsin") }, { name: "Arccos", trigger: ["\\arccos"], parse: K("Arccos") }, { name: "Arctan", trigger: ["\\arctan"], parse: K("Arctan") }, { trigger: ["\\arctg"], parse: K("Arctan") }, { name: "Arccot", trigger: ["\\arcctg"], parse: K("Arccot") }, { name: "Arcsec", trigger: ["\\arcsec"], parse: K("Arcsec") }, { name: "Arccsc", trigger: ["\\arccsc"], parse: K("Arccsc") }, { name: "Arsinh", trigger: ["\\arsinh"], parse: K("Arsinh") }, { name: "Arcosh", trigger: ["\\arcosh"], parse: K("Arcosh") }, { name: "Artanh", trigger: ["\\artanh"], parse: K("Artanh") }, { name: "Arsech", trigger: ["\\arsech"], parse: K("Arsech") }, { name: "Arcsch", trigger: ["\\arcsch"], parse: K("Arcsch") }, { trigger: ["\\ch"], parse: K("Cosh") }, { name: "Cosec", trigger: ["\\cosec"], parse: K("Cosec") }, { name: "Cosh", trigger: ["\\cosh"], parse: K("Cosh") }, { name: "Cot", trigger: ["\\cot"], parse: K("Cot") }, { trigger: ["\\cotg"], parse: K("Cot") }, { name: "Coth", trigger: ["\\coth"], parse: K("Coth") }, { name: "Csc", trigger: ["\\csc"], parse: K("Csc") }, { trigger: ["\\ctg"], parse: K("Cot") }, { trigger: ["\\cth"], parse: K("Cotanh") }, { name: "Sec", trigger: ["\\sec"], parse: K("Sec") }, { name: "Sinh", trigger: ["\\sinh"], parse: K("Sinh") }, { trigger: ["\\sh"], parse: K("Sinh") }, { name: "Tan", trigger: ["\\tan"], parse: K("Tan") }, { trigger: ["\\tg"], parse: K("Tan") }, { name: "Tanh", trigger: ["\\tanh"], parse: K("Tanh") }, { trigger: ["\\th"], parse: K("Tanh") }, { name: "Cos", trigger: ["\\cos"], parse: K("Cos") }, { name: "Sin", trigger: ["\\sin"], parse: K("Sin") }], J = [{ name: "AlgebraicNumber", trigger: "\\bar\\Q" }, { name: "ComplexNumber", trigger: ["\\C"] }, { trigger: "\\mathbb{C}", parse: "ComplexNumber" }, { name: "ImaginaryNumber", trigger: ["\\imaginaryI\\R"] }, { name: "ExtendedComplexNumber", trigger: ["\\bar\\C"] }, { name: "EmptySet", trigger: ["\\emptyset"] }, { trigger: ["\\varnothing"], parse: "EmptySet" }, { name: "Integer", trigger: ["\\Z"] }, { trigger: "\\mathbb{Z}", parse: "Integer" }, { name: "RationalNumber", trigger: ["\\Q"] }, { name: "RealNumber", trigger: ["\\R"] }, { name: "ExtendedRealNumber", trigger: ["\\bar\\R"] }, { name: "TranscendentalNumber", trigger: "\\R-\\bar\\Q" }, { trigger: "\\R\\backslash\\bar\\Q", parse: "TranscendentalNumber" }, { name: "NegativeNumber", trigger: "\\R^-" }, { trigger: "\\R^{-}", parse: "NegativeNumber" }, { trigger: "\\R_-", parse: "NegativeNumber" }, { trigger: "\\R_{-}", parse: "NegativeNumber" }, { trigger: "\\R^{\\lt}", parse: "NegativeNumber" }, { name: "PositiveNumber", trigger: "\\R^+" }, { trigger: "\\R^{+}", parse: "PositiveNumber" }, { trigger: "\\R_+", parse: "PositiveNumber" }, { trigger: "\\R_{+}", parse: "PositiveNumber" }, { trigger: "\\R^{\\gt}", parse: "PositiveNumber" }, { name: "NonPositiveNumber", trigger: "\\R^{0-}" }, { trigger: "\\R^{-0}", parse: "NonPositiveNumber" }, { trigger: "\\R^{\\leq}", parse: "NonPositiveNumber" }, { name: "NegativeInteger", trigger: "\\Z^-" }, { trigger: "\\Z^-", parse: "NegativeInteger" }, { trigger: "\\Z^{-}", parse: "NegativeInteger" }, { trigger: "\\Z_-", parse: "NegativeInteger" }, { trigger: "\\Z_{-}", parse: "NegativeInteger" }, { trigger: "\\Z^{\\lt}", parse: "NegativeInteger" }, { name: "PositiveInteger", trigger: "\\Z^+" }, { trigger: "\\Z^{+}", parse: "PositiveInteger" }, { trigger: "\\Z_+", parse: "PositiveInteger" }, { trigger: "\\Z_{+}", parse: "PositiveInteger" }, { trigger: "\\Z^{\\gt}", parse: "PositiveInteger" }, { trigger: "\\Z^{\\gt0}", parse: "PositiveInteger" }, { trigger: "\\N^+", parse: "PositiveInteger" }, { trigger: "\\N^{+}", parse: "PositiveInteger" }, { trigger: "\\N^*", parse: "PositiveInteger" }, { trigger: "\\N^{*}", parse: "PositiveInteger" }, { trigger: "\\N^\\star", parse: "PositiveInteger" }, { trigger: "\\N^{\\star}", parse: "PositiveInteger" }, { trigger: "\\N_1", parse: "PositiveInteger" }, { trigger: "\\N_{1}", parse: "PositiveInteger" }, { name: "NonNegativeInteger", trigger: ["\\N"] }, { trigger: "\\Z^{+0}", parse: "NonNegativeInteger" }, { trigger: "\\Z^{\\geq}", parse: "NonNegativeInteger" }, { trigger: "\\Z^{\\geq0}", parse: "NonNegativeInteger" }, { trigger: "\\Z^{0+}", parse: "NonNegativeInteger" }, { trigger: "\\mathbb{N}", parse: "NonNegativeInteger" }, { trigger: "\\N_0", parse: "NonNegativeInteger" }, { trigger: "\\N_{0}", parse: "NonNegativeInteger" }, { name: "CartesianProduct", trigger: ["\\times"], kind: "infix", associativity: "right", precedence: 390, parse: (e, t, i) => { if (390 < t.minPrec) return null; const n = e.computeEngine; if (!n || !n.box(i).valueDomain.isSubdomainOf("Set")) return null; const r = e.index, s = e.matchExpression({ ...t, minPrec: 390 }); return null === s || !0 !== n.box(i).valueDomain.isSubdomainOf("Set") ? (e.index = r, null) : ["CartesianProduct", i, s]; } }, { name: "Complement", trigger: ["^", "\\complement"], kind: "infix" }, { name: "Intersection", trigger: ["\\cap"], kind: "infix", precedence: 350 }, { name: "Interval", serialize: X }, { name: "Multiple", serialize: X }, { name: "Union", trigger: ["\\cup"], kind: "infix", precedence: 350 }, { name: "Range", serialize: X }, { name: "SetMinus", trigger: ["\\setminus"], kind: "infix", precedence: 650 }, { name: "SymmetricDifference", trigger: ["\\triangle"], kind: "infix", precedence: 260 }, { trigger: ["\\ni"], kind: "infix", associativity: "right", precedence: 160, parse: (e, t, i) => { const n = e.matchExpression(t); return null === n ? null : ["Element", n, i]; } }, { name: "Element", trigger: ["\\in"], kind: "infix", precedence: 240 }, { name: "NotElement", trigger: ["\\notin"], kind: "infix", precedence: 240 }, { name: "NotSubset", trigger: ["\\nsubset"], kind: "infix", associativity: "right", precedence: 240 }, { name: "NotSuperset", trigger: ["\\nsupset"], kind: "infix", associativity: "right", precedence: 240 }, { name: "NotSubsetNotEqual", trigger: ["\\nsubseteq"], kind: "infix", associativity: "right", precedence: 240 }, { name: "NotSupersetNotEqual", trigger: ["\\nsupseteq"], kind: "infix", associativity: "right", precedence: 240 }, { name: "SquareSubset", trigger: ["\\sqsubset"], kind: "infix", associativity: "right", precedence: 265 }, { name: "SquareSubsetEqual", trigger: ["\\sqsubseteq"], kind: "infix", associativity: "right", precedence: 265 }, { name: "SquareSuperset", trigger: ["\\sqsupset"], kind: "infix", associativity: "right", precedence: 265 }, { name: "SquareSupersetEqual", trigger: ["\\sqsupseteq"], kind: "infix", associativity: "right", precedence: 265 }, { name: "Subset", trigger: ["\\subset"], kind: "infix", associativity: "right", precedence: 240 }, { trigger: ["\\subsetneq"], kind: "infix", associativity: "right", precedence: 240, parse: "Subset" }, { trigger: ["\\varsubsetneqq"], kind: "infix", associativity: "right", precedence: 240, parse: "Subset" }, { name: "SubsetEqual", trigger: ["\\subseteq"], kind: "infix", precedence: 240 }, { name: "Superset", trigger: ["\\supset"], kind: "infix", associativity: "right", precedence: 240 }, { trigger: ["\\supsetneq"], kind: "infix", associativity: "right", precedence: 240, parse: "Superset" }, { trigger: ["\\varsupsetneq"], kind: "infix", associativity: "right", precedence: 240, parse: "Superset" }, { name: "SupersetEqual", trigger: ["\\supseteq"], kind: "infix", associativity: "right", precedence: 240 }]; function X(e, t) { if (null === t) return ""; const i = _(t); return null === i ? "" : "Set" === i ? 0 === E(t) ? "\\emptyset" : 2 === E(t) && "Condition" === _(w(t, 2)) ? d(["\\left\\lbrace", e.serialize(w(t, 1)), "\\middle\\mid", e.serialize(w(t, 2)), "\\right\\rbrace"]) : d(["\\left\\lbrace", ...q(t).map((t => e.serialize(t) + " ,")), "\\right\\rbrace"]) : (e.numericSetStyle(t, e.level), ""); } const ee = [["Alpha", "\\alpha", 945], ["Beta", "\\beta", 946], ["Delta", "\\delta", 948], ["Epsilon", "\\epsilon", 949], ["EpsilonSymbol", "\\varepsilon", 1013], ["Zeta", "\\zeta", 950], ["Eta", "\\eta", 951], ["Theta", "\\theta", 952], ["ThetaSymbol", "\\vartheta", 977], ["Iota", "\\iota", 953], ["Kappa", "\\kappa", 954], ["KappaSymbol", "\\varkappa", 1008], ["Lambda", "\\lambda", 955], ["Mu", "\\mu", 956], ["Nu", "\\nu", 957], ["Xi", "\\xi", 958], ["Omicron", "\\omicron", 959], ["PiSymbol", "\\varpi", 982], ["Rho", "\\rho", 961], ["RhoSymbol", "\\varrho", 1009], ["Sigma", "\\sigma", 963], ["FinalSigma", "\\varsigma", 962], ["Tau", "\\tau", 964], ["Phi", "\\phi", 981], ["PhiLetter", "\\varphi", 966], ["Upsilon", "\\upsilon", 965], ["Chi", "\\chi", 967], ["Psi", "\\psi", 968], ["Omega", "\\omega", 969], ["CapitalAlpha", "\\Alpha", 913], ["CapitalBeta", "\\Beta", 914], ["CapitalGamma", "\\Gamma", 915], ["CapitalDelta", "\\Delta", 916], ["CapitalEpsilon", "\\Epsilon", 917], ["CapitalZeta", "\\Zeta", 918], ["CapitalEta", "\\Eta", 919], ["CapitalTheta", "\\Theta", 920], ["CapitaIota", "\\Iota", 921], ["CapitalKappa", "\\Kappa", 922], ["CapitalLambda", "\\Lambda", 923], ["CapitalMu", "\\Mu", 924], ["CapitalNu", "\\Nu", 925], ["CapitalXi", "\\Xi", 926], ["CapitalOmicron", "\\Omicron", 927], ["CapitalPi", "\\Pi", 928], ["CapitalRho", "\\Rho", 929], ["CapitalSigma", "\\Sigma", 931], ["CapitalTau", "\\Tau", 932], ["CapitalPhi", "\\Phi", 934], ["CapitalUpsilon", "\\Upsilon", 933], ["CapitalChi", "\\Chi", 935], ["CapitalPsi", "\\Psi", 936], ["CapitalOmega", "\\Omega", 937], ["Digamma", "\\digamma", 989], ["Alef", "\\aleph", 8501], ["Bet", "\\beth", 8502], ["Gimel", "\\gimel", 8503], ["Dalet", "\\daleth", 8504], ["TurnedCapitalF", "\\Finv", 8498], ["TurnedCapitalG", "\\Game", 8513], ["Weierstrass", "\\wp", 8472], ["Eth", "\\eth", 240], ["InvertedOhm", "\\mho", 8487], ["BlackClubSuit", "\\clubsuit", 9827], ["WhiteHeartSuit", "\\heartsuit", 9825], ["BlackSpadeSuit", "\\spadesuit", 9824], ["WhiteDiamondSuit", "\\diamondsuit", 9826], ["Sharp", "\\sharp", 9839], ["Flat", "\\flat", 9837], ["Natural", "\\natural", 9838]], te = [...ee.map((([e, t, i]) => ({ name: e, trigger: [t], parse: e }))), ...ee.map((([e, t, i]) => ({ trigger: [String.fromCodePoint(i)], parse: e })))], ie = { "(": "(", ")": ")", "[": "\\lbrack", "]": "\\rbrack", "{": "\\lbrace", "}": "\\rbrace", "<": "\\langle", ">": "\\rangle", "|": "\\vert", "||": "\\Vert", "\\lceil": "\\lceil", "\\lfloor": "\\lfloor", "\\rceil": "\\rceil", "\\rfloor": "\\rfloor" }; function ne(e) { return Array.isArray(e) ? e.length : 1; } function re(a, o) { var _a, _b, _c, _d, _f, _g, _h, _j, _k; if (!function (s, a) { var _a, _b; const o = (_b = (_a = s.name) !== null && _a !== void 0 ? _a : s.trigger) !== null && _b !== void 0 ? _b : s.openDelimiter; if (void 0 !== s.serialize && !s.name) return a({ severity: "warning", message: ["invalid-dictionary-entry", o, "Unexpected serialize property without a name property"] }), !1; if (t(s)) { if (s.trigger) return a({ severity: "warning", message: ["invalid-dictionary-entry", o, `Unexpected 'trigger' "${s.trigger}". 'matchfix' operators use a 'openDelimiter' and 'closeDelimiter' instead of a trigger. `] }), !1; if (!s.openDelimiter || !s.closeDelimiter) return a({ severity: "warning", message: ["invalid-dictionary-entry", o, "Expected `openDelimiter` and a `closeDelimiter` for matchfix operator"] }), !1; if (typeof s.openDelimiter != typeof s.closeDelimiter) return a({ severity: "warning", message: ["invalid-dictionary-entry", o, "Expected `openDelimiter` and `closeDelimiter` to both be strings or array of LatexToken"] }), !1; } if (i(s) || r(s) || n(s)) { if (Array.isArray(s.trigger) && ("_" === s.trigger[0] || "^" === s.trigger[0]) || "string" == typeof s.trigger && (s.trigger.startsWith("^") || s.trigger.startsWith("_"))) { if (void 0 !== s.precedence || void 0 !== s.associativity) return a({ severity: "warning", message: ["invalid-dictionary-entry", o, 'Unexpected "precedence" or "associativity" for superscript/subscript operator'] }), !1; } else if (void 0 === s.precedence) return a({ severity: "warning", message: ["invalid-dictionary-entry", o, `Expected a "precedence" for ${s.kind} operator`] }), !1; } else if (void 0 !== s.associativity) return a({ severity: "warning", message: ["invalid-dictionary-entry", o, 'Unexpected "associativity" operator'] }), !1; return e(s) || void 0 === s.optionalLatexArg && void 0 === s.requiredLatexArg ? t(s) || s.trigger || s.name ? void 0 !== s.parse || void 0 !== s.name || (a({ severity: "warning", message: ["invalid-dictionary-entry", o, "Expected a 'parse' or 'name'"] }), !1) : (a({ severity: "warning", message: ["invalid-dictionary-entry", o, "Expected at least a 'trigger' or a 'name'"] }), !1) : (a({ severity: "warning", message: ["invalid-dictionary-entry", o, 'Unexpected "optionalLatexArg" or "requiredLatexArg" for non-symbol'] }), !1); }(a, o)) return [null, null]; const l = { name: a.name, kind: "kind" in a ? a.kind : "symbol" }; if ("matchfix" === l.kind && t(a)) { if (l.openDelimiter = a.openDelimiter, l.closeDelimiter = a.closeDelimiter, "function" == typeof a.serialize) l.serialize = a.serialize; else { const e = "string" == typeof l.openDelimiter ? ie[l.openDelimiter] : v(l.openDelimiter), t = "string" == typeof l.closeDelimiter ? ie[l.closeDelimiter] : v(l.closeDelimiter); l.serialize = (i, n) => d([e, i.serialize(n), t]); } if ("function" == typeof a.parse) l.parse = a.parse; else { const e = (_a = a.parse) !== null && _a !== void 0 ? _a : a.name; l.parse = (t, i) => [e, i]; } return [null, l]; } if ("environment" === l.kind && s(a)) { const e = a.trigger; return l.serialize = (t, i) => `\\begin{${e}${t.serialize(w(i, 1))}\\end{${e}`, l.parse = (_b = a.parse) !== null && _b !== void 0 ? _b : (() => null), [e, l]; } const u = "string" == typeof a.trigger ? p(a.trigger, []) : a.trigger, c = u ? v(u) : ""; if (a.trigger, "symbol" === l.kind && e(a) && (l.precedence = (_c = a.precedence) !== null && _c !== void 0 ? _c : 1e4, l.optionalLatexArg = (_d = a.optionalLatexArg) !== null && _d !== void 0 ? _d : 0, l.requiredLatexArg = (_f = a.requiredLatexArg) !== null && _f !== void 0 ? _f : 0), "infix" !== l.kind && "prefix" !== l.kind && "postfix" !== l.kind || !(i(a) || n(a) || r(a)) || (!u || "^" !== u[0] && "_" !== u[0] ? l.precedence = (_g = a.precedence) !== null && _g !== void 0 ? _g : 1e4 : l.precedence = 720), "infix" === l.kind && i(a)) if (l.associativity = (_h = a.associativity) !== null && _h !== void 0 ? _h : "non", "function" == typeof a.parse) l.parse = a.parse; else if (!u || "^" !== u[0] && "_" !== u[0]) { const e = (_j = a.parse) !== null && _j !== void 0 ? _j : a.name, t = l.precedence, i = l.associativity; l.parse = (n, r, s) => { if (t < r.minPrec) return null; const a = n.matchExpression({ ...r, minPrec: t }); return "string" != typeof e ? [e, s, a !== null && a !== void 0 ? a : "Missing"] : V(e, s, a !== null && a !== void 0 ? a : "Missing", i); }; } else { const e = (_k = a.parse) !== null && _k !== void 0 ? _k : a.name; l.parse = (t, i, n) => { var _a, _b; return [e, (_a = w(n, 1)) !== null && _a !== void 0 ? _a : "Missing", (_b = w(n, 2)) !== null && _b !== void 0 ? _b : "Missing"]; }; } else if ("function" == typeof a.parse) l.parse = a.parse; else if (void 0 !== a.parse) l.parse = () => a.parse; else if (void 0 === a.parse && void 0 !== a.name) if ("postfix" === l.kind) l.parse = (e, t) => t ? [a.name, t] : null; else if ("prefix" === l.kind) { const e = l.precedence, t = a.name; l.parse = (i, n) => { if (e < n.minPrec) return null; const r = i.matchExpression({ ...n, minPrec: e }); return null === r ? null : [t, r]; }; } return "function" == typeof a.serialize || "string" == typeof a.serialize ? l.serialize = a.serialize : u && ("postfix" === l.kind ? l.serialize = "#1" + c : "prefix" === l.kind ? l.serialize = c + "#1" : "infix" === l.kind ? l.serialize = "#1" + c + "#2" : "symbol" === l.kind ? l.serialize = c : l.serialize = ""), [u !== null && u !== void 0 ? u : null, l]; } const se = { algebra: [{ name: "To", trigger: ["\\to"], kind: "infix", precedence: 270 }], arithmetic: $, calculus: [{ name: "Integral", trigger: ["\\int"], parse: function (e) { var _a; let t = "Nothing", i = "Nothing", n = !1; for (; !n;) e.skipSpace(), e.match("_") ? i = e.matchRequiredLatexArgument() : e.match("^") ? t = e.matchRequiredLatexArgument() : n = !0; return ["Integral", (_a = e.matchExpression({ tokens: ["d"] })) !== null && _a !== void 0 ? _a : "", t !== null && t !== void 0 ? t : "Nothing", i !== null && i !== void 0 ? i : "Nothing"]; }, serialize: function (e, t) { return ""; } }], core: G, logic: [{ name: "True", trigger: ["\\mathrm", "<{>", "T", "r", "u", "e", "<}>"], serialize: "\\mathrm{True}" }, { name: "False", trigger: ["\\mathrm", "<{>", "F", "a", "l", "s", "e", "<}>"], serialize: "\\mathrm{False}" }, { name: "Maybe", trigger: ["\\mathrm", "<{>", "M", "a", "y", "b", "e", "<}>"], serialize: "\\mathrm{Maybe}" }], relop: [{ trigger: ["!", "<"], kind: "infix", associativity: "right", precedence: 246, parse: "NotLess" }, { name: "NotLess", trigger: ["\\nless"], kind: "infix", associativity: "right", precedence: 246 }, { trigger: ["<"], kind: "infix", associativity: "right", precedence: 245, parse: "Less" }, { name: "Less", trigger: ["\\lt"], kind: "infix", associativity: "right", precedence: 245 }, { trigger: ["<", "="], kind: "infix", associativity: "right", precedence: 241, parse: "LessEqual" }, { name: "LessEqual", trigger: ["\\le"], kind: "infix", associativity: "right", precedence: 241 }, { trigger: ["\\leq"], kind: "infix", associativity: "right", precedence: 241, parse: "Equal" }, { trigger: ["\\leqslant"], kind: "infix", associativity: "right", precedence: 265, parse: "LessEqual" }, { name: "LessNotEqual", trigger: ["\\lneqq"], kind: "infix", associativity: "right", precedence: 260 }, { name: "NotLessNotEqual", trigger: ["\\nleqq"], kind: "infix", associativity: "right", precedence: 260 }, { name: "LessOverEqual", trigger: ["\\leqq"], kind: "infix", associativity: "right", precedence: 265 }, { name: "GreaterOverEqual", trigger: ["\\geqq"], kind: "infix", associativity: "right", precedence: 265, parse: "GreaterEqual" }, { name: "Equal", trigger: ["="], kind: "infix", associativity: "right", precedence: 260 }, { trigger: ["*", "="], kind: "infix", associativity: "right", precedence: 260, parse: "StarEqual" }, { name: "StarEqual", trigger: ["\\star", "="], kind: "infix", associativity: "right", precedence: 260 }, { name: "PlusEqual", trigger: ["+", "="], kind: "infix", associativity: "right", precedence: 260 }, { name: "MinusEqual", trigger: ["-", "="], kind: "infix", associativity: "right", precedence: 260 }, { name: "SlashEqual", trigger: ["/", "="], kind: "infix", associativity: "right", precedence: 260 }, { name: "EqualEqual", trigger: ["=", "="], kind: "infix", associativity: "right", precedence: 260 }, { name: "EqualEqualEqual", trigger: ["=", "=", "="], kind: "infix", associativity: "right", precedence: 265 }, { name: "TildeFullEqual", trigger: ["\\cong"], kind: "infix", associativity: "right", precedence: 260 }, { name: "NotTildeFullEqual", trigger: ["\\ncong"], kind: "infix", associativity: "right", precedence: 260 }, { trigger: [":", "="], kind: "infix", associativity: "right", precedence: 260, parse: "Assign" }, { name: "Assign", trigger: ["\\coloneq"], kind: "infix", associativity: "right", precedence: 260 }, { name: "Approx", trigger: ["\\approx"], kind: "infix", associativity: "right", precedence: 247 }, { name: "NotApprox", trigger: ["\\approx"], kind: "infix", associativity: "right", precedence: 247 }, { name: "ApproxEqual", trigger: ["\\approxeq"], kind: "infix", associativity: "right", precedence: 260 }, { name: "NotApproxEqual", trigger: ["!", "\\approxeq"], kind: "infix", associativity: "right", precedence: 250 }, { name: "NotEqual", trigger: ["\\ne"], kind: "infix", associativity: "right", precedence: 255 }, { name: "Unequal", trigger: ["!", "="], kind: "infix", associativity: "right", precedence: 260 }, { name: "GreaterEqual", trigger: ["\\ge"], kind: "infix", associativity: "right", precedence: 242 }, { trigger: ["\\geq"], kind: "infix", associativity: "right", precedence: 242, parse: "GreaterEqual" }, { trigger: [">", "="], kind: "infix", associativity: "right", precedence: 243, parse: "GreaterEqual" }, { trigger: ["\\geqslant"], kind: "infix", associativity: "right", precedence: 265, parse: "GreaterEqual" }, { name: "GreaterNotEqual", trigger: ["\\gneqq"], kind: "infix", associativity: "right", precedence: 260 }, { name: "NotGreaterNotEqual", trigger: ["\\ngeqq"], kind: "infix", associativity: "right", precedence: 260 }, { trigger: [">"], kind: "infix", associativity: "right", precedence: 245, parse: "Greater" }, { name: "Greater", trigger: ["\\gt"], kind: "infix", associativity: "right", precedence: 245 }, { name: "NotGreater", trigger: ["\\ngtr"], kind: "infix", associativity: "right", precedence: 244 }, { trigger: ["!", ">"], kind: "infix", associativity: "right", precedence: 244, parse: "NotGreater" }, { name: "RingEqual", trigger: ["\\circeq"], kind: "infix", associativity: "right", precedence: 260 }, { name: "TriangleEqual", trigger: ["\\triangleq"], kind: "infix", associativity: "right", precedence: 260 }, { name: "DotEqual", trigger: ["\\doteq"], kind: "infix", associativity: "right", precedence: 265 }, { name: "DotEqualDot", trigger: ["\\doteqdot"], kind: "infix", associativity: "right", precedence: 265 }, { name: "FallingDotEqual", trigger: ["\\fallingdotseq"], kind: "infix", associativity: "right", precedence: 265 }, { name: "RisingDotEqual", trigger: ["\\fallingdotseq"], kind: "infix", associativity: "right", precedence: 265 }, { name: "QuestionEqual", trigger: ["\\questeq"], kind: "infix", associativity: "right", precedence: 260 }, { name: "Equivalent", trigger: ["\\equiv"], kind: "infix", associativity: "right", precedence: 260 }, { trigger: ["\\iff"], kind: "infix", parse: "Equivalent", associativity: "right", precedence: 260 }, { name: "MuchLess", trigger: ["\\ll"], kind: "infix", associativity: "right", precedence: 260 }, { name: "MuchGreater", trigger: ["\\gg"], kind: "infix", associativity: "right", precedence: 260 }, { name: "Precedes", trigger: ["\\prec"], kind: "infix", associativity: "right", precedence: 260 }, { name: "Succeeds", trigger: ["\\succ"], kind: "infix", associativity: "right", precedence: 260 }, { name: "PrecedesEqual", trigger: ["\\preccurlyeq"], kind: "infix", associativity: "right", precedence: 260 }, { name: "SucceedsEqual", trigger: ["\\curlyeqprec"], kind: "infix", associativity: "right", precedence: 260 }, { name: "NotPrecedes", trigger: ["\\nprec"], kind: "infix", associativity: "right", precedence: 260 }, { name: "NotSucceeds", trigger: ["\\nsucc"], kind: "infix", associativity: "right", precedence: 260 }, { name: "Between", trigger: ["\\between"], kind: "infix", associativity: "right", precedence: 265 }], other: U, physics: [{ name: "mu-0", trigger: "\\mu_0" }], sets: J, symbols: te, trigonometry: Y }, ae = { "(": ["\\lparen", "("], ")": ["\\rparen", ")"], "[": ["\\lbrack"], "]": ["\\rbrack"], "<": ["<", "\\langle"], ">": [">", "\\rangle"], "{": ["\\{", "\\lbrace"], "}": ["\\}", "\\rbrace"], ":": [":", "\\colon"], "|": ["|", "\\|", "\\lvert", "\\rvert"], "||": ["||", "\\Vert", "\\lVert", "\\rVert"], "\\lfloor": ["\\lfloor"], "\\rfloor": ["\\rfloor"], "\\lceil": ["\\lceil"], "\\rceil": ["\\rceil"], "\\ulcorner": ["\\ulcorner"], "\\urcorner": ["\\urcorner"], "\\llcorner": ["\\llcorner"], "\\lrcorner": ["\\lrcorner"], "\\lgroup": ["\\lgroup"], "\\rgroup": ["\\rgroup"], "\\lmoustache": ["\\lmoustache"], "\\rmoustache": ["\\rmoustache"] }, oe = { ":": [":", "\\colon"], "|": ["|", "\\|", "\\mid", "\\mvert"] }, le = { "\\left": "\\right", "\\bigl": "\\bigr", "\\Bigl": "\\Bigr", "\\biggl": "\\biggr", "\\Biggl": "\\Biggr", "\\big": "\\big", "\\Big": "\\Big", "\\bigg": "\\bigg", "\\Bigg": "\\Bigg" }, ue = ["\\middle", "\\bigm", "\\Bigm", "\\biggm", "\\Biggm", "\\big", "\\Big", "\\bigg", "\\Bigg"], ce = { "(": ")", "[": "]", "\\{": "\\}", "\\lbrace": "\\rbrace", "\\lparen": "\\rparen", "\\langle": "\\rangle", "\\lfloor": "\\rfloor", "\\lceil": "\\rceil", "\\vert": "\\vert", "\\lvert": "\\rvert", "\\Vert": "\\Vert", "\\lVert": "\\rVert", "\\lbrack": "\\rbrack", "\\ulcorner": "\\urcorner", "\\llcorner": "\\lrcorner", "\\lgroup": "\\rgroup", "\\lmoustache": "\\rmoustache" }, he = { precision: 6, positiveInfinity: "\\infty", negativeInfinity: "-\\infty", notANumber: "\\operatorname{NaN}", decimalMarker: ".", groupSeparator: "\\,", exponentProduct: "\\cdot", beginExponentMarker: "10^{", endExponentMarker: "}", notation: "auto", truncationMarker: "\\ldots", beginRepeatingDigits: "\\overline{", endRepeatingDigits: "}", imaginaryNumber: "\\imaginaryI", avoidExponentsInRange: [-7, 20] }, me = { applyInvisibleOperator: "auto", skipSpace: !0, parseArgumentsOfUnknownLatexCommands: !0, parseNumbers: !0, parseUnknownSymbol: e => /^[fg]$/.test(e) ? "function" : /^[a-zA-Z]+$/.test(e) ? "symbol" : "unknown", preserveLatex: !0 }; class fe { constructor(e, t, i, n, r) { this.index = 0, this._lastPeek = "", this._peekCounter = 0, this.options = { ...he, ...me, ...t }, this.engine = n, this._tokens = e, this.onError = r, this._dictionary = i; } updateOptions(e) { for (const [t, i] of Object.entries(e)) this.options[t] = i; } clone(e, t) { return new fe(this._tokens.slice(e, t), this.options, this._dictionary, this.engine, this.onError); } get atEnd() { return this.index >= this._tokens.length; } get peek() { if (this._tokens[this.index] === this._lastPeek ? this._peekCounter += 1 : this._peekCounter = 0, this._peekCounter >= 1024) throw Error(`Infinite loop detected while parsing "${this.latex(0)}" at ${this._lastPeek} (index ${this.index})`); return this._lastPeek = this._tokens[this.index], this._tokens[this.index]; } atTerminator(e) { const t = this.index; return !!this.atEnd || !!e && (!(!e.condition || !e.condition(this)) || !!(e.tokens && e.tokens.length > 0 && this.matchAll(e.tokens)) && (this.index = t, !0)); } latex(e, t) { return v(this._tokens.slice(e, t)); } latexAhead(e) { return v(this._tokens.slice(this.index, this.index + e)); } latexBefore() { return this.latex(0, this.index); } latexAfter() { return this.latex(this.index); } lookAhead() { let e = Math.min(this._dictionary.lookahead, this._tokens.length - this.index); const t = []; for (; e > 0;) t[e] = this.latexAhead(e--); return t; } peekDefinitions(e) { let t; t = "operator" === e ? this.lookAhead().map(((e, t) => { var _a, _b, _c, _d, _f; return (_d = (_b = (_a = this._dictionary.infix[t]) === null || _a === void 0 ? void 0 : _a.get(e)) !== null && _b !== void 0 ? _b : (_c = this._dictionary.postfix[t]) === null || _c === void 0 ? void 0 : _c.get(e)) !== null && _d !== void 0 ? _d : (_f = this._dictionary.prefix[t]) === null || _f === void 0 ? void 0 : _f.get(e); })) : this.lookAhead().map(((t, i) => { var _a; return (_a = this._dictionary[e][i]) === null || _a === void 0 ? void 0 : _a.get(t); })); const i = []; for (let e = t.length; e > 0; e--) if (void 0 !== t[e]) for (const n of t[e]) i.push([n, e]); return 0 === i.length ? null : i; } next() { return this._tokens[this.index++]; } skipSpace() { if (!this.atEnd && "<{>" === this.peek && "<}>" === this._tokens[this.index + 1]) return this.index += 2, this.skipSpace(), !0; let e = !1; if (!this.options.skipSpace) return !1; for (; this.match("<space>");) e = !0; return e && this.skipSpace(), e; } matchChar() { var _a; const e = this.index; let t = 0; for (; this.match("^");) t += 1; if (t >= 2) { let e = "", i = 0; for (; i != t;) { const t = this.matchAny(["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"]); if (!t) break; e += t, i += 1; } if (e.length === t) return String.fromCodePoint(Number.parseInt(e, 16)); } else { if (this.match("\\char")) { let e = Math.floor((_a = this.matchLatexNumber()) !== null && _a !== void 0 ? _a : NaN); return (!Number.isFinite(e) || e < 0 || e > 1114111) && (e = 10067), String.fromCodePoint(e); } if (this.match("\\unicode")) if ("<{>" === this.peek) { const e = this.matchLatexNumber(); if (this.match("<}>") && null !== e && e >= 0 && e <= 1114111) return String.fromCodePoint(e); } else { const e = this.matchLatexNumber(); if (null !== e && e >= 0 && e <= 1114111) return String.fromCodePoint(e); } } return this.index = e, this.next(); } matchColor(e = !1) { let t = ""; for (; !this.atEnd && "}" !== this.peek;) t += this.next(); return t; } matchLatexDimension() { return null; } match(e) { return this._tokens[this.index] === e && (this.index++, !0); } matchAll(e) { "string" == typeof e && (e = [e]); let t = !0, i = 0; do { t = this._tokens[this.index + i] === e[i++]; } while (t && i < e.length); return t && (this.index += i), t; } matchAny(e) { return e.includes(this._tokens[this.index]) ? this._tokens[this.index++] : ""; } matchWhile(e) { const t = []; for (; e.includes(this._tokens[this.index]);) t.push(this._tokens[this.index++]); return t; } matchSign() { let e = !1, t = !1; for (; !t;) this.skipSpace() ? t = !1 : this.matchAny(["-", "\u2212"]) ? (e = !e, t = !1) : t = !this.matchAny(["+", "\ufe62"]); return e ? "-" : "+"; } matchDecimalDigits() { let e = "", t = !1; for (; !t;) if (e += this.matchWhile(["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]).join(""), t = !0, this.options.groupSeparator) { const e = this.index; this.skipSpace(), this.match(this.options.groupSeparator) && (this.skipSpace(), /[0-9]/.test(this.peek) ? t = !1 : this.index = e); } return e; } matchSignedInteger() { const e = this.index, t = this.matchSign(), i = this.matchDecimalDigits(); return i ? ("-" === t ? "-" : "") + i : (this.index = e, ""); } matchExponent() { const e = this.index; let t = ""; if (this.matchAny(["e", "E"])) { const e = this.matchSignedInteger(); e && (t = "e" + e); } if (t) return t; if (this.match("\\times") && (this.skipSpace(), this.match("1") && this.match("0") && this.match("^"))) { if (/[0-9]/.test(this.peek)) return "e" + this.next(); if (this.match("<{>")) { this.skipSpace(); const e = this.matchSignedInteger(); if (this.skipSpace(), this.match("<}>") && e) return "e" + e; } } return this.index = e, ""; } matchNumber() { var _a, _b; if (!this.options.parseNumbers) return ""; const e = this.index; this.skipSpace(), this.match("+"); let t = !1; if (this.match(this.options.decimalMarker)) { const i = this.index; if (!this.matchAny(["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"])) return this.index = e, ""; this.index = i, t = !0; } let i = this.matchDecimalDigits(); return i ? (!t && this.match((_a = this.options.decimalMarker) !== null && _a !== void 0 ? _a : "") && (i += "." + ((_b = this.matchDecimalDigits()) !== null && _b !== void 0 ? _b : "0")), (t ? "0." : "") + i + this.matchExponent()) : (this.index = e, ""); } matchLatexNumber(e = !0) { var _a, _b; let t = !1, i = this.peek; for (; "<space>" === i || "+" === i || "-" === i;) "-" === i && (t = !t), this.next(), i = this.peek; let n = 10, r = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]; if (this.match("'")) n = 8, r = ["0", "1", "2", "3", "4", "5", "6", "7"], e = !0; else if (this.match('"') || this.match("x")) n = 16, r = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"], e = !0; else if (this.match("`")) return i = this.next(), i ? i.startsWith("\\") && 2 === i.length ? (t ? -1 : 1) * ((_a = i.codePointAt(1)) !== null && _a !== void 0 ? _a : 0) : (t ? -1 : 1) * ((_b = i.codePointAt(0)) !== null && _b !== void 0 ? _b : 0) : null; let s = ""; for (; r.includes(this.peek);) s += this.next(); if (!e && this.match(".")) for (s += "."; r.includes(this.peek);) s += this.next(); const a = e ? Number.parseInt(s, n) : Number.parseFloat(s); return Number.isNaN(a) ? null : t ? -a : a; } matchPrefixOperator(e) { e || (e = { minPrec: 0 }), e.minPrec || (e = { ...e, minPrec: 0 }); const t = this.peekDefinitions("prefix"); if (null === t) return null; const i = this.index; for (const [n, r] of t) { this.index = i + r; const t = n.parse(this, e); if (t) return t; } return this.index = i, null; } matchInfixOperator(e, t) { t || (t = { minPrec: 0 }), t.minPrec || (t = { ...t, minPrec: 0 }); const i = this.peekDefinitions("infix"); if (null === i) return null; const n = this.index; for (const [r, s] of i) if (r.precedence >= t.minPrec) { this.index = n + s; const i = r.parse(this, t, e); if (i) return i; } return this.index = n, null; } matchArguments(e) { if (!e) return null; const t = this.index, i = this.matchEnclosure(); if ("enclosure" === e && "Delimiter" === _(i)) return q(i); if ("group" === e) { const e = this.matchExpression({ tokens: ["<}>"] }); return null === e ? [] : [e]; } if ("implicit" === e) { if ("Delimiter" === _(i)) return q(i); if (null !== i) return [i]; const e = this.matchPrimary(); return null !== e ? [e] : null; } return this.index = t, null; } matchOpenDelimiter(e, t) { var _a; const i = this.index, n = le[this.peek]; n && this.next(); const r = (_a = ae[e]) !== null && _a !== void 0 ? _a : [e], s = n ? [n] : []; return r.includes("||") && this.matchAll(["|", "|"]) ? (s.push("|"), s.push("|"), s) : r.includes(this.peek) ? (ce[e] === t ? s.push(ce[this.peek]) : s.push(t), this.next(), s) : (this.index = i, null); } matchMiddleDelimiter(e) { var _a; const t = (_a = oe[e]) !== null && _a !== void 0 ? _a : [e]; if (ue.includes(this.peek)) { const e = this.index; return this.next(), t.includes(this.peek) ? (this.next(), !0) : (this.index = e, !1); } return !!t.include(this.peek) && (this.next(), !0); } matchEnclosure() { const e = this._dictionary.matchfix; if (0 === e.length) return null; const t = this.index; for (const i of e) { if (this.index = t, Array.isArray(i.openDelimiter)) { if (!this.matchAll(i.openDelimiter)) continue; const e = this.matchExpression({ tokens: i.closeDelimiter, minPrec: 0 }); if (this.skipSpace(), !this.matchAll(i.closeDelimiter)) continue; if ("function" == typeof i.parse) { const t = i.parse(this, e !== null && e !== void 0 ? e : "Nothing"); if (null === t) continue; return t; } return [i.name, e !== null && e !== void 0 ? e : "Nothing"]; } const e = this.matchOpenDelimiter(i.openDelimiter, i.closeDelimiter); if (null === e) continue; const n = this.matchExpression({ minPrec: 0, tokens: e }); if (n && this.matchAll(e)) { if ("function" == typeof i.parse) { const e = i.parse(this, n !== null && n !== void 0 ? n : "Nothing"); if (null === e) continue; return e; } return [i.name, n]; } } return this.index = t, null; } matchSymbol() { var _a, _b; const e = this.index, t = this.peekDefinitions("symbol"); if (t) for (const [i, n] of t) { let t = null; if (this.index = e + n, "function" != typeof i.parse) { if (0 === i.optionalLatexArg && 0 === i.requiredLatexArg) return i.name; { const e = []; let t = i.optionalLatexArg; for (; 0 !== t;) { const i = this.matchOptionalLatexArgument(); if (null === i) break; e.push(i), t -= 1; } t = i.requiredLatexArg; const n = []; for (; 0 !== t;) { const e = this.matchRequiredLatexArgument(); if (null === e) break; n.push(e), t -= 1; } return n.length === i.requiredLatexArg ? [i.name, ...n, ...e] : i.name; } } if (t = i.parse(this), t) return t; } this.index = e; let i = ""; (this.matchAll(["\\operatorname", "<{>"]) || this.matchAll(["\\mathit", "<{>"]) || this.matchAll(["\\mathrm", "<{>"])) && (i = this.matchString({ tokens: ["<}>"] }), i && this.match("<}>") || (this.index = e)), i || (i = this.next()); const n = (_b = (_a = this.options).parseUnknownSymbol) === null || _b === void 0 ? void 0 : _b.call(_a, i, this); if ("symbol" === n) return i; if ("function" === n) { const e = this.matchEnclosure(); return null === e ? i : "Delimiter" !== _(e) ? null : [i, ...q(e)]; } return this.index = e, this.matchUnknownLatexCommand(); } matchOptionalLatexArgument() { const e = this.index; if (this.skipSpace(), this.match("[")) { const e = this.matchExpression(); if (this.skipSpace(), this.match("]")) return e; } return this.index = e, null; } matchRequiredLatexArgument() { const e = this.index; if (this.skipSpace(), this.match("<{>")) { const t = this.matchExpression({ tokens: ["<}>"] }); return this.skipSpace(), this.match("<}>") ? t : (this.index = e, null); } return /^[0-9]$/.test(this.peek) ? parseInt(this.next()) : /^[^\\#]$/.test(this.peek) ? this.next() : this.matchSymbol() || (this.index = e, null); } matchSupsub(e) { var _a, _b, _c; if (null === e) return null; const t = this.index; this.skipSpace(); const i = [], n = []; for (; "_" === this.peek || "^" === this.peek;) { if (this.match("_")) { let e = this.matchRequiredLatexArgument(); !e && this.match("<{>") && (e = this.matchString({ tokens: ["<}>"] }), e && this.match("<}>")), n.push(e !== null && e !== void 0 ? e : "Missing"); } else this.match("^") && i.push((_a = this.matchRequiredLatexArgument()) !== null && _a !== void 0 ? _a : "Missing"); this.skipSpace(); } if (0 === i.length && 0 === n.length) return this.index = t, e; let r = e; if (n.length > 0) { const e = (_b = this._dictionary.infix[1]) === null || _b === void 0 ? void 0 : _b.get("_"); if (e) { const t = ["Subscript", r, 1 === n.length ? n[0] : ["Sequence", ...n]]; for (const i of e) if (r = "function" == typeof i.parse ? i.parse(this, { minPrec: 0 }, t) : t, r) break; } } if (i.length > 0) { const e = (_c = this._dictionary.infix[1]) === null || _c === void 0 ? void 0 : _c.get("^"); if (e) { const t = ["Superscript", r, 1 === i.length ? i[0] : ["Sequence", ...i]]; for (const i of e) if (r = "function" == typeof i.parse ? i.parse(this, { minPrec: 0 }, t) : t, r) break; } } return null === r && (this.index = t), r; } matchPostfix(e) { if (null === e) return null; const t = this.peekDefinitions("postfix"); if (null === t) return null; const i = this.index; for (const [n, r] of t) { this.index = i + r; const t = n.parse(this, e); if (null !== t) return t; } return this.index = i, null; } matchString(e) { e.minPrec || (e = { ...e, minPrec: 0 }); let t = "", i = this.atEnd; for (; !i;) { const n = this.peek; "<space>" === n ? t += " " : "\\" === n[0] ? (this.onError([{ severity: "warning", message: "unexpected-command" }]), t += this.next()) : /^<(\$|\$\$)>$/.test(n) ? i = !0 : t += this.next(), i = i || this.atTerminator(e); } return t; } matchEnvironmentName(e, t) { if (!this.match(e)) return !1; const i = this.index; if (this.match("<{>")) { const e = this.matchString({ tokens: ["<}>"] }); if (this.match("<}>") && e === t) return !0; } return this.index = i, !1; } matchTabular(e) { const t = ["List"], i = { minPrec: 0, tokens: ["\\end", "<{>", ...e.split(""), "<}>"] }; let n = ["List"], r = null; for (; !this.atTerminator(i);) this.skipSpace(), this.match("&") ? (n.push(r !== null && r !== void 0 ? r : "Nothing"), r = null) : this.match("\\\\") || this.match("\\cr") ? (this.skipSpace(), this.matchOptionalLatexArgument(), null !== r && n.push(r), t.push(n), n = ["List"], r = null) : r = this.matchExpression({ ...i, condition: e => { const t = e.peek; return "&" === t || "\\\\" === t || "\\cr" === t; } }); return null !== r && n.push(r), n.length > 1 && t.push(n), t; } matchEnvironment() { if (!this.match("\\begin")) return null; const e = this.index; if (this.match("<{>")) { const t = this.matchString({ tokens: ["<}>"] }); if (this.match("<}>")) { const i = this._dictionary.environment.get(t), n = i ? i.parse(this, [], []) : this.matchTabular(t); if (this.skipSpace(), this.matchAll(["\\end", "<{>", ...t.split(""), "<}>"]), null !== n) return this.decorate(n, e); } } return this.index = e, null; } applyInvisibleOperator(e, t) { if (null === t || this.atTerminator(e)) return null; if (null === this.options.applyInvisibleOperator) return null; const i = this.index, n = this.matchExpression({ ...e, minPrec: 390 }); if (null === n) return this.index = i, null; if ("function" == typeof this.options.applyInvisibleOperator) return this.options.applyInvisibleOperator(this, t, n); const r = this.engine.box(n), s = I(t); if (s && this.engine && this.engine.getFunctionDefinition(s)) { let e = []; return e = "Delimiter" === r.head ? "Sequence" === r.op1.head ? [...r.op1.ops] : [r.op1] : [r], [s, ...e.map((e => e.json))]; } const a = this.engine.box(t); if (a.isLiteral && a.isInteger && r.isLiteral) { const [e, i] = r.rationalValue; if (null !== e && null !== i) return ["Add", t, n]; } return (a.isMissing || "Nothing" === a.symbol || a.isNumber) && (r.isMissing || "Nothing" === r.symbol || r.isNumber) ? V("Multiply", t, n) : (this.index = i, null); } matchUnknownLatexCommand() { const e = this.peek; if (!e || "\\" !== e[0]) return null; this.next(); const t = []; if ("\\operatorname" === e) { if (this.match("<{>")) { for (; !this.atEnd && "<}>" !== this.peek;) t.push(this.next()); this.match("<}>"); } else t.push(this.next()); return 0 === t.length ? ["Error", "Missing", { str: "syntax-error" }, ["LatexForm", { str: e }]] : ["Error", "Missing", { str: "unknown-command" }, ["LatexForm", { str: `${e}{${v(t)}}` }]]; } for (; this.match("[");) { t.push("["); let e = 0; for (; !this.atEnd && 0 === e && "]" !== this.peek;) "[" === this.peek && (e += 1), "]" === this.peek && (e -= 1), t.push(this.next()); this.match("]") && t.push("]"); } for (; this.match("<{>");) { t.push("<{>"); let e = 0; for (; !this.atEnd && 0 === e && "<}>" !== this.peek;) "<{>" === this.peek && (e += 1), "<}>" === this.peek && (e -= 1), t.push(this.next()); this.match("<}>") && t.push("<}>"); } return ["Error", "Missing", { str: "unknown-command" }, ["LatexForm", { str: `${e}${v(t)}` }]]; } matchPrimary() { let e = null; const t = this.index; if (this.match("<{>") ? (e = this.matchExpression({ tokens: ["<}>"] }), this.match("<}>")) : this.match("<}>"), null === e) { const t = this.matchNumber(); t && (e = { num: t }); } if (null === e && (e = this.matchEnclosure()), null === e && (e = this.matchEnvironment()), null === e && (e = this.matchSymbol()), null !== e) { e = this.decorate(e, t); let i = null, n = this.index; do { if (i = this.matchPostfix(e), e = i !== null && i !== void 0 ? i : e, this.index === n && null !== i) break; n = this.index; } while (null !== i); } return null !== e && (e = this.matchSupsub(e)), this.decorate(e, t); } matchExpression(e) { const t = this.index; e || (e = { minPrec: 0 }), void 0 === e.minPrec && (e.minPrec = 0), this.skipSpace(); let i = this.matchPrefixOperator({ ...e, minPrec: 0 }); if (null === i && (i = this.matchPrimary()), i) { let t = !1; for (; !this.atTerminator(e) && !t;) { this.skipSpace(); let n = this.matchInfixOperator(i, e); null === n && null === this.peekDefinitions("operator") && (n = this.applyInvisibleOperator(e, i)), null !== n ? i = n : t = !0; } } return this.decorate(i, t); } decorate(e, t) { if (null === e) return null; if (!this.options.preserveLatex) return e; const i = this.latex(t, this.index); return Array.isArray(e) ? e = { latex: i, fn: e } : "number" == typeof e ? e = { latex: i, num: Number(e).toString() } : "string" == typeof e ? e = { latex: i, sym: e } : "object" == typeof e && null !== e && (e.latex = i), e; } } function ge(e, t) { const i = e.length, n = e; e = e.slice(0, -1); for (let i = 0; i < e.length - 16; i++) { const n = e.substring(0, i); for (let r = 0; r < 17; r++) { const s = e.substring(i, i + r + 1), a = Math.floor((e.length - n.length) / s.length); if (a > 1 && (n + s.repeat(a + 1)).startsWith(e)) return "0" === s ? n.replace(/(\d{3})/g, "$1" + t.groupSeparator) : n.replace(/(\d{3})/g, "$1" + t.groupSeparator) + t.beginRepeatingDigits + s + t.endRepeatingDigits; } } const r = i > t.precision - 1; return e = n, r && (e = e.substring(0, t.precision - 1)), t.groupSeparator && (e = e.replace(/(\d{3})/g, "$1" + t.groupSeparator)).endsWith(t.groupSeparator) && (e = e.slice(0, -t.groupSeparator.length)), r ? e + t.truncationMarker : e; } function pe(e, t) { var _a; return e ? t.beginExponentMarker ? t.beginExponentMarker + e + ((_a = t.endExponentMarker) !== null && _a !== void 0 ? _a : "") : "10^{" + e + "}" : ""; } class de { constructor(e, t, i) { this.level = -1, this.options = e, e.invisibleMultiply && (/#1/.test(e.invisibleMultiply) && /#2/.test(e.invisibleMultiply) || i([{ severity: "warning", message: ["expected-argument", "invisibleMultiply"] }])), this.onError = i, this.dictionary = t; } updateOptions(e) { for (const t of Object.keys(this.options)) t in e && (this.options[t] = e[t]); } wrap(e, t) { if (null === e) return ""; if (void 0 === t) return "(" + this.serialize(e) + ")"; if ("number" == typeof e || b(e) || "string" == typeof e || y(e)) return this.serialize(e); const i = _(e); if ("string" == typeof i && "Delimiter" !== i) { const n = this.dictionary.name.get(i); if (n && ("symbol" === n.kind || "prefix" === n.kind || "infix" === n.kind || "postfix" === n.kind) && n.precedence < t) return this.wrapString(this.serialize(e), this.options.applyFunctionStyle(e, this.level)); } return this.serialize(e); } wrapShort(e) { if (null === e) return ""; const t = this.serialize(e); return "Delimiter" === _(e) || "number" == typeof e || b(e) || /(^(.|\\[a-zA-Z*]+))$/.test(t) ? t : this.wrapString(t, this.options.groupStyle(e, this.level + 1)); } wrapString(e, t) { return "none" === t ? e : "(" + e + ")"; } serializeSymbol(e, t) { var _a; const i = _(e); if (!i) return "string" == typeof (t === null || t === void 0 ? void 0 : t.serialize) ? t.serialize : "function" == typeof (t === null || t === void 0 ? void 0 : t.serialize) ? t.serialize(this, e) : (_a = ye(I(e), "upright.")) !== null && _a !== void 0 ? _a : ""; const n = q(e); if (!t) { if ("string" == typeof i && i.length > 0 && "\\" === i[0]) { let e = i; for (const t of n) e += "{" + this.serialize(t) + "}"; return e; } return "string" == typeof i ? `${ye(i, "upright.")}(${n.map((e => this.serialize(e))).join(", ")})` : `\\operatorname{Apply}(${this.serialize(i)}, ${this.serialize(["List", ...n])})`; } if (t.requiredLatexArg > 0) { let e = "", i = "", r = 0; for (; r < t.requiredLatexArg;) i += "{" + this.serialize(n[r++]) + "}"; for (; r < Math.min(n.length, t.optionalLatexArg + t.requiredLatexArg);) { const t = this.serialize(n[1 + r++]); t && (e += "[" + t + "]"); } return t.serialize + (e + i); } return "function" == typeof t.serialize ? t.serialize(this, e) : "none" === this.options.applyFunctionStyle(e, this.level) ? t.serialize + d(n.map((e => this.serialize(e)))) : t.serialize + this.serialize(["Delimiter", ...n]); } serializeDictionary(e) { return `\\left[\\begin{array}{lll}${Object.keys(e).map((t => `\\textbf{${t}} & \\rightarrow & ${this.serialize(e[t])}`)).join("\\\\")}\\end{array}\\right]`; } serialize(e) { if (null == e) return ""; this.level += 1; try { const t = (() => { const t = function (e, t) { var _a, _b, _c; if (null === e) return ""; let i; if ("number" == typeof e) i = e; else { if ("object" != typeof e || !("num" in e)) return ""; i = e.num; } if (i === 1 / 0 || "Infinity" === i || "+Infinity" === i) return t.positiveInfinity; if (i === -1 / 0 || "-Infinity" === i) return t.negativeInfinity; if ("NaN" === i || "number" == typeof i && Number.isNaN(i)) return t.notANumber; if ("number" == typeof i) return "engineering" === t.notation ? function (e, t) { if (0 === e) return "0"; const i = Math.abs(e); let n = Math.round(Math.log10(i)); n -= n % 3, i > Math.pow(10, t.avoidExponentsInRange[0]) && i < Math.pow(10, t.avoidExponentsInRange[1]) && (n = 0); const r = i / Math.pow(10, n); let s = ""; const a = r.toString().match(/^(.*)\.(.*)$/); (a === null || a === void 0 ? void 0 : a[1]) && a[2] && (s = a[1] + t.decimalMarker + a[2]), t.groupSeparator && (s = ge(r.toExponential(), t)); let o = ""; return 0 !== n && (o = pe(n.toString(), t)), (e < 0 ? "-" : "") + s + o; }(i, t) : function (e, t) { var _a; let i, n = e.match(/^(.*)[e|E]([-+]?[0-9]+)$/i); (n === null || n === void 0 ? void 0 : n[1]) && n[2] && (i = pe(n[2], t)); let r = (_a = n === null || n === void 0 ? void 0 : n[1]) !== null && _a !== void 0 ? _a : e, s = ""; return n = (i ? n[1] : e).match(/^(.*)\.(.*)$/), (n === null || n === void 0 ? void 0 : n[1]) && n[2] && (r = n[1], s = n[2]), t.groupSeparator && (r = r.replace(/\B(?=(\d{3})+(?!\d))/g, t.groupSeparator), s = ge(s, t)), s && (s = t.decimalMarker + s), i ? "1" !== r || s ? r + s + t.exponentProduct + i : i : r + s; }(i.toString(), t); if (/[0-9][nd]$/.test(i) && (i = i.slice(0, -1)), i = i.replace(/[\u0009-\u000d\u0020\u00a0]/g, ""), /\([0-9]+\)$/.test(i)) { const [e, n, r] = (_a = i.match(/(.+)\(([0-9]+)\)$/)) !== null && _a !== void 0 ? _a : []; i = n + r.repeat(Math.ceil(t.precision / r.length)); } let n = ""; for ("-" === i[0] ? (n = "-", i = i.substring(1)) : "+" === i[0] && (i = i.substring(1)); "0" === i[0];) i = i.substring(1); if (0 === i.length) return n + "0"; "." === i[0] && (i = "0" + i); let r = ""; if (i.indexOf(".") >= 0) { const e = i.match(/(\d*)\.(\d*)([e|E]([-+]?[0-9]*))?/); if (!e) return ""; const s = e[1], a = e[2]; if (r = (_b = e[4]) !== null && _b !== void 0 ? _b : "", "0" === s) { let e = 0; for (; "0" === a[e] && e < a.length;) e += 1; let r = ""; if (e <= 4) r = "0" + t.decimalMarker, r += a.substring(0, e), r += ge(i.substring(r.length), t); else if (e + 1 >= t.precision) r = "0", n = ""; else { r = i[e]; const n = ge(i.substring(e + 1), t); n && (r += t.decimalMarker + n); } "0" !== r && (!(i.length - 1 > t.precision) || t.endRepeatingDigits && r.endsWith(t.endRepeatingDigits) || !t.truncationMarker || r.endsWith(t.truncationMarker) || (r += t.truncationMarker), e > 4 && (r += t.exponentProduct + pe("" + (1 - e), t))), i = r; } else { i = s.replace(/\B(?=(\d{3})+(?!\d))/g, t.groupSeparator); const e = ge(a, t); e && (i += t.decimalMarker + e); } } else if (i.length > t.precision) { const e = i.length; if (e > t.avoidExponentsInRange[1]) { let n = i[0]; const r = ge(i.substring(1), t); r && (n += t.decimalMarker + r, t.truncationMarker && !n.endsWith(t.truncationMarker) && t.endRepeatingDigits && !n.endsWith(t.endRepeatingDigits) && (n += t.truncationMarker)), "1" !== n ? n += t.exponentProduct : n = "", i = n + pe("" + (e - 1), t); } } else { const e = i.match(/([0-9]*)\.?([0-9]*)([e|E]([-+]?[0-9]+))?/); e && (i = e[1], e[2] && (i += t.decimalMarker + e[2]), r = (_c = e[4]) !== null && _c !== void 0 ? _c : ""), i = i.replace(/\B(?=(\d{3})+(?!\d))/g, t.groupSeparator); } const s = pe(r, t); return "1" === i && s ? n + s : (s && (i = i + t.exponentProduct + s), n + i); }(e, this.options); if (t) return t; const i = x(e); if (null !== i) return `\\text{${i}}`; const n = I(e); if (null !== n) { const t = this.dictionary.name.get(n); if ("symbol" === (t === null || t === void 0 ? void 0 : t.kind)) return this.serializeSymbol(e, t); } const r = M(e); if (null !== r) return this.serializeDictionary(r); const s = S(e); if (s) { if ("\\" === s[0]) { const t = q(e); return 0 === t.length ? s : s + "{" + t.map((e => this.serialize(e))).filter((e => !!e)).join("}{") + "}"; } const t = this.dictionary.name.get(s); if (t) return "function" == typeof t.serialize ? t.serialize(this, e) : "infix" === t.kind || "postfix" === t.kind || "prefix" === t.kind ? function (e, t, i) { let n = ""; const r = E(t), s = S(t); if ("postfix" === i.kind) return 1 !== r && e.onError([{ severity: "warning", message: ["postfix-operator-requires-one-operand", e.serializeSymbol(s)] }]), ve(i.serialize, [e.wrap(w(t, 1), i.precedence)]); if ("prefix" === i.kind) return 1 !== r && e.onError([{ severity: "warning", message: ["prefix-operator-requires-one-operand", e.serializeSymbol(s)] }]), ve(i.serialize, [e.wrap(w(t, 1), i.precedence + 1)]); if ("infix" === i.kind) { n = e.wrap(w(t, 1), i.precedence); for (let s = 2; s < r + 1; s++) { const r = w(t, s); null !== r && (n = ve(i.serialize, [n, e.wrap(r, i.precedence)])); } } return n; }(this, e, t) : "matchfix" === t.kind ? function (e, t, i) { var _a; return ve(i.serialize, [e.serialize((_a = w(t, 1)) !== null && _a !== void 0 ? _a : "Nothing")]); }(this, e, t) : "symbol" === t.kind ? this.serializeSymbol(e, t) : ""; } if (Array.isArray(e) || N(e) || null !== I(e)) return this.serializeSymbol(e); this.onError([{ severity: "warning", message: ["syntax-error", e ? JSON.stringify(e) : "undefined"] }]); })(); return this.level -= 1, t !== null && t !== void 0 ? t : ""; } catch (e) { } return this.level -= 1, ""; } applyFunctionStyle(e, t) { return this.options.applyFunctionStyle(e, t); } groupStyle(e, t) { return this.options.groupStyle(e, t); } rootStyle(e, t) { return this.options.rootStyle(e, t); } fractionStyle(e, t) { return this.options.fractionStyle(e, t); } logicStyle(e, t) { return this.options.logicStyle(e, t); } powerStyle(e, t) { return this.options.powerStyle(e, t); } numericSetStyle(e, t) { return this.options.numericSetStyle(e, t); } } function ve(e, t) { var _a; let i = e; for (let e = 0; e < t.length; e++) { let n = (_a = t[e]) !== null && _a !== void 0 ? _a : ""; if (/[a-zA-Z*]/.test(n[0])) { const t = i.match(RegExp("(.*)#" + Number(e + 1).toString())); t && /\\[a-zA-Z*]+/.test(t[1]) && (n = " " + n); } i = i.replace("#" + Number(e + 1).toString(), n); } return i; } const be = ["alpha", "beta", "gamma", "Gamma", "delta", "Delta", "epsilon", "zeta", "eta", "theta", "Theta", "iota", "kappa", "lambda", "Lambda", "mu", "nu", "xi", "Xi", "pi", "Pi", "rho", "sigma", "Sigma", "tau", "upsilon", "phi", "Phi", "varphi", "chi", "psi", "Psi", "omega", "Omega", "aleph", "ast", "blacksquare", "bot", "bullet", "circ", "diamond", "times", "top", "square", "star"]; function ye(e, t = "italic.") { var _a; if (null === e) return null; const i = e.match(/^(_+)(.*)/); if (i) return `\\text{${"\\_".repeat(i[1].length) + Ne(i[2])}}`; let n; [n, e] = function (e) { const t = e.match(/^([a-zA-Z-]+\.)(.*)/); return t ? [t[1], t[2]] : ["", e]; }(e); const r = Ne(e); return 1 !== r.length || n ? (n || (n = t), ((_a = { "upright.": "\\mathrm{_}", "italic.": "\\mathit{_}", "bold-italic.": "\\mathbf{\\mathit{_}}", "script.": "\\mathscr{_}", "calligraphic.": "\\mathcal{_}", "bold-script.": "\\mathbf{\\mathscr{_}}", "bold-calligraphic.": "\\mathbf{\\mathcal{_}}", "fraktur.": "\\mathfrak{_}", "gothic.": "\\mathfrak{_}", "bold-gothic.": "\\mathbf{\\mathfrak{_}}", "bold-fraktur.": "\\mathbf{\\mathfrak{_}}", "sans-serif.": "\\mathsf{_}", "bold-sans-serif.": "\\mathbf{\\mathsf{_}}", "italic-sans-serif.": "\\mathit{\\mathsf{_}}", "monospace.": "\\mathtt{_}", "blackboard.": "\\mathbb{_}", "double-struck.": "\\mathbb{_}" }[n]) !== null && _a !== void 0 ? _a : "\\mathit{_}").replace("_", r)) : r; } function Ne(e) { const t = e.indexOf("_"); if (t > 0) { const i = e.substring(0, t), n = e.substring(t + 1); return n ? n.startsWith('"') && n.endsWith('"') ? `${Ne(i)}_\\mathrm{${Ne(n.substring(1, -1))}}` : `${Ne(i)}_{${Ne(n)}}` : ye(i) + "\\_"; } const i = e.match(/([^0-9]+?)([0-9]+)$/); return i ? 0 === i[1].length ? e : `${Ne(i[1])}_{${i[2]}}` : be.includes(e) ? "\\" + e : e = e.replace(/[{}\[\]\\:\-\$%]/g, (e => { var _a; return ((_a = { "{": "\\lbrace ", "}": "\\rbrace ", "[": "\\lbrack ", "]": "\\rbrack ", ":": "\\colon ", "\\": "\\backslash ", "-": '\\unicode{"2013}' }[e]) !== null && _a !== void 0 ? _a : "\\" + e); })); } const xe = { invisibleMultiply: "", invisiblePlus: "", multiply: "\\times", missingSymbol: "\\placeholder{}", applyFunctionStyle: function (e, t) { return "paren"; }, groupStyle: function (e, t) { return "paren"; }, rootStyle: D, fractionStyle: L, logicStyle: function (e, t) { return "boolean"; }, powerStyle: function (e, t) { return "solidus"; }, numericSetStyle: function (e, t) { return "compact"; } }; class _e { constructor(e) { var _a, _b; this.onError = (_a = e.onError) !== null && _a !== void 0 ? _a : (e => { if ("undefined" != typeof window) for (const t of e) ; }), this.computeEngine = e.computeEngine; const t = { ...e }; delete t.dictionary, delete t.onError, this.options = { ...he, ...xe, ...me, ...t }, this.dictionary = function (e, t) { var _a, _b; const i = { lookahead: 1, name: new Map, symbol: [], infix: [], prefix: [], postfix: [], environment: new Map, matchfix: [] }; for (const n of e) { const [e, r] = re(n, t); if (null !== r) if (void 0 !== r.name && (i.name.has(r.name) && t({ severity: "warning", message: ["invalid-dictionary-entry", r.name, "Duplicate definition"] }), i.name.set(r.name, r)), "matchfix" === r.kind) i.matchfix.push(r); else if ("environment" === r.kind) { const e = v((_a = n.trigger) !== null && _a !== void 0 ? _a : ""); i.environment.has(e) && t({ severity: "warning", message: ["invalid-dictionary-entry", e, "Duplicate environment definition"] }), i.environment.set(e, r); } else if (e) { const t = v((_b = n.trigger) !== null && _b !== void 0 ? _b : ""), s = ne(e); if (i.lookahead = Math.max(i.lookahead, s), "symbol" === r.kind) { void 0 === i.symbol[s] && (i.symbol[s] = new Map); const e = i.symbol[s]; e.has(t) ? e.get(t).push(r) : e.set(t, [r]); } else if ("prefix" === r.kind) { void 0 === i.prefix[s] && (i.prefix[s] = new Map); const e = i.prefix[s]; e.has(t) ? e.get(t).push(r) : e.set(t, [r]); } else if ("infix" === r.kind) { void 0 === i.infix[s] && (i.infix[s] = new Map); const e = i.infix[s]; e.has(t) ? e.get(t).push(r) : e.set(t, [r]); } else if ("postfix" === r.kind) { void 0 === i.postfix[s] && (i.postfix[s] = new Map); const e = i.postfix[s]; e.has(t) ? e.get(t).push(r) : e.set(t, [r]); } } } return i; }((_b = e.dictionary) !== null && _b !== void 0 ? _b : _e.getDictionary(), (e => this.onError([e]))); } updateOptions(e) { for (const t of Object.keys(this.options)) t in e && (this.options[t] = e[t]); this.serializer.updateOptions(e); } static getDictionary(e = "all") { if ("all" === e) { let e = []; for (const t of Object.keys(se)) se[t] && (e = [...e, ...se[t]]); return e; } return se[e] ? [...se[e]] : []; } parse(e) { const t = new fe(p(e, []), this.options, this.dictionary, this.computeEngine, this.onError); let i = t.matchExpression(); if (!t.atEnd) { const e = []; for (; !t.atEnd;) e.push(t.next()); i = ["Error", i !== null && i !== void 0 ? i : "Nothing", { str: "syntax-error" }, ["LatexForm", { str: v(e) }]]; } return i || (i = "Nothing"), this.options.preserveLatex && (Array.isArray(i) ? i = { latex: e, fn: i } : "number" == typeof i ? i = { latex: e, num: Number(i).toString() } : "string" == typeof i ? i = { latex: e, sym: i } : "object" == typeof i && null !== i && (i.latex = e)), i !== null && i !== void 0 ? i : "Nothing"; } serialize(e) { return this.serializer.serialize(e); } get serializer() { return this._serializer || (this._serializer = new de(this.options, this.dictionary, this.onError)), this._serializer; } } const Se = Math.log10(Math.pow(2, 53)), we = new Set([2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053, 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129, 2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213, 2221, 2237, 2239, 2243, 2251, 2267, 2269, 2273, 2281, 2287, 2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, 2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423, 2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503, 2521, 2531, 2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617, 2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687, 2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741, 2749, 2753, 2767, 2777, 2789, 2791, 2797, 2801, 2803, 2819, 2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897, 2903, 2909, 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999, 3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061, 3067, 3079, 3083, 3089, 3109, 3119, 3121, 3137, 3163, 3167, 3169, 3181, 3187, 3191, 3203, 3209, 3217, 3221, 3229, 3251, 3253, 3257, 3259, 3271, 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331, 3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391, 3407, 3413, 3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511, 3517, 3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571, 3581, 3583, 3593, 3607, 3613, 3617, 3623, 3631, 3637, 3643, 3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709, 3719, 3727, 3733, 3739, 3761, 3767, 3769, 3779, 3793, 3797, 3803, 3821, 3823, 3833, 3847, 3851, 3853, 3863, 3877, 3881, 3889, 3907, 3911, 3917, 3919, 3923, 3929, 3931, 3943, 3947, 3967, 3989, 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051, 4057, 4073, 4079, 4091, 4093, 4099, 4111, 4127, 4129, 4133, 4139, 4153, 4157, 4159, 4177, 4201, 4211, 4217, 4219, 4229, 4231, 4241, 4243, 4253, 4259, 4261, 4271, 4273, 4283, 4289, 4297, 4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409, 4421, 4423, 4441, 4447, 4451, 4457, 4463, 4481, 4483, 4493, 4507, 4513, 4517, 4519, 4523, 4547, 4549, 4561, 4567, 4583, 4591, 4597, 4603, 4621, 4637, 4639, 4643, 4649, 4651, 4657, 4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751, 4759, 4783, 4787, 4789, 4793, 4799, 4801, 4813, 4817, 4831, 4861, 4871, 4877, 4889, 4903, 4909, 4919, 4931, 4933, 4937, 4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999, 5003, 5009, 5011, 5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087, 5099, 5101, 5107, 5113, 5119, 5147, 5153, 5167, 5171, 5179, 5189, 5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273, 5279, 5281, 5297, 5303, 5309, 5323, 5333, 5347, 5351, 5381, 5387, 5393, 5399, 5407, 5413, 5417, 5419, 5431, 5437, 5441, 5443, 5449, 5471, 5477, 5479, 5483, 5501, 5503, 5507, 5519, 5521, 5527, 5531, 5557, 5563, 5569, 5573, 5581, 5591, 5623, 5639, 5641, 5647, 5651, 5653, 5657, 5659, 5669, 5683, 5689, 5693, 5701, 5711, 5717, 5737, 5741, 5743, 5749, 5779, 5783, 5791, 5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851, 5857, 5861, 5867, 5869, 5879, 5881, 5897, 5903, 5923, 5927, 5939, 5953, 5981, 5987, 6007, 6011, 6029, 6037, 6043, 6047, 6053, 6067, 6073, 6079, 6089, 6091, 6101, 6113, 6121, 6131, 6133, 6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211, 6217, 6221, 6229, 6247, 6257, 6263, 6269, 6271, 6277, 6287, 6299, 6301, 6311, 6317, 6323, 6329, 6337, 6343, 6353, 6359, 6361, 6367, 6373, 6379, 6389, 6397, 6421, 6427, 6449, 6451, 6469, 6473, 6481, 6491, 6521, 6529, 6547, 6551, 6553, 6563, 6569, 6571, 6577, 6581, 6599, 6607, 6619, 6637, 6653, 6659, 6661, 6673, 6679, 6689, 6691, 6701, 6703, 6709, 6719, 6733, 6737, 6761, 6763, 6779, 6781, 6791, 6793, 6803, 6823, 6827, 6829, 6833, 6841, 6857, 6863, 6869, 6871, 6883, 6899, 6907, 6911, 6917, 6947, 6949, 6959, 6961, 6967, 6971, 6977, 6983, 6991, 6997, 7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, 7103, 7109, 7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207, 7211, 7213, 7219, 7229, 7237, 7243, 7247, 7253, 7283, 7297, 7307, 7309, 7321, 7331, 7333, 7349, 7351, 7369, 7393, 7411, 7417, 7433, 7451, 7457, 7459, 7477, 7481, 7487, 7489, 7499, 7507, 7517, 7523, 7529, 7537, 7541, 7547, 7549, 7559, 7561, 7573, 7577, 7583, 7589, 7591, 7603, 7607, 7621, 7639, 7643, 7649, 7669, 7673, 7681, 7687, 7691, 7699, 7703, 7717, 7723, 7727, 7741, 7753, 7757, 7759, 7789, 7793, 7817, 7823, 7829, 7841, 7853, 7867, 7873, 7877, 7879, 7883, 7901, 7907, 7919]); function Ee(e, t) { const i = function (e) { if (e <= 3) return { [e]: 1 }; const t = {}; let i = !1; for (; !i;) { if (e % 2 == 0) { t[2] ? t[2] += 1 : t[2] = 1, e /= 2; continue; } if (e % 3 == 0) { t[3] ? t[3] += 1 : t[3] = 1, e /= 3; continue; } if (1 === e) return t; const n = Math.sqrt(e); i = !0; for (let r = 6; r <= n + 6; r += 6) { if (e % (r - 1) == 0) { t[r - 1] ? t[r - 1] += 1 : t[r - 1] = 1, e /= r - 1, i = !1; break; } if (e % (r + 1) == 0) { t[r + 1] ? t[r + 1] += 1 : t[r + 1] = 1, e /= r + 1, i = !1; break; } } } return t[e] = 1, t; }(e); let n = 1, r = 1; for (const e of Object.keys(i)) { const s = parseFloat(e); n *= Math.pow(s, Math.floor(i[e] / t)), r *= Math.pow(s, i[e] % t); } return [n, r]; } function Ie(e, t) { if (0 === e) return t; if (0 === t) return e; if (e === t) return e; if (!Number.isInteger(e) || !Number.isInteger(t)) return NaN; for (; 0 !== t;) [e, t] = [t, e % t]; return e < 0 ? -e : e; } function ke([e, t]) { if (1 === e || 1 === t) return [e, t]; t < 0 && ([e, t] = [-e, -t]); const i = Ie(e, t); return i <= 1 ? [e, t] : [e / i, t / i]; } const Ae = [.9999999999998099, 676.5203681218851, -1259.1392167224028, 771.3234287776531, -176.6150291621406, 12.507343278686905, -.13857109526572012, 9984369578019572e-21, 1.5056327351493116e-7], Me = [.999999999999997, 57.15623566586292, -59.59796035547549, 14.13609797474174, -.4919138160976202, 3399464998481188e-20, 4652362892704857e-20, -9837447530487956e-20, .0001580887032249125, -.00021026444172410488, .0002174396181152126, -.0001643181065367639, 8441822398385274e-20, -261908384015814e-19, 3689918265953162e-21]; function Fe(e) { if (e < 0) return NaN; let t = Me[0]; for (let i = 14; i > 0; --i) t += Me[i] / (e + i); const i = e + 4.7421875 + .5; return .9189385332046727 + (e + .5) * Math.log(i) - i + Math.log(t) - Math.log(e); } function Oe(e) { if (e < .5) return Math.PI / (Math.sin(Math.PI * e) * Oe(1 - e)); if (e > 100) return Math.exp(Fe(e)); e -= 1; let t = Ae[0]; for (let i = 1; i < 9; i++) t += Ae[i] / (e + i); const i = e + 7 + .5; return 2.5066282746310002 * Math.pow(i, e + .5) * Math.exp(-i) * t; } function qe(e, t = 10) { let i = 0; for (let n = 0; n < e.length; n++) { const r = { " ": -1, "\xa0": -1, "\u2000": -1, "\u2001": -1, "\u2002": -1, "\u2003": -1, "\u2004": -1, "\u2005": -1, "\u2006": -1, "\u2007": -1, "\u2008": -1, "\u2009": -1, "\u200a": -1, "\u200b": -1, "\u202f": -1, "\u205f": -1, _: -1, ",": -1, 0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, a: 10, b: 11, c: 12, d: 13, e: 14, f: 15, g: 16, h: 17, i: 18, j: 19, k: 20, l: 21, m: 22, n: 23, o: 24, p: 25, q: 26, r: 27, s: 28, t: 29, u: 30, v: 31, w: 32, x: 33, y: 34, z: 35 }[e[n]]; if (-1 !== r) { if (void 0 === r) return [i, e.substring(n)]; if (r >= t) return [i, e.substring(n)]; i = i * t + r; } } return [i, ""]; } class Pe { constructor(e, t) { this.engine = e, (t === null || t === void 0 ? void 0 : t.latex) && (this._latex = t.latex), (t === null || t === void 0 ? void 0 : t.wikidata) && (this._wikidata = t.wikidata); } toJSON() { return JSON.stringify(this.json); } toString() { return this.latex; } valueOf() { var _a, _b, _c; const [e, t] = this.rationalValue; return null !== e && null !== t ? [e, t] : (_c = (_b = (_a = this.asFloat) !== null && _a !== void 0 ? _a : this.string) !== null && _b !== void 0 ? _b : this.symbol) !== null && _c !== void 0 ? _c : this.toString(); } is(e) { return null != e && this.isSame(this.engine.box(e)); } has(e) { return !1; } get description() { return []; } get url() { return ""; } _purge() { } get isPure() { return !1; } get isLiteral() { return !1; } get latex() { var _a; return (_a = this._latex) !== null && _a !== void 0 ? _a : this.engine.serialize(this); } set latex(e) { this._latex = e; } get wikidata() { var _a; return (_a = this._wikidata) !== null && _a !== void 0 ? _a : ""; } set wikidata(e) { this._wikidata = e; } get complexity() { return 1; } get ops() { return null; } get nops() { return 0; } get op1() { return this.engine.symbol("Missing"); } get op2() { return this.engine.symbol("Missing"); } get op3() { return this.engine.symbol("Missing"); } get symbolDefinition() { } get functionDefinition() { } _repairDefinition() { } get keys() { return null; } get keysCount() { return 0; } getKey(e) { } hasKey(e) { return !1; } get machineValue() { var _a, _b; return (_b = (_a = this.numericValue) === null || _a === void 0 ? void 0 : _a.machineValue) !== null && _b !== void 0 ? _b : null; } get rationalValue() { var _a, _b; return (_b = (_a = this.numericValue) === null || _a === void 0 ? void 0 : _a.rationalValue) !== null && _b !== void 0 ? _b : [null, null]; } get decimalValue() { var _a, _b; return (_b = (_a = this.numericValue) === null || _a === void 0 ? void 0 : _a.decimalValue) !== null && _b !== void 0 ? _b : null; } get complexValue() { var _a, _b; return (_b = (_a = this.numericValue) === null || _a === void 0 ? void 0 : _a.complexValue) !== null && _b !== void 0 ? _b : null; } get asFloat() { var _a, _b; return (_b = (_a = this.numericValue) === null || _a === void 0 ? void 0 : _a.asFloat) !== null && _b !== void 0 ? _b : null; } get asSmallInteger() { var _a, _b; return (_b = (_a = this.numericValue) === null || _a === void 0 ? void 0 : _a.asSmallInteger) !== null && _b !== void 0 ? _b : null; } get asRational() { var _a, _b; return (_b = (_a = this.numericValue) === null || _a === void 0 ? void 0 : _a.asRational) !== null && _b !== void 0 ? _b : [null, null]; } get sgn() { var _a, _b; return (_b = (_a = this.numericValue) === null || _a === void 0 ? void 0 : _a.sgn) !== null && _b !== void 0 ? _b : null; } get symbol() { return null; } get isMissing() { return !1; } get value() { return this; } set value(e) { throw Error(`Can't change the value of \\(${this.latex}\\)`); } get numericValue() { } isSubdomainOf(e) { } get domain() { return this.engine.domain("Nothing"); } set domain(e) { throw Error(`Can't change the domain of \\(${this.latex}\\)`); } get valueDomain() { return this.domain; } get string() { return null; } isLess(e) { } isLessEqual(e) { } isGreater(e) { } isGreaterEqual(e) { } get isZero() { } get isNotZero() { } get isPositive() { } get isNonNegative() { } get isNegative() { } get isNonPositive() { } get isInfinity() { } get isNaN() { } get isFinite() { } get isNumber() { } get isInteger() { } get isRational() { } get isAlgebraic() { return !1; } get isReal() { } get isExtendedReal() { } get isComplex() { } get isImaginary() { } get isExtendedComplex() { } get isOne() { } get isNegativeOne() { } get isEven() { } get isOdd() { } get isPrime() { } get isComposite() { } get canonical() { return this; } apply(e, t) { return this; } evaluate(e) { return this.simplify(e); } simplify(e) { return this; } N(e) { return this; } replace(e) { return null; } subs(e) { return this; } solve(e) { return null; } } function Ve(e, t) { if (!t.isInteger() || t.isNegative()) return e.DECIMAL_NAN; if (t.lessThan(10)) return e.decimal([1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800][t.toNumber()]); if (t.gt(Number.MAX_SAFE_INTEGER)) { let i = e.DECIMAL_ONE, n = e.DECIMAL_TWO; for (; n.lessThan(t);) i = i.mul(n), n = n.add(1); return i; } if (t.modulo(2).eq(1)) return t.times(Ve(e, t.minus(1))); let i = t.toNumber(), n = t, r = t; for (; i > 2;) i -= 2, n = n.add(i), r = r.mul(n); return r; } function Ce(e, t) { if (t.isNegative()) return e.DECIMAL_NAN; const i = e.cache("gamma-p-ln", (() => ["0.99999999999999709182", "57.156235665862923517", "-59.597960355475491248", "14.136097974741747174", "-0.49191381609762019978", "0.33994649984811888699e-4", "0.46523628927048575665e-4", "-0.98374475304879564677e-4", "0.15808870322491248884e-3", "-0.21026444172410488319e-3", "0.2174396181152126432e-3", "-0.16431810653676389022e-3", "0.84418223983852743293e-4", "-0.2619083840158140867e-4", "0.36899182659531622704e-5"].map((t => e.decimal(t))))); let n = i[0]; for (let e = i.length - 1; e > 0; --e) n = n.add(i[e].div(t.add(e))); const r = e.cache("gamma-g-ln", (() => e.decimal(607).div(128))), s = t.add(r).add(e.DECIMAL_HALF); return e.DECIMAL_NEGATIVE_ONE.acos().mul(e.DECIMAL_TWO).log().mul(e.DECIMAL_HALF).add(s.log().mul(t.add(e.DECIMAL_HALF)).minus(s).add(n.log()).minus(t.log())); } function De(e, t) { if (t.lessThan(e.DECIMAL_HALF)) { const i = e.DECIMAL_NEGATIVE_ONE.acos(); return i.div(i.mul(t).sin().mul(De(e, e.DECIMAL_ONE.sub(t)))); } if (t.greaterThan(100)) return Ce(e, t).exp(); t = t.sub(1); const i = e.cache("lanczos-7-c", (() => ["0.99999999999980993227684700473478", "676.520368121885098567009190444019", "-1259.13921672240287047156078755283", "771.3234287776530788486528258894", "-176.61502916214059906584551354", "12.507343278686904814458936853", "-0.13857109526572011689554707", "9.984369578019570859563e-6", "1.50563273514931155834e-7"].map(e.decimal))); let n = i[0]; for (let e = 1; e < 9; e++) n = n.add(i[e].div(t.add(e))); const r = t.add(7).add(e.DECIMAL_HALF); return e.DECIMAL_NEGATIVE_ONE.acos().times(e.DECIMAL_TWO).sqrt().mul(n.mul(r.neg().exp()).mul(r.pow(t.add(e.DECIMAL_HALF)))); } function Le(e) { return e.e < 308 && e.e > -306; } function Te(e, t) { var _a; const i = []; for (const n of e) n.ops && n.head === t ? i.push(...(_a = Te(n.ops, t)) !== null && _a !== void 0 ? _a : n.ops) : i.push(n); return i.length === e.length ? null : i; } function Re(e) { if ("Power" === e.head && e.op2.isLiteral) { const t = e.op2.asSmallInteger; return null !== t && t > 0 ? t : 1; } if ("Multiply" === e.head) { let t = 1; for (const i of e.ops) { const e = Re(i); e > 1 && (t += e); } return t; } return 1; } function ze(e) { if ("Power" === e.head && e.op2.isLiteral) { const t = e.op2.asSmallInteger; return null !== t && t > 0 ? t : 1; } if ("Multiply" === e.head) { let t = 1; for (const i of e.ops) t = Math.max(t, Re(i)); return t; } return 1; } function $e(e) { return e.symbol ? e.symbol : e.ops ? e.ops.map((e => $e(e))).filter((e => e.length > 0)).join('"') : ""; } function Ze(e) { return "string" == typeof e && e.startsWith("$") && e.endsWith("$"); } function je(e) { return "string" == typeof e && e.startsWith("$") && e.endsWith("$") ? e.slice(1, -1) : null; } function Ge(e) { var _a; if ("ImaginaryUnit" === e.symbol) return 1; const t = e.complexValue; if (t && 0 === t.re) return t.im; if ("Negate" === e.head) return -Ge(e.op1); let i = 0; if ("Multiply" === e.head && 2 === e.nops) { let t; "ImaginaryUnit" === e.op1.symbol ? t = e.op2 : "ImaginaryUnit" === e.op2.symbol && (t = e.op1), t && t.isLiteral && (i = (_a = t.asFloat) !== null && _a !== void 0 ? _a : 0); } return i; } function He(e) { var _a; if (e.symbol) return ((_a = e.symbolDefinition) === null || _a === void 0 ? void 0 : _a.constant) ? [] : [e.symbol]; if (!e.ops && !e.keys) return []; const t = []; if (e.ops) for (const i of e.ops) t.push(...He(i)); if (e.keys) for (const i of e.keys) t.push(...He(e.getKey(i))); return t; } function We(e) { return "decimal" === e.numericMode || "auto" === e.numericMode && e.precision > Math.floor(Se); } function Be(e) { return "auto" === e.numericMode || "complex" === e.numericMode; } function Qe(e) { let t = 0; for (let i = 0; i < e.length; i++) t = Math.imul(31, t) + e.charCodeAt(i) | 0; return Math.abs(t); } function Ue(e, t) { var _a, _b, _c, _d; if (e.isLiteral && null !== e.asFloat) return t.isLiteral && null !== t.asFloat ? e.asFloat - t.asFloat : -1; if (e.isLiteral && e.complexValue) return t.isLiteral && t.complexValue ? e.complexValue.re === t.complexValue.re ? Math.abs(e.complexValue.im) === Math.abs(t.complexValue.im) ? e.complexValue.im - t.complexValue.im : Math.abs(e.complexValue.im) - Math.abs(t.complexValue.im) : e.complexValue.re - t.complexValue.re : t.isLiteral && t.isNumber ? 1 : -1; if (e.symbol) return t.symbol ? e.symbol === t.symbol ? 0 : e.symbol > t.symbol ? 1 : -1 : t.isLiteral && t.isNumber ? 1 : -1; if (e.ops) { if (t.ops) { const i = (_b = (_a = e.functionDefinition) === null || _a === void 0 ? void 0 : _a.complexity) !== null && _b !== void 0 ? _b : 1e5, n = (_d = (_c = t.functionDefinition) === null || _c === void 0 ? void 0 : _c.complexity) !== null && _d !== void 0 ? _d : 1e5; return i === n ? "string" == typeof e.head && "string" == typeof t.head ? e.head === t.head ? Ke(e) - Ke(t) : e.head < t.head ? 1 : -1 : Ke(e) - Ke(t) : i - n; } return t.isLiteral && t.isNumber || t.symbol ? 1 : -1; } if (e.string) return t.string ? e.string.length !== t.string.length ? t.string.length - e.string.length : t.string < e.string ? -1 : e.string > t.string ? 1 : 0 : t.keys ? -1 : 1; if (e.keys && t.keys) { if (e.keysCount !== t.keysCount) return t.keysCount - e.keysCount; let i = 0, n = 0; for (const e of t.keys) i += t.getKey(e).complexity; for (const t of e.keys) n += e.getKey(t).complexity; return n - i; } return e.complexity - t.complexity; } function Ke(e) { return null !== e.keys ? 1 + e.keysCount : e.ops ? ("string" == typeof e.head ? 1 : Ke(e.head)) + [...e.ops].reduce(((e, t) => e + Ke(t)), 0) : 1; } function Ye(e) { var _a; const t = e.engine; let i = 1, n = 1; if ("Multiply" === e.head) { const r = []; for (const t of e.ops) if (t.isLiteral) { const [e, s] = t.asRational; null !== e && null !== s ? (i *= e, n *= s) : r.push(t); } else r.push(t); return [i, n] = ke([i, n]), i === n ? [[1, 1], e] : 0 === r.length ? [[i, n], t.ONE] : 1 === r.length ? [[i, n], r[0]] : [[i, n], t.mul(r)]; } if ("Divide" === e.head) { let [[i, n], r] = Ye(e.op1); const [[s, a], o] = Ye(e.op2), [l, u] = ke([i * a, s * n]); return r.isOne && o.isOne ? [[l, u], t.ONE] : o.isOne ? [[l, u], r] : [[l, u], t.fn("Divide", [r, o]).canonical]; } if ("Power" === e.head) { if (!e.op2.isLiteral) return [[1, 1], e]; let [[i, n], r] = Ye(e.op1); if (1 === i && 1 === n) return [[1, 1], e]; const s = e.op2, a = s.asSmallInteger; if (null !== a) { if (-1 === a) return [[n, i], t.inverse(r)]; if (Math.log10(Math.abs(i)) * Math.abs(a) < 15 && Math.log10(Math.abs(n)) * Math.abs(a) < 15) return a > 0 ? [[Math.pow(i, a), Math.pow(n, a)], t.power(r, s)] : [[Math.pow(n, -a), Math.pow(i, -a)], t.power(r, s)]; } const [o, l] = s.rationalValue; if (null !== o && null !== l && i > 0 && 1 === Math.abs(o)) { const [a, u] = Ee(i, l), [c, h] = Ee(n, l); return 1 === a && 1 === c ? [[1, 1], e] : [1 === o ? [a, c] : [c, a], t.power(t.mul([t.number([u, h]), r]), s)]; } return [[1, 1], e]; } if ("Negate" === e.head) { const [t, i] = Ye(e.op1); return [[-t[0], t[1]], i]; } if (e.isLiteral) { if (e.decimalValue) { if (e.decimalValue.isInteger() && Le(e.decimalValue)) return [[e.decimalValue.toNumber(), 1], t.ONE]; if ((_a = e.decimalValue) === null || _a === void 0 ? void 0 : _a.isNegative()) return [[-1, 1], t.number(e.decimalValue.neg())]; } if (null !== e.machineValue) { if (Number.isInteger(e.machineValue)) return [[e.machineValue, 1], t.ONE]; if (e.machineValue < 0) return [[-1, 1], t.number(-e.machineValue)]; } const [i, n] = e.rationalValue; if (null !== i && null !== n) return [[i, n], t.ONE]; if (null !== e.complexValue) { const i = e.complexValue; if (i.re < 0) return [[-1, 1], t.number(t.complex(-i.re, -i.im))]; } } return [[1, 1], e]; } function Je(e) { var _a; if ("Negate" === e.head) return [-1, e.op1]; const t = e.engine; if (null !== e.machineValue && e.machineValue < 0) return [-1, t.number(-e.machineValue)]; if ((_a = e.decimalValue) === null || _a === void 0 ? void 0 : _a.isNegative()) return [-1, t.number(e.decimalValue.neg())]; if (null !== e.complexValue) { const i = e.complexValue; if (i.re < 0) return [-1, t.number(t.complex(-i.re, -i.im))]; } const [i, n] = e.rationalValue; return null !== i && null !== n && i < 0 ? [-1, t.number([-i, n])] : [1, e]; } class Xe { constructor(e, t) { if (this._terms = [], this._literal = [1, 1], this._hasInfinity = !1, this._hasZero = !1, this.engine = e, t) for (const e of t) this.addTerm(e); } get isEmpty() { return !1 === this._hasInfinity && !1 === this._hasZero && this._literal[0] === this._literal[1] && 0 === this._terms.length; } addTerm(e) { if ("Nothing" === e.symbol) return; if (e.isLiteral) { if (e.isOne) return; if (e.isZero) return void (this._hasZero = !0); if (e.isNegativeOne) return void (this._literal[0] *= -1); if (e.isInfinity) return this._hasInfinity = !0, void (e.isNegative && (this._literal[0] *= -1)); } let [t, i] = Ye(e); if (this._literal = [this._literal[0] * t[0], this._literal[1] * t[1]], i.isLiteral && i.isOne) return; let n = [1, 1]; if ("Power" === i.head && i.op2.isLiteral) { const [e, t] = i.op2.asRational; null !== e && null !== t && (n = [e, t], i = i.op1); } let r = !1; if (1 === n[1] || i.isNonNegative) for (const e of this._terms) if (e.term.isSame(i)) { const [t, i] = e.exponent, [s, a] = n; e.exponent = [t * a + i * s, i * a], r = !0; break; } r || this._terms.push({ exponent: n, term: i }); } groupedByDegrees(e) { const t = this.engine, i = [], n = []; this._hasInfinity && n.push(t.POSITIVE_INFINITY), this._literal = ke(this._literal), 1 === this._literal[0] && 1 === this._literal[1] || ((e === null || e === void 0 ? void 0 : e.splitRational) ? (1 !== this._literal[0] && n.push(t.number(this._literal[0]).canonical), 1 !== this._literal[1] && i.push({ exponent: [-1, 1], terms: [t.number(this._literal[1]).canonical] })) : n.push(t.number(this._literal).canonical)), n.length > 0 && i.push({ exponent: [1, 1], terms: n }); for (const e of this._terms) { if (0 === e.exponent[0]) continue; let t = !1; for (const n of i) if (e.exponent[0] === n.exponent[0] && e.exponent[1] === n.exponent[1]) { n.terms.push(e.term), t = !0; break; } t || i.push({ exponent: ke(e.exponent), terms: [e.term] }); } return i; } terms() { return it(this.engine, this.groupedByDegrees()); } asNumeratorDenominator() { var _a, _b; const e = this.groupedByDegrees(), t = [], i = []; for (const n of e) n.exponent[0] >= 0 ? t.push(n) : i.push({ exponent: [-n.exponent[0], n.exponent[1]], terms: n.terms }); const n = this.engine; let r = it(n, t); r = (_a = Te(r, "Multiply")) !== null && _a !== void 0 ? _a : r; let s = n.ONE; 1 === r.length ? s = r[0] : r.length > 0 && (s = n._fn("Multiply", r)); let a = it(n, i); a = (_b = Te(a, "Multiply")) !== null && _b !== void 0 ? _b : a; let o = n.ONE; return 1 === a.length ? o = a[0] : a.length > 0 && (o = n._fn("Multiply", a)), [s, o]; } asExpression() { var _a; const e = this.engine; if (this._hasInfinity) { if (this._hasZero) return e.NAN; if (0 === this._terms.length) return this._literal[0] > 0 ? e.POSITIVE_INFINITY : e.NEGATIVE_INFINITY; } if (this._hasZero) return e.ZERO; if (0 === this._terms.length) return e.number(this._literal).canonical; let t = it(e, this.groupedByDegrees({ splitRational: !1 })); return this._hasInfinity && t.push(e.POSITIVE_INFINITY), t = (_a = Te(t, "Multiply")) !== null && _a !== void 0 ? _a : t, 0 === t.length ? e.ONE : 1 === t.length ? t[0] : this.engine._fn("Multiply", t); } asRationalExpression() { const [e, t] = this.asNumeratorDenominator(); return t.isOne ? e : t.isNegativeOne ? this.engine.negate(e) : this.engine._fn("Divide", [e, t]); } } function et(e) { const [t, i] = e; return t === i ? 0 : t > 0 && Number.isInteger(t / i) ? 1 : t > 0 ? 2 : Number.isInteger(t / i) ? 3 : 4; } function tt(e, t) { const i = et(e.exponent), n = et(t.exponent); return i !== n ? i - n : e.exponent[0] / e.exponent[1] - t.exponent[0] / t.exponent[1]; } function it(e, t) { var _a; const i = (t = t.sort(tt)).map((t => { var _a; const i = (_a = Te(t.terms, "Multiply")) !== null && _a !== void 0 ? _a : t.terms, n = i.length <= 1 ? i[0] : e._fn("Multiply", i.sort(Ue)); return t.exponent[0] === t.exponent[1] ? n : e.power(n, t.exponent); })); return (_a = Te(i, "Multiply")) !== null && _a !== void 0 ? _a : i; } function nt(e, t, i, n) { var _a, _b, _c, _d, _f, _g, _h; const r = e.jsonSerializationOptions.exclude; if (("Rational" === t || "Divide" === t) && ((_a = i[0]) === null || _a === void 0 ? void 0 : _a.isLiteral) && ((_b = i[1]) === null || _b === void 0 ? void 0 : _b.isLiteral) && 1 === ((_c = i[0]) === null || _c === void 0 ? void 0 : _c.asSmallInteger) && 2 === ((_d = i[1]) === null || _d === void 0 ? void 0 : _d.asSmallInteger) && !r.includes("Half")) return rt(e, "Half", { ...n, wikidata: "Q39373172" }); if ("Negate" === t && i[0].isLiteral) { if (null !== i[0].machineValue) return st(e, -i[0].machineValue); if (null !== i[0].decimalValue) return st(e, i[0].decimalValue.neg()); if (null !== i[0].complexValue) return st(e, i[0].complexValue.neg()); const [t, n] = i[0].rationalValue; if (null !== t && null !== n) return st(e, [-t, n]); } if ("Rational" === t && r.includes(t) && i.length > 1) return nt(e, "Divide", i, n); if ("Complex" === t && r.includes(t)) return nt(e, "Add", [i[0], e._fn("Multiply", [i[1], e.symbol("ImaginaryUnit")])], n); if ("Sqrt" === t && r.includes(t)) return nt(e, "Power", [i[0], r.includes("Half") ? e.number([1, 2]) : e.HALF], n); if ("Root" === t && r.includes(t) && i[1].isLiteral) { const t = i[1].asSmallInteger; if (2 === t) return nt(e, "Sqrt", [i[0]]); if (null !== t) return t < 0 ? nt(e, "Divide", [e.ONE, e._fn("Power", [i[0], e.number([1, -t])])], n) : nt(e, "Power", [i[0], e.number([1, -t])], n); } if ("Square" === t && r.includes(t)) return nt(e, "Power", [i[0], e.TWO], n); if ("Exp" === t && r.includes(t)) return nt(e, "Power", [e.symbol("ExponentialE"), i[0]], n); if ("Subtract" === t && r.includes(t)) return nt(e, "Add", [i[0], e._fn("Negate", [i[1]])], n); if ("Add" === t && 2 === i.length && !r.includes("Subtract")) { if (i[1].isLiteral) { const t = i[1].asSmallInteger; if (null !== t && t < 0) return nt(e, "Subtract", [i[0], e.number(-t)], n); } if ("Negate" === i[1].head) return nt(e, "Subtract", [i[0], i[1].op1], n); } if ("Tuple" === t) { if (1 === i.length && !r.includes("Single")) return nt(e, "Single", i, n); if (2 === i.length && !r.includes("Pair")) return nt(e, "Pair", i, n); if (3 === i.length && !r.includes("Triple")) return nt(e, "Triple", i, n); } const s = ["string" == typeof t ? t : t.json, ...i.map((e => e.json))], a = { ...n !== null && n !== void 0 ? n : {} }; return e.jsonSerializationOptions.metadata.includes("latex") ? a.latex = (_f = a.latex) !== null && _f !== void 0 ? _f : e.serialize({ fn: s }) : a.latex = "", e.jsonSerializationOptions.metadata.includes("wikidata") ? (n === null || n === void 0 ? void 0 : n.wikidata) || "string" != typeof t || (a.wikidata = (_h = (_g = e.getFunctionDefinition(t, i)) === null || _g === void 0 ? void 0 : _g.wikidata) !== null && _h !== void 0 ? _h : "") : a.wikidata = "", a.latex || a.wikidata || !e.jsonSerializationOptions.shorthands.includes("function") ? a.latex && a.wikidata ? { fn: s, latex: a.latex, wikidata: a.wikidata } : a.latex ? { fn: s, latex: a.latex } : a.wikidata ? { fn: s, wikidata: a.wikidata } : { fn: s } : s; } function rt(e, t, i) { var _a, _b, _c; return "Half" === t && e.jsonSerializationOptions.exclude.includes("Half") ? st(e, [1, 2], i) : (i = { ...i }, e.jsonSerializationOptions.metadata.includes("latex") ? (i.latex = (_a = i.latex) !== null && _a !== void 0 ? _a : e.serialize({ sym: t }), i.latex === t && (i.latex = ""), i.latex = i.latex) : i.latex = "", e.jsonSerializationOptions.metadata.includes("wikidata") ? i.wikidata || (i.wikidata = (_c = (_b = e.getSymbolDefinition(t)) === null || _b === void 0 ? void 0 : _b.wikidata) !== null && _c !== void 0 ? _c : "") : i.wikidata = "", t = t, i.latex || i.wikidata || !e.jsonSerializationOptions.shorthands.includes("symbol") ? i.latex && i.wikidata ? { sym: t, latex: i.latex, wikidata: i.wikidata } : i.latex ? { sym: t, latex: i.latex } : i.wikidata ? { sym: t, wikidata: i.wikidata } : { sym: t } : t); } function st(e, t, i) { var _a, _b, _c; i = { ...i }, e.jsonSerializationOptions.metadata.includes("latex") || (i.latex = ""); const n = !i.latex && !e.jsonSerializationOptions.metadata.includes("latex") && e.jsonSerializationOptions.shorthands.includes("number"); let r = ""; if (t instanceof u) { if (t.isNaN() ? r = "NaN" : t.isFinite() || (r = t.isPositive() ? "+Infinity" : "-Infinity"), !r) { if (n && Le(t) && t.precision() < 15) return t.toNumber(); r = t.isInteger() && t.e < t.precision() + 4 ? t.toFixed(0) : at(e, t.toJSON()); } return e.jsonSerializationOptions.metadata.includes("latex") && (i.latex = (_a = i.latex) !== null && _a !== void 0 ? _a : e.serialize({ num: r })), i.latex ? { num: r, latex: i.latex } : { num: r }; } if (t instanceof c.exports.Complex) return t.isInfinite() ? rt(e, "ComplexInfinity", i) : t.isNaN() ? (r = "NaN", e.jsonSerializationOptions.metadata.includes("latex") && (i.latex = (_b = i.latex) !== null && _b !== void 0 ? _b : e.serialize({ num: r })), i.latex ? { num: r, latex: i.latex } : { num: r }) : nt(e, "Complex", [e.number(t.re), e.number(t.im)], { ...i, wikidata: "Q11567" }); if (Array.isArray(t)) return i.latex || i.wikidata || e.jsonSerializationOptions.metadata.includes("latex") || !e.jsonSerializationOptions.shorthands.includes("function") || !e.jsonSerializationOptions.shorthands.includes("number") ? nt(e, "Rational", [e.number(t[0]), e.number(t[1])], i) : ["Rational", t[0], t[1]]; if (Number.isNaN(t) && (r = "NaN"), !Number.isFinite(t) && t > 0 && (r = "+Infinity"), !Number.isFinite(t) && t < 0 && (r = "-Infinity"), !r) { if (n) return t; r = at(e, t.toString()); } return e.jsonSerializationOptions.metadata.includes("latex") && (i.latex = (_c = i.latex) !== null && _c !== void 0 ? _c : e.serialize({ num: r })), i.latex ? { num: r, latex: i.latex } : { num: r }; } function at(e, t) { var _a; if (!e.jsonSerializationOptions.repeatingDecimal) return t; let [i, n, r, s] = (_a = t.match(/^(.*)\.([0-9]+)([e|E][-+]?[0-9]+)?$/)) !== null && _a !== void 0 ? _a : []; if (!r) return t; const a = r[r.length - 1]; r = r.slice(0, -1); let o = ""; for (let e = 0; e < r.length - 16; e++) { o = r.substring(0, e); for (let i = 0; i <= 16; i++) { const l = r.substring(e, e + i + 1), u = Math.floor((r.length - o.length) / l.length); if (u > 1 && (o + l.repeat(u + 1)).startsWith(r)) return "0" === l ? "0" === a ? n + "." + o + (s !== null && s !== void 0 ? s : "") : t : n + "." + o + "(" + l + ")" + (s !== null && s !== void 0 ? s : ""); } } return t; } class ot extends Pe { constructor(e, t, i) { super(e, i), this._value = new Map, this._isCanonical = !1; for (const i of Object.keys(t)) this._value.set(i, e.box(t[i])); e._register(this); } _purge() { for (const [e, t] of this._value) t._purge(); } get hash() { let e = Qe("Dictionary"); for (const [t, i] of this._value) e ^= Qe(t) ^ i.hash; return e; } get complexity() { return 97; } get head() { return "Dictionary"; } get isPure() { return !1; } getKey(e) { return this._value.get(e); } hasKey(e) { return this._value.has(e); } get keys() { return this._value.keys(); } get keysCount() { return this._value.size; } has(e) { for (const [t, i] of this._value) if (i.has(e)) return !0; return !1; } get domain() { const e = ["Dictionary"]; for (const [t, i] of this._value) e.push(["Tuple", t, i.domain]); return this.engine.domain(e); } get json() { if (this.engine.jsonSerializationOptions.shorthands.includes("dictionary")) { const e = {}; for (const t of this._value.keys()) e[t] = this._value.get(t).json; return { dict: e }; } const e = []; for (const t of this._value.keys()) e.push(this.engine._fn("KeyValuePair", [this.engine.string(t), this._value.get(t)])); return nt(this.engine, "Dictionary", e, { latex: this._latex }); } isSame(e) { if (this === e) return !0; if (!(e instanceof ot)) return !1; if (this._value.size !== e._value.size) return !1; for (const [t, i] of this._value) { const n = e.getKey(t); if (!n || !i.isSame(n)) return !1; } return !0; } match(e, t) { if (!(e instanceof ot)) return null; if (this._value.size !== e._value.size) return null; let i = {}; for (const [t, n] of this._value) { const r = e.getKey(t); if (!r) return null; const s = n.match(r); if (null === s) return null; i = { ...i, ...s }; } return i; } isEqual(e) { if (this === e) return !0; if (!(e instanceof ot)) return !1; if (!e.keys || this._value.size !== e._value.size) return !1; for (const [t, i] of this._value) { const n = e.getKey(t); if (!n || !i.isEqual(n)) return !1; } return !0; } apply(e, t) { const i = {}; for (const t of this.keys) i[t] = this.engine.box(e(this.getKey(t))); return t ? this.engine.fn(t, [{ dict: i }]) : new ot(this.engine, i); } evaluate(e) { return this.apply((t => { var _a; return (_a = t.evaluate(e)) !== null && _a !== void 0 ? _a : t; })); } get isCanonical() { return this._isCanonical; } set isCanonical(e) { this._isCanonical = e; } get canonical() { if (this.isCanonical) return this; const e = this.apply((e => e.canonical)); return e.isCanonical = !0, e; } simplify(e) { var _a; return ((_a = e === null || e === void 0 ? void 0 : e.recursive) !== null && _a !== void 0 ? _a : 1) ? this.apply((t => { var _a; return (_a = t.simplify(e)) !== null && _a !== void 0 ? _a : t; })) : this; } N(e) { return this.apply((t => t.N(e))); } replace(e, t) { let i = 0; const n = {}; for (const r of this.keys) { const s = this.getKey(r), a = s.replace(e, t); null !== a && (i += 1), n[r] = a !== null && a !== void 0 ? a : s; } return 0 === i ? null : new ot(this.engine, n); } subs(e) { const t = {}; for (const i of this.keys) t[i] = this.getKey(i).subs(e); return new ot(this.engine, t); } } function lt(e, t) { var _a; const i = new Set; for (const [n, r, s] of t) { let t = e.pattern(n); const a = {}; for (const i of He(t)) a[i] = e.symbol("_" + i); let o; t = t.subs(a); const l = je(s === null || s === void 0 ? void 0 : s.condition); if (l) { const t = e.parse(l).subs(a); o = e => { var _a; return "True" === ((_a = t.subs(e).value) === null || _a === void 0 ? void 0 : _a.symbol); }; } else o = s === null || s === void 0 ? void 0 : s.condition; const u = Ze(r) ? e.parse(r) : e.box(r); u && i.add([t, u.subs(a), (_a = s === null || s === void 0 ? void 0 : s.priority) !== null && _a !== void 0 ? _a : 0, o]); } return i; } function ut([e, t, i, n], r, s) { const a = e.match(r, s); return null === a ? null : "function" != typeof n || n(a) ? t.subs(a) : null; } function ct(e, t, i) { var _a, _b; const n = (_a = i === null || i === void 0 ? void 0 : i.iterationLimit) !== null && _a !== void 0 ? _a : 1; let r = 0; const s = (_b = i === null || i === void 0 ? void 0 : i.once) !== null && _b !== void 0 ? _b : !1; let a = !1, o = !1; try { for (; !a && r < n;) { a = !0; for (const n of t) { const t = ut(n, e, i); if (null !== t && t !== e) { if (s) return t; a = !1, o = !0, e = t; } } r += 1; } } catch (e) { } return o ? e : null; } const ht = []; function mt(e, t) { if (null == t) return e; if (e === t) return e; const i = e.engine, n = i.box(t); return i.costFunction(n) <= 1.7 * i.costFunction(e) ? n : e; } class ft extends Pe { constructor(e, t, i, n) { var _a; super(e, n), this._head = "string" == typeof t ? t : (_a = t.symbol) !== null && _a !== void 0 ? _a : t, this._ops = i, "string" == typeof this._head && (this._def = e.getFunctionDefinition(this._head, i)), this._isCanonical = !1, e._register(this); } get hash() { if (void 0 !== this._hash) return this._hash; let e = 0; for (const t of this._ops) e = e << 1 ^ t.hash | 0; return e = "string" == typeof this._head ? e ^ Qe(this._head) | 0 : e ^ this._head.hash | 0, this._hash = e, e; } _purge() { "string" != typeof this._head && this._head._purge(); for (const e of this._ops) e._purge(); this._value && this._value._purge(), this._numericValue && this._numericValue._purge(); } get wikidata() { var _a, _b, _c; return (_c = (_a = this._wikidata) !== null && _a !== void 0 ? _a : (_b = this._def) === null || _b === void 0 ? void 0 : _b.wikidata) !== null && _c !== void 0 ? _c : ""; } get description() { return this._def && this._def.description ? "string" == typeof this._def.description ? [this._def.description] : this._def.description : []; } get url() { var _a, _b; return (_b = (_a = this._def) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : ""; } get complexity() { var _a, _b; return (_b = (_a = this._def) === null || _a === void 0 ? void 0 : _a.complexity) !== null && _b !== void 0 ? _b : 1e5; } get head() { return this._head; } get value() { if (this.isPure) return this._value || (this._value = this.evaluate()), this._value; } get numericValue() { if (!this.isPure) return; if (this._numericValue) return this._numericValue; const e = this.N(); return this._numericValue = e.isLiteral ? e : void 0, this._numericValue; } get isPure() { var _a; if (void 0 !== this._isPure) return this._isPure; let e; return void 0 !== ((_a = this._def) === null || _a === void 0 ? void 0 : _a.pure) && (e = this._def.pure), !1 !== e && (e = this._ops.every((e => e.isPure))), this._isPure = e, e; } get isLiteral() { return !1; } get ops() { return this._ops; } get nops() { return this._ops.length; } get op1() { var _a; return (_a = this._ops[0]) !== null && _a !== void 0 ? _a : this.engine.symbol("Missing"); } get op2() { var _a; return (_a = this._ops[1]) !== null && _a !== void 0 ? _a : this.engine.symbol("Missing"); } get op3() { var _a; return (_a = this._ops[2]) !== null && _a !== void 0 ? _a : this.engine.symbol("Missing"); } get functionDefinition() { return this._def; } _repairDefinition() { if ("string" == typeof this._head) { if ("_" === this._head[0]) return; this._def = this.engine.getFunctionDefinition(this._head, this._ops), this._def && (this._head = this._def.name); } } get domain() { var _a; if ((_a = this._def) === null || _a === void 0 ? void 0 : _a.domain) return "function" == typeof this._def.domain ? this.engine.domain(this._def.domain(this.engine, this._ops)) : this.engine.domain(this._def.domain); const e = [this.head]; for (const t of this._ops) e.push(t.domain); return e.push(this.engine.domain("Anything")), this.engine.domain(e); } get valueDomain() { var _a; return (_a = this.domain.codomain) !== null && _a !== void 0 ? _a : this.engine.domain("Nothing"); } isLess(e) { if (e.isZero) { const e = this.sgn; if (null === e) return !1; if (void 0 !== e) return e < 0; } } isLessEqual(e) { if (e.isZero) { const e = this.sgn; if (null === e) return !1; if (void 0 !== e) return e <= 0; } } isGreater(e) { if (e.isZero) { const e = this.sgn; if (null === e) return !1; if (void 0 !== e) return e > 0; } } isGreaterEqual(e) { if (e.isZero) { const e = this.sgn; if (null === e) return !1; if (void 0 !== e) return e >= 0; } } get isZero() { const e = this.sgn; return null !== e && ("number" == typeof e ? 0 === e : void 0); } get isNotZero() { const e = this.sgn; return null !== e && ("number" == typeof e ? 0 !== e : void 0); } get isOne() { } get isNegativeOne() { } get isPositive() { const e = this.sgn; return null !== e && ("number" == typeof e ? 0 !== e : void 0); } get isNonPositive() { const e = this.sgn; return null !== e && ("number" == typeof e ? e <= 0 : void 0); } get isNegative() { const e = this.sgn; return null !== e && ("number" == typeof e ? e < 0 : void 0); } get isNonNegative() { const e = this.sgn; return null !== e && ("number" == typeof e ? e >= 0 : void 0); } get isNumber() { return this.valueDomain.isSubdomainOf("Number"); } get isInteger() { return this.valueDomain.isSubdomainOf("Integer"); } get isRational() { return this.valueDomain.isSubdomainOf("RationalNumber"); } get isAlgebraic() { return this.valueDomain.isSubdomainOf("AlgebraicNumber"); } get isReal() { return this.valueDomain.isSubdomainOf("RealNumber"); } get isExtendedReal() { return this.valueDomain.isSubdomainOf("ExtendedRealNumber"); } get isComplex() { return this.valueDomain.isSubdomainOf("ComplexNumber"); } get isImaginary() { return this.valueDomain.isSubdomainOf("ImaginaryNumber"); } get json() { return this._isCanonical ? function (e, t, i, n) { var _a, _b; const r = e.jsonSerializationOptions.exclude; if ("Add" === t && 2 === i.length && !r.includes("Subtract")) { const t = i[0].asSmallInteger; if (null !== t && t < 0) return nt(e, "Subtract", [i[1], e.number(-t)], n); if ("Negate" === i[0].head) return nt(e, "Subtract", [i[1], i[0].op1], n); } if ("Divide" === t && e.jsonSerializationOptions.exclude.includes("Divide")) return nt(e, "Multiply", [i[0], e._fn("Power", [i[1], e.NEGATIVE_ONE])], n); if ("Multiply" === t && !e.jsonSerializationOptions.exclude.includes("Divide")) { const t = new Xe(e, i).asRationalExpression(); if ("Divide" === t.head) return nt(e, t.head, t.ops, n); } if ("Power" === t) { if (!r.includes("Exp") && "ExponentialE" === ((_a = i[0]) === null || _a === void 0 ? void 0 : _a.symbol)) return nt(e, "Exp", [i[1]], n); if ((_b = i[1]) === null || _b === void 0 ? void 0 : _b.isLiteral) { const t = i[1].asSmallInteger; if (!r.includes("Square") && 2 === t) return nt(e, "Square", [i[0]], n); if (!e.jsonSerializationOptions.exclude.includes("Divide")) { if (-1 === t) return nt(e, "Divide", [e.ONE, i[0]], n); if (null !== t && t < 0) return nt(e, "Divide", [e.ONE, e.power(i[0], -t)], n); } const [s, a] = i[1].rationalValue; if (1 === s) { if (!r.includes("Sqrt") && 2 === a) return nt(e, "Sqrt", [i[0]], n); if (!r.includes("Root")) return nt(e, "Root", [i[0], e.number(a)], n); } if (-1 === s) { if (!r.includes("Sqrt") && 2 === a) return nt(e, "Divide", [e.ONE, e._fn("Sqrt", [i[0]])], n); if (!r.includes("Root")) return nt(e, "Divide", [e.ONE, e._fn("Root", [i[0], e.number(a)])], n); } } } return nt(e, t, i, n); }(this.engine, this._head, this._ops, { latex: this._latex, wikidata: this._wikidata }) : nt(this.engine, this._head, this._ops, { latex: this._latex, wikidata: this._wikidata }); } has(e) { if ("string" == typeof this._head) if ("string" == typeof e) { if (this._head === e) return !0; } else if (e.includes(this._head)) return !0; for (const t of this._ops) if (t.has(e)) return !0; return !1; } isSame(e) { if (this === e) return !0; if (!(e instanceof ft)) return !1; if (this.nops !== e.nops) return !1; if ("string" == typeof this.head) { if (this.head !== e.head) return !1; } else { if ("string" == typeof e.head) return !1; if (!e.head || !this.head.isSame(e.head)) return !1; } const t = this._ops, i = e._ops; for (let e = 0; e < t.length; e++) if (!t[e].isSame(i[e])) return !1; return !0; } match(e, t) { if (!(e instanceof ft)) return null; let i = {}; if ("string" == typeof this.head) { if (this.head !== e.head) return null; } else { if ("string" == typeof e.head) return null; { if (!e.head) return null; const n = this.head.match(e.head, t); if (null === n) return null; i = { ...i, ...n }; } } const n = this._ops, r = e._ops; for (let e = 0; e < n.length; e++) { const s = n[e].match(r[e], t); if (null === s) return null; i = { ...i, ...s }; } return i; } isEqual(e) { if (!this.isCanonical) return this.canonical.isEqual(e); if ((e = e.canonical).isNumber && this.isNumber) { const t = this.engine, i = t.add([this, t.negate(e)]).N(); return !!i.isZero || null !== i.asFloat && 0 === t.chop(i.asFloat) || this.evaluate().isSame(e.evaluate()); } return this.domain.isRelationalOperator && e.domain.isRelationalOperator ? this.evaluate().isSame(e.evaluate()) : this.isSame(e); } get sgn() { var _a, _b, _c, _d, _f, _g, _h, _j; const e = this.head; if ("Negate" === e) { const e = (_a = this._ops[0]) === null || _a === void 0 ? void 0 : _a.sgn; if (void 0 === e) return; return null === e ? null : 0 === e ? 0 : e > 0 ? -1 : 1; } if ("Multiply" === e) { const e = this._ops.reduce(((e, t) => { var _a; return e * ((_a = t.sgn) !== null && _a !== void 0 ? _a : NaN); }), 1); return isNaN(e) ? null : e > 0 ? 1 : e < 0 ? -1 : 0; } if ("Add" === e) { let e = 0, t = 0, i = 0; const n = this._ops.length; for (const n of this._ops) { const r = n.sgn; if (null == r) break; 0 === r && (i += 1), r > 0 && (e += 1), r < 0 && (t += 1); } return i === n ? 0 : e === n ? 1 : t === n ? -1 : null; } if ("Divide" === e) { const e = (_b = this._ops[0]) === null || _b === void 0 ? void 0 : _b.sgn, t = (_c = this._ops[1]) === null || _c === void 0 ? void 0 : _c.sgn; return null === e || null === t || void 0 === e || void 0 === t ? null : 0 === e ? 0 : e > 0 && t > 0 || e < 0 && t < 0 ? 1 : -1; } return "Square" === e ? ((_d = this._ops[0]) === null || _d === void 0 ? void 0 : _d.isImaginary) ? -1 : ((_f = this._ops[0]) === null || _f === void 0 ? void 0 : _f.isZero) ? 0 : 1 : "Abs" === e ? ((_g = this._ops[0]) === null || _g === void 0 ? void 0 : _g.isZero) ? 0 : 1 : "Sqrt" === e ? ((_h = this._ops[0]) === null || _h === void 0 ? void 0 : _h.isZero) ? 0 : ((_j = this._ops[0]) === null || _j === void 0 ? void 0 : _j.isImaginary) ? null : 1 : null; } *map(e) { let t = 0; for (; t < this._ops.length;) yield e(this._ops[t++]); } get isCanonical() { return this._isCanonical; } set isCanonical(e) { this._isCanonical = e; } get canonical() { var _a, _b, _c, _d, _f, _g, _h; if (this.isCanonical) return this; let e = ((_a = this._def) === null || _a === void 0 ? void 0 : _a.associative) ? (_b = Te(this._ops, this._def.name)) !== null && _b !== void 0 ? _b : this._ops : this._ops; if (e = pt(e, (_d = (_c = this._def) === null || _c === void 0 ? void 0 : _c.hold) !== null && _d !== void 0 ? _d : "none", (e => e.canonical)), ((_f = this._def) === null || _f === void 0 ? void 0 : _f.associative) && (e = (_g = Te(e, this._def.name)) !== null && _g !== void 0 ? _g : e), (_h = this._def) === null || _h === void 0 ? void 0 : _h.canonical) return this._def.canonical(this.engine, e); if (!this._def) return this.engine._fn(this._head, e); if (1 === e.length && e[0].head === this._head) if (this._def.idempotent) e = e[0].ops; else if (this._def.involution) return e[0].op1; return e.length > 1 && !0 === this._def.commutative && (e = e.sort(Ue)), this.engine._fn(this._head, e); } apply(e, t) { const i = t !== null && t !== void 0 ? t : this.head; let n = !1; const r = []; for (const t of this._ops) { const i = e(t); t !== i && (n = !0), r.push(this.engine.box(i)); } return n || this.head !== i ? this.engine.fn(i, r) : this; } simplify(e) { var _a, _b, _c, _d; if (!this.isCanonical) return this.canonical.simplify(e); const t = this._def; let i, n = this._ops; if (t ? (t.associative && (n = (_a = Te(n, t.name)) !== null && _a !== void 0 ? _a : n), n = pt(n, t.hold, (t => t.simplify(e))), t.associative && (n = (_b = Te(n, t.name)) !== null && _b !== void 0 ? _b : n)) : n = pt(this._ops, "none", (t => t.simplify(e))), "string" != typeof this._head) return gt(this._head, n).simplify(e); t && (t.simplify ? i = t.simplify(this.engine, n) : t.inert && (i = (_c = n[0]) !== null && _c !== void 0 ? _c : this.engine.symbol("Missing"))), i || (i = this.engine.fn(this._head, n).canonical); const r = (_d = e === null || e === void 0 ? void 0 : e.rules) !== null && _d !== void 0 ? _d : this.engine.cache("standard-simplification-rules", (() => lt(this.engine, ht)), (e => { for (const [t, i, n, r] of e) t._purge(), i._purge(); return e; })); let s = 0, a = !1; do { const e = i.replace(r); null !== e ? (i = mt(e, i), i === e && (a = !0)) : a = !0, s += 1; } while (!a && s < this.engine.iterationLimit); return i; } evaluate(e) { var _a, _b, _c, _d; if (!this.isCanonical) return this.canonical.evaluate(e); const t = this._def; let i; return t ? (i = pt(t.associative ? (_a = Te(this._ops, t.name)) !== null && _a !== void 0 ? _a : this._ops : this._ops, t.hold, (t => t.evaluate(e))), t.associative && (i = (_b = Te(i, t.name)) !== null && _b !== void 0 ? _b : i)) : i = pt(this._ops, "none", (t => t.evaluate(e))), "string" != typeof this._head ? gt(this._head, i).evaluate(e) : t ? void 0 === t.evaluate ? t.inert ? (_c = i[0]) !== null && _c !== void 0 ? _c : this.engine.symbol("Missing") : this.engine.fn(this._head, i).canonical : "function" != typeof t.evaluate ? gt(t.evaluate, i).canonical : (_d = t.evaluate(this.engine, i)) !== null && _d !== void 0 ? _d : this.engine.fn(this._head, i).canonical : this.engine.fn(this._head, i).canonical; } N(e) { var _a, _b, _c, _d; if (!this.isCanonical) return this.canonical.N(e); const t = this._def; let i; if (t ? (i = pt(t.associative ? (_a = Te(this._ops, t.name)) !== null && _a !== void 0 ? _a : this._ops : this._ops, t.hold, (t => t.N(e))), t.associative && (i = (_b = Te(i, t.name)) !== null && _b !== void 0 ? _b : i)) : i = pt(this._ops, "none", (t => t.N(e))), "string" != typeof this._head) return gt(this._head, i).N(e); if (!t) return this.engine.fn(this._head, i).canonical; const n = (_d = (_c = t.N) === null || _c === void 0 ? void 0 : _c.call(t, this.engine, i)) !== null && _d !== void 0 ? _d : this.engine.fn(this._head, i).evaluate(); if (n.isLiteral) { if (!Be(this.engine) && n.complexValue) return this.engine.NAN; if (!We(this.engine) && n.decimalValue) return this.engine.number(n.decimalValue.toNumber()); } return n; } solve(e) { return null; } replace(e, t) { return ct(this, e, t); } subs(e) { return this.engine.fn(this._head, this._ops.map((t => t.subs(e)))).canonical; } } function gt(e, t) { const i = { __: e.engine.tuple(t), "_#": e.engine.number(t.length) }; let n = 1; for (const e of t) i["_" + n++] = e; return i._ = i._1, e.subs(i); } function pt(e, t, i) { if (0 === e.length) return []; const n = []; for (let r = 0; r < e.length; r++) if ("Nothing" !== e[r].symbol) if ("Hold" === e[r].head) n.push(e[r].op1); else if ("ReleaseHold" === e[r].head) { const t = i(e[r].op1); null !== t && "Nothing" !== t.symbol && n.push(t); } else if (dt(e, t, r)) { const t = i(e[r]); null !== t && "Nothing" !== t.symbol && n.push(t); } else n.push(e[r]); return n; } function dt(e, t, i) { return "all" !== t && ("none" === t || ("first" === t ? 0 !== i : "rest" === t ? 0 === i : "last" === t ? i !== e.length - 1 : "most" === t && i === e.length - 1)); } function vt(e) { if ("number" == typeof e && !isNaN(e)) return isFinite(e) ? 0 === e ? "NonNegativeInteger" : Number.isInteger(e) ? e > 0 ? "PositiveInteger" : e < 0 ? "NegativeInteger" : "Integer" : e > 0 ? "PositiveNumber" : e < 0 ? "NegativeNumber" : "RealNumber" : "ExtendedRealNumber"; if (e instanceof l.exports.Decimal) return e.isNaN() ? "Number" : e.isFinite() ? e.isZero() ? "NonNegativeInteger" : e.isInteger() ? e.gt(0) ? "PositiveInteger" : e.lt(0) ? "NegativeInteger" : "Integer" : e.gt(0) ? "PositiveNumber" : e.lt(0) ? "NegativeNumber" : "RealNumber" : "ExtendedRealNumber"; if (e instanceof c.exports.Complex) { const t = e; return 0 === t.im ? vt(t.re) : 0 === t.re && 0 !== t.im ? "ImaginaryNumber" : "ComplexNumber"; } if (Array.isArray(e)) { const [t, i] = ke(e); if (!Number.isNaN(t) && !Number.isNaN(i)) return 1 !== i ? "RationalNumber" : vt(t); } return "Number"; } function bt(e) { if (!Number.isInteger(e) || !Number.isFinite(e) || Number.isNaN(e) || e <= 1) return !1; if (e <= 7919) return we.has(e); for (const t of we) if (e % t == 0) return !1; return e >= 0x3ffffffffffe5 ? !!function (e, t) { let i = 0, n = e - 1; for (; n % 2 == 0;) n /= 2, ++i; e: do { let t = Math.pow(2 + Math.floor(Math.random() * (e - 3)), n) % e; if (1 !== t && t !== e - 1) { for (let n = i - 1; n--;) { if (t = t * t % e, 1 === t) return !1; if (t === e - 1) continue e; } return !1; } } while (--t); return !0; }(e, 30) && void 0 : e === function (e) { if (1 === e) return 1; if (e % 2 == 0) return 2; if (e % 3 == 0) return 3; if (e % 5 == 0) return 5; const t = Math.floor(Math.sqrt(e)); let i = 7; for (; i <= t;) { if (e % i == 0) return i; if (e % (i + 4) == 0) return i + 4; if (e % (i + 6) == 0) return i + 6; if (e % (i + 10) == 0) return i + 10; if (e % (i + 12) == 0) return i + 12; if (e % (i + 16) == 0) return i + 16; if (e % (i + 22) == 0) return i + 22; if (e % (i + 24) == 0) return i + 24; i += 30; } return e; }(e); } class yt extends Pe { constructor(e, t, i) { if (super(e, i), this._isCanonical = !0, t instanceof c.exports.Complex) Number.isNaN(t.re) || Number.isNaN(t.im) ? this._value = NaN : 0 === e.chop(t.im) ? this._value = t.re : this._value = Be(e) ? t : NaN; else if (Array.isArray(t)) { let [e, i] = t; i < 0 && ([e, i] = [-e, -i]), 1 === i ? this._value = e : 0 === e ? this._value = 0 === i ? NaN : e : (this._value = [e, i], this._isCanonical = 1 === Ie(e, i)); } else t instanceof l.exports.Decimal ? this._value = We(e) ? t : t.toNumber() : "number" == typeof t ? this._value = t : We(e) ? this._value = e.decimal(t) : "string" == typeof t && (this._value = Number.parseFloat(t)); "number" == typeof this._value ? Number.isInteger(this._value) ? this._head = "Integer" : this._head = "Number" : this._value instanceof c.exports.Complex ? this._head = "ComplexNumber" : Array.isArray(this._value) ? this._head = "RationalNumber" : this._value instanceof l.exports.Decimal ? this._value.isInteger() ? this._head = "Integer" : this._head = "RealNumber" : this._head = "Number", e._register(this); } get hash() { if (void 0 !== this._hash) return this._hash; let e = 0; return e = "number" == typeof this._value ? Qe(this._value.toString()) : this._value instanceof c.exports.Complex ? Qe(this._value.re.toString() + " +i " + this._value.im.toString()) : this._value instanceof l.exports.Decimal ? Qe(this._value.toString()) : Qe(this._value[0].toString() + " / " + this._value[1].toString()), this._hash = e, e; } get head() { return this._head; } get isPure() { return !0; } get isLiteral() { return !0; } get isCanonical() { return this._isCanonical; } set isCanonical(e) { this._isCanonical = e; } get numericValue() { if (!Array.isArray(this._value)) return this; const [e, t] = this._value, i = this.engine; return We(i) ? new yt(i, i.decimal(e).div(t)) : new yt(i, e / t); } get machineValue() { return "number" == typeof this._value ? this._value : null; } get decimalValue() { return this._value instanceof l.exports.Decimal ? this._value : null; } get complexValue() { return this._value instanceof c.exports.Complex ? this._value : null; } get rationalValue() { return Array.isArray(this._value) ? this._value : [null, null]; } get asFloat() { if ("number" == typeof this._value) return this._value; if (this._value instanceof l.exports.Decimal) { if (this._value.isNaN()) return NaN; if (!this._value.isFinite()) return this._value.isPositive() ? 1 / 0 : -1 / 0; if (Le(this._value)) return this._value.toNumber(); } return Array.isArray(this._value) ? this._value[0] / this._value[1] : null; } get asSmallInteger() { if ("number" == typeof this._value) return Number.isInteger(this._value) && this._value >= -1e6 && this._value <= 1e6 ? this._value : null; if (this._value instanceof l.exports.Decimal) return this._value.isInteger() && this._value.gte(-1e6) && this._value.lte(1e6) ? this._value.toNumber() : null; if (Array.isArray(this._value)) { const e = this._value[0] / this._value[1]; return Number.isInteger(e) && e >= -1e6 && e <= 1e6 ? e : null; } return 0 === this.engine.chop(this._value.im) && Number.isInteger(this._value.re) && this._value.re >= -1e6 && this._value.re <= 1e6 ? this._value.re : null; } get asRational() { const [e, t] = this.rationalValue; if (null !== e && null !== t) return [e, t]; const i = this.asSmallInteger; return null !== i ? [i, 1] : [null, null]; } get domain() { return void 0 === this._domain && (this._domain = this.engine.domain(vt(this._value))), this._domain; } get json() { return st(this.engine, this._value, { latex: this._latex }); } get sgn() { if (this.isZero) return 0; if (this._value instanceof c.exports.Complex) return null; if ("number" == typeof this._value) return this._value < 0 ? -1 : this._value > 0 ? 1 : null; if (this._value instanceof l.exports.Decimal) return this._value.isNegative() ? -1 : this._value.isPositive() ? 1 : null; if (Array.isArray(this._value)) { const [e, t] = this._value; return 0 === e && 0 !== t ? 0 : e < 0 ? -1 : e > 0 ? 1 : null; } return null; } isSame(e) { if (this === e) return !0; if (!(e instanceof yt)) return !1; if (Array.isArray(this._value)) { if (!Array.isArray(e._value)) return !1; const [t, i] = e._value; return this._value[0] === t && this._value[1] === i; } return this._value instanceof l.exports.Decimal ? e._value instanceof l.exports.Decimal && this._value.eq(e._value) : this._value instanceof c.exports.Complex ? e._value instanceof c.exports.Complex && this._value.equals(e._value) : "number" == typeof this._value && "number" == typeof e._value && this._value === e._value; } isEqual(e) { var _a, _b; if (this === e) return !0; const t = e.numericValue; if (void 0 === t) return !1; if (!(t instanceof yt)) return !1; if (Array.isArray(this._value)) { const e = t.asFloat; return null !== e && 0 === this.engine.chop(this._value[0] / this._value[1] - e); } if (this._value instanceof l.exports.Decimal) return 0 === this.engine.chop(this._value.sub((_b = (_a = t.decimalValue) !== null && _a !== void 0 ? _a : t.asFloat) !== null && _b !== void 0 ? _b : NaN)); if (this._value instanceof c.exports.Complex) { if (t instanceof c.exports.Complex) return 0 === this.engine.chop(t.re - this._value.re) && 0 === this.engine.chop(t.im - this._value.im); if (0 !== this._value.im) return !1; } const i = this.asFloat, n = t.asFloat; return null !== i && null !== n && 0 === this.engine.chop(n - i); } match(e, t) { var _a; return this.isEqualWithTolerance(e, (_a = t === null || t === void 0 ? void 0 : t.numericTolerance) !== null && _a !== void 0 ? _a : 0) ? {} : null; } isEqualWithTolerance(e, t) { var _a, _b; if (this === e) return !0; if (!(e instanceof yt)) return !1; if (Array.isArray(this._value)) { const i = e.asFloat; return null !== i && Math.abs(this._value[0] / this._value[1] - i) <= t; } if (this._value instanceof l.exports.Decimal) return this._value.sub((_b = (_a = e.decimalValue) !== null && _a !== void 0 ? _a : e.asFloat) !== null && _b !== void 0 ? _b : NaN).abs().lte(t); if (this._value instanceof c.exports.Complex) { if (e._value instanceof c.exports.Complex) return Math.abs(e._value.re - this._value.re) <= t && Math.abs(e._value.im - this._value.im) <= t; if (0 !== this._value.im) return !1; } const i = this.asFloat, n = e.asFloat; return null !== i && null !== n && Math.abs(n - i) <= t; } isLess(e) { if (e = e.N(), !this.isImaginary && !e.isImaginary) { if ("number" == typeof this._value) { const t = e.machineValue; if (null !== t) return this._value < t; const i = e.decimalValue; if (null !== i) return i.greaterThanOrEqualTo(this._value); const [n, r] = e.rationalValue; return null !== n && null !== r && this._value * r < n; } if (this._value instanceof l.exports.Decimal) { const t = e.machineValue; if (null !== t) return this._value.lt(t); const i = e.decimalValue; if (null !== i) return this._value.lt(i); const [n, r] = e.rationalValue; return null !== n && null !== r && this._value.mul(r).lt(n); } if (Array.isArray(this._value)) { const [t, i] = this._value; if ("number" == typeof e) return t < e * i; const [n, r] = e.rationalValue; if (null !== n && null !== r) return t * r < n * i; const s = e.decimalValue; return null !== s && s.mul(t).lt(i); } this._value, c.exports.Complex; } } isLessEqual(e) { e = e.N(); const t = this.isLess(e); if (void 0 === t) return; const i = this.isEqual(e); return void 0 !== i ? t || i : void 0; } isGreater(e) { const t = this.isLess(e); if (void 0 !== t) return !t; } isGreaterEqual(e) { e = e.N(); const t = this.isLess(e); if (void 0 === t) return; const i = this.isEqual(e); return void 0 !== i ? !t || i : void 0; } get isPositive() { const e = this.sgn; if (null != e) return e > 0; } get isNonNegative() { const e = this.sgn; if (null != e) return e >= 0; } get isNegative() { const e = this.sgn; if (null != e) return e < 0; } get isNonPositive() { const e = this.sgn; if (null != e) return e <= 0; } get isZero() { return !Array.isArray(this._value) && 0 === this.engine.chop(this._value); } get isNotZero() { return !!Array.isArray(this._value) || 0 !== this.engine.chop(this._value); } get isOne() { if ("number" == typeof this._value) return 1 === this._value; if (this._value instanceof l.exports.Decimal) return this._value.equals(this.engine.DECIMAL_ONE); if (Array.isArray(this._value)) { const [e, t] = this._value; return 0 !== t && e === t; } return this._value.equals(1); } get isNegativeOne() { if ("number" == typeof this._value) return -1 === this._value; if (this._value instanceof l.exports.Decimal) return this._value.equals(this.engine.DECIMAL_NEGATIVE_ONE); if (Array.isArray(this._value)) { const [e, t] = this._value; return e < 0 && 0 !== t && -e === t; } return this._value.equals(-1); } get isOdd() { return !(!this.isOne && !this.isNegativeOne) || !this.isZero && !!this.isInteger && ("number" == typeof this._value ? this._value % 2 != 0 : this._value instanceof l.exports.Decimal ? !this._value.mod(2).isZero() : void 0); } get isEven() { return !this.isOne && !this.isNegativeOne && (!!this.isZero || !!this.isInteger && ("number" == typeof this._value ? this._value % 2 == 0 : this._value instanceof l.exports.Decimal ? this._value.mod(2).isZero() : void 0)); } get isPrime() { return !(!this.isInteger || !this.isFinite || this.isNonPositive || this.isOne || this.isZero) && ("number" == typeof this._value ? bt(this._value) : void 0); } get isComposite() { return !(!this.isInteger || !this.isFinite || this.isNonPositive || this.isOne || this.isZero) && ("number" == typeof this._value ? !bt(this._value) : void 0); } get isInfinity() { return "number" == typeof this._value ? !Number.isFinite(this._value) && !Number.isNaN(this._value) : (this._value instanceof l.exports.Decimal || this._value instanceof c.exports.Complex) && !this._value.isFinite() && !this._value.isNaN(); } get isNaN() { return "number" == typeof this._value ? Number.isNaN(this._value) : (this._value instanceof l.exports.Decimal && this._value.isNaN(), this._value instanceof c.exports.Complex && this._value.isNaN(), !1); } get isFinite() { return !this.isInfinity && !isNaN; } get isNumber() { return !0; } get isInteger() { return "number" == typeof this._value ? Number.isInteger(this._value) : this._value instanceof l.exports.Decimal && this._value.isInteger(); } get isRational() { return !!Array.isArray(this._value) || this.isInteger; } get isAlgebraic() { if (this.isRational) return !0; } get isReal() { return !(!this.isFinite || this._value instanceof c.exports.Complex && 0 !== this.engine.chop(this._value.im)); } get isExtendedReal() { return this.isInfinity || this.isReal; } get isComplex() { return !this.isNaN; } get isImaginary() { return this._value instanceof c.exports.Complex && 0 !== this._value.im; } get isExtendedComplex() { return this.isInfinity || !this.isNaN; } get canonical() { if (this._isCanonical) return this; if (Array.isArray(this._value)) { const [e, t] = ke(this._value); return Number.isNaN(e) || Number.isNaN(t) ? this.engine.NAN : 1 === t ? this.engine.number(e) : 0 === t ? 0 !== e && Number.isFinite(e) ? e < 0 ? this.engine.NEGATIVE_INFINITY : this.engine.POSITIVE_INFINITY : this.engine.NAN : 0 === e ? this.engine.ZERO : this.engine.number([e, t]); } return this; } simplify(e) { return this.canonical; } N(e) { if (Array.isArray(this._value)) { const e = this.engine, [t, i] = this._value; return We(e) ? e.number(e.decimal(t).div(i)) : e.number(t / i); } return this; } } class Nt extends Pe { constructor(e, t, i) { super(e, i), this._string = t.normalize(), e._register(this); } get hash() { return Qe("String" + this._string); } get json() { return e = this.engine, t = t = this._string, e.jsonSerializationOptions.shorthands.includes("string") ? `'${t}'` : { str: t }; var e, t; } get head() { return "String"; } get isPure() { return !0; } get isLiteral() { return !0; } get isCanonical() { return !0; } set isCanonical(e) { } get domain() { return this.engine.domain("String"); } get complexity() { return 19; } get string() { return this._string; } isEqual(e) { return e.string === this._string; } isSame(e) { return e.string === this._string; } match(e, t) { return e instanceof Nt && this._string === e._string ? {} : null; } } function xt(e, t) { if (null == t) return e.symbol("Nothing"); if (t instanceof Pe) return t; if (Array.isArray(t)) { if ("number" != typeof t[0]) return new ft(e, "string" == typeof t[0] ? t[0] : xt(e, t[0]), t.slice(1).map((t => xt(e, t)))); const [i, n] = t; return "number" == typeof n && Number.isInteger(i) && Number.isInteger(n) ? e.number(t) : e.fn("Divide", t); } if ("number" == typeof t || t instanceof c.exports.Complex || t instanceof l.exports.Decimal) return e.number(t); if ("string" == typeof t) return t.startsWith("'") && t.endsWith("'") ? new Nt(e, t.slice(1, -1)) : e.symbol(t); if ("object" == typeof t) { const i = { latex: t.latex, wikidata: t.wikidata }; if ("dict" in t) return new ot(e, t.dict, i); if ("fn" in t) return "string" == typeof t.fn[0] ? function (e, t, i, n) { var _a, _b, _c; if ("Hold" === t) return new ft(e, "Hold", [St(e, (_a = i[0]) !== null && _a !== void 0 ? _a : "Missing")], n); if ("String" === t) return 0 === i.length ? new Nt(e, "", n) : new Nt(e, i.map((e => { var _a; return (_a = wt(e)) !== null && _a !== void 0 ? _a : ""; })).join(""), n); if ("Symbol" === t && i.length > 0) return e.symbol(i.map((e => { var _a; return (_a = wt(e)) !== null && _a !== void 0 ? _a : ""; })).join(""), n); if (("Divide" === t || "Rational" === t) && 2 === i.length) { const t = F(i[0]), n = F(i[1]); if (null !== t && null !== n && Number.isInteger(t) && Number.isInteger(n)) return e.number([t, n]); } if ("Number" === t && 1 === i.length) return xt(e, i[0]); if ("Complex" === t) { if (1 === i.length) { const t = xt(e, i[0]), r = t.asFloat; return null !== r && 0 !== r ? new yt(e, e.complex(0, r), n) : e.mul([t, e.I]); } if (2 === i.length) { const t = xt(e, i[0]), r = xt(e, i[1]), s = t.asFloat, a = r.asFloat; return null !== a && null !== s ? 0 === a && 0 === s ? e.ZERO : null !== a && 0 !== a ? new yt(e, e.complex(s, a), n) : t : e.add([t, e.mul([r, e.I])], n); } } if ("Negate" === t && i.length > 0 && "number" == typeof i[0]) return e.number(-i[0], n); if ("Single" === t || "Pair" === t || "Triple" === t) return e.fn("Tuple", i, n); if ("Dictionary" === t) { const t = {}; for (const n of i) { const i = e.box(n), r = i.head; if ("KeyValuePair" === r || "Pair" === r || "Tuple" === r) { const e = i.op1; if (!e.isMissing) { const n = i.op2; let r = (_b = e.symbol) !== null && _b !== void 0 ? _b : e.string; if (!r && e.isLiteral) { const t = (_c = e.machineValue) !== null && _c !== void 0 ? _c : e.asSmallInteger; t && Number.isFinite(t) && Number.isInteger(t) && (r = t.toString()); } r && (t[r] = n); } } } return new ot(e, t, n); } return new ft(e, t, i.map((t => xt(e, t))), n); }(e, t.fn[0], t.fn.slice(1), i) : e.fn(xt(e, t.fn[0]), t.fn.slice(1).map((t => xt(e, t))), i); if ("str" in t) return new Nt(e, t.str, i); if ("sym" in t) return e.symbol(t.sym, i); if ("num" in t) return e.number(t, i); } return e.symbol("Undefined"); } function _t(e, t, i) { var _a; if (t instanceof yt) return t; if (Array.isArray(t)) { if (2 !== t.length) throw Error("Array argument to boxNumber() should be two integers"); const [n, r] = t; if ("number" != typeof n || "number" != typeof r) throw Error("Array argument to boxNumber() should be two integers"); if (!Number.isInteger(n) || !Number.isInteger(r)) throw Error("Array argument to boxNumber() should be two integers"); if (r === n) return 0 === r ? e.NAN : e.ONE; if (1 === r) t = n; else { if (-1 !== r) return 1 === n && 2 === r ? e.HALF : new yt(e, [n, r], i); t = -n; } } if (t instanceof c.exports.Complex) { if (t.isNaN()) return e.NAN; if (t.isZero()) return e.ZERO; if (t.isInfinite()) return e.COMPLEX_INFINITY; if (0 !== t.im) return Be(e) ? new yt(e, t, i) : e.NAN; t = t.re; } if (t instanceof l.exports.Decimal) return t.isNaN() ? e.NAN : t.equals(e.DECIMAL_NEGATIVE_ONE) ? e.NEGATIVE_ONE : t.isZero() ? e.ZERO : t.equals(e.DECIMAL_ONE) ? e.ONE : t.equals(e.DECIMAL_TWO) ? e.TWO : !t.isFinite() && t.isPositive() ? e.POSITIVE_INFINITY : !t.isFinite() && t.isNegative() ? e.NEGATIVE_INFINITY : new yt(e, We(e) ? t : t.toNumber(), i); if ("object" == typeof t && "num" in t) if ("number" == typeof t.num) t = t.num; else if ("string" == typeof t.num) { let n = t.num.toLowerCase(); if (/[0-9][nd]$/.test(n) && (n = n.slice(0, -1)), n = n.replace(/[\u0009-\u000d\u0020\u00a0]/g, ""), /\([0-9]+\)$/.test(n)) { const [t, i, r] = (_a = n.match(/(.+)\(([0-9]+)\)$/)) !== null && _a !== void 0 ? _a : []; n = i + r.repeat(Math.ceil(e.precision / r.length)); } return "nan" === n ? e.NAN : "infinity" === n || "+infinity" === n ? e.POSITIVE_INFINITY : "-infinity" === n ? e.NEGATIVE_INFINITY : "0" === n ? e.ZERO : "1" === n ? e.ONE : "-1" === n ? e.NEGATIVE_ONE : "2" === n ? e.TWO : new yt(e, n, i); } if ("number" == typeof t) { if (Number.isNaN(t)) return e.NAN; if (!Number.isFinite(t) && t > 0 && e.POSITIVE_INFINITY, !Number.isFinite(t) && t < 0 && e.NEGATIVE_INFINITY, -1 === t) return e.NEGATIVE_ONE; if (0 === t) return e.ZERO; if (1 === t) return e.ONE; if (2 === t) return e.TWO; } return "number" == typeof t ? new yt(e, t, i) : null; } function St(e, t) { if ("object" == typeof t && t instanceof Pe) return t; if ("string" == typeof t) return xt(e, t); if (Array.isArray(t)) { const i = t.map((t => St(e, t))); return new ft(e, i[0], i.slice(1)); } if ("object" == typeof t) { if ("dict" in t) return new ot(e, t.dict); if ("fn" in t) return St(e, t.fn); if ("str" in t) return new Nt(e, t.str); if ("sym" in t) return xt(e, t.sym); if ("num" in t) return xt(e, t.num); } return xt(e, t); } function wt(e) { var _a, _b; if ("string" == typeof e) return e; if (e instanceof Pe) return (_b = (_a = e.string) !== null && _a !== void 0 ? _a : e.symbol) !== null && _b !== void 0 ? _b : e.toString(); if ("object" == typeof e) { if ("str" in e) return e.str; if ("fn" in e && "String" === e.fn[0] && "string" == typeof e.fn[1]) return e.fn[1]; } return Array.isArray(e) && "String" === e[0] && "string" == typeof e[1] ? e[1] : null; } function Et(e, t) { if (!e.isLiteral) return null; let i; null !== e.machineValue && (i = -e.machineValue), e.decimalValue && (i = e.decimalValue.neg()), e.complexValue && (i = e.complexValue.neg()); const [n, r] = e.rationalValue; return null !== n && null !== r && (i = [-n, r]), void 0 !== i ? e.engine.number(i, t) : null; } function It(e, t) { var _a; if ("Negate" === e.head) return e.op1; if (e.isLiteral) return Et(e, t); if ("Add" === e.head) { let i = e.ops.map((e => It(e))); return i = (_a = Te(i, "Add")) !== null && _a !== void 0 ? _a : i, e.engine.add(i, t); } return e.engine._fn("Negate", [e], t); } function kt(e) { var _a; if ("Negate" === e.head) return e.op1; if (e.isLiteral) return Et(e); const t = e.engine; if ("Add" === e.head) { let i = e.ops.map((e => kt(e))); return i = (_a = Te(i, "Add")) !== null && _a !== void 0 ? _a : i, t.add(i); } return "Multiply" === e.head ? function (e, t) { let i = [], n = !1; for (const e of t) n || "Negate" !== e.head ? i.push(e) : (n = !0, i.push(e.op1)); if (n) return e.mul(i); i = []; for (const e of t) !n && e.isLiteral && e.isInteger ? (n = !0, i.push(kt(e))) : i.push(e); if (n) return e.mul(i); i = []; for (const e of t) !n && e.isLiteral && e.isNumber ? (n = !0, i.push(kt(e))) : i.push(e); return n ? e.mul(i) : e._fn("Negate", [e._fn("Multiply", t)]); }(t, e.ops) : "Divide" === e.head ? t.divide(kt(e.op1), e.op2) : t._fn("Negate", [e]); } function At(e, t, i = "simplify") { return kt(t); } class Mt { constructor(e, t) { if (this._literal = [0, 1], this._imaginary = 0, this._posInfinityCount = 0, this._negInfinityCount = 0, this._terms = [], this.engine = e, t) for (const e of t) this.addTerm(e); } get isEmpty() { return 0 === this._terms.length && 0 === this._literal[0] && 0 === this._imaginary && 0 === this._negInfinityCount && 0 === this._posInfinityCount; } addTerm(e, t) { if ("Nothing" === e.symbol) return; if (void 0 === t && (t = [1, 1]), e.isLiteral) { if (e.isInfinity) return void (e.isPositive ? this._posInfinityCount += 1 : this._negInfinityCount += 1); const [i, n] = e.asRational; if (null !== i && null !== n) return void (this._literal = [t[0] * (this._literal[0] * n + i * this._literal[1]), t[1] * n * this._literal[1]]); if (e.complexValue) { let i = e.complexValue.re, n = e.complexValue.im; if (Number.isInteger(i) && Math.abs(i) <= 1e6 && (this._literal[0] += this._literal[1] * i * t[0] / t[1], i = 0), Number.isInteger(n) && Math.abs(n) <= 1e6 && (this._imaginary += n * t[0] / t[1], n = 0), 0 === i && 0 === n) return; e = this.engine.number(this.engine.complex(i, n)).canonical; } } let i; if ([i, e] = Ye(e), 0 === i[0]) return; if (i = [i[0] * t[0], i[1] * t[1]], "Add" === e.head) { for (const t of e.ops) this.addTerm(t, i); return; } let n = !1; if (!e.isLiteral) if (this._terms.length > 500) { const t = e.hash; for (let r = 0; r < this._terms.length; r++) if (!this._terms[r].term.isLiteral && t === this._terms[r].term.hash && e.isSame(this._terms[r].term)) { const [e, t] = this._terms[r].coef, [s, a] = i; this._terms[r].coef = [e * a + t * s, t * a], n = !0; break; } } else for (let t = 0; t < this._terms.length; t++) if (!this._terms[t].term.isLiteral && e.isSame(this._terms[t].term)) { const [e, r] = this._terms[t].coef, [s, a] = i; this._terms[t].coef = [e * a + r * s, r * a], n = !0; break; } n || this._terms.push({ term: e, coef: i }); } terms() { var _a; const e = this.engine; if (this._posInfinityCount > 0 && this._negInfinityCount > 0) return [e.NAN]; if (this._posInfinityCount > 0) return [e.POSITIVE_INFINITY]; if (this._negInfinityCount > 0) return [e.NEGATIVE_INFINITY]; if (0 === this._terms.length) return 0 === this._literal[0] && 0 === this._imaginary ? [] : 0 === this._imaginary ? [e.number(this._literal).canonical] : 0 === this._literal[0] ? [e.number(e.complex(0, this._imaginary)).canonical] : [e.number(this._literal).canonical, e.number(e.complex(0, this._imaginary)).canonical]; const t = []; for (const { coef: [i, n], term: r } of this._terms) 0 !== i && (i === n ? t.push(r) : i === -n ? t.push(e.negate(r)) : 1 === n ? t.push(e.mul([e.number(i).canonical, r])) : 1 === i ? t.push(e.divide(r, e.number(n).canonical)) : 0 !== i && t.push(e.mul([e.number([i, n]).canonical, r]))); return 0 !== this._literal[0] && t.push(e.number(this._literal).canonical), 0 !== this._imaginary && t.push(e.number(e.complex(0, this._imaginary)).canonical), (_a = Te(t, "Add")) !== null && _a !== void 0 ? _a : t; } asExpression() { const e = this.engine, t = this.terms(); return 0 === t.length ? e.ZERO : 1 === t.length ? t[0] : e._fn("Add", t.sort(((e, t) => { const i = $e(e), n = $e(t); if (i < n) return -1; if (i > n) return 1; const r = Re(e), s = Re(t); if (r !== s) return s - r; const a = ze(e), o = ze(t); return a !== o ? a - o : Ue(e, t); }))); } } function Ft(e, t) { var _a, _b, _c, _d; if ((t = (_a = Te(t, "Add")) !== null && _a !== void 0 ? _a : t).length <= 1) return (_b = t[0]) !== null && _b !== void 0 ? _b : e.symbol("Nothing"); if (2 === t.length) { let i = 0, n = 0; if (t[0].isLiteral && (n = t[0].machineValue, n = null === n && t[0].decimalValue ? (_c = t[0].asFloat) !== null && _c !== void 0 ? _c : 0 : 0), 0 !== n ? i = Ge(t[1]) : (i = Ge(t[0]), 0 !== i && (n = t[1].machineValue, n = null === n && t[1].decimalValue ? (_d = t[1].asFloat) !== null && _d !== void 0 ? _d : 0 : 0)), 0 !== i) return e.number(e.complex(n, i)); if (t[0].isLiteral && t[1].isLiteral) { if (t[0].isZero) return t[1]; if (t[1].isZero) return t[0]; const [i, n] = t[0].asRational, [r, s] = t[1].asRational; if (null !== i && null !== n && null !== r && null !== s) return e.number([i * s + r * n, n * s]); } } return new Mt(e, t).asExpression(); } function Ot(e, t, i) { const n = new Mt(e); for (const i of t) { if (i.isImaginary && i.isInfinity) return e.symbol("ComplexInfinity"); if (i.isNaN || i.isMissing || "Undefined" === i.symbol) return e.NAN; n.addTerm(i); } return n.asExpression(); } function qt(e, t, i, n) { if ("ComplexInfinity" === i.symbol) return e.NAN; if (i.isLiteral) { if (i.isZero) return e.ONE; if (t.isLiteral) { if (t.isOne) return e.ONE; if (t.isZero) { if (i.isPositive) return e.ZERO; if (i.isNegative) return e.COMPLEX_INFINITY; } if (i.isOne) return t; if (i.isNegativeOne) { if (t.isOne) return e.ONE; if (t.isNegativeOne) return e.NEGATIVE_ONE; if (t.isInfinity) return e.ZERO; const [i, r] = t.rationalValue; if (null !== i && null !== r) return e.number([r, i], n); const s = t.asFloat; return null !== s && Number.isInteger(s) ? e.number([1, s], n) : e._fn("Power", [t, e.NEGATIVE_ONE], n); } const r = i.asFloat; if (.5 === r || -.5 === r) { const i = t.asSmallInteger; if (null !== i && i > 0) { const [t, n] = Ee(i, 2); if (1 === n && 1 === t) return e.ONE; if (1 !== t) return 1 === n ? e.number(r >= 0 ? t : [1, t]) : e.mul([e.number(t), e.power(e.number(n), e.HALF)]); } return r > 0 ? e._fn("Power", [t, e.HALF], n) : e._fn("Power", [t, e.number([-1, 2])], n); } if (t.isInfinity) { if (i.complexValue) { const t = i.complexValue.re; if (0 === t) return e.NAN; if (t < 0) return e.ZERO; if (t > 0) return e.COMPLEX_INFINITY; } if (t.isNegative) { if (i.isInfinity) return e.NAN; } else if (t.isPositive) { if (i.isNegativeOne) return e.ZERO; if (i.isInfinity) return i.isNegative ? e.ZERO : e.POSITIVE_INFINITY; } } if (i.isInfinity && (t.isOne || t.isNegativeOne)) return e.NAN; const [s, a] = t.asRational; if (null !== s && null !== a) { const t = i.asSmallInteger; if (null !== t) return -1 === t ? e.number([a, s]) : t > 0 ? e.number([Math.pow(s, t), Math.pow(a, t)]) : e.number([Math.pow(a, -t), Math.pow(s, -t)]); } } } if ("Power" === t.head && t.op1.isReal) { const n = i.asSmallInteger; if (null !== n) { const i = t.op2.asSmallInteger; if (null !== i) return e._fn("Power", [t.op1, e.number(n * i)]); } if (t.op1.isNonNegative) { const [n, r] = i.asRational; if (null !== n && null !== r) { const [i, s] = t.op2.asRational; if (null !== i && null !== s) return e._fn("Power", [t.op1, e.number([n * i, r * s])]); } } } return "Multiply" === t.head && null !== i.asSmallInteger ? e._fn("Multiply", t.ops.map((t => e.power(t, i)))) : null; } function Pt(e, t) { if (t.machineValue) return e.number(Math.pow(t.machineValue, 2)); if (t.decimalValue) return e.number(t.decimalValue.pow(2)); if (t.complexValue) return e.number(t.complexValue.pow(2)); const [i, n] = t.rationalValue; if (null !== i && null !== n) return e.number([Math.pow(n, 2), Math.pow(i, 2)]); if ("Multiply" === t.head) return e._fn("Multiply", t.ops.map((t => Pt(e, t)))); if ("Power" === t.head) { const i = t.op2.asSmallInteger; return null !== i ? e._fn("Power", [t.op1, e.number(2 * i)]) : e._fn("Power", [t.op1, e.mul([e.TWO, t.op2])]); } return e._fn("Power", [t, e.TWO]); } function Vt(e, t, i, n) { var _a, _b, _c, _d, _f, _g; if ("N" === n && t.isLiteral && i.isLiteral) return t.complexValue ? e.number(t.complexValue.pow((_b = (_a = i.complexValue) !== null && _a !== void 0 ? _a : i.asFloat) !== null && _b !== void 0 ? _b : NaN)) : i.complexValue && t.asFloat ? e.number(e.complex(t.asFloat).pow(i.complexValue)) : t.decimalValue ? e.number(t.decimalValue.pow((_c = i.decimalValue) !== null && _c !== void 0 ? _c : i.asFloat)) : t.asFloat && (i.decimalValue || We(e)) ? e.number(e.decimal(t.asFloat).pow((_d = i.decimalValue) !== null && _d !== void 0 ? _d : i.asFloat)) : e.number(Math.pow((_f = t.asFloat) !== null && _f !== void 0 ? _f : NaN, (_g = i.asFloat) !== null && _g !== void 0 ? _g : NaN)); if (null !== t.asSmallInteger) { const [n, r] = i.rationalValue; if (!(1 !== n && -1 !== n || 2 !== r && 3 !== r)) { const [s, a] = Ee(t.asSmallInteger, r); if (1 === a && 1 === s) return e.ONE; if (1 === s) return; return 1 === a ? e.number(n >= 0 ? s : [1, s]) : e.mul([e.number(s), e.power(e.number(a), i)]); } } } function Ct(e, t) { var _a; return 0 === (t = (_a = Te(t, "Multiply")) !== null && _a !== void 0 ? _a : t).length ? e.symbol("Nothing") : 1 === t.length ? t[0] : 2 === t.length ? Lt(t[0], t[1]) : new Xe(e, t).asExpression(); } function Dt(e, t, i) { const n = new Xe(e); for (const i of t) { if (i.isNaN || i.isMissing || "Undefined" === i.symbol) return e.NAN; n.addTerm(i); } return n.asExpression(); } function Lt(e, t, i) { const n = e.engine; if (e.isLiteral && t.isLiteral && e.isInteger && t.isInteger) { if (e.decimalValue && t.decimalValue) return n.number(e.decimalValue.mul(t.decimalValue)); if (e.machineValue && t.machineValue) return n.number(e.machineValue * t.machineValue); } if ("Nothing" === e.symbol) return t; if ("Nothing" === t.symbol) return e; if (e.isLiteral && e.isOne) return t; if (t.isLiteral && t.isOne) return e; if (e.isLiteral && e.isNegativeOne) return It(t); if (t.isLiteral && t.isNegativeOne) return It(e); if (e.isMissing || t.isMissing) return n._fn("Multiply", [e, t]); let r = 1, s = e, a = t; s.isLiteral && null !== s.asRational || (a = t, s = e), "Negate" === a.head && (a = a.op1, r = -r); const [o, l] = s.asRational; if (s.isLiteral && null !== o && null !== l) { if (o === l) return a; if (0 === o) return n.ZERO; if ("Add" === a.head) return r < 0 && (s = It(s)), n.add(a.ops.map((e => Lt(s, e))), i); if (a.isLiteral) { const [e, t] = a.asRational; if (null !== e && null !== t) return n.number(ke([r * o * e, t * l]), i); } return r < 0 ? n._fn("Multiply", [It(s), a], i) : n._fn("Multiply", [s, a], i); } if (s.hash === a.hash && s.isSame(a)) return Pt(n, s); const u = new Xe(n, [s, a]); return r > 0 ? u.asExpression() : It(u.asExpression(), i); } function Tt(e, t, i) { if (t.isLiteral && i.isLiteral) { if (t.isOne) return e.inverse(i); if (t.isNegativeOne) return It(e.inverse(i)); if (i.isOne) return t; if (i.isNegativeOne) return It(t); const [n, r] = [t.asSmallInteger, i.asSmallInteger]; if (null !== n && null !== r && 0 !== r) return e.number(ke([n, r])); } if ("Divide" === t.head && "Divide" === i.head) return e.divide(e.mul([t.op1, i.op2]), e.mul([t.op2, i.op1])); if ("Divide" === t.head) return e.divide(e.mul([t.op1, i]), t.op2); if ("Divide" === i.head) return e.divide(e.mul([t, i.op2]), i.op1); let [n, r] = Je(t), [s, a] = Je(i); return r = r.canonical, a = a.canonical, a.isLiteral && a.isOne ? n * s < 0 ? It(r) : r : (a = e.inverse(a), r.isOne ? a : r.isNegativeOne ? It(a) : n * s > 0 ? e.mul([r, a]) : e.negate(e.mul([r, a]))); } const Rt = [{ functions: [{ name: "Abs", domain: ["Function", "ExtendedRealNumber", "ExtendedRealNumber"], range: [0, 1 / 0], wikidata: "Q3317982", threadable: !0, idempotent: !0, complexity: 1200, simplify: (e, t) => zt(e, t[0], "simplify"), evaluate: (e, t) => zt(e, t[0], "evaluate"), N: (e, t) => zt(e, t[0], "N") }, { name: "Add", wikidata: "Q32043", associative: !0, commutative: !0, threadable: !0, idempotent: !0, domain: "NumericFunction", complexity: 1300, canonical: (e, t) => Ft(e, t), simplify: (e, t) => Ot(e, t), evaluate: (e, t) => Ot(e, t), N: (e, t) => function (e, t) { for (const i of t) { if (i.isImaginary && i.isInfinity) return e.symbol("ComplexInfinity"); if (i.isNaN || i.isMissing || "Undefined" === i.symbol) return e.NAN; } let [i, n] = [0, 1], r = 0, s = e.DECIMAL_ZERO, a = c.exports.Complex.ZERO; const o = new Mt(e); for (const l of t) if ("Nothing" !== l.symbol && !l.isZero) { const [t, u] = l.rationalValue; null !== t && null !== u ? [i, n] = [i * u + n * t, n * u] : null !== l.decimalValue ? s = s.add(l.decimalValue) : null !== l.machineValue ? We(e) ? s = s.add(l.machineValue) : r += l.machineValue : null !== l.complexValue ? a = a.add(l.complexValue) : o.addTerm(l); } if (!Be(e) && 0 !== a.im) return e.NAN; if (We(e) || 0 !== e.chop(s)) { let t = s; if (0 !== i && (t = t.mul(n).add(i).div(n)), 0 !== r && (t = t.add(r)), 0 !== a.re && (t = t.add(a.re)), 0 !== a.im) if (Le(t)) { const i = e.number(e.complex(t.toNumber(), a.im)); if (o.isEmpty) return i; o.addTerm(i); } else o.addTerm(e.number(e.complex(0, a.im))), o.addTerm(e.number(t)); else { if (o.isEmpty) return e.number(t); o.addTerm(e.number(t)); } } else { const t = r + a.re + i / n, s = e.number(0 === a.im ? t : e.complex(t, a.im)); if (o.isEmpty) return s; o.addTerm(s); } return o.asExpression(); }(e, t) }, { name: "Ceil", description: "Rounds a number up to the next largest integer", complexity: 1250, domain: "NumericFunction", evaluate: (e, t) => { const i = t[0]; return i.decimalValue ? e.number(i.decimalValue.ceil()) : i.complexValue ? e.number(i.complexValue.ceil(0)) : null !== i.asFloat ? e.number(Math.ceil(i.asFloat)) : void 0; } }, { name: "Chop", associative: !0, threadable: !0, idempotent: !0, complexity: 1200, domain: "NumericFunction", N: (e, t) => { const i = t[0]; return i.decimalValue ? e.number(e.chop(i.decimalValue)) : i.complexValue ? e.number(e.chop(i.complexValue)) : null !== i.asFloat ? e.number(e.chop(i.asFloat)) : void 0; } }, { name: "Complex", wikidata: "Q11567", domain: "NumericFunction", complexity: 500 }, { name: "Divide", wikidata: "Q1226939", domain: "NumericFunction", complexity: 2500, canonical: (e, t) => t[0] && t[1] ? Tt(e, t[0], t[1]) : e.symbol("Missing") }, { name: "Exp", domain: "NumericFunction", wikidata: "Q168698", threadable: !0, complexity: 3500, canonical: (e, t) => t[0] ? e.power(e.symbol("ExponentialE"), t[0]) : e.symbol("Missing") }, { name: "Erf", description: "Complementary Error Function", domain: "NumericFunction", complexity: 7500 }, { name: "Erfc", description: "Complementary Error Function", domain: "NumericFunction", complexity: 7500 }, { name: "Factorial", description: "The factorial function", wikidata: "Q120976", complexity: 9e3, domain: "NumericFunction", evaluate: (e, t) => { const i = t[0].asSmallInteger; return null !== i && i >= 0 ? We(e) ? e.number(Ve(e, e.decimal(i))) : e.number(function (e) { if (!Number.isInteger(e) || e < 0) return NaN; let t = 1; for (let i = 2; i <= e; i++) t *= i; return t; }(i)) : t[0].complexValue ? e.number(t[0].complexValue.add(1)) : null !== t[0].asFloat ? e.number(Oe(1 + t[0].asFloat)) : void 0; } }, { name: "Floor", wikidata: "Q56860783", domain: ["Function", "ExtendedRealNumber", "ExtendedRealNumber"], complexity: 1250, evaluate: (e, t) => t[0].decimalValue ? e.number(t[0].decimalValue.floor()) : t[0].complexValue ? e.number(t[0].complexValue.floor(0)) : null !== t[0].asFloat ? e.number(Math.floor(t[0].asFloat)) : void 0 }, { name: "Gamma", wikidata: "Q190573", domain: ["Function", "Number", "Number"], complexity: 8e3, N: (e, t) => t[0].decimalValue ? e.number(De(e, t[0].decimalValue)) : t[0].complexValue ? e.number(t[0].complexValue) : null !== t[0].asFloat ? e.number(Oe(t[0].asFloat)) : void 0 }, { name: "LogGamma", domain: ["Function", "Number", "Number"], complexity: 8e3, N: (e, t) => t[0].decimalValue ? e.number(Ce(e, t[0].decimalValue)) : t[0].complexValue ? e.number(t[0].complexValue) : null !== t[0].asFloat ? e.number(Fe(t[0].asFloat)) : void 0 }, { name: "Ln", description: "Natural Logarithm", domain: ["Function", "Number", "Number"], wikidata: "Q204037", complexity: 4e3, N: (e, t) => t[0].decimalValue ? e.number(t[0].decimalValue.log()) : t[0].complexValue ? e.number(t[0].complexValue.log()) : null !== t[0].asFloat ? e.number(Math.log(t[0].asFloat)) : void 0 }, { name: "Log", description: "Log(b, z) = Logarithm of base b", wikidata: "Q11197", domain: ["Function", "Number", "Number", "Number"], complexity: 4100, N: (e, t) => { var _a, _b, _c, _d, _f; const i = t[0], n = t[1]; return i.decimalValue ? e.number(i.decimalValue.log().div((_b = (_a = n.decimalValue) !== null && _a !== void 0 ? _a : n.asFloat) !== null && _b !== void 0 ? _b : NaN)) : i.complexValue ? e.number(i.complexValue.log().div((_d = (_c = n.complexValue) !== null && _c !== void 0 ? _c : n.asFloat) !== null && _d !== void 0 ? _d : NaN)) : null !== i.asFloat ? e.number(Math.log(i.asFloat) / Math.log((_f = n.asFloat) !== null && _f !== void 0 ? _f : NaN)) : void 0; } }, { name: "Lb", description: "Base-2 Logarithm", domain: ["Function", "Number", "Number"], wikidata: "Q581168", complexity: 4100, N: (e, t) => { const i = t[0]; return i.decimalValue ? e.number(i.decimalValue.log().div(e.DECIMAL_TWO)) : i.complexValue ? e.number(i.complexValue.log().div(e.complex(2))) : null !== i.asFloat ? e.number(Math.log2(i.asFloat)) : void 0; } }, { name: "Lg", description: "Base-10 Logarithm", domain: ["Function", "Number", "Number"], wikidata: "Q966582", complexity: 4100, N: (e, t) => { const i = t[0]; return i.decimalValue ? e.number(i.decimalValue.log().div(e.decimal(10))) : i.complexValue ? e.number(i.complexValue.log().div(e.complex(10))) : null !== i.asFloat ? e.number(Math.log10(i.asFloat)) : void 0; } }, { name: "Multiply", domain: "NumericFunction", wikidata: "Q40276", associative: !0, commutative: !0, idempotent: !0, complexity: 2100, canonical: (e, t) => Ct(e, t), simplify: (e, t) => Dt(e, t), evaluate: (e, t) => Dt(e, t), N: (e, t) => function (e, t) { for (const i of t) if (i.isNaN || i.isMissing || "Undefined" === i.symbol) return e.NAN; let [i, n] = [1, 1], r = 1, s = e.DECIMAL_ONE, a = c.exports.Complex.ONE; const o = new Xe(e); for (const l of t) if ("Nothing" !== l.symbol && !l.isOne) { const [t, u] = l.rationalValue; null !== t && null !== u ? [i, n] = [i * t, n * u] : null !== l.decimalValue ? s = s.mul(l.decimalValue) : null !== l.machineValue ? We(e) ? s = s.mul(l.machineValue) : r *= l.machineValue : null !== l.complexValue ? a = a.mul(l.complexValue) : o.addTerm(l); } if (!Be(e) && 0 !== a.im) return e.NAN; if (We(e) || !s.eq(e.DECIMAL_ONE)) { const t = s.mul(i).div(n).mul(r); if (1 !== a.re || 0 !== a.im) if (Le(t)) { const i = e.number(e.complex(a.mul(t.toNumber()))); if (o.isEmpty) return i; o.addTerm(i); } else { if (o.isEmpty) return e._fn("Multiply", [e.number(a), e.number(t)]); o.addTerm(e.number(a)), o.addTerm(e.number(t)); } else { if (o.isEmpty) return e.number(t); o.addTerm(e.number(t)); } } else { const t = r * i / n; let s; if (s = 1 !== a.re || 0 !== a.im ? e.number(a.mul(t)) : e.number(t), o.isEmpty) return s; o.addTerm(s); } return o.asExpression(); }(e, t) }, { name: "Negate", description: "Additive Inverse", wikidata: "Q715358", domain: ["Function", "Number", "Number"], complexity: 2e3, canonical: (e, t) => t[0] ? It(t[0]) : e.box("Missing"), simplify: (e, t) => At(0, t[0], "simplify"), evaluate: (e, t) => At(0, t[0], "evaluate"), N: (e, t) => At(0, t[0], "N"), sgn: (e, t) => { var _a; const i = (_a = t[0]) !== null && _a !== void 0 ? _a : e.symbol("Missing"); return i.isZero ? 0 : i.isPositive ? -1 : i.isNegative ? 1 : void 0; } }, { name: "Power", domain: ["Function", "Number", "Number", "Number"], wikidata: "Q33456", commutative: !1, complexity: 3500, canonical: (e, t) => { var _a, _b, _c; const i = (_a = t[0]) !== null && _a !== void 0 ? _a : e.symbol("Missing"), n = (_b = t[1]) !== null && _b !== void 0 ? _b : e.symbol("Missing"); return (_c = qt(e, i, n)) !== null && _c !== void 0 ? _c : e._fn("Power", t); }, simplify: (e, t) => Vt(e, t[0], t[1], "simplify"), evaluate: (e, t) => Vt(e, t[0], t[1], "evaluate"), N: (e, t) => Vt(e, t[0], t[1], "N") }, { name: "Rational", domain: ["Function", "Number", ["Optional", "Number"], "RationalNumber"], complexity: 2400, canonical: (e, t) => 2 === t.length ? Tt(e, t[0], t[1]) : e._fn("Rational", t), simplify: (e, t) => { if (2 === t.length) return null !== t[0].asSmallInteger && null !== t[1].asSmallInteger ? e.number([t[0].asSmallInteger, t[1].asSmallInteger]) : void 0; }, evaluate: (e, t) => { if (2 === t.length) return null !== t[0].asSmallInteger && null !== t[1].asSmallInteger ? e.number([t[0].asSmallInteger, t[1].asSmallInteger]) : void 0; const i = t[0].asFloat; if (null === i) return t[0]; const n = function (e) { if (!Number.isFinite(e)) return e; if (0 == e % 1) return e; let t = Math.floor(e), i = 1, n = 0, r = t, s = 1; for (; e - t > 1e-15 * s * s;) { t = Math.floor(e = 1 / (e - t)); const a = i; i = r; const o = n; n = s, r = a + t * i, s = o + t * n; } return [r, s]; }(i); return "number" == typeof n ? e.number(n) : e.number([n[0], n[1]]); }, N: (e, t) => { if (2 === t.length) { if (null === t[0].asSmallInteger || null === t[1].asSmallInteger) return; return e.number(t[0].asSmallInteger / t[1].asSmallInteger); } } }, { name: "Root", domain: ["Function", "Number", "RationalNumber", "Number"], complexity: 3200, canonical: (e, t) => { var _a, _b, _c; const i = (_a = t[0]) !== null && _a !== void 0 ? _a : e.symbol("Missing"), n = (_b = t[1]) !== null && _b !== void 0 ? _b : e.symbol("Missing"), r = e.inverse(n); return (_c = qt(e, i, r)) !== null && _c !== void 0 ? _c : e._fn("Power", [i, r]); }, N: (e, t) => { var _a, _b, _c; const i = t[0], n = t[1]; if (i.decimalValue) return e.number(i.decimalValue.pow(e.DECIMAL_ONE.div((_a = n.asFloat) !== null && _a !== void 0 ? _a : NaN))); if (i.complexValue) { const t = n.complexValue ? c.exports.Complex.ONE.div(n.complexValue) : e.complex(1 / ((_b = n.asFloat) !== null && _b !== void 0 ? _b : NaN)); return e.number(i.complexValue.pow(t)); } return null !== i.asFloat ? e.number(Math.pow(i.asFloat, (_c = n.asFloat) !== null && _c !== void 0 ? _c : NaN)) : void 0; } }, { name: "Round", domain: ["Function", "Number", "Number"], complexity: 1250, N: (e, t) => t[0].decimalValue ? e.number(t[0].decimalValue.round()) : t[0].complexValue ? e.number(t[0].complexValue.round(0)) : null !== t[0].asFloat ? e.number(Math.round(t[0].asFloat)) : void 0 }, { name: "Sign", domain: ["Function", "Number", "Integer"], range: [-1, 1], complexity: 1200, simplify: (e, t) => { const i = t[0].sgn; return 0 === i ? e.ZERO : 1 === i ? e.ONE : -1 === i ? e.NEGATIVE_ONE : void 0; }, evaluate: (e, t) => { const i = t[0].sgn; return 0 === i ? e.ZERO : 1 === i ? e.ONE : -1 === i ? e.NEGATIVE_ONE : void 0; }, N: (e, t) => { const i = t[0].sgn; return 0 === i ? e.ZERO : 1 === i ? e.ONE : -1 === i ? e.NEGATIVE_ONE : void 0; } }, { name: "SignGamma", description: "The sign of the gamma function: -1 or +1", domain: ["Function", "Number", "Integer"], complexity: 7900, range: [-1, 1] }, { name: "Sqrt", description: "Square Root", domain: ["Function", "Number", "Number"], wikidata: "Q134237", complexity: 3e3, canonical: (e, t) => { var _a; return t[0] ? (_a = qt(e, t[0], e.HALF)) !== null && _a !== void 0 ? _a : e._fn("Power", [t[0], e.HALF]) : e._fn("Power", [e.symbol("Missing"), e.HALF]); }, simplify: (e, t) => $t(e, t[0], "simplify"), evaluate: (e, t) => $t(e, t[0], "evaluate"), N: (e, t) => $t(e, t[0], "N") }, { name: "Square", domain: ["Function", "Number", "Number"], wikidata: "Q3075175", complexity: 3100, canonical: (e, t) => { var _a; return t[0] ? (_a = qt(e, t[0], e.TWO)) !== null && _a !== void 0 ? _a : e._fn("Power", [t[0], e.TWO]) : e._fn("Power", [e.symbol("Missing"), e.TWO]); }, N: (e, t) => t[0].decimalValue ? e.number(t[0].decimalValue.mul(t[0].decimalValue)) : t[0].complexValue ? e.number(t[0].complexValue.mul(t[0].complexValue)) : null !== t[0].asFloat ? e.number(t[0].asFloat * t[0].asFloat) : void 0 }, { name: "Subscript", domain: ["Function", "Anything", "Anything", "Anything"], hold: "last", canonical: (e, t) => { var _a, _b, _c, _d; const i = (_a = t[0]) !== null && _a !== void 0 ? _a : e.symbol("Missing"), n = (_b = t[1]) !== null && _b !== void 0 ? _b : e.symbol("Missing"); if (i.string && n.isLiteral && null !== n.asSmallInteger) { const t = n.asSmallInteger; if (t > 1 && t <= 36) { const [n, r] = qe(i.string, t); return r ? e._fn("Error", [e.number(n), e.string("unexpected-digits"), e._fn("LatexForm", [e.string(r)])]) : e.number(n); } } if (i.symbol) { if ((_c = i.symbolDefinition) === null || _c === void 0 ? void 0 : _c.at) return e._fn("At", [i, n]); let t = (_d = n.string) !== null && _d !== void 0 ? _d : n.symbol; if (t || null !== n.asSmallInteger && (t = n.asSmallInteger.toString()), t) return e.symbol(i.symbol + "_" + t); } return e._fn("Subscript", t); } }, { name: "Subtract", domain: ["Function", "Number", "Number", "Number"], wikidata: "Q40754", complexity: 1350, canonical: (e, t) => 0 === t.length ? e.symbol("Nothing") : 1 === t.length ? It(t[0]) : Ft(e, [t[0], It(t[1])]), N: (e, t) => { var _a, _b, _c, _d, _f, _g; const i = t[0], n = t[1]; return i.complexValue || n.complexValue ? e.number(e.complex((_a = i.complexValue) !== null && _a !== void 0 ? _a : i.asFloat).sub((_b = n.complexValue) !== null && _b !== void 0 ? _b : n.asFloat)) : i.decimalValue || n.decimalValue ? e.number(e.decimal((_d = (_c = i.decimalValue) !== null && _c !== void 0 ? _c : i.asFloat) !== null && _d !== void 0 ? _d : NaN).sub((_g = (_f = n.decimalValue) !== null && _f !== void 0 ? _f : n.asFloat) !== null && _g !== void 0 ? _g : NaN)) : null !== i.asFloat && null !== n.asFloat ? e.number(i.asFloat - n.asFloat) : void 0; } }] }, { symbols: [{ name: "MachineEpsilon", domain: "RealNumber", constant: !0, real: !0, value: { num: Number.EPSILON.toString() } }, { name: "Half", constant: !0, hold: !1, value: ["Rational", 1, 2] }, { name: "ImaginaryUnit", domain: "ImaginaryNumber", constant: !0, hold: !0, wikidata: "Q193796", imaginary: !0, value: ["Complex", 0, 1] }, { name: "ExponentialE", domain: "TranscendentalNumber", algebraic: !1, wikidata: "Q82435", constant: !0, hold: !0, real: !0, value: e => We(e) ? e.DECIMAL_ONE.exp() : Math.exp(1) }, { name: "GoldenRatio", domain: "AlgebraicNumber", wikidata: "Q41690", constant: !0, algebraic: !0, hold: !1, value: ["Divide", ["Add", 1, ["Sqrt", 5]], 2] }, { name: "CatalanConstant", domain: "RealNumber", algebraic: void 0, wikidata: "Q855282", constant: !0, value: { num: "0.91596559417721901505460351493238411077414937428167\n 21342664981196217630197762547694793565129261151062\n 48574422619196199579035898803325859059431594737481\n 15840699533202877331946051903872747816408786590902\n 47064841521630002287276409423882599577415088163974\n 70252482011560707644883807873370489900864775113225\n 99713434074854075532307685653357680958352602193823\n 23950800720680355761048235733942319149829836189977\n 06903640418086217941101917532743149978233976105512\n 24779530324875371878665828082360570225594194818097\n 53509711315712615804242723636439850017382875977976\n 53068370092980873887495610893659771940968726844441\n 66804621624339864838916280448281506273022742073884\n 31172218272190472255870531908685735423498539498309\n 91911596738846450861515249962423704374517773723517\n 75440708538464401321748392999947572446199754961975\n 87064007474870701490937678873045869979860644874974\n 64387206238513712392736304998503539223928787979063\n 36440323547845358519277777872709060830319943013323\n 16712476158709792455479119092126201854803963934243\n " } }, { name: "EulerGamma", domain: "RealNumber", algebraic: void 0, wikidata: "Q273023", constant: !0, value: { num: "0.57721566490153286060651209008240243104215933593992359880576723488486772677766\n 467093694706329174674951463144724980708248096050401448654283622417399764492353\n 625350033374293733773767394279259525824709491600873520394816567085323315177661\n 152862119950150798479374508570574002992135478614669402960432542151905877553526\n 733139925401296742051375413954911168510280798423487758720503843109399736137255\n 306088933126760017247953783675927135157722610273492913940798430103417771778088\n 154957066107501016191663340152278935867965497252036212879226555953669628176388\n 792726801324310104765059637039473949576389065729679296010090151251959509222435\n 014093498712282479497471956469763185066761290638110518241974448678363808617494\n 551698927923018773910729457815543160050021828440960537724342032854783670151773\n 943987003023703395183286900015581939880427074115422278197165230110735658339673" } }] }, { functions: [{ name: "PreIncrement", domain: ["Function", "Number", "Number"] }, { name: "PreDecrement", domain: ["Function", "Number", "Number"] }] }]; function zt(e, t, i) { if ("simplify" !== i || t.isLiteral) { if (null !== t.machineValue) return e.number(Math.abs(t.machineValue)); if (t.decimalValue) return e.number(t.decimalValue.abs()); if (t.complexValue) return e.number(t.complexValue.abs()); const [n, r] = t.rationalValue; if (null === n || null === r) return; return e.number("N" === i ? Math.abs(n / r) : [Math.abs(n), r]); } if (!t.isMissing) return t.isNonNegative ? t : t.isNegative ? e.negate(t) : void 0; } function $t(e, t, i) { if (t.isOne) return e.ONE; if (t.isZero) return e.ZERO; if ("N" === i) return t.complexValue ? e.number(t.complexValue.sqrt()) : t.isNonNegative ? t.decimalValue ? e.number(t.decimalValue.sqrt()) : null !== t.asFloat ? e.number(Math.sqrt(t.asFloat)) : void 0 : Be(e) ? e.number(e.complex(t.asFloat).sqrt()) : e.NAN; if (null !== t.asSmallInteger) { const [i, n] = Ee(t.asSmallInteger, 2); if (1 === n) return e.number(i); if (1 !== i) return this._fn("Multiply", [i, e._fn("Sqrt", [e.box(n).canonical])]); } } const Zt = [{ symbols: [{ name: "Missing", domain: "Anything" }, { name: "Nothing", domain: "Nothing" }] }, { functions: [{ name: "KeyValuePair", domain: ["Function", "String", "Anything", ["Tuple", "String", "Anything"]], description: "A key/value pair", complexity: 8200, canonical: (e, t) => e.tuple(t) }, { name: "Single", domain: ["Function", "Anything", ["Tuple", "Anything"]], description: "A tuple with a single element", complexity: 8200, canonical: (e, t) => e.tuple(t) }, { name: "Pair", domain: ["Function", "Anything", "Anything", ["Tuple", "Anything", "Anything"]], description: "A tuple of two elements", complexity: 8200, canonical: (e, t) => e.tuple(t) }, { name: "Triple", domain: ["Function", "Anything", "Anything", "Anything", ["Tuple", "Anything", "Anything", "Anything"]], description: "A tuple of three elements", complexity: 8200, canonical: (e, t) => e.tuple(t) }, { name: "Tuple", domain: ["Function", ["Some", "Anything"], ["Tuple", ["Some", "Anything"]]], description: "A fixed number of heterogeneous elements", complexity: 8200 }] }, { functions: [{ name: "BaseForm", domain: ["Function", "Anything", ["Optional", "Integer"], "String"], description: "`BaseForm(expr, base=10)`", complexity: 9e3, inert: !0 }, { name: "Delimiter", domain: (e, t) => { if (!t[0]) return "Nothing"; const i = t[0].domain.domainExpression; return ["Function", i, ["Optional", "String"], i]; }, complexity: 9e3, inert: !0, canonical: (e, t) => !t[0] || t[0].isMissing ? e.symbol("Nothing") : t[0] }, { name: "Error", domain: (e, t) => { if (!t[0]) return "Nothing"; const i = t[0].domain.domainExpression; return ["Function", i, ["Optional", "String"], ["Optional", "Expression"], i]; }, complexity: 500, inert: !0, hold: "all", evaluate: (e, t) => t[0].evaluate() }, { name: "Style", domain: (e, t) => { if (!t[0]) return "Nothing"; const i = t[0].domain.domainExpression; return ["Function", i, ["Optional", ["Head", "Dictionary"]], i]; }, complexity: 9e3, inert: !0 }] }, { functions: [{ name: "Apply", domain: "Function" }, { name: "About", domain: "Function" }, { name: "Block", domain: "Function" }, { name: "Domain", domain: "Function" }, { name: "Evaluate", domain: "Function", hold: "all", evaluate: (e, t) => t[0].evaluate() }, { name: "FromDigits", description: "`FromDigits(s, base=10)` return an integer representation of the string `s` in base `base`.", evaluate: (e, t) => { const i = t[0]; if (i.isMissing) return; if (!i.string) return e.error(e._fn("FromDigits", t), "Expected first argument as a string", ["LatexForm", i.latex]); const n = t[1]; if (n.isMissing && e.number(Number.parseInt(i.string, 10)), null === n.machineValue) return ["Error", e._fn("FromDigits", t), { str: "Expected `base` as an integer between 2 and 36" }, ["LatexForm", n.latex]]; const r = n.machineValue; if (r < 2 || r > 36) return ["Error", e._fn("FromDigits", t), { str: "Expected `base` as an integer between 2 and 36" }, ["LatexForm", n.latex]]; const [s, a] = qe(i.string, r); return a ? ["Error", s, { str: "unexpected-digits" }, ["LatexForm", a]] : e.number(s); } }, { name: "Head", domain: "Function", evaluate: (e, t) => { const i = t[0]; return "string" == typeof i.head ? e.symbol(i.head) : i.head; } }, { name: "Html", domain: ["Function", "Expression", "String"], evaluate: (e, t) => (t.length, e.string("")) }, { name: "IntegerString", domain: ["Function", "Integer", ["Optional", "Integer"], "String"], description: "`IntegerString(n, base=10)` return a string representation of the integer `n` in base `base`.", evaluate: (e, t) => { var _a, _b, _c, _d; const i = t[0]; if (i.isMissing) return; const n = (_c = (_a = i.machineValue) !== null && _a !== void 0 ? _a : (_b = i.decimalValue) === null || _b === void 0 ? void 0 : _b.toNumber()) !== null && _c !== void 0 ? _c : NaN; if (Number.isNaN(n) || !Number.isInteger(n)) return void e.signal(e._fn("IntegerString", t), `Expected first argument as an integer. Got \\(${i.latex}$\\)`); const r = t[1]; if (r.isMissing) return i.machineValue ? e.string(Math.abs(i.machineValue).toString()) : i.decimalValue ? e.string(i.decimalValue.abs().toString()) : e.string(Math.abs(Math.round((_d = i.asFloat) !== null && _d !== void 0 ? _d : NaN)).toString()); if (null === r.asSmallInteger) return void e.signal(e._fn("IntegerString", t), `Expected \`base\` as an integer between 2 and 36. Got \\(${r.latex}$\\)`); const s = r.asSmallInteger; if (!(s < 2 || s > 36)) return e.string(Math.abs(n).toString(s)); e.signal(e._fn("IntegerString", t), "Expected `base` as an integer between 2 and 36. Got " + s); } }, { name: "Lambda", domain: "Function", wikidata: "Q567612", hold: "all" }, { name: "Latex", domain: "Function", evaluate: (e, t) => 0 === t.length ? e._fn("LatexString", []) : e._fn("LatexString", [e.string(d(t.map((e => e.latex))))]) }, { name: "LatexString", domain: "Function", evaluate: (e, t) => 0 === t.length ? e._fn("LatexString", []) : e._fn("LatexString", [e.string(d(t.map((t => e.serialize(t)))))]) }, { name: "LatexTokens", domain: "Function", evaluate: (e, t) => 0 === t.length ? e._fn("LatexString", []) : e._fn("LatexString", [e.string(d(t.map((t => e.serialize(t)))))]) }, { name: "Parse", domain: "Function", evaluate: (e, t) => { if (0 === t.length) return e.symbol("Nothing"); const i = d(t.map((t => e.serialize(t)))); return e.parse(i); } }, { name: "String", domain: ["Function", ["Some", "Anything"], "String"], threadable: !0, evaluate: (e, t) => 0 === t.length ? e.string("") : e.string(t.map((e => { var _a; return (_a = e.string) !== null && _a !== void 0 ? _a : `\\(${e.latex}$\\)`; })).join("")) }, { name: "Symbol", complexity: 500, description: "Construct a new symbol with a name formed by concatenating the arguments", domain: ["Function", ["Some", "Anything"], "Symbol"], threadable: !0, evaluate: (e, t) => { if (0 === t.length) return e.symbol("Nothing"); const i = t.map((e => { const t = e.symbol; if (null !== t) return t; const n = i.string; if (null !== n) return n; const r = i.smallIntegerValue; return null !== r ? r.toString() : ""; })).join(""); return i.length > 0 ? e.symbol(i) : e.symbol("Nothing"); } }, { name: "SymbolName", domain: ["Function", "Anything", "String"], evaluate: (e, t) => t[0].symbol ? e.string(t[0].symbol) : e.symbol("Nothing") }, { name: "Tail", domain: ["Function", "Expression", ["List", "Expression]"]], evaluate: (e, t) => { var _a; return e._fn("List", (_a = t[0].ops) !== null && _a !== void 0 ? _a : []); } }, { name: "Timing", description: "`Timing(expr)` evaluates `expr` and return a `Pair` of the number of second elapsed for the evaluation, and the value of the evaluation", domain: ["Function", "Expression", ["Tuple", "Expression", "Number"]], evaluate: (e, t) => { var _a; if (!t[1] || t[1].isMissing) { const i = globalThis.performance.now(), n = t[0].evaluate(), r = 1e3 * (globalThis.performance.now() - i); return e.pair(e.number(r), n); } let i, n = Math.max(3, Math.round((_a = t[1].asSmallInteger) !== null && _a !== void 0 ? _a : 3)), r = []; for (; n > 0;) { const e = globalThis.performance.now(); i = t[0].evaluate(), r.push(1e3 * (globalThis.performance.now() - e)), n -= 1; } const s = Math.max(...r), a = Math.min(...r); r = r.filter((e => e > a && e < s)); const o = r.reduce(((e, t) => e + t), 0); return 0 === o ? e.pair(e.number(s), i) : e.pair(e.number(o / r.length), i); } }] }], jt = { symbols: [{ name: "True", wikidata: "Q16751793", domain: "Boolean", constant: !0 }, { name: "False", wikidata: "Q5432619", domain: "Boolean", constant: !0 }, { name: "Maybe", wikidata: "Q781546", domain: "MaybeBoolean", constant: !0 }], functions: [{ name: "And", wikidata: "Q191081", domain: "LogicOperator", threadable: !0, associative: !0, commutative: !0, idempotent: !0, complexity: 1e4, simplify: Gt, evaluate: Gt }, { name: "Or", wikidata: "Q1651704", domain: "LogicOperator", threadable: !0, associative: !0, commutative: !0, idempotent: !0, complexity: 1e4, simplify: Ht, evaluate: Ht }, { name: "Not", wikidata: "Q190558", domain: "LogicOperator", involution: !0, complexity: 10100, simplify: Wt, evaluate: Wt }, { name: "Equivalent", wikidata: "Q220433", domain: "LogicOperator", complexity: 10200, simplify: Bt, evaluate: Bt }, { name: "Implies", wikidata: "Q7881229", domain: "LogicOperator", complexity: 10200, simplify: Qt, evaluate: Qt }, { name: "Exists", domain: "MaybeBoolean" }] }; function Gt(e, t) { if (0 === t.length) return e.symbol("True"); const i = []; for (const n of t) { if ("False" === n.symbol) return e.symbol("False"); if ("True" !== n.symbol) { let t = !1; for (const r of i) if (r.isSame(n)) t = !0; else if ("Not" === n.head && n.op1.isSame(r) || "Not" === r.head && r.op1.isSame(n)) return e.symbol("False"); t || i.push(n); } } return 0 === i.length ? e.symbol("True") : 1 === i.length ? i[0] : e._fn("And", i); } function Ht(e, t) { if (0 === t.length) return e.symbol("True"); const i = []; for (const n of t) { if ("True" === n.symbol) return e.symbol("True"); if ("False" !== n.symbol) { let t = !1; for (const r of i) if (r.isSame(n)) t = !0; else if ("Not" === n.head && n.op1.isSame(r) || "Not" === r.head && r.op1.isSame(n)) return e.symbol("True"); t || i.push(n); } } return 0 === i.length ? e.symbol("True") : 1 === i.length ? i[0] : e._fn("Or", i); } function Wt(e, t) { const i = t[0].symbol; return "True" === i ? e.symbol("False") : "False" === i ? e.symbol("True") : "Maybe" === i ? e.symbol("Maybe") : void 0; } function Bt(e, t) { const i = t[0].symbol, n = t[1].symbol; return "True" === i && "True" === n || "False" === i && "False" === n ? e.symbol("True") : "True" === i && "False" === n || "False" === i && "True" === n ? e.symbol("False") : "Maybe" === i || "Maybe" === n ? e.symbol("Maybe") : void 0; } function Qt(e, t) { const i = t[0].symbol, n = t[1].symbol; return "True" === i && "True" === n || "False" === i && "False" === n || "False" === i && "True" === n ? e.symbol("True") : "True" === i && "False" === n ? e.symbol("False") : "Maybe" === i || "Maybe" === n ? e.symbol("Maybe") : void 0; } const Ut = [{ symbols: [{ name: "Degrees", domain: "RealNumber", constant: !0, value: ["Divide", "Pi", 180] }, { name: "Pi", domain: "TranscendentalNumber", algebraic: !1, constant: !0, hold: !0, wikidata: "Q167", value: e => We(e) ? e.DECIMAL_PI : Math.PI }], functions: [{ name: "Hypot", evaluate: ["Sqrt", ["Square", "_1"], ["Square", "_2"]], domain: ["Function", "Number", "Number", "Number"] }, { name: "Sin", domain: ["Function", "Number", "Number"], complexity: 5e3, simplify: (e, t) => { var _a; return (_a = ii(e, "Sin", t[0])) !== null && _a !== void 0 ? _a : (Be(e) ? e.box(["Divide", ["Subtract", ["Exp", ["Multiply", "ImaginaryUnit", t[0]]], ["Exp", ["Multiply", "ImaginaryUnit", ["Negate", t[0]]]]], ["Complex", 0, 2]]).canonical : void 0); }, evaluate: (e, t) => { var _a; return (_a = ii(e, "Sin", t[0])) !== null && _a !== void 0 ? _a : (Be(e) ? e.box(["Divide", ["Subtract", ["Exp", ["Multiply", "ImaginaryUnit", t[0]]], ["Exp", ["Multiply", "ImaginaryUnit", ["Negate", t[0]]]]], ["Complex", 0, 2]]).canonical : void 0); }, N: (e, t) => t[0].decimalValue ? e.number(e.chop(t[0].decimalValue.sin())) : t[0].complexValue ? e.number(t[0].complexValue.sin()) : null !== t[0].asFloat ? e.number(Math.sin(t[0].asFloat)) : void 0 }] }, { functions: [{ name: "Arctan", wikidata: "Q2257242", domain: ["Function", "Number", "ExtendedRealNumber"], complexity: 5200, simplify: (e, t) => ii(e, "Arctan", t[0]), N: (e, t) => t[0].decimalValue ? e.number(t[0].decimalValue.atan()) : t[0].complexValue ? e.number(t[0].complexValue.atan()) : null !== t[0].asFloat ? e.number(Math.atan(t[0].asFloat)) : void 0 }, { name: "Arctan2", wikidata: "Q776598", domain: ["Function", "Number", "Number", "Number"], complexity: 5200, N: (e, t) => t[0].decimalValue && t[1].decimalValue ? e.number(l.exports.Decimal.atan2(t[0].decimalValue, t[1].decimalValue)) : null !== t[0].asFloat && null !== t[1].asFloat ? e.number(Math.atan2(t[0].asFloat, t[1].asFloat)) : void 0 }, { name: "Cos", domain: ["Function", "Number", "Number"], complexity: 5050, simplify: (e, t) => { var _a; return (_a = ii(e, "Cos", t[0])) !== null && _a !== void 0 ? _a : e.box(["Sin", ["Add", t[0], ["Multiply", "Half", "Pi"]]]).canonical; }, evaluate: ["Sin", ["Add", "_1", ["Multiply", "Half", "Pi"]]], N: (e, t) => t[0].decimalValue ? e.number(t[0].decimalValue.cos()) : t[0].complexValue ? e.number(t[0].complexValue.cos()) : null !== t[0].asFloat ? e.number(Math.cos(t[0].asFloat)) : void 0 }, { name: "Tan", domain: ["Function", "Number", "Number"], complexity: 5100, simplify: (e, t) => { var _a; return (_a = ii(e, "Tan", t[0])) !== null && _a !== void 0 ? _a : e.box(["Divide", ["Sin", t[0]], ["Cos", t[0]]]).canonical; }, evaluate: ["Divide", ["Sin", "_1"], ["Cos", "_1"]], N: (e, t) => t[0].decimalValue ? e.number(t[0].decimalValue.tan()) : t[0].complexValue ? e.number(t[0].complexValue.tan()) : null !== t[0].asFloat ? e.number(Math.tan(t[0].asFloat)) : void 0 }] }, { functions: [{ name: "Arcosh", domain: ["Function", "Number", "Number"], complexity: 6200, simplify: (e, t) => { var _a; return (_a = ii(e, "Arcoshh", t[0])) !== null && _a !== void 0 ? _a : e.box(["Ln", ["Add", t[0], ["Sqrt", ["Subtract", ["Square", t[0]], 1]]]]).canonical; }, evaluate: ["Ln", ["Add", "_1", ["Sqrt", ["Subtract", ["Square", "_1"], 1]]]], N: (e, t) => t[0].decimalValue ? e.number(t[0].decimalValue.acosh()) : t[0].complexValue ? e.number(t[0].complexValue.acosh()) : null !== t[0].asFloat ? e.number(Math.acosh(t[0].asFloat)) : void 0 }, { name: "Arcsin", domain: ["Function", "Number", "Number"], complexity: 5500, simplify: (e, t) => { var _a; return (_a = ii(e, "Arcsin", t[0])) !== null && _a !== void 0 ? _a : e.box(["Multiply", 2, ["Arctan2", t[0], ["Add", 1, ["Sqrt", ["Subtract", 1, ["Square", t[0]]]]]]]).canonical; }, evaluate: ["Multiply", 2, ["Arctan2", "_1", ["Add", 1, ["Sqrt", ["Subtract", 1, ["Square", "_1"]]]]]], N: (e, t) => t[0].decimalValue ? e.number(t[0].decimalValue.asin()) : t[0].complexValue ? e.number(t[0].complexValue.asin()) : null !== t[0].asFloat ? e.number(Math.asin(t[0].asFloat)) : void 0 }, { name: "Arsinh", domain: ["Function", "Number", "Number"], complexity: 6100, simplify: (e, t) => { var _a; return (_a = ii(e, "Arsinh", t[0])) !== null && _a !== void 0 ? _a : e.box(["Ln", ["Add", t[0], ["Sqrt", ["Add", ["Square", t[0]], 1]]]]).canonical; }, evaluate: ["Ln", ["Add", "_1", ["Sqrt", ["Add", ["Square", "_1"], 1]]]], N: (e, t) => t[0].decimalValue ? e.number(t[0].decimalValue.asinh()) : t[0].complexValue ? e.number(t[0].complexValue.asinh()) : null !== t[0].asFloat ? e.number(Math.asinh(t[0].asFloat)) : void 0 }, { name: "Artanh", domain: ["Function", "Number", "Number"], complexity: 6300, simplify: (e, t) => { var _a; return (_a = ii(e, "Artanh", t[0])) !== null && _a !== void 0 ? _a : e.box(["Multiply", "Half", ["Ln", ["Divide", ["Add", 1, t[0]], ["Subtract", 1, t[0]]]]]).canonical; }, evaluate: ["Multiply", "Half", ["Ln", ["Divide", ["Add", 1, "_1"], ["Subtract", 1, "_1"]]]], N: (e, t) => t[0].decimalValue ? e.number(t[0].decimalValue.atanh()) : t[0].complexValue ? e.number(t[0].complexValue.atanh()) : null !== t[0].asFloat ? e.number(Math.atanh(t[0].asFloat)) : void 0 }, { name: "Cosh", domain: ["Function", "Number", "Number"], complexity: 6050, simplify: (e, t) => ii(e, "Cosh", t[0]), evaluate: ["Multiply", "Half", ["Add", ["Exp", "_1"], ["Exp", ["Negate", "_1"]]]], N: (e, t) => t[0].decimalValue ? e.number(t[0].decimalValue.cosh()) : t[0].complexValue ? e.number(t[0].complexValue.cosh()) : null !== t[0].asFloat ? e.number(Math.cosh(t[0].asFloat)) : void 0 }, { name: "Cot", domain: ["Function", "Number", "Number"], complexity: 5600, simplify: (e, t) => { var _a; return (_a = ii(e, "Cot", t[0])) !== null && _a !== void 0 ? _a : e.box(["Divide", ["Cos", t[0]], ["Sin", t[0]]]).canonical; }, evaluate: ["Divide", ["Cos", "_1"], ["Sin", "_1"]], N: (e, t) => t[0].decimalValue ? e.number(e.DECIMAL_ONE.div(t[0].decimalValue.tan())) : t[0].complexValue ? e.number(t[0].complexValue.tan().inverse()) : null !== t[0].asFloat ? e.number(1 / Math.tan(t[0].asFloat)) : void 0 }, { name: "Csc", description: "Cosecant", domain: ["Function", "Number", "Number"], complexity: 5600, simplify: (e, t) => { var _a; return (_a = ii(e, "Csc", t[0])) !== null && _a !== void 0 ? _a : e.box(["Divide", 1, ["Sin", t[0]]]).canonical; }, evaluate: ["Divide", 1, ["Sin", "_1"]], N: (e, t) => t[0].decimalValue ? e.number(e.DECIMAL_ONE.div(t[0].decimalValue.sin())) : t[0].complexValue ? e.number(t[0].complexValue.sin().inverse()) : null !== t[0].asFloat ? e.number(1 / Math.sin(t[0].asFloat)) : void 0 }, { name: "Haversine", wikidata: "Q2528380", domain: ["Function", "ExtendedRealNumber", "RealNumber"], evaluate: ["Divide", ["Subtract", 1, ["Cos", "_1"]], 2] }, { name: "InverseHaversine", domain: ["Function", "ExtendedRealNumber", "RealNumber"], evaluate: ["Multiply", 2, ["Arcsin", ["Sqrt", "_1"]]] }, { name: "Sec", description: "Secant, inverse of cosine", domain: ["Function", "Number", "Number"], complexity: 5500, simplify: (e, t) => { var _a; return (_a = ii(e, "Sec", t[0])) !== null && _a !== void 0 ? _a : e.box(["Divide", 1, ["Cos", t[0]]]).canonical; }, evaluate: ["Divide", 1, ["Cos", "_1"]], N: (e, t) => t[0].decimalValue ? e.number(e.DECIMAL_ONE.div(t[0].decimalValue.cos())) : t[0].complexValue ? e.number(t[0].complexValue.cos().inverse()) : null !== t[0].asFloat ? e.number(1 / Math.cos(t[0].asFloat)) : void 0 }, { name: "Sinh", domain: ["Function", "Number", "Number"], complexity: 6e3, simplify: (e, t) => { var _a; return (_a = ii(e, "Sinh", t[0])) !== null && _a !== void 0 ? _a : e.box(["Multiply", "Half", ["Subtract", ["Exp", t[0]], ["Exp", ["Negate", t[0]]]]]).canonical; }, evaluate: ["Multiply", "Half", ["Subtract", ["Exp", "_1"], ["Exp", ["Negate", "_1"]]]] }] }, { functions: [{ name: "Csch", domain: ["Function", "Number", "ExtendedRealNumber"], complexity: 6200, simplify: (e, t) => { var _a; return (_a = ii(e, "Csch", t[0])) !== null && _a !== void 0 ? _a : e.box(["Divide", 1, ["Sinh", t[0]]]).canonical; }, evaluate: ["Divide", 1, ["Sinh", "_1"]], N: (e, t) => t[0].decimalValue ? e.number(e.DECIMAL_ONE.div(t[0].decimalValue.sinh())) : t[0].complexValue ? e.number(t[0].complexValue.sinh().inverse()) : null !== t[0].asFloat ? e.number(1 / Math.sinh(t[0].asFloat)) : void 0 }, { name: "Sech", domain: ["Function", "Number", "Number"], complexity: 6200, simplify: (e, t) => { var _a; return (_a = ii(e, "Sech", t[0])) !== null && _a !== void 0 ? _a : e.box(["Divide", 1, ["Cosh", t[0]]]).canonical; }, evaluate: ["Divide", 1, ["Cosh", "_1"]], N: (e, t) => t[0].decimalValue ? e.number(e.DECIMAL_ONE.div(t[0].decimalValue.cosh())) : t[0].complexValue ? e.number(t[0].complexValue.cosh().inverse()) : null !== t[0].asFloat ? e.number(1 / Math.cosh(t[0].asFloat)) : void 0 }, { name: "Tanh", domain: ["Function", "Number", "Number"], complexity: 6200, simplify: (e, t) => { var _a; return (_a = ii(e, "Tanh", t[0])) !== null && _a !== void 0 ? _a : e.box(["Divide", ["Sinh", t[0]], ["Cosh", t[0]]]).canonical; }, evaluate: ["Divide", ["Sinh", "_1"], ["Cosh", "_1"]], N: (e, t) => t[0].decimalValue ? e.number(t[0].decimalValue.tanh()) : t[0].complexValue ? e.number(t[0].complexValue.tanh()) : null !== t[0].asFloat ? e.number(Math.tanh(t[0].asFloat)) : void 0 }] }, { functions: [{ name: "Arccos", domain: ["Function", "Number", "ExtendedRealNumber"], complexity: 5550, evaluate: ["Subtract", ["Divide", "Pi", 2], ["Arcsin", "_1"]], simplify: (e, t) => { var _a; return (_a = ii(e, "Arccos", t[0])) !== null && _a !== void 0 ? _a : e.box(["Subtract", ["Divide", "Pi", 2], ["Arcsin", t[0]]]).canonical; }, N: (e, t) => t[0].decimalValue ? e.number(t[0].decimalValue.acos()) : t[0].complexValue ? e.number(t[0].complexValue.acos()) : null !== t[0].asFloat ? e.number(Math.acos(t[0].asFloat)) : void 0 }, { name: "Coth", domain: ["Function", "Number", "Number"], complexity: 6300, simplify: (e, t) => { var _a; return (_a = ii(e, "Coth", t[0])) !== null && _a !== void 0 ? _a : e.box(["Divide", 1, ["Tanh", t[0]]]).canonical; }, evaluate: ["Divide", 1, ["Tanh", "_1"]], N: (e, t) => t[0].decimalValue ? e.number(e.DECIMAL_ONE.div(t[0].decimalValue.tanh())) : t[0].complexValue ? e.number(t[0].complexValue.tanh().inverse()) : null !== t[0].asFloat ? e.number(1 / Math.tanh(t[0].asFloat)) : void 0 }, { name: "InverseFunction", domain: "Function", simplify: (e, t) => ni(e, t[0]), evaluate: (e, t) => ni(e, t[0]) }] }], Kt = ["Sqrt", 2], Yt = ["Sqrt", 3], Jt = ["Sqrt", 5], Xt = ["Sqrt", 6], ei = [[[0, 1], { Sin: 0, Cos: 1, Tan: 0, Cot: NaN, Sec: 1, Csc: NaN }], [[1, 12], { Sin: ["Divide", ["Subtract", Xt, Kt], 4], Cos: ["Divide", ["Add", Xt, Kt], 4], Tan: ["Subtract", 2, Yt], Cot: ["Add", 2, Yt], Sec: ["Subtract", Xt, Kt], Csc: ["Add", Xt, Kt] }], [[1, 10], { Sin: ["Divide", ["Subtract", Jt, 1], 4], Cos: ["Divide", ["Sqrt", ["Add", 10, ["Multiply", 2, Jt]]], 4], Tan: ["Divide", ["Sqrt", ["Subtract", 25, ["Multiply", 10, Jt]]], 4], Cot: ["Sqrt", ["Add", 5, ["Multiply", 2, Jt]]], Sec: ["Divide", ["Sqrt", ["Subtract", 50, ["Multiply", 10, Jt]]], 5], Csc: ["Add", 1, Jt] }], [[1, 8], { Sin: "$\\frac\\sqrt{2-\\sqrt2}{2}$", Cos: "$\\frac {\\sqrt {2+{\\sqrt {2}}}}{2}$", Tan: "$\\sqrt{2} - 1$", Cot: "$\\sqrt{2} + 1$", Sec: "$\\sqrt{ 4 - 2\\sqrt{2}$", Csc: "$\\sqrt{ 4 + 2\\sqrt{2}$" }], [[1, 6], { Sin: "$\\frac{1}{2}$", Cos: "$\\frac{\\sqrt{3}}{2}$", Tan: "$\\frac{\\sqrt{3}}{3}$", Cot: "$\\frac{2\\sqrt{3}}{3}$", Sec: "$\\sqrt{3}$", Csc: 2 }], [[1, 5], { Sin: "$\\frac{\\sqrt{10- 2\\sqrt{5}}} {4}$", Cos: "$\\frac{1+ \\sqrt{5}} {4}$", Tan: "$\\sqrt{5-2\\sqrt5}$", Cot: "$\\frac{\\sqrt{25+10\\sqrt5}} {5}$", Sec: "$\\sqrt{5} - 1$", Csc: "$\\frac{\\sqrt{50+10\\sqrt{5}}} {5}$" }], [[1, 4], { Sin: ["Divide", Kt, 2], Cos: ["Divide", Kt, 2], Tan: 1, Cot: 1, Sec: Kt, Csc: Kt }], [[3, 10], { Sin: "$\\frac{1+ \\sqrt{5}} {4}$", Cos: "$\\frac{\\sqrt{10- 2\\sqrt{5}}} {4}$", Tan: "$\\frac{\\sqrt{25+10\\sqrt5}} {5}$", Cot: "$\\sqrt{5-2\\sqrt5}$", Sec: "$$", Csc: "$\\frac{\\sqrt{50+10\\sqrt{5}}} {5}$" }], [[1, 3], { Sin: ["Divide", Yt, 2], Cos: "Half", Tan: Yt, Cot: ["Divide", Yt, 3], Sec: 2, Csc: ["Divide", ["Multiply", 2, Yt], 3] }], [[3, 8], { Sin: "$\\frac{ \\sqrt{2 + \\sqrt{2}} } {2}$", Cos: "$\\frac{ \\sqrt{2 - \\sqrt{2}} } {2}$", Tan: "$\\sqrt{2} + 1$", Cot: "$\\sqrt{2} - 1$", Sec: "$\\sqrt{ 4 + 2 \\sqrt{2} }$", Csc: "$\\sqrt{ 4 - 2 \\sqrt{2} }$" }], [[2, 5], { Sin: "$\\frac{\\sqrt{10+ 2\\sqrt{5}}} {4}$", Cos: "$\\frac{\\sqrt{5}-1} {4}$", Tan: "$\\sqrt{5+2\\sqrt{5}}$", Cot: "$\\frac{\\sqrt{25-10\\sqrt{5}}} {5}$", Sec: "$1 + \\sqrt{5}$", Csc: "$\\frac{\\sqrt{50-10\\sqrt{5}}} {5}$" }], [[5, 12], { Sin: "$\\frac{\\sqrt{6} + \\sqrt{2}} {4}$", Cos: "$\\frac{ \\sqrt{6} - \\sqrt{2}} {4}$", Tan: "$2+\\sqrt{3}$", Cot: "$2-\\sqrt{3}$", Sec: "$\\sqrt{6}+\\sqrt{2}$", Csc: "$\\sqrt{6} - \\sqrt{2}$" }], [[1, 2], { Sin: 1, Cos: 0, Tan: NaN, Cot: 0, Sec: NaN, Csc: 1 }]], ti = { Sin: [[1, "Sin"], [1, "Cos"], [-1, "Sin"], [-1, "Cos"]], Cos: [[1, "Cos"], [-1, "Sin"], [-1, "Cos"], [1, "Sin"]], Sec: [[1, "Sec"], [-1, "Csc"], [-1, "Sec"], [1, "Csc"]], Csc: [[1, "Csc"], [1, "Sec"], [-1, "Csc"], [-1, "Sec"]], Tan: [[1, "Tan"], [-1, "Cot"], [1, "Tan"], [-1, "Cot"]], Cot: [[1, "Cot"], [-1, "Tan"], [1, "Cot"], [-1, "Tan"]] }; function ii(e, t, i) { var _a; if (!i || i.isMissing) return; const n = e.cache("constructible-trigonometric-values", (() => { var _a; const t = []; for (const [i, n] of ei) { const r = {}; for (const t of Object.keys(n)) r[t] = ((_a = e.parse(je(n[t]))) !== null && _a !== void 0 ? _a : e.box(n[t])).canonical; t.push([i, r]); } return t; }), (e => { for (const [t, i] of e) for (const e of Object.values(i)) e._purge(); return e; })); if (!(i = (_a = i.numericValue) !== null && _a !== void 0 ? _a : i).isLiteral) return; let r = i.asFloat; if (null === r) return; r %= 2 * Math.PI; const s = "Cos" !== t && "Sec" !== t ? Math.sign(r) : 1; r = Math.abs(r); const a = Math.floor(2 * r / Math.PI); let o; r %= Math.PI / 2, [o, t] = ti[t][a], o *= s; for (const [[i, s], a] of n) if (0 === e.chop(r - Math.PI * i / s)) return o < 0 ? It(a[t]) : a[t]; } function ni(e, t) { const i = t.op1.head; if ("string" != typeof i) return t; const n = { Sin: "Arcsin", Cos: "Arccos", Tan: "Arctan", Sec: "Arcsec", Csc: " Arccsc", Sinh: "Arsinh", Cosh: "Arcosh", Tanh: "Artanh", Sech: "Arcsech", Csch: "Arcsch", Arcosh: "Cosh", Arcos: "Cos", Arccsc: "Csc", Arcsch: "Csch", Arcsec: "Sec", Arcsin: "Sin", Arsinh: "Sinh", Arctan: "Tan", Artanh: "Tanh" }[i]; return n ? e._fn(n, [t.op1.op1]) : t; } function ri(e) { return Object.fromEntries(Object.entries(e).filter((([e, t]) => void 0 !== t))); } function si(e) { const t = { ...e }; return e.zero || e.one || e.negativeOne ? (t.number = !0, t.integer = !0, t.rational = !0, t.algebraic = !0, t.real = !0, t.extendedReal = !0, t.complex = !0, t.extendedComplex = !0, t.imaginary = !1, t.positive = !1, t.nonPositive = !0, t.negative = !1, t.nonNegative = !0, t.zero = e.zero, t.notZero = !e.zero, t.one = e.one, t.negativeOne = e.negativeOne, t.negativeOne = !1, t.infinity = !1, t.NaN = !1, t.finite = !0, t.even = e.one, t.odd = !e.one, t.prime = !1, t.composite = !1, t) : (!0 === t.notZero && (t.imaginary || (t.real = !0), t.zero = !1), (t.positive || t.nonNegative) && (t.negativeOne = !1), t.positive ? (t.nonPositive = !1, t.negative = !1, t.nonNegative = !0) : t.nonPositive ? (t.positive = !1, t.negative = t.notZero, t.nonNegative = !t.zero) : t.negative ? (t.positive = !1, t.nonPositive = t.notZero, t.nonNegative = !1) : t.nonNegative && (t.positive = t.notZero, t.nonPositive = !t.zero, t.negative = !1), (t.positive || t.negative || t.nonPositive || t.nonNegative) && (t.number = !0, t.finite ? t.real = !0 : t.finite || (t.complex = !0), t.imaginary = !1), t.infinity && (t.finite = !1, t.NaN = !1), t.finite && (t.number = !0, t.complex = !0, t.infinity = !1, t.NaN = !1), e.even && (t.odd = !1), e.odd && (t.even = !1), t.integer && (t.rational = !0), t.rational && (t.algebraic = !0), t.algebraic && (t.real = !0), t.extendedReal && (t.real = !0), t.real && (t.complex = !0), t.imaginary && (t.complex = !0), t.extendedComplex && (t.complex = !0), t.complex && (t.number = !0), t.real && t.infinity && (t.extendedReal = !0), t.complex && t.infinity && (t.extendedComplex = !0), (t.even || t.infinity || t.NaN || t.negative || t.imaginary || !1 === t.integer) && (t.prime = !1), t.number && t.prime && (t.composite = !1), t); } function ai(e) { if (!e) return {}; const t = e.domainExpression, i = {}; return e.isSubdomainOf("Number") ? (i.number = !0, "Integer" === t && (i.integer = !0), "RationalNumber" === t && (i.rational = !0), "AlgebraicNumber" === t && (i.algebraic = !0), "TranscendentalNumber" === t && (i.algebraic = !1, i.real = !0), "ExtendedRealNumber" === t && (i.extendedReal = !0), "RealNumber" === t && (i.real = !0), "ImaginaryNumber" === t && (i.imaginary = !0), "ExtendedComplexNumber" === t && (i.extendedComplex = !0), "ComplexNumber" === t && (i.complex = !0)) : (i.number = !1, i.integer = !1, i.rational = !1, i.algebraic = !1, i.real = !1, i.extendedReal = !1, i.complex = !1, i.extendedComplex = !1, i.imaginary = !1, i.positive = !1, i.nonPositive = !1, i.negative = !1, i.nonNegative = !1, i.zero = !1, i.notZero = !1, i.one = !1, i.negativeOne = !1, i.infinity = !1, i.NaN = !1, i.odd = !1, i.even = !1, i.prime = !1, i.composite = !1), ri(si(i)); } function oi(e) { return ri({ number: (e = e.canonical).isNumber, integer: e.isInteger, rational: e.isRational, algebraic: e.isAlgebraic, real: e.isReal, extendedReal: e.isExtendedReal, complex: e.isComplex, extendedComplex: e.isExtendedComplex, imaginary: e.isImaginary, positive: e.isPositive, nonPositive: e.isNonPositive, negative: e.isNegative, nonNegative: e.isNonNegative, zero: e.isZero, notZero: e.isNotZero, one: e.isOne, negativeOne: e.isNegativeOne, infinity: e.isInfinity, NaN: e.isNaN, finite: e.isFinite, even: e.isEven, odd: e.isOdd, prime: e.isPrime, composite: e.isComposite }); } class li { constructor(e, t) { var _a, _b; this._engine = e, this._def = t, this.scope = e.context, this.name = t.name, this.constant = (_a = t.constant) !== null && _a !== void 0 ? _a : !1, this.hold = (_b = t.hold) !== null && _b !== void 0 ? _b : !0, this._purge(); } _purge() { var _a, _b, _c, _d; this._value = (_a = this._value) === null || _a === void 0 ? void 0 : _a._purge(); const e = this._def, t = this._engine, i = ri({ description: e.description, wikidata: e.wikidata, number: e.number, integer: e.integer, rational: e.rational, algebraic: e.algebraic, real: e.real, extendedReal: e.extendedReal, complex: e.complex, zero: e.zero, notZero: e.notZero, one: e.one, negativeOne: e.negativeOne, infinity: e.infinity, NaN: e.NaN, finite: e.finite, even: e.even, odd: e.odd, prime: e.prime, composite: e.composite }); if ("value" in e && "number" == typeof e.value) { const n = t.number(e.value); let r; const s = e.domain ? t.domain(e.domain) : void 0; return r = s && n.valueDomain.isSubdomainOf(s) ? s : n.valueDomain, this._value = n, this._domain = r, this.setProps(oi(n)), this.setProps(ai(r)), void this.setProps(i); } let n, r; if (Ze(e.value) ? n = t.parse(e.value) : "function" == typeof e.value ? n = t.box((_b = e.value(t)) !== null && _b !== void 0 ? _b : "Undefined") : e.value && (n = t.box(e.value)), !n && !1 === e.hold) throw Error(`Symbol definition "${e.name}": Expected a value "hold=false" `); n = n === null || n === void 0 ? void 0 : n.canonical; const s = e.domain ? t.domain(e.domain) : void 0; if (r = !s || n && !n.valueDomain.isSubdomainOf(s) ? (_d = (_c = n === null || n === void 0 ? void 0 : n.valueDomain) !== null && _c !== void 0 ? _c : t.defaultDomain) !== null && _d !== void 0 ? _d : t.domain("Anything") : s, !n) return this._value = void 0, this._domain = r, this.setProps(ai(r)), void this.setProps(i); this._value = n, this._domain = r, this.setProps(oi(n)), this.setProps(ai(r)), this.setProps(i); } get value() { return this._value; } set value(e) { if (this.constant) throw Error(`The value of the constant "${this.name}" cannot be changed`); "number" == typeof e && (e = this._engine.box(e)), this._value = e, e && this.setProps(oi(e)); } get domain() { return this._domain; } set domain(e) { var _a; if (!e) return void (this._domain = void 0); e = this._engine.domain(e); const t = (_a = this.value) === null || _a === void 0 ? void 0 : _a.valueDomain; t && !t.isSubdomainOf(e) && (e = t), this._domain = e, this.setProps(ai(e)); } updateFlags(e) { this.setProps(si(e)); } setProps(e) { e.wikidata && (this.wikidata = e.wikidata), e.description && (this.description = e.description), void 0 !== e.number && (this._number = e.number), void 0 !== e.integer && (this._integer = e.integer), void 0 !== e.rational && (this._rational = e.rational), void 0 !== e.algebraic && (this._algebraic = e.algebraic), void 0 !== e.real && (this._real = e.real), void 0 !== e.extendedReal && (this._extendedReal = e.extendedReal), void 0 !== e.complex && (this._complex = e.complex), void 0 !== e.extendedComplex && (this._extendedComplex = e.extendedComplex), void 0 !== e.imaginary && (this._imaginary = e.imaginary), void 0 !== e.positive && (this._positive = e.positive), void 0 !== e.nonPositive && (this._nonPositive = e.nonPositive), void 0 !== e.negative && (this._negative = e.negative), void 0 !== e.nonNegative && (this._nonNegative = e.nonNegative), void 0 !== e.zero && (this._zero = e.zero), void 0 !== e.notZero && (this._notZero = e.notZero), void 0 !== e.one && (this._one = e.one), void 0 !== e.negativeOne && (this._negativeOne = e.negativeOne), void 0 !== e.infinity && (this._infinity = e.infinity), void 0 !== e.finite && (this._finite = e.finite), void 0 !== e.NaN && (this._NaN = e.NaN), void 0 !== e.even && (this._even = e.even), void 0 !== e.odd && (this._odd = e.odd), void 0 !== e.prime && (this._prime = e.prime), void 0 !== e.composite && (this._composite = e.composite); } get number() { return this._number; } set number(e) { this.updateFlags({ number: e }); } get integer() { return this._integer; } set integer(e) { this.updateFlags({ integer: e }); } get rational() { return this._rational; } set rational(e) { this.updateFlags({ rational: e }); } get algebraic() { return this._algebraic; } set algebraic(e) { this.updateFlags({ algebraic: e }); } get real() { return this._real; } set real(e) { this.updateFlags({ real: e }); } get extendedReal() { return this._extendedReal; } set extendedReal(e) { this.updateFlags({ extendedReal: e }); } get complex() { return this._complex; } set complex(e) { this.updateFlags({ complex: e }); } get extendedComplex() { return this._extendedComplex; } set extendedComplex(e) { this.updateFlags({ extendedComplex: e }); } get imaginary() { return this._imaginary; } set imaginary(e) { this.updateFlags({ imaginary: e }); } get positive() { return this._positive; } set positive(e) { this.updateFlags({ positive: e }); } get nonPositive() { return this._nonPositive; } set nonPositive(e) { this.updateFlags({ nonPositive: e }); } get negative() { return this._negative; } set negative(e) { this.updateFlags({ negative: e }); } get nonNegative() { return this._nonNegative; } set nonNegative(e) { this.updateFlags({ nonNegative: e }); } get zero() { return this._zero; } set zero(e) { this.updateFlags({ zero: e }); } get notZero() { return this._notZero; } set notZero(e) { this.updateFlags({ notZero: e }); } get one() { return this._one; } set one(e) { this.updateFlags({ one: e }); } get negativeOne() { return this._negativeOne; } set negativeOne(e) { this.updateFlags({ negativeOne: e }); } get infinity() { return this._infinity; } set infinity(e) { this.updateFlags({ infinity: e }); } get finite() { return this._finite; } set finite(e) { this.updateFlags({ finite: e }); } get NaN() { return this._NaN; } set NaN(e) { this.updateFlags({ NaN: e }); } get even() { return this._even; } set even(e) { this.updateFlags({ even: e }); } get odd() { return this._odd; } set odd(e) { this.updateFlags({ odd: e }); } get prime() { var _a; if (void 0 === this._prime && ((_a = this._value) === null || _a === void 0 ? void 0 : _a.isNumber)) if (!this._value.isInteger || this._value.isNonPositive) this._prime = !1, this._composite = !1; else { const e = this._value.asFloat; null !== e ? (this._prime = bt(e), this._composite = !this._prime) : (this._prime = void 0, this._composite = void 0); } return this._prime; } set prime(e) { this.updateFlags({ prime: e }); } get composite() { if (void 0 === this._composite) { const e = this.prime; this._composite = void 0 === e ? void 0 : !e; } return this._composite; } set composite(e) { this.updateFlags({ composite: e }); } } class ui { constructor(e, t) { var _a, _b, _c, _d, _f, _g, _h, _j, _k, _l; const i = t.domain ? "function" == typeof t.domain ? t.domain : e.domain(t.domain) : e.domain("Function"), n = (_a = t.hold) !== null && _a !== void 0 ? _a : "none", r = (_b = t.idempotent) !== null && _b !== void 0 ? _b : !1, s = (_c = t.involution) !== null && _c !== void 0 ? _c : !1; if (r && s) throw Error(`Function Definition "${t.name}": the 'idempotent' and 'involution' flags are mutually exclusive in function `); this.name = t.name, this.description = t.description, this.wikidata = t.wikidata, this.scope = e.context, this.threadable = (_d = t.threadable) !== null && _d !== void 0 ? _d : !1, this.associative = (_f = t.associative) !== null && _f !== void 0 ? _f : !1, this.commutative = (_g = t.commutative) !== null && _g !== void 0 ? _g : !1, this.idempotent = r, this.involution = s, this.inert = (_h = t.inert) !== null && _h !== void 0 ? _h : !1, this.pure = (_j = t.pure) !== null && _j !== void 0 ? _j : !0, this.complexity = (_k = t.complexity) !== null && _k !== void 0 ? _k : 1e5, this.hold = n, this.sequenceHold = (_l = t.sequenceHold) !== null && _l !== void 0 ? _l : !1, this.range = t.range, this.domain = i, this.canonical = t.canonical, this.simplify = t.simplify, this.evaluate = t.evaluate ? "function" == typeof t.evaluate ? t.evaluate : e.box(t.evaluate).canonical : void 0, this.N = t.N, this.evalDimension = t.evalDimension, this.sgn = t.sgn, this.compile = t.compile; } _purge() { } } function ci(e, t) { return new ui(e, t); } function hi(e, t) { const i = e.engine; return "Negate" === e.head && "Negate" === t.head ? hi(e.op1, t.op1) : "Negate" === e.head ? It(hi(e.op1, t)) : "Negate" === t.head ? It(hi(e, t.op1)) : "Add" === e.head ? i.add(e.ops.map((e => hi(e, t)))) : "Add" === t.head ? i.add(t.ops.map((t => hi(e, t)))) : i.mul([e, t]); } function mi(e, t) { if (1 === t) return e; const i = hi(e, e); return 2 === t ? i : t % 2 == 0 ? mi(i, t / 2) : hi(mi(i, Math.round(t / 2) - 1), e); } function fi(e = "all") { if ("all" === e) return fi(["domains", "core", "collections", "algebra", "arithmetic", "calculus", "combinatorics", "dimensions", "linear-algebra", "logic", "numeric", "other", "physics", "polynomials", "relop", "statistics", "trigonometry", "units"]); const t = []; for (const i of e) { const e = gi[i]; e && Array.isArray(e) ? t.push(...e) : e && t.push(e); } return t; } const gi = { arithmetic: Rt, core: Zt, collections: [{ symbols: [{ name: "EmptySet", domain: "Set", constant: !0, wikidata: "Q226183" }], functions: [{ name: "Element", domain: "Predicate", complexity: 11200 }, { name: "NotElement", domain: "Predicate", complexity: 11200, canonical: (e, t) => e.fn("Not", [e.fn("Element", t)]) }, { name: "Subset", domain: "Predicate", complexity: 11200 }, { name: "NotSubset", domain: "Predicate", complexity: 11200, canonical: (e, t) => e.fn("Not", [e.fn("Subset", t)]) }, { name: "Superset", domain: "Predicate", complexity: 11200 }, { name: "SupersetEqual", domain: "Predicate", complexity: 11200 }, { name: "NotSuperset", domain: "Predicate", complexity: 11200, canonical: (e, t) => e.fn("Not", [e.fn("Superset", t)]) }, { name: "NotSupersetEqual", domain: "Predicate", complexity: 11200, canonical: (e, t) => e.fn("Not", [e.fn("SupersetEqual", t)]) }, { name: "SubsetEqual", domain: "Predicate", complexity: 11200 }, { name: "NotSubsetNotEqual", domain: "Predicate", complexity: 11200, canonical: (e, t) => e.fn("Not", [e.fn("SubsetEqual", t)]) }, { name: "CartesianProduct", domain: ["Function", ["Some", "Set"], "Set"], wikidata: "Q173740" }, { name: "Complement", domain: ["Function", "Set", "Set"], wikidata: "Q242767" }, { name: "Intersection", domain: ["Function", ["Some", "Set"], "Set"], wikidata: "Q185837", threadable: !0, associative: !0, commutative: !0, involution: !0, evaluate: function (e, t) { return e.symbol("EmptySet"); } }, { name: "Union", domain: ["Function", ["Some", "Set"], "Set"], wikidata: "Q185359", threadable: !0, associative: !0, commutative: !0, involution: !0, evaluate: function (e, t) { return e.symbol("False"); } }, { name: "Set", domain: ["Function", ["Some", "Anything"], "Set"] }, { name: "SetMinus", domain: ["Function", "Set", "Expresison", "Set"], wikidata: "Q18192442", evaluate: function (e, t) { return e.symbol("EmptySet"); } }, { name: "SymmetricDifference", domain: ["Function", ["Some", "Set"], "Set"], wikidata: "Q1147242" }] }, { functions: [{ name: "Sequence" }] }], logic: jt, relop: { functions: [{ name: "Equal", domain: "RelationalOperator", commutative: !0, complexity: 11e3, evaluate: (e, t) => { if (t.length < 2) return e.symbol("True"); let i; for (const n of t) if (i) { if (!1 === i.isEqual(n)) return e.symbol("False"); } else i = n; return e.symbol("True"); } }, { name: "NotEqual", wikidata: "Q28113351", commutative: !0, complexity: 11e3, domain: "RelationalOperator", evaluate: (e, t) => { if (t.length < 2) return e.symbol("False"); let i; for (const n of t) if (i) { if (!0 === i.isEqual(n)) return e.symbol("False"); } else i = n; return e.symbol("True"); } }, { name: "Less", complexity: 11e3, domain: "RelationalOperator", evaluate: (e, t) => { if (t.length < 2) return e.symbol("True"); let i; for (const n of t) { if (!n.isNumber) return; if (i) { const t = e.fn("Subtract", [n, i]).N().sgn; if (null == t) return; if (t <= 0) return e.symbol("False"); i = n; } else i = n; } return e.symbol("True"); } }, { name: "NotLess", complexity: 11e3, domain: "RelationalOperator", canonical: (e, t) => e._fn("Not", [e._fn("Less", t)]) }, { name: "Greater", complexity: 11e3, domain: "RelationalOperator", canonical: (e, t) => e._fn("Less", t.reverse()), evaluate: (e, t) => { if (t.length < 2) return e.symbol("True"); let i; for (const n of t) { if (!n.isNumber) return; if (i) { const t = e.fn("Subtract", [n, i]).N().sgn; if (null == t) return; if (t >= 0) return e.symbol("False"); i = n; } else i = n; } return e.symbol("True"); } }, { name: "NotGreater", complexity: 11e3, domain: "RelationalOperator", canonical: (e, t) => e._fn("Not", [e._fn("Greater", t)]) }, { name: "LessEqual", complexity: 11e3, domain: "RelationalOperator", evaluate: (e, t) => { if (t.length < 2) return e.symbol("True"); let i; for (const n of t) { if (!n.isNumber) return; if (i) { const t = e.fn("Subtract", [n, i]).N().sgn; if (null == t) return; if (t < 0) return e.symbol("False"); i = n; } else i = n; } return e.symbol("True"); } }, { name: "NotLessNotEqual", complexity: 11e3, domain: "RelationalOperator", canonical: (e, t) => e._fn("Not", [e._fn("LessEqual", t)]) }, { name: "GreaterEqual", complexity: 11e3, domain: "RelationalOperator", canonical: (e, t) => e._fn("LessEqual", t.reverse()), evaluate: (e, t) => { if (t.length < 2) return e.symbol("True"); let i; for (const n of t) { if (!n.isNumber) return; if (i) { const t = e.fn("Subtract", [n, i]).N().sgn; if (null == t) return; if (t > 0) return e.symbol("False"); i = n; } else i = n; } return e.symbol("True"); } }, { name: "NotGreaterNotEqual", complexity: 11e3, domain: "RelationalOperator", canonical: (e, t) => e._fn("Not", [e._fn("GreaterEqual", t)]) }, { name: "TildeFullEqual", description: "Indicate isomorphism, congruence and homotopic equivalence", domain: "RelationalOperator" }, { name: "NotTildeFullEqual", complexity: 11100, domain: "RelationalOperator", canonical: (e, t) => e._fn("Not", [e._fn("TildeFullEqual", t)]) }, { name: "TildeEqual", description: "Approximately or asymptotically equal", domain: "RelationalOperator", complexity: 11e3 }, { name: "NotTildeEqual", complexity: 11100, domain: "RelationalOperator", canonical: (e, t) => e._fn("Not", [e._fn("TildeEqual", t)]) }, { name: "Approx", complexity: 11100, domain: "RelationalOperator" }, { name: "NotApprox", complexity: 11100, domain: "RelationalOperator", canonical: (e, t) => e._fn("Not", [e._fn("Approx", t)]) }, { name: "ApproxEqual", complexity: 11100, domain: "RelationalOperator" }, { name: "NotApproxEqual", complexity: 11100, domain: "RelationalOperator", canonical: (e, t) => e._fn("Not", [e._fn("ApproxEqual", t)]) }, { name: "ApproxNotEqual", domain: "RelationalOperator", complexity: 11100 }, { name: "NotApproxNotEqual", complexity: 11100, domain: "RelationalOperator", canonical: (e, t) => e._fn("Not", [e._fn("ApproxNotEqual", t)]) }, { name: "Precedes", complexity: 11100, domain: "RelationalOperator" }, { name: "NotPrecedes", complexity: 11100, domain: "RelationalOperator", canonical: (e, t) => e._fn("Not", [e._fn("Precedes", t)]) }, { name: "Succeeds", domain: "RelationalOperator" }, { name: "NotSucceeds", complexity: 11100, domain: "RelationalOperator", canonical: (e, t) => e._fn("Not", [e._fn("Succeeds", t)]) }] }, polynomials: [{ functions: [{ name: "Expand", description: "Expand out products and positive integer powers", evaluate: (e, t) => t[0] ? function (e) { if ("Multiply" === (e = e.simplify()).head) return 2 === e.nops ? hi(e.op1, e.op2) : e.ops.reduce(((e, t) => hi(e, t)), e.engine.ONE); if ("Power" === e.head) { const t = e.op1.head; if ("Multiply" === t) return e.engine.mul(e.op1.ops.map((t => e.engine.power(t, e.op2)))); if ("Negate" === t) { const t = e.op2.asSmallInteger; if (null !== t && t > 0) return t % 2 == 0 ? e.engine.power(e.op1.op1, e.op2) : e.engine.negate(e.engine.power(e.op1.op1, e.op2)); } if ("Add" === t) { const t = e.op2.asSmallInteger; if (null !== t) return t > 0 ? mi(e.op1, t) : e.engine.inverse(mi(e.op1, -t)); } } return e; }(t[0]) : e.symbol("Nothing") }] }], physics: { symbols: [{ name: "Mu-0", description: "Vaccum permeability", constant: !0, wikidata: "Q1515261", domain: "RealNumber", value: 125663706212e-17 }] }, trigonometry: Ut }; function pi(e, t) { if ("object" != typeof t || !("name" in t) || !t.name) throw Error("Missing name for definition " + JSON.stringify(t)); if (!/[A-Za-z][A-Za-z0-9-]*/.test(t.name) && 1 !== t.name.length) throw Error("Invalid definition name " + t.name); } function di(e, t) { if (void 0 === t) return; if (Array.isArray(t)) { for (const i of t) di(e, i); return; } e.context.dictionary || (e.context.dictionary = { symbols: new Map, functions: new Map, symbolWikidata: new Map, functionWikidata: new Map }); const i = e.context.dictionary; if (t.symbols) for (const n of t.symbols) { pi(0, n); const t = new li(e, n); if (n.wikidata) { if (i.symbolWikidata.has(n.wikidata)) throw Error(`Duplicate symbol with wikidata ${n.wikidata}, ${n.name} and ${i.symbolWikidata.get(n.wikidata).name}`); i.symbolWikidata.set(n.wikidata, t); } if (i.symbols.has(n.name)) throw Error(`Duplicate symbol definition ${n.name}:\n${JSON.stringify(i.symbols.get(n.name))}\n${JSON.stringify(n)}`); i.symbols.set(n.name, t); } if (t.functions) for (const n of t.functions) { pi(0, n); const t = ci(e, n); if (i.functions.has(n.name) ? i.functions.set(n.name, [...i.functions.get(n.name), t]) : i.functions.set(n.name, [t]), n.wikidata) { if (i.functionWikidata.has(n.wikidata)) throw Error(`Duplicate function with wikidata ${n.wikidata}, ${n.name} and ${i.functionWikidata.get(n.wikidata).name}`); i.functionWikidata.set(n.wikidata, t); } } } function vi(e) { return Number.isInteger(e) ? Math.floor(Math.log2(Math.abs(e)) / Math.log2(10)) + (e > 0 ? 1 : 2) : 2; } const bi = function e(t) { var _a, _b; if (t.symbol) return 1; if (t.isLiteral) { if (t.isZero) return 1; if (t.isInteger && null !== t.asFloat) return vi(t.asFloat); const [e, i] = t.rationalValue; if (null !== e && null !== i) return vi(e) + vi(i) + 1; if (t.complexValue) { const e = t.complexValue; return vi(e.re) + vi(e.im) + 1; } if (t.isNumber) return 2; } const i = t.head; return ("string" == typeof i ? 1 : e(i)) + ((_b = (_a = t.ops) === null || _a === void 0 ? void 0 : _a.reduce(((t, i) => t + e(i)), 0)) !== null && _b !== void 0 ? _b : 0); }; class yi { constructor(e) { this._items = e ? e instanceof yi ? new Map(e._items) : new Map(e) : new Map; } has(e) { for (const t of this._items.keys()) if (t.isSame(e)) return !0; return !1; } get(e) { for (const [t, i] of this._items) if (t.isSame(e)) return i; } clear() { this._items.clear(); } set(e, t) { for (const i of this._items.keys()) if (i.isSame(e)) return void this._items.set(i, t); this._items.set(e, t); } delete(e) { this._items.delete(e); } [Symbol.iterator]() { return this._items.entries(); } entries() { return this._items.entries(); } } class Ni extends Pe { constructor(e, t, i) { super(e, i), this._pattern = Ze(t) ? e.parse(t) : e.box(t), this._pattern.isCanonical && (this._canonicalPattern = this._pattern); } get hash() { return Qe("Pattern") ^ this._pattern.hash; } _purge() { var _a; this._pattern._purge(), (_a = this._canonicalPattern) === null || _a === void 0 ? void 0 : _a._purge(); } get json() { return nt(this.engine, "Pattern", [this._pattern]); } get head() { return "Pattern"; } get valueDomain() { return this.engine.domain("Pattern"); } get isCanonical() { return !0; } set isCanonical(e) { } isSame(e) { return this === e || e instanceof Ni && this._pattern.isSame(e._pattern); } isEqual(e) { return e instanceof Ni && this._pattern.isEqual(e._pattern); } match(e, t) { var _a, _b; let i = this._pattern; return (t === null || t === void 0 ? void 0 : t.exact) || (this._canonicalPattern || (this._canonicalPattern = this._pattern.canonical), i = this._canonicalPattern), function (e, t, i) { var _a; return _i(e, t, {}, { numericTolerance: (_a = i === null || i === void 0 ? void 0 : i.numericTolerance) !== null && _a !== void 0 ? _a : 1e-10 }) || null; }(e, i, { recursive: (_a = t === null || t === void 0 ? void 0 : t.recursive) !== null && _a !== void 0 ? _a : !1, numericTolerance: (_b = t === null || t === void 0 ? void 0 : t.numericTolerance) !== null && _b !== void 0 ? _b : 0 }); } test(e, t) { return null !== this.match(e, t); } count(e, t) { let i = 0; for (const n of e) null !== this.match(n, t) && (i += 1); return i; } subs(e) { return new Ni(this.engine, this._pattern.subs(e).canonical); } } function xi(e, t, i) { const n = function (e) { const t = e.match(/^__?_?([a-zA-Z0-9]+)/); return null === t ? "" : t[1]; }(e); return "" === n ? i : i[n] ? t.isSame(i[n]) ? i : null : (i[n] = t, i); } function _i(e, t, i, n) { const r = e.engine; if (t instanceof yt) return e instanceof yt ? 0 === n.numericTolerance ? t.isSame(e) ? i : null : t.isEqualWithTolerance(e, n.numericTolerance) ? i : null : null; const s = t.string; if (null !== s) return e.string === s ? i : null; const a = t.symbol; if (null !== a) return a.startsWith("_") ? xi(a, e, i) : a === e.symbol ? i : null; if (t.nops !== e.nops) return null; const o = t.keys; if (null !== o) { const t = e.keys; if (null === t) return null; for (const e of o) { const r = _i(t[e], o[e], i, n); if (null === r) return null; i = r; } return i; } if (t.ops) { const s = t.head; if ("string" == typeof s && s.startsWith("_")) return xi(s, r.box(e.head), i); { const t = _i(r.box(e.head), r.pattern(s), i, n); if (null === t) return null; i = t; } const a = e.ops; let o = { ...i }, l = 0; const u = t.ops.map((e => r.pattern(e))); for (; l < t.nops;) { const e = u[l], t = e.symbol; if (null !== t) if (t.startsWith("__")) { let e = 0; if (void 0 === u[l + 1]) e = a.length + 1; else { let t = !1; for (; !t && e < a.length;) t = null !== _i(a[e], u[l + 1], i, n), e += 1; if (!t) return null; } if (!t.startsWith("___") && e <= 1) return null; o = xi(t, r.fn("Sequence", a.splice(0, e - 1)), o); } else if (t.startsWith("_")) o = xi(t, a.shift(), o); else { const t = _i(a.shift(), e, i, n); if (null === t) return null; o = { ...o, ...t }; } else { const t = _i(a.shift(), e, i, n); if (null === t) return null; o = { ...o, ...t }; } if (null === o) return null; l += 1; } return o; } return null; } class Si extends Pe { constructor(e, t, i) { if (super(e, i), this._name = t.normalize(), n = this._name, /[\u0000-\u0020\u0022\u0060\ufffe\uffff]/.test(n) || /^[\u0021\u0022\u0024-\u002e\u003a\u003f\u0040\u005b\u005d\u005e\u007b\u007d\u007e]$/.test(n[0])) throw Error(`The name "${this._name}" cannot be used as a symbol name`); var n; this._repairDefinition(), e._register(this); } get hash() { return void 0 === this._hash && (this._hash = Qe(this._name)), this._hash; } _purge() { var _a; return (_a = this._def) === null || _a === void 0 ? void 0 : _a._purge(); } get isPure() { var _a, _b, _c; return (_c = (((_a = this._def) === null || _a === void 0 ? void 0 : _a.constant) && ((_b = this._def.value) === null || _b === void 0 ? void 0 : _b.isPure))) !== null && _c !== void 0 ? _c : !1; } get isCanonical() { return !0; } set isCanonical(e) { } get wikidata() { var _a, _b, _c; return (_c = (_a = this._wikidata) !== null && _a !== void 0 ? _a : (_b = this._def) === null || _b === void 0 ? void 0 : _b.wikidata) !== null && _c !== void 0 ? _c : ""; } get description() { return this._def && this._def.description ? "string" == typeof this._def.description ? [this._def.description] : this._def.description : []; } get url() { var _a, _b; return (_b = (_a = this._def) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : ""; } get complexity() { return 7; } get head() { return "Symbol"; } get symbol() { return this._name; } get isMissing() { return "Missing" === this._name; } get isLiteral() { return !1; } get symbolDefinition() { return this._def; } _repairDefinition() { if ("_" === this._name[0]) return; let e; this._wikidata && (e = this.engine.getSymbolDefinition("", this._wikidata)), e || (e = this.engine.getSymbolDefinition(this._name), e && e.wikidata && this._wikidata && e.wikidata !== this._wikidata && (e = void 0)), e ? (this._name = e.name, this._def = e) : null !== this.engine.defaultDomain ? (this._def = this.engine.defineSymbol({ name: this._name, wikidata: this._wikidata, domain: this.engine.defaultDomain, ...ai(this.engine.defaultDomain) }), this._name = this._def.name) : this._def = void 0; } get value() { var _a; return (_a = this._def) === null || _a === void 0 ? void 0 : _a.value; } set value(e) { var _a, _b; if ("_" === this._name[0]) throw Error(`The value of the wildcard "${this._name}" cannot be changed`); let t; if (this.engine.forget(this._name), void 0 !== e) { const i = this.engine.box(e); t = (_a = i.value) !== null && _a !== void 0 ? _a : i.evaluate(); } this._def ? this._def.value = t : this._def = this.engine.defineSymbol({ name: this._name, value: t, domain: (_b = this.engine.defaultDomain) !== null && _b !== void 0 ? _b : this.engine.domain("Anything") }); } get numericValue() { var _a, _b; return (_b = (_a = this._def) === null || _a === void 0 ? void 0 : _a.value) === null || _b === void 0 ? void 0 : _b.numericValue; } get domain() { var _a, _b, _c; return "function" == typeof ((_a = this._def) === null || _a === void 0 ? void 0 : _a.domain) ? this.engine.domain(this._def.domain(this.engine, [])) : (_c = (_b = this._def) === null || _b === void 0 ? void 0 : _b.domain) !== null && _c !== void 0 ? _c : this.engine.domain("Anything"); } set domain(e) { if ("_" === this._name[0]) throw Error(`The domain of the wildcard "${this._name}" cannot be changed`); this._def ? this._def.domain = e : this._def = this.engine.defineSymbol({ name: this._name, domain: e, ...ai(e) }); } get json() { return rt(this.engine, this._name, { wikidata: this._wikidata }); } get sgn() { var _a, _b, _c, _d; const e = (_a = this.value) === null || _a === void 0 ? void 0 : _a.sgn; return void 0 !== e ? e : !0 === ((_b = this._def) === null || _b === void 0 ? void 0 : _b.zero) ? 0 : !0 === ((_c = this._def) === null || _c === void 0 ? void 0 : _c.positive) ? 1 : !0 === ((_d = this._def) === null || _d === void 0 ? void 0 : _d.negative) ? -1 : void 0; } has(e) { return "string" == typeof e ? this._name === e : e.includes(this._name); } isSame(e) { return e instanceof Si && this._name === e._name; } match(e, t) { return e instanceof Si && this._name === e._name ? {} : null; } isEqual(e) { var _a; if (this === e) return !0; if (null !== e.symbol) return e.symbol === this._name; const t = (_a = this._def) === null || _a === void 0 ? void 0 : _a.value; if (t) return t.isEqual(e); if (e.isZero) { if (this.isZero) return !0; if (this.isNotZero) return !1; } return this.isZero && e.isNotZero || this.engine.ask(["NotEqual", this, e]).length, !1; } isLess(e) { if (null !== e.symbol && e.symbol === this._name) return !1; if (e.isZero) { const e = this.sgn; if (null === e) return !1; if (void 0 !== e) return e < 0; } } isLessEqual(e) { if (null !== e.symbol && e.symbol === this._name) return !0; if (e.isZero) { const e = this.sgn; if (null === e) return !1; if (void 0 !== e) return e <= 0; } return this.isLess(e) || this.isEqual(e); } isGreater(e) { if (null !== e.symbol && e.symbol === this._name) return !1; if (e.isZero) { const e = this.sgn; if (null === e) return !1; if (void 0 !== e) return e > 0; } } isGreaterEqual(e) { if (null !== e.symbol && e.symbol === this._name) return !0; if (e.isZero) { const e = this.sgn; if (null === e) return !1; if (void 0 !== e) return e >= 0; } return this.isGreater(e) || this.isEqual(e); } get isZero() { var _a, _b, _c, _d; return (_b = (_a = this._def) === null || _a === void 0 ? void 0 : _a.zero) !== null && _b !== void 0 ? _b : (_d = (_c = this._def) === null || _c === void 0 ? void 0 : _c.value) === null || _d === void 0 ? void 0 : _d.isZero; } get isNotZero() { var _a; const e = (_a = this._def) === null || _a === void 0 ? void 0 : _a.notZero; if ("boolean" == typeof e) return e; const t = this.sgn; return "number" == typeof t ? 0 !== t : void 0; } get isOne() { var _a; return (_a = this._def) === null || _a === void 0 ? void 0 : _a.one; } get isNegativeOne() { var _a; return (_a = this._def) === null || _a === void 0 ? void 0 : _a.negativeOne; } get isOdd() { var _a; return (_a = this._def) === null || _a === void 0 ? void 0 : _a.odd; } get isEven() { var _a; return (_a = this._def) === null || _a === void 0 ? void 0 : _a.even; } get isPrime() { var _a; return (_a = this._def) === null || _a === void 0 ? void 0 : _a.prime; } get isComposite() { var _a; return (_a = this._def) === null || _a === void 0 ? void 0 : _a.composite; } get isInfinity() { var _a; return (_a = this._def) === null || _a === void 0 ? void 0 : _a.infinity; } get isNaN() { var _a; return (_a = this._def) === null || _a === void 0 ? void 0 : _a.NaN; } get isPositive() { var _a; return (_a = this._def) === null || _a === void 0 ? void 0 : _a.positive; } get isNonPositive() { var _a; return (_a = this._def) === null || _a === void 0 ? void 0 : _a.nonPositive; } get isNegative() { var _a; return (_a = this._def) === null || _a === void 0 ? void 0 : _a.negative; } get isNonNegative() { var _a; return (_a = this._def) === null || _a === void 0 ? void 0 : _a.nonNegative; } get isNumber() { var _a; return (_a = this._def) === null || _a === void 0 ? void 0 : _a.number; } get isInteger() { var _a; return (_a = this._def) === null || _a === void 0 ? void 0 : _a.integer; } get isRational() { var _a; return (_a = this._def) === null || _a === void 0 ? void 0 : _a.rational; } get isAlgebraic() { var _a; return (_a = this._def) === null || _a === void 0 ? void 0 : _a.rational; } get isReal() { var _a; return (_a = this._def) === null || _a === void 0 ? void 0 : _a.real; } get isExtendedReal() { var _a; return (_a = this._def) === null || _a === void 0 ? void 0 : _a.extendedReal; } get isComplex() { var _a; return (_a = this._def) === null || _a === void 0 ? void 0 : _a.complex; } get isImaginary() { var _a; return (_a = this._def) === null || _a === void 0 ? void 0 : _a.imaginary; } get canonical() { var _a, _b, _c, _d, _f, _g; return !1 === ((_a = this._def) === null || _a === void 0 ? void 0 : _a.hold) ? (_g = (_d = (_c = (_b = this._def) === null || _b === void 0 ? void 0 : _b.value) === null || _c === void 0 ? void 0 : _c.value) !== null && _d !== void 0 ? _d : (_f = this._def) === null || _f === void 0 ? void 0 : _f.value) !== null && _g !== void 0 ? _g : this : this; } simplify(e) { var _a, _b; const t = (e === null || e === void 0 ? void 0 : e.rules) ? (_a = this.replace(e.rules)) !== null && _a !== void 0 ? _a : this : this; if (!1 === ((_b = t.symbolDefinition) === null || _b === void 0 ? void 0 : _b.hold)) { const i = t.value; if (i) return i.simplify(e); } return t; } evaluate(e) { var _a, _b, _c, _d; return !0 === ((_a = this.symbolDefinition) === null || _a === void 0 ? void 0 : _a.hold) ? this : (_d = (_c = (_b = this._def) === null || _b === void 0 ? void 0 : _b.value) === null || _c === void 0 ? void 0 : _c.evaluate(e)) !== null && _d !== void 0 ? _d : this; } N(e) { var _a, _b, _c, _d; const t = (_a = this._def) === null || _a === void 0 ? void 0 : _a.value; return (_d = (_c = (_b = t === null || t === void 0 ? void 0 : t.N(e)) !== null && _b !== void 0 ? _b : t === null || t === void 0 ? void 0 : t.evaluate(e)) !== null && _c !== void 0 ? _c : t) !== null && _d !== void 0 ? _d : this; } replace(e, t) { return ct(this, e, t); } subs(e) { var _a; return (_a = e[this._name]) !== null && _a !== void 0 ? _a : this; } } class wi extends Pe { constructor(e, t, i) { super(e, i), this._value = t; } get domainExpression() { return this._value; } get hash() { return void 0 === this._hash && (this._hash = Ai(this._value)), this._hash; } get isCanonical() { return !0; } isEqual(e) { return !1; } isSame(e) { return !1; } isSubdomainOf(e) { return function (e, t) { const i = t instanceof wi ? t._value : t, n = e instanceof wi ? e._value : e; if ("string" == typeof n && "string" == typeof i) { const e = Ii(n, i); if ("boolean" == typeof e) return e; } return !0; }(this, e); } isMemberOf(e) { return !1; } get json() { return "string" == typeof this._value ? rt(this.engine, this._value, { wikidata: this._wikidata }) : ["Domain", this._value]; } match(e, t) { return e instanceof wi && this.isSame(e) ? {} : null; } get head() { return "Domain"; } get domain() { return this.engine.domain("Domain"); } get codomain() { return "string" == typeof this._value || "Function" !== this._value[0] ? null : this.engine.domain(this._value[this._value.length - 1]); } is(e) { return this.isSame(e); } get isNothing() { return "Nothing" === this._value; } get isFunction() { return "string" != typeof this._value && "Function" === this._value[0]; } get isPredicate() { if ("string" == typeof this._value) return !1; if ("Function" !== this._value[0]) return !1; const e = this._value[this._value.length]; return e instanceof wi && e.isBoolean; } get isNumericFunction() { if ("string" == typeof this._value) return !1; if ("Function" !== this._value[0]) return !1; for (const e of this._value) if (!Ii(e, "Number")) return !1; return !0; } get isBoolean() { return ki(this._value); } get isRealFunction() { if ("string" == typeof this._value) return !1; if ("Function" !== this._value[0]) return !1; for (const e of this._value) if (!Ii(e, "ExtendedRealNumber")) return !1; return !0; } get isNumeric() { return this.isSubdomainOf("Number"); } get isLogicOperator() { return !("string" == typeof this._value || "Function" !== this._value[0] || this._value.length < 2 || this._value.length > 3 || !ki(this._value[this._value.length - 1]) || !ki(this._value[1]) || 3 === this._value.length && !ki(this._value[2])); } get isRelationalOperator() { return "string" != typeof this._value && "Function" === this._value[0] && 3 === this._value.length && !!ki(this._value[this._value.length - 1]); } } function Ei(e, t, i) { if (t instanceof wi) return t; let n; if (!n && "string" == typeof t) { const r = { Function: ["Function", ["Optional", ["Some", "Anything"]], "Anything"], NumericFunction: ["Function", ["Optional", ["Some", "Number"]], "Number"], RealFunction: ["Function", ["Optional", ["Some", "ExtendedRealNumber"]], "ExtendedRealNumber"], TrigonometricFunction: ["Function", "Number", "Number"], HyperbolicFunction: ["Function", "Number", "Number"], LogicOperator: ["Function", "MaybeBoolean", ["Optional", "MaybeBoolean"], "MaybeBoolean"], Predicate: ["Function", ["Optional", ["Some", "Anything"]], "MaybeBoolean"], RelationalOperator: ["Function", "Anything", "Anything", "MaybeBoolean"] }[t]; r && (n = new wi(e, r, i)); } return n || (n = new wi(e, t, i)), n; } function Ii(e, t) { var _a, _b; return "string" == typeof e && ((_b = (_a = { Number: ["Number", "ExtendedComplexNumber", "ExtendedRealNumber", "ComplexNumber", "ImaginaryNumber", "RealNumber", "TranscendentalNumber", "AlgebraicNumber", "RationalNumber", "Integer", "NegativeInteger", "NegativeNumber", "NonNegativeNumber", "NonNegativeInteger", "NonPositiveNumber", "NonPositiveInteger", "PositiveInteger", "PositiveNumber"], ExtendedComplexNumber: ["Number", "ExtendedRealNumber", "ComplexNumber", "ImaginaryNumber", "RealNumber", "TranscendentalNumber", "AlgebraicNumber", "RationalNumber", "Integer", "NegativeInteger", "NegativeNumber", "NonNegativeNumber", "NonNegativeInteger", "NonPositiveNumber", "NonPositiveInteger", "PositiveInteger", "PositiveNumber"], ExtendedRealNumber: ["ExtendedRealNumber", "RealNumber", "TranscendentalNumber", "AlgebraicNumber", "RationalNumber", "Integer", "NegativeInteger", "NegativeNumber", "NonNegativeNumber", "NonNegativeInteger", "NonPositiveNumber", "NonPositiveInteger", "PositiveInteger", "PositiveNumber"], ComplexNumber: ["ComplexNumber", "ImaginaryNumber"], ImaginaryNumber: ["ImaginaryNumber"], RealNumber: ["RealNumber", "TranscendentalNumber", "AlgebraicNumber", "RationalNumber", "Integer", "NegativeInteger", "NegativeNumber", "NonNegativeNumber", "NonNegativeInteger", "NonPositiveNumber", "NonPositiveInteger", "PositiveInteger", "PositiveNumber"], TranscendentalNumber: ["TranscendentalNumber"], AlgebraicNumber: ["AlgebraicNumber", "RationalNumber", "Integer", "NegativeInteger", "NonNegativeInteger", "NonPositiveInteger", "PositiveInteger"], RationalNumber: ["RationalNumber", "Integer", "NegativeInteger", "NonNegativeInteger", "NonPositiveInteger", "PositiveInteger"], Integer: ["Integer", "NegativeInteger", "NonNegativeInteger", "NonPositiveInteger", "PositiveInteger"], NegativeNumber: ["NegativeNumber", "NegativeInteger"], NonNegativeNumber: ["NonNegativeNumber", "PositiveNumber", "NonNegativeInteger", "PositiveInteger"], NonPositiveNumber: ["NonPositiveNumber", "NegativeNumber", "NegativeInteger"], PositiveNumber: ["PositiveNumber", "PositiveInteger"], NegativeInteger: ["NegativeInteger"], PositiveInteger: ["PositiveInteger"], NonNegativeInteger: ["NonNegativeInteger", "PositiveInteger"], NonPositiveInteger: ["NegativeInteger"] }[t]) === null || _a === void 0 ? void 0 : _a.includes(e)) !== null && _b !== void 0 ? _b : void 0); } function ki(e) { return "string" == typeof e && ["Boolean", "MaybeBoolean", "True", "False", "Maybe"].includes(e); } function Ai(e) { if ("string" == typeof e) return Qe("domain:" + e); let t = ""; for (const i of e) t += "" + Ai(i); return Qe(t); } class Mi { constructor(e) { var _a, _b, _c, _d, _f; this._cache = {}, this._commonSymbols = { True: null, False: null, Maybe: null, All: null, Missing: null, Nothing: null, None: null, Undefined: null, Pi: null, ImaginaryUnit: null }, this._commonNumbers = { 0: null, 1: null, 2: null, 3: null, 4: null, 5: null, 6: null, 7: null, 8: null, 9: null, 10: null }, this._commonDomains = { Anything: null, Nothing: null, Boolean: null, MaybeBoolean: null, String: null, Domain: null, Symbol: null, Integer: null, RationalNumber: null, AlgebraicNumber: null, RealNumber: null, ExtendedRealNumber: null, ImaginaryNumber: null, ComplexNumber: null, ExtendedComplexNumber: null, Number: null, PositiveInteger: null, TranscendentalNumber: null, PositiveNumber: null, Function: null, NumericFunction: null, RealFunction: null, TrigonometricFunction: null, HyperbolicFunction: null, LogicOperator: null, Predicate: null, RelationalOperator: null, Expression: null, BooleanExpression: null, NumericExpression: null }, this._latexDictionary = e === null || e === void 0 ? void 0 : e.latexDictionary, this._jsonSerializationOptions = { exclude: [], shorthands: ["function", "symbol", "string", "dictionary", "number"], metadata: [], repeatingDecimal: !0 }, this._stats = { highwaterMark: 0, symbols: new Set, expressions: new Set }, this._defaultDomain = null, this._numericMode = (_a = e === null || e === void 0 ? void 0 : e.numericMode) !== null && _a !== void 0 ? _a : "auto", this._precision = Math.max((_b = e === null || e === void 0 ? void 0 : e.numericPrecision) !== null && _b !== void 0 ? _b : 100, Math.floor(Se)), this._decimal = l.exports.Decimal.clone({ precision: this._precision }), this.decimal = e => new this._decimal(e), this.complex = (e, t) => new c.exports.Complex(e, t), this.tolerance = (_c = e === null || e === void 0 ? void 0 : e.tolerance) !== null && _c !== void 0 ? _c : 1e-10, this.ZERO = new yt(this, 0), this.ONE = new yt(this, 1), this.TWO = new yt(this, 2), this.HALF = new yt(this, [1, 2]), this.NEGATIVE_ONE = new yt(this, -1), this.I = new yt(this, c.exports.Complex.I), this.NAN = new yt(this, NaN), this.POSITIVE_INFINITY = new yt(this, 1 / 0), this.NEGATIVE_INFINITY = new yt(this, -1 / 0), this.COMPLEX_INFINITY = new yt(this, c.exports.Complex.INFINITY), this.purge(); const t = (_d = e === null || e === void 0 ? void 0 : e.dictionaries) !== null && _d !== void 0 ? _d : Mi.getDictionaries(); this.pushScope({ dictionary: t, scope: { warn: e => { for (const t of e) t.message; }, timeLimit: 2, memoryLimit: 1, recursionLimit: 1024 } }), null === (e === null || e === void 0 ? void 0 : e.assumptions) ? this.pushScope() : this.pushScope({ assumptions: e === null || e === void 0 ? void 0 : e.assumptions }); for (const e of Object.keys(this._commonDomains)) this._commonDomains[e] && !this._commonDomains[e].symbolDefinition && this._commonDomains[e]._repairDefinition(); for (const e of Object.keys(this._commonSymbols)) this._commonSymbols[e] && !this._commonSymbols[e].symbolDefinition && this._commonSymbols[e]._repairDefinition(); this._defaultDomain = null === (e === null || e === void 0 ? void 0 : e.defaultDomain) ? null : this.domain((_f = e === null || e === void 0 ? void 0 : e.defaultDomain) !== null && _f !== void 0 ? _f : "ExtendedRealNumber"); } static getDictionaries(e = "all") { return fi(e); } purge() { var _a, _b; this.DECIMAL_NEGATIVE_ONE = this.decimal(-1), this.DECIMAL_NAN = this.decimal(NaN), this.DECIMAL_ZERO = this.decimal(0), this.DECIMAL_ONE = this.decimal(1), this.DECIMAL_TWO = this.decimal(2), this.DECIMAL_HALF = this.DECIMAL_ONE.div(this.DECIMAL_TWO), this.DECIMAL_PI = this.DECIMAL_NEGATIVE_ONE.acos(); const e = this._stats.symbols.values(), t = this._stats.expressions.values(); this._stats.symbols = new Set, this._stats.expressions = new Set; for (const t of e) t._purge(); for (const e of t) e._purge(); for (const e of Object.values(this._commonDomains)) e === null || e === void 0 ? void 0 : e._purge(); for (const e of Object.values(this._commonSymbols)) e === null || e === void 0 ? void 0 : e._purge(); let i = this.context; for (; i;) { if ((_a = i.dictionary) === null || _a === void 0 ? void 0 : _a.functions) for (const [e, t] of i.dictionary.functions) for (const e of t) e._purge(); if ((_b = i.dictionary) === null || _b === void 0 ? void 0 : _b.symbols) for (const [e, t] of i.dictionary.symbols) t._purge(); i = i.parentScope; } for (const e of Object.keys(this._cache)) this._cache[e].value && (this._cache[e].purge ? this._cache[e].value = this._cache[e].purge(this._cache[e].value) : delete this._cache[e]); } _register(e) { this._stats.highwaterMark += 1; } _unregister(e) { } get stats() { const e = this._stats.expressions; return this._stats.expressions = null, this._stats.expressions = e, { ...this._stats }; } get precision() { return this._precision; } set precision(e) { const t = this._precision; "machine" === e && (e = Math.floor(Se)), e !== t && (this._latexSyntax && this.latexSyntax.updateOptions({ precision: e, avoidExponentsInRange: [-6, e] }), this._precision = Math.max(e, Math.floor(Se)), this._decimal = this._decimal.config({ precision: this._precision }), this.purge()); } get numericMode() { return this._numericMode; } set numericMode(e) { e !== this._numericMode && (this._numericMode = e, "complex" !== e && "machine" !== e || (this._precision = Math.floor(Se)), this._latexSyntax && this.latexSyntax.options.precision > this._precision && this.latexSyntax.updateOptions({ precision: this._precision }), this.purge()); } get timeLimit() { let e = this.context; for (; e;) { if (void 0 !== e.timeLimit) return e.timeLimit; e = e.parentScope; } return 2; } get iterationLimit() { let e = this.context; for (; e;) { if (void 0 !== e.iterationLimit) return e.iterationLimit; e = e.parentScope; } return 1024; } get recursionLimit() { let e = this.context; for (; e;) { if (void 0 !== e.recursionLimit) return e.recursionLimit; e = e.parentScope; } return 1024; } get defaultDomain() { return this._defaultDomain; } set defaultDomain(e) { this._defaultDomain = null === e ? null : this.domain(e); } get tolerance() { return this._tolerance; } set tolerance(e) { this._tolerance = "number" == typeof e && Number.isFinite(e) ? Math.max(e, 0) : 1e-10, this._decimalTolerance = this.decimal(this._tolerance); } chop(e) { return "number" == typeof e && Math.abs(e) <= this._tolerance || e instanceof l.exports.Decimal && e.abs().lte(this._decimalTolerance) || e instanceof c.exports.Complex && Math.abs(e.re) <= this._tolerance && Math.abs(e.im) <= this._tolerance ? 0 : e; } get latexSyntax() { return this._latexSyntax || (this._latexSyntax = new _e({ computeEngine: this, dictionary: this._latexDictionary, precision: this.precision, avoidExponentsInRange: [-6, this.precision], onError: e => { throw Error(e[0].message.toString()); } })), this._latexSyntax; } static getLatexDictionary(e = "all") { return _e.getDictionary(e); } set costFunction(e) { "function" != typeof e && (this._cost = bi), this._cost = e; } get costFunction() { var _a; return (_a = this._cost) !== null && _a !== void 0 ? _a : bi; } getSymbolDefinition(e, t) { var _a, _b, _c; let i, n = this.context; if (t) for (; n && !i;) i = (_a = n.dictionary) === null || _a === void 0 ? void 0 : _a.symbolWikidata.get(t), n = n.parentScope; for (; n && !i;) t && (i = (_b = n.dictionary) === null || _b === void 0 ? void 0 : _b.symbolWikidata.get(t)), i || (i = (_c = n.dictionary) === null || _c === void 0 ? void 0 : _c.symbols.get(e)), n = n.parentScope; return i; } getFunctionDefinition(e, t) { var _a, _b; if (!t) { let t = this.context; for (; t;) { const i = (_a = t.dictionary) === null || _a === void 0 ? void 0 : _a.functions.get(e); if (i) return i[0]; t = t.parentScope; } return; } const i = this.domain(["Function", ...t === null || t === void 0 ? void 0 : t.map((e => e.domain.json))]); let n = this.context; for (; n;) { const r = (_b = n.dictionary) === null || _b === void 0 ? void 0 : _b.functions.get(e); if (r) for (const e of r) if (e.domain) if ("function" == typeof e.domain) { const n = this.domain(e.domain(this, t)); if (n && i.isSubdomainOf(n)) return e; } else if (i.isSubdomainOf(e.domain)) return e; n = n.parentScope; } } defineSymbol(e) { const t = new li(this, e); return this.context.dictionary || (this.context.dictionary = { symbols: new Map, functions: new Map, symbolWikidata: new Map, functionWikidata: new Map }), e.name && this.context.dictionary.symbols.set(e.name, t), e.wikidata && this.context.dictionary.symbolWikidata.set(e.wikidata, t), t; } pushScope(e) { var _a; if (this.context = { ...e === null || e === void 0 ? void 0 : e.scope, parentScope: this.context, assumptions: this.context ? new yi(this.context.assumptions) : new yi }, di(this, e === null || e === void 0 ? void 0 : e.dictionary), void 0 !== (e === null || e === void 0 ? void 0 : e.assumptions)) for (const t of e.assumptions) this.assume((_a = this.parse(je(t))) !== null && _a !== void 0 ? _a : t); } popScope() { var _a; const e = (_a = this.context) === null || _a === void 0 ? void 0 : _a.parentScope; if (this.context.warnings) { const e = [...this.context.warnings]; this.context.warnings = [], this.context.warn && this.context.warn(e); } e && this.context.warnings && this.context.warnings.length > 0 && (e.warnings ? e.warnings = [...e.warnings, ...this.context.warnings] : e.warnings = [...this.context.warnings]), this.context = e; } get assumptions() { return this.context.assumptions || (this.context.assumptions = new yi), this.context.assumptions; } shouldContinueExecution() { return void 0 === this.deadline || this.deadline >= Date.now(); } checkContinueExecution() { if (!this.shouldContinueExecution()) throw Error("timeout"); } assert(e, t, i, n) { e || this.signal(t, i, n); } signal(e, t, i) { "object" == typeof e && "message" in e ? i = e.message : e.latex, void 0 === i || "string" == typeof i || Array.isArray(i) && i.map((e => e.toString())).join(", "); } cache(e, t, i) { var _a; if (void 0 === this._cache[e]) try { this._cache[e] = { build: t, purge: i, value: t() }; } catch (e) { } return (_a = this._cache[e]) === null || _a === void 0 ? void 0 : _a.value; } box(e) { return xt(this, e); } fn(e, t, i) { var _a, _b, _c, _d, _f, _g, _h, _j, _k; if ("Hold" === e) return this._fn("Hold", t, i); if (t = t.map((e => e.canonical)), "String" === e) return this.string(t.map((e => { var _a; return (_a = e.string) !== null && _a !== void 0 ? _a : e.latex; })).join(""), i); if ("Symbol" === e) return this.symbol(t.map((e => { var _a; return (_a = e.string) !== null && _a !== void 0 ? _a : e.latex; })).join(""), i); if (("Divide" === e || "Rational" === e) && 2 === t.length) { const e = t[0].asSmallInteger, i = t[1].asSmallInteger; if (null !== e && null !== i && Number.isInteger(e) && Number.isInteger(i)) return this.number([e, i]); } if ("Number" === e) { if (1 === t.length) { const e = t[0], i = (_b = (_a = e.decimalValue) !== null && _a !== void 0 ? _a : e.complexValue) !== null && _b !== void 0 ? _b : e.machineValue; if (null !== i) return this.number(i); const [n, r] = e.rationalValue; if (null !== n && null !== r) return this.number([n, r]); } return this.NAN; } if ("Complex" === e) { if (1 === t.length) { const e = t[0].asFloat; return null !== e ? this.number(this.complex(0, e)) : this.mul([t[0], this.I]); } if (2 === t.length) { const e = t[0].asFloat, n = t[1].asFloat; return null !== e && null !== n && this.number(this.complex(e, n)), 0 === n ? t[0] : this.add([t[0], this.mul([t[1], this.I])], i); } } if ("Negate" === e && 1 === t.length) return It((_c = t[0]) !== null && _c !== void 0 ? _c : this.symbol("Missing"), i); if ("Single" === e || "Pair" === e || "Triple" === e || "KeyValuePair" === e) return this.tuple(t, i); if ("Dictionary" === e) { const e = {}; for (const i of t) if ("Tuple" === i.head) { const t = i.op1; if (!t.isMissing) { i.op2; let n = (_d = t.symbol) !== null && _d !== void 0 ? _d : t.string; if (!n && t.isLiteral) { const e = (_f = t.machineValue) !== null && _f !== void 0 ? _f : t.asSmallInteger; e && Number.isFinite(e) && Number.isInteger(e) && (n = e.toString()); } n && e[n]; } } return new ot(this, e, i); } return "Add" === e ? this.add(t, i) : "Multiply" === e ? this.mul(t, i) : "Divide" === e ? this.divide((_g = t[0]) !== null && _g !== void 0 ? _g : this.symbol("Missing"), (_h = t[1]) !== null && _h !== void 0 ? _h : this.symbol("Missing"), i) : "Power" === e ? this.power((_j = t[0]) !== null && _j !== void 0 ? _j : this.symbol("Missing"), (_k = t[1]) !== null && _k !== void 0 ? _k : this.symbol("Missing"), i) : new ft(this, e, t, i).canonical; } _fn(e, t, i) { const n = new ft(this, e, t, i); return n.isCanonical = !0, n; } error(e, t, i) { return this._fn("Error", [e, this.string(t), this.box(i)]); } add(e, t) { const i = Ft(this, e); return (t === null || t === void 0 ? void 0 : t.latex) && (i.latex = t.latex), (t === null || t === void 0 ? void 0 : t.wikidata) && (i.wikidata = t.wikidata), i; } mul(e, t) { const i = Ct(this, e); return (t === null || t === void 0 ? void 0 : t.latex) && (i.latex = t.latex), (t === null || t === void 0 ? void 0 : t.wikidata) && (i.wikidata = t.wikidata), i; } power(e, t, i) { var _a; let n = null; if ("number" == typeof t ? n = t : Array.isArray(t) && 1 === t[1] && (n = t[0]), 1 === n) return e; if (-1 === n && e.isLiteral) { const [t, i] = e.rationalValue; if (null !== t && null !== i) return this.number([i, t]); const n = e.asSmallInteger; if (null !== n) return this.number([1, n]); } return ("number" == typeof t || Array.isArray(t)) && (t = this.number(t)), (_a = qt(this, e, t, i)) !== null && _a !== void 0 ? _a : this._fn("Power", [e, t], i); } inverse(e, t) { var _a; let i = this.NEGATIVE_ONE; if ("Power" === e.head) { if (e.op2.isNegativeOne) return e.op1; i = It(e.op2), e = e.op1; } return (_a = qt(this, e, i, t)) !== null && _a !== void 0 ? _a : this._fn("Power", [e, i], t); } negate(e, t) { return It(e, t); } divide(e, t, i) { const n = Tt(this, e, t); return (i === null || i === void 0 ? void 0 : i.latex) && (n.latex = i.latex), (i === null || i === void 0 ? void 0 : i.wikidata) && (n.wikidata = i.wikidata), n; } pair(e, t, i) { return this._fn("Tuple", [e, t], i); } tuple(e, t) { return this._fn("Tuple", e, t); } string(e, t) { return new Nt(this, e, t); } symbol(e, t) { if ("Infinity" === e) return this.POSITIVE_INFINITY; if ("+Infinity" === e) return this.POSITIVE_INFINITY; if ("-Infinity" === e) return this.NEGATIVE_INFINITY; if ("Half" === e) return this.HALF; let i = this._commonSymbols[e]; return i ? (t === null || t === void 0 ? void 0 : t.wikidata) && i.wikidata && i.wikidata !== t.wikidata ? new Si(this, e, t) : i : null === i ? (i = new Si(this, e), this._commonSymbols[e] = i, i) : new Si(this, e, t); } domain(e, t) { return e instanceof wi ? e : (e instanceof Pe && e.symbol && (e = e.symbol), "string" == typeof e && (null === this._commonDomains[e] && (this._commonDomains[e] = Ei(this, e, t)), this._commonDomains[e] && this._commonDomains[e]), e instanceof Pe ? Ei(this, "Anything") : Ei(this, e, t)); } number(e, t) { var _a, _b; if (Array.isArray(e) && 1 === e[1] && (e = e[0]), "number" == typeof e) { if (-1 === e) return this.NEGATIVE_ONE; if (null === this._commonNumbers[e] && (this._commonNumbers[e] = (_a = _t(this, e)) !== null && _a !== void 0 ? _a : null), this._commonNumbers[e]) return this._commonNumbers[e]; } return (_b = _t(this, e, t)) !== null && _b !== void 0 ? _b : this.NAN; } rules(e) { return lt(this, e); } pattern(e) { return new Ni(this, e); } parse(e) { var _a; return null === e ? null : this.box(this.latexSyntax.parse((_a = je(e)) !== null && _a !== void 0 ? _a : e)); } serialize(e) { return "object" == typeof e && "json" in e ? this.latexSyntax.serialize(e.json) : this.latexSyntax.serialize(e); } get latexOptions() { return { ...this.latexSyntax.options, ...this.latexSyntax.serializer.options }; } set latexOptions(e) { this.latexSyntax.updateOptions(e); } get jsonSerializationOptions() { return this._jsonSerializationOptions; } set jsonSerializationOptions(e) { e.exclude && (this._jsonSerializationOptions.exclude = [...e.exclude]), e.shorthands && ("all" === e.shorthands || e.shorthands.includes("all") ? this._jsonSerializationOptions.shorthands = ["function", "symbol", "string", "dictionary", "number"] : this._jsonSerializationOptions.shorthands = [...e.shorthands]), e.metadata && ("all" === e.metadata || e.metadata.includes("all") ? this._jsonSerializationOptions.metadata = ["latex", "wikidata"] : this._jsonSerializationOptions.metadata = [...e.metadata]); } ask(e) { const t = this.pattern(e), i = []; for (const [e, n] of this.assumptions) { const r = t.match(e, { numericTolerance: this._tolerance }); null !== r && !0 === n && i.push(r); } return i; } assume(e, t) { try { const i = je(e); let n = i ? this.parse(i) : this.box(e); return t && (n = this.box(["Element", e, this.domain(t)])), "not-a-predicate"; } catch { return "internal-error"; } } forget(e) { var _a; if (void 0 !== e) { if (Array.isArray(e)) for (const t of e) this.forget(t); else if ("string" == typeof e) { (_a = this.context.dictionary) === null || _a === void 0 ? void 0 : _a.symbols.delete(e); for (const [t, i] of this.assumptions) He(t).includes(e) && this.assumptions.delete(t); } } else this.assumptions.clear(); } } const Fi = "0.6.0"; let CORE_STYLESHEET_HASH = undefined; let MATHFIELD_STYLESHEET_HASH = undefined; /** @internal */ class MathfieldPrivate { /** * * - `options.computeEngine`: An instance of a `ComputeEngine`. It is used to parse and serialize * LaTeX strings, using the information contained in the dictionaries * of the Compute Engine to determine, for example, which symbols are * numbers or which are functions, and therefore corectly interpret * `bf(x)` as `b \\times f(x)`. * * If no instance is provided, a new, default, one is created. * * @param element - The DOM element that this mathfield is attached to. * Note that `element.mathfield` is this object. */ constructor(element, options) { var _a, _b, _c; this.focusBlurInProgress = false; this.stylesheets = []; // Setup default config options this.options = update(getDefault(), options.readOnly ? { ...options, virtualKeyboardMode: 'off' } : { plonkSound: 'plonk.wav', keypressSound: { spacebar: 'keypress-spacebar.wav', return: 'keypress-return.wav', delete: 'keypress-delete.wav', default: 'keypress-standard.wav', }, ...options, }); if (this.options.virtualKeyboardMode === 'auto') this.options.virtualKeyboardMode = isTouchCapable() ? 'onfocus' : 'off'; if (options.computeEngine) this._computeEngine = options.computeEngine; this._placeholders = new Map(); this.colorMap = (name) => { let result = undefined; if (typeof this.options.colorMap === 'function') result = this.options.colorMap(name); if (!result) result = defaultColorMap(name); return result; }; this.backgroundColorMap = (name) => { let result = undefined; if (typeof this.options.backgroundColorMap === 'function') result = this.options.backgroundColorMap(name); if (!result && typeof this.options.colorMap === 'function') result = this.options.colorMap(name); if (!result) result = defaultBackgroundColorMap(name); return result; }; this.plonkSound = this.options.plonkSound; if (!this.options.keypressSound) { this.keypressSound = null; this.spacebarKeypressSound = null; this.returnKeypressSound = null; this.deleteKeypressSound = null; } else if (this.options.keypressSound && typeof this.options.keypressSound !== 'string' && !(this.options.keypressSound instanceof HTMLAudioElement)) { this.keypressSound = this.options.keypressSound .default; this.spacebarKeypressSound = this.options.keypressSound .spacebar; this.returnKeypressSound = this.options.keypressSound .return; this.deleteKeypressSound = this.options.keypressSound .delete; } this.element = element; element.mathfield = this; let elementText = (_a = options.value) !== null && _a !== void 0 ? _a : this.element.textContent; if (elementText) elementText = elementText.trim(); // Load the fonts, inject the core and mathfield stylesheets void loadFonts(this.options.fontsDirectory, this.options.onError); if (!CORE_STYLESHEET_HASH) CORE_STYLESHEET_HASH = hashCode(css_248z$4).toString(36); this.stylesheets.push(inject(element, css_248z$4, CORE_STYLESHEET_HASH)); if (!MATHFIELD_STYLESHEET_HASH) MATHFIELD_STYLESHEET_HASH = hashCode(css_248z).toString(36); this.stylesheets.push(inject(element, css_248z, MATHFIELD_STYLESHEET_HASH)); // Additional elements used for UI. // They are retrieved in order a bit later, so they need to be kept in sync let markup = ''; // 1/ The keyboard event capture element. // On touch capable devices, we do not create a textarea to capture keyboard // events as this has the side effect of triggering the OS virtual keyboard // which we want to avoid markup += "<span class='ML__textarea'>"; if (isTouchCapable()) markup += `<span class='ML__textarea__textarea' tabindex="-1" role="textbox"></span>`; else { markup += '<textarea class="ML__textarea__textarea" autocapitalize="off" autocomplete="off" ' + `autocorrect="off" spellcheck="false" aria-hidden="true" tabindex="${(_b = element.tabIndex) !== null && _b !== void 0 ? _b : 0}"></textarea>`; } markup += '</span>'; // 2/ The field, where the math equation will be displayed markup += '<span part="container" class="ML__container"><span part="content" class="ML__content"></span>'; // 2.1/ The virtual keyboard toggle markup += `<div part='virtual-keyboard-toggle' class="ML__virtual-keyboard-toggle" role="button" data-ML__tooltip="${localize('tooltip.toggle virtual keyboard')}">`; markup += (_c = this.options.virtualKeyboardToggleGlyph) !== null && _c !== void 0 ? _c : DEFAULT_KEYBOARD_TOGGLE_GLYPH; markup += '</div>'; markup += "<div class='ML__placeholdercontainer'></div>"; markup += '</span>'; // 3.1/ The aria-live region for announcements // 3.1/ The area to stick MathML for screen reading larger exprs // (not used right now). The idea for the area is that focus would bounce // there and then back triggering the screen reader to read it markup += '<div class="ML__sr-only">' + '<span aria-role="status" aria-live="assertive" aria-atomic="true"></span>' + '<span></span>' + '</div>'; this.element.innerHTML = this.options.createHTML(markup); if (!this.element.children) { console.error('%cMathlive: Something went wrong and the mathfield could not be created.%c\n' + 'If you are using Vue, this may be because you are using the ' + 'runtime-only build of Vue. Make sure to include ' + "'runtimeCompiler: true' in your Vue configuration. There" + 'may a warning from Vue in the log above.', 'color:red;font-family:system-ui;font-size:1.2rem;font-weight:bold', 'color:inherith;font-family:system-ui;font-size:inherit;font-weight:inherit'); return; } let iChild = 0; // Index of child -- used to make changes below easier const textarea = this.element.children[iChild++] .firstElementChild; this.field = this.element.children[iChild].children[0]; // Listen to 'wheel' events to scroll (horizontally) the field when it overflows this.field.addEventListener('wheel', this, { passive: false }); iChild++; this.virtualKeyboardToggle = this.element.querySelector('.ML__virtual-keyboard-toggle'); if (!this.options.readOnly && this.options.virtualKeyboardMode === 'manual') this.virtualKeyboardToggle.classList.add('is-visible'); else this.virtualKeyboardToggle.classList.remove('is-visible'); if (this.options.readOnly) this.element.classList.add('ML__isReadOnly'); else this.element.classList.remove('ML__isReadOnly'); attachButtonHandlers((command) => this.executeCommand(command), this.virtualKeyboardToggle, { default: 'toggleVirtualKeyboard', alt: 'toggleVirtualKeyboardAlt', shift: 'toggleVirtualKeyboardShift', }); this.ariaLiveText = this.element.children[iChild] .children[0]; this.accessibleNode = this.element.children[iChild++] .children[1]; // The keystroke caption panel and the popover are initially hidden this.keystrokeCaptionVisible = false; this.popoverVisible = false; // This index indicates which of the suggestions available to // display in the popover panel this.suggestionIndex = 0; this.keystrokeBuffer = ''; this.keystrokeBufferStates = []; this.keystrokeBufferResetTimer = 0; // The input mode (text, math, command) // While model.getMode() represent the mode of the current selection, // this.mode is the mode chosen by the user. It indicates the mode the // next character typed will be interpreted in. // It is often identical to getAnchorMode() since changing the selection // changes the mode, but sometimes it is not, for example when a user // enters a mode changing command. this.mode = effectiveMode(this.options); this.smartModeSuppressed = false; // Current style (color, weight, italic, etc...): // reflects the style to be applied on next insertion. this.style = {}; if (this.options.defaultMode === 'inline-math') this.element.classList.add('ML__isInline'); else this.element.classList.remove('ML__isInline'); // Focus/blur state this.blurred = true; on(this.element, 'focus', this); on(this.element, 'blur', this); // Capture clipboard events // Delegate keyboard events this.keyboardDelegate = delegateKeyboardEvents(textarea, this.element, { typedText: (text) => onTypedText(this, text), cut: (ev) => { // Ignore if in read-only mode if (this.options.readOnly) { this.model.announce('plonk'); return; } if (contentWillChange(this.model, { inputType: 'deleteByCut' })) { // Snapshot the undo state this.snapshot(); // Copy to the clipboard ModeEditor.onCopy(this, ev); // Clearing the selection will have the side effect of clearing the // content of the textarea. However, the textarea value is what will // be copied to the clipboard (in some cases), so defer the clearing of the selection // to later, after the cut operation has been handled. setTimeout(() => { deleteRange(this.model, range(this.model.selection), 'deleteByCut'); requestUpdate(this); }, 0); } }, copy: (ev) => ModeEditor.onCopy(this, ev), paste: (ev) => { // Ignore if in read-only mode if (this.options.readOnly) { this.model.announce('plonk'); return; } ModeEditor.onPaste(this.model.at(this.model.position).mode, this, ev); }, keystroke: (keystroke, event) => onKeystroke(this, keystroke, event), focus: () => this.onFocus(), blur: () => this.onBlur(), compositionStart: (composition) => this.onCompositionStart(composition), compositionUpdate: (composition) => this.onCompositionUpdate(composition), compositionEnd: (composition) => this.onCompositionEnd(composition), }); // Delegate mouse and touch events if (isBrowser() && 'PointerEvent' in window) { // Use modern pointer events if available on(this.field, 'pointerdown', this); } else on(this.field, 'touchstart:active mousedown', this); // Request notification for when the window is resized or the device // switched from portrait to landscape, to adjust the UI (popover, etc...) on(window, 'resize', this); // Setup the model this.model = new ModelPrivate({ mode: effectiveMode(this.options), macros: this.options.macros, removeExtraneousParentheses: this.options.removeExtraneousParentheses, }, { onContentWillChange: (_sender, options) => this.options.onContentWillChange(this, options), onContentDidChange: (_sender, options) => this.options.onContentDidChange(this, options), onSelectionWillChange: () => this.options.onSelectionWillChange(this), onSelectionDidChange: (_sender) => this._onSelectionDidChange(), onError: this.options.onError, onPlaceholderDidChange: (_sender, placeholderId) => this.options.onPlaceholderDidChange(this, placeholderId), }, { announce: (_sender, command, previousPosition, atoms) => { var _a, _b; return (_b = (_a = this.options).onAnnounce) === null || _b === void 0 ? void 0 : _b.call(_a, this, command, previousPosition, atoms); }, moveOut: (_sender, direction) => this.options.onMoveOutOf(this, direction), tabOut: (_sender, direction) => this.options.onTabOutOf(this, direction), }, this); // Prepare to manage undo/redo this.undoManager = new UndoManager(this.model); // Use the content of the element for the initial value of the mathfield if (elementText) { ModeEditor.insert('math', this.model, elementText, { insertionMode: 'replaceAll', selectionMode: 'after', format: 'latex', suppressChangeNotifications: true, smartFence: this.options.smartFence, macros: this.options.macros, }); } // Now start recording potentially undoable actions this.undoManager.startRecording(); this.undoManager.snapshot(this.options); this.model.setListeners({ onContentWillChange: (_sender, options) => this.options.onContentWillChange(this, options), onContentDidChange: (_sender, options) => this.options.onContentDidChange(this, options), onSelectionWillChange: () => this.options.onSelectionWillChange(this), onSelectionDidChange: (_sender) => this._onSelectionDidChange(), onError: this.options.onError, onPlaceholderDidChange: (_sender, placeholderId) => this.options.onPlaceholderDidChange(this, placeholderId), }); this.model.setHooks({ announce: (_sender, command, previousPosition, atoms) => { var _a, _b; return (_b = (_a = this.options).onAnnounce) === null || _b === void 0 ? void 0 : _b.call(_a, this, command, previousPosition, atoms); }, moveOut: (_sender, direction) => this.options.onMoveOutOf(this, direction), tabOut: (_sender, direction) => this.options.onTabOutOf(this, direction), }); requestUpdate(this); // When fonts are done loading, re-render // (the selection highlighting may be out of date due to the HTML layout // having been updated with the new font metrics) if (isBrowser()) document.fonts.ready.then(() => render(this)); } get virtualKeyboard() { var _a; if (this.options.readOnly) return undefined; // The virtual keyboard can be either attached to this mathfield // or a delegate that mirrors a global virtual keyboard attached // to the document. This is useful for example when using // mathfield in iframes so that all the mathfields share the keyboard // at the document level (rather than having one in each iframe) // If there is a shared virtual keyboard attached to this document, use it // even if `options.useSharedVirtualKeyboard` is false. if (!this._virtualKeyboard) { if (((_a = window.mathlive) === null || _a === void 0 ? void 0 : _a.sharedVirtualKeyboard) || this.options.useSharedVirtualKeyboard) { this._virtualKeyboard = new VirtualKeyboardDelegate({ targetOrigin: this.options.sharedVirtualKeyboardTargetOrigin, originValidator: this.options.originValidator, mathfield: this, }); } else this._virtualKeyboard = new VirtualKeyboard(this.options, this); } return this._virtualKeyboard; } get computeEngine() { if (!this._computeEngine) { this._computeEngine = new Mi(); if (this.options.decimalSeparator === ',') this._computeEngine.latexOptions.decimalMarker = '{,}'; } return this._computeEngine; } get virtualKeyboardState() { var _a; if ((_a = this.virtualKeyboard) === null || _a === void 0 ? void 0 : _a.visible) return 'visible'; return 'hidden'; } set virtualKeyboardState(value) { if (!this.virtualKeyboard) return; if (value === 'hidden') this.virtualKeyboard.executeCommand('hideVirtualKeyboard'); else if (value === 'visible') this.virtualKeyboard.executeCommand('showVirtualKeyboard'); } get keybindings() { var _a, _b; if (this._keybindings) return this._keybindings; const keybindings = normalizeKeybindings(this.options.keybindings, (_a = getActiveKeyboardLayout()) !== null && _a !== void 0 ? _a : DEFAULT_KEYBOARD_LAYOUT, (e) => { if (typeof this.options.onError === 'function') { this.options.onError({ code: 'invalid-keybinding', arg: e.join('\n'), }); } console.error(e.join('\n')); }); if (((_b = getActiveKeyboardLayout()) === null || _b === void 0 ? void 0 : _b.score) > 0) this._keybindings = keybindings; return keybindings; } setOptions(config) { var _a, _b, _c, _d; this.options = update(this.options, config); if (this._computeEngine && 'decimalSeparator' in config) { this._computeEngine.latexOptions.decimalMarker = this.options.decimalSeparator === ',' ? '{,}' : '.'; } this.model.setListeners({ onContentWillChange: (_sender, options) => this.options.onContentWillChange(this, options), onContentDidChange: (_sender, options) => this.options.onContentDidChange(this, options), onSelectionWillChange: () => this.options.onSelectionWillChange(this), onSelectionDidChange: (_sender) => this._onSelectionDidChange(), onError: this.options.onError, onPlaceholderDidChange: (_sender, placeholderId) => this.options.onPlaceholderDidChange(this, placeholderId), }); this.model.setHooks({ announce: (_sender, command, previousPosition, atoms) => { var _a, _b; return (_b = (_a = this.options).onAnnounce) === null || _b === void 0 ? void 0 : _b.call(_a, this, command, previousPosition, atoms); }, moveOut: (_sender, direction) => this.options.onMoveOutOf(this, direction), tabOut: (_sender, direction) => this.options.onTabOutOf(this, direction), }); this.model.options.macros = this.options .macros; this._keybindings = undefined; this.plonkSound = this.options.plonkSound; if (this.options.keypressSound && typeof this.options.keypressSound !== 'string' && !(this.options.keypressSound instanceof HTMLAudioElement)) { this.keypressSound = this.options.keypressSound .default; this.spacebarKeypressSound = this.options.keypressSound .spacebar; this.returnKeypressSound = this.options.keypressSound .return; this.deleteKeypressSound = this.options.keypressSound .delete; } if (this.options.readOnly) { this.onBlur(); this.element.classList.add('ML__isReadOnly'); } else this.element.classList.remove('ML__isReadOnly'); if (this.options.defaultMode === 'inline-math') this.element.classList.add('ML__isInline'); else this.element.classList.remove('ML__isInline'); (_a = this.virtualKeyboard) === null || _a === void 0 ? void 0 : _a.setOptions(this.options); if (!this.options.readOnly && this.options.virtualKeyboardMode === 'manual') (_b = this.virtualKeyboardToggle) === null || _b === void 0 ? void 0 : _b.classList.add('is-visible'); else (_c = this.virtualKeyboardToggle) === null || _c === void 0 ? void 0 : _c.classList.remove('is-visible'); if ('virtualKeyboardToggleGlyph' in config) { const toggle = (_d = this.element) === null || _d === void 0 ? void 0 : _d.querySelector('.ML__virtual-keyboard-toggle'); if (toggle) { toggle.innerHTML = this.options.createHTML(this.options.virtualKeyboardToggleGlyph); } } this.colorMap = (name) => { let result = undefined; if (typeof this.options.colorMap === 'function') result = this.options.colorMap(name); if (!result) result = defaultColorMap(name); return result; }; this.backgroundColorMap = (name) => { let result = undefined; if (typeof this.options.backgroundColorMap === 'function') result = this.options.backgroundColorMap(name); if (!result && typeof this.options.colorMap === 'function') result = this.options.colorMap(name); if (!result) result = defaultBackgroundColorMap(name); return result; }; // Changing some config options (i.e. `macros`) may // require the content to be reparsed and re-rendered const content = Atom.serialize(this.model.root, { expandMacro: false, defaultMode: this.options.defaultMode, }); if ('macros' in config || this.model.getValue() !== content) { ModeEditor.insert('math', this.model, content, { insertionMode: 'replaceAll', selectionMode: 'after', format: 'latex', suppressChangeNotifications: true, }); } requestUpdate(this); } getOptions(keys) { return get(this.options, keys); } getOption(key) { return get(this.options, key); } /* * HandleEvent is a function invoked when an event is registered with an * object instead ( see `addEventListener()` in `on()`) * The name is defined by `addEventListener()` and cannot be changed. * This pattern is used to be able to release bound event handlers, * (event handlers that need access to `this`) as the bind() function * would create a new function that would have to be kept track off * to be able to properly remove the event handler later. */ handleEvent(evt) { if (!isValidMathfield(this)) return; switch (evt.type) { case 'focus': this.onFocus(); break; case 'blur': this.onBlur(); break; case 'touchstart': case 'mousedown': // iOS <=13 Safari and Firefox on Android onPointerDown(this, evt); break; case 'pointerdown': onPointerDown(this, evt); break; case 'resize': if (this.resizeTimer) cancelAnimationFrame(this.resizeTimer); this.resizeTimer = requestAnimationFrame(() => isValidMathfield(this) && this.onResize()); break; case 'wheel': this.onWheel(evt); break; default: console.warn('Unexpected event type', evt.type); } } dispose() { if (!isValidMathfield(this)) return; const element = this.element; delete this.element; delete element.mathfield; element.innerHTML = this.model.getValue(); off(element, 'pointerdown', this); off(element, 'touchstart:active mousedown', this); off(element, 'focus', this); off(element, 'blur', this); off(window, 'resize', this); delete this.accessibleNode; delete this.ariaLiveText; delete this.field; delete this.fieldContent; this.virtualKeyboardToggle.remove(); delete this.virtualKeyboardToggle; if (this._virtualKeyboard) { this._virtualKeyboard.dispose(); delete this._virtualKeyboard; } disposePopover(this); disposeKeystrokeCaption(this); this.stylesheets.forEach((x) => x === null || x === void 0 ? void 0 : x.release()); } resetKeystrokeBuffer(options) { options = options !== null && options !== void 0 ? options : { defer: false }; if (options.defer) { // If there is a timeout greater than 0, defer the reset // If the timeout is 0, never do the reset: regardless of the amount // of time between keystrokes, consider them as candidates for // a shortcut if (this.options.inlineShortcutTimeout > 0) { // Set a timer to reset the shortcut buffer this.keystrokeBufferResetTimer = setTimeout(() => { this.resetKeystrokeBuffer(); }, this.options.inlineShortcutTimeout); } return; } this.keystrokeBuffer = ''; this.keystrokeBufferStates = []; clearTimeout(this.keystrokeBufferResetTimer); } executeCommand(command) { var _a, _b; if (getCommandTarget(command) === 'virtual-keyboard') return (_b = (_a = this.virtualKeyboard) === null || _a === void 0 ? void 0 : _a.executeCommand(command)) !== null && _b !== void 0 ? _b : false; return perform(this, command); } getValue(arg1, arg2, arg3) { return this.model.getValue(arg1, arg2, arg3); } setValue(value, options) { var _a; options = options !== null && options !== void 0 ? options : { mode: 'math' }; if (options.insertionMode === undefined) options.insertionMode = 'replaceAll'; if (options.format === undefined || options.format === 'auto') options.format = 'latex'; let mode = 'math'; if (options.mode === undefined || options.mode === 'auto') mode = (_a = getMode(this.model, this.model.position)) !== null && _a !== void 0 ? _a : 'math'; if (ModeEditor.insert(mode, this.model, value, { ...options, colorMap: this.colorMap, backgroundColorMap: this.backgroundColorMap, })) { this.undoManager.snapshot(this.options); requestUpdate(this); } } get expression() { const ce = this.computeEngine; return ce.box(ce.parse(this.model.getValue())); } /** Make sure the caret is visible within the matfield. * If the mathfield is inside a mathfield element, make sure the mathfield * element is visible in the page */ scrollIntoView() { var _a; if (!this.element) return; // // 1/ If inside a mathfield element, make sure that element is visible. // const host = (_a = this.element.getRootNode()) === null || _a === void 0 ? void 0 : _a.host; host === null || host === void 0 ? void 0 : host.scrollIntoView({ block: 'nearest', inline: 'nearest' }); // // 2/ If a render is pending, do it now to make sure we have correct layout // and caret position // if (this.dirty) render(this, { interactive: true }); // // 3/ Get the position of the caret // const fieldBounds = this.field.getBoundingClientRect(); let caretPoint = null; if (this.model.selectionIsCollapsed) caretPoint = getCaretPoint(this.field); else { const selectionBounds = getSelectionBounds(this); if (selectionBounds.length > 0) { let maxRight = -Infinity; let minTop = -Infinity; for (const r of selectionBounds) { if (r.right > maxRight) maxRight = r.right; if (r.top < minTop) minTop = r.top; } caretPoint = { x: maxRight + fieldBounds.left - this.field.scrollLeft, y: minTop + fieldBounds.top - this.field.scrollTop, height: 0, }; } } // // 4/ Make sure that the caret is vertically visible, but because // vertical scrolling of the field occurs via a scroller that includes // the field and the virtual keyboard toggle, we'll handle the horizontal // scrolling separately // if (host && caretPoint) { const hostBounds = host.getBoundingClientRect(); const y = caretPoint.y; let top = host.scrollTop; if (y < hostBounds.top) top = y - hostBounds.top + host.scrollTop; else if (y > hostBounds.bottom) top = y - hostBounds.bottom + host.scrollTop + caretPoint.height; host.scroll({ top, left: 0 }); } // // 5/ Make sure the caret is horizontally visible within the field // if (caretPoint) { const x = caretPoint.x - window.scrollX; let left = this.field.scrollLeft; if (x < fieldBounds.left) left = x - fieldBounds.left + this.field.scrollLeft - 20; else if (x > fieldBounds.right) left = x - fieldBounds.right + this.field.scrollLeft + 20; this.field.scroll({ top: this.field.scrollTop, left, }); } } insert(s, options) { var _a; if (typeof s === 'string' && s.length > 0) { options = options !== null && options !== void 0 ? options : { mode: 'math' }; if (options.focus) this.focus(); if (options.feedback) { if (this.options.keypressVibration && canVibrate()) navigator.vibrate(HAPTIC_FEEDBACK_DURATION); void ((_a = this.keypressSound) === null || _a === void 0 ? void 0 : _a.play().catch(console.warn)); } if (options.scrollIntoView) this.scrollIntoView(); if (s === '\\\\') { // This string is interpreted as an "insert row after" command addRowAfter(this.model); } else if (s === '&') addColumnAfter(this.model); else { const savedStyle = this.style; ModeEditor.insert(this.mode, this.model, s, { style: this.model.at(this.model.position).computedStyle, ...options, }); if (options.resetStyle) this.style = savedStyle; } this.undoManager.snapshot(this.options); requestUpdate(this); return true; } return false; } switchMode(mode, prefix = '', suffix = '') { if (this.mode === mode || this.options.readOnly) return; const currentMode = this.mode; const { model } = this; model.deferNotifications({ content: Boolean(suffix) || Boolean(prefix), selection: true, type: 'insertText', }, () => { var _a; let contentChanged = false; this.resetKeystrokeBuffer(); // Suppress (temporarily) smart mode if switching to/from text or math // This prevents switching to/from command mode from suppressing smart mode. this.smartModeSuppressed = /text|math/.test(this.mode) && /text|math/.test(mode); if (prefix && mode !== 'latex') { const atoms = parseLatex(prefix, { parseMode: mode }); model.collapseSelection('forward'); const cursor = model.at(model.position); model.position = model.offsetOf(cursor.parent.addChildrenAfter(atoms, cursor)); contentChanged = true; } this.mode = mode; if (mode === 'latex') { let wasCollapsed = model.selectionIsCollapsed; // We can have only a single latex group at a time. // If a latex group is open, close it first complete(this, 'accept'); // Switch to the command mode keyboard layer if ((_a = this.virtualKeyboard) === null || _a === void 0 ? void 0 : _a.visible) this.executeCommand(['switchKeyboardLayer', 'latex-lower']); // Insert a latex group atom let latex; let cursor = model.at(model.position); if (wasCollapsed) latex = '\\'; else { const selRange = range(model.selection); latex = this.model.getValue(selRange, 'latex'); const extractedAtoms = this.model.extractAtoms(selRange); if (extractedAtoms.length === 1 && extractedAtoms[0] instanceof PlaceholderAtom) { // If we just had a placeholder selected, pretend we had an empty // selection latex = prefix; wasCollapsed = true; } cursor = model.at(selRange[0]); } const atom = new LatexGroupAtom(latex); cursor.parent.addChildAfter(atom, cursor); if (wasCollapsed) model.position = model.offsetOf(atom.lastChild); else { model.setSelection(model.offsetOf(atom.firstChild), model.offsetOf(atom.lastChild)); } } else { // Remove any error indicator on the current command sequence (if there is one) getLatexGroupBody(model).forEach((x) => { x.isError = false; }); } if (suffix) { const atoms = parseLatex(suffix, { parseMode: currentMode }); model.collapseSelection('forward'); const cursor = model.at(model.position); model.position = model.offsetOf(cursor.parent.addChildrenAfter(atoms, cursor)); contentChanged = true; } // Notify of mode change if (typeof this.options.onModeChange === 'function') this.options.onModeChange(this, mode); requestUpdate(this); return contentChanged; }); this.mode = mode; } hasFocus() { return isBrowser() && this.keyboardDelegate.hasFocus(); } focus(options) { var _a; this.keyboardDelegate.focus(); this.model.announce('line'); if ((_a = options === null || options === void 0 ? void 0 : options.scrollIntoView) !== null && _a !== void 0 ? _a : true) this.scrollIntoView(); } blur() { this.keyboardDelegate.blur(); } select() { this.model.selection = { ranges: [[0, this.model.lastOffset]] }; } applyStyle(inStyle, inOptions = {}) { var _a, _b; const options = { operation: 'set', suppressChangeNotifications: false, }; if (isRange(inOptions)) options.range = inOptions; else { options.range = inOptions.range; options.suppressChangeNotifications = (_a = inOptions.suppressChangeNotifications) !== null && _a !== void 0 ? _a : false; } const style = validateStyle(this, inStyle); const operation = (_b = options.operation) !== null && _b !== void 0 ? _b : 'set'; this.model.deferNotifications({ content: !options.suppressChangeNotifications, type: 'insertText' }, () => { if (options.range === undefined) { this.model.selection.ranges.forEach((range) => applyStyle$1(this.model, range, style, { operation })); } else applyStyle$1(this.model, options.range, style, { operation }); }); requestUpdate(this); } getCaretPoint() { const caretOffset = getCaretPoint(this.field); return caretOffset ? { x: caretOffset.x, y: caretOffset.y } : null; } setCaretPoint(x, y) { const newPosition = offsetFromPoint(this, x, y, { bias: 0 }); if (newPosition < 0) return false; const previousPosition = this.model.position; this.model.position = newPosition; this.model.announce('move', previousPosition); requestUpdate(this); return true; } getPlaceholderField(placeholderId) { var _a; return (_a = this._placeholders.get(placeholderId)) === null || _a === void 0 ? void 0 : _a.field; } attachNestedMathfield() { let needsUpdate = false; this._placeholders.forEach((v) => { var _a, _b, _c, _d, _e, _f, _g, _h; const container = (_a = this.field) === null || _a === void 0 ? void 0 : _a.querySelector(`[data-placeholder-id=${v.atom.placeholderId}]`); if (container) { const placeholderPosition = container.getBoundingClientRect(); const parentPosition = (_b = this.field) === null || _b === void 0 ? void 0 : _b.getBoundingClientRect(); const scaleDownFontsize = parseInt(window.getComputedStyle(container).fontSize) * 0.6; if (!v.field.style.fontSize || Math.abs(scaleDownFontsize - parseFloat(v.field.style.fontSize)) >= 0.2) { needsUpdate = true; v.field.style.fontSize = `${scaleDownFontsize}px`; } const newTop = ((_c = placeholderPosition === null || placeholderPosition === void 0 ? void 0 : placeholderPosition.top) !== null && _c !== void 0 ? _c : 0) - ((_d = parentPosition === null || parentPosition === void 0 ? void 0 : parentPosition.top) !== null && _d !== void 0 ? _d : 0) + ((_e = this.element.offsetTop) !== null && _e !== void 0 ? _e : 0); const newLeft = ((_f = placeholderPosition === null || placeholderPosition === void 0 ? void 0 : placeholderPosition.left) !== null && _f !== void 0 ? _f : 0) - ((_g = parentPosition === null || parentPosition === void 0 ? void 0 : parentPosition.left) !== null && _g !== void 0 ? _g : 0) + ((_h = this.element.offsetLeft) !== null && _h !== void 0 ? _h : 0); if (!v.field.style.left || Math.abs(newLeft - parseFloat(v.field.style.left)) >= 1) { needsUpdate = true; v.field.style.left = `${newLeft}px`; } if (!v.field.style.top || Math.abs(newTop - parseFloat(v.field.style.top)) >= 1) { needsUpdate = true; v.field.style.top = `${newTop}px`; } } }); if (needsUpdate) requestUpdate(this); } canUndo() { return this.undoManager.canUndo(); } canRedo() { return this.undoManager.canRedo(); } popUndoStack() { this.undoManager.pop(); } snapshot() { this.undoManager.snapshot({ ...this.options, onUndoStateDidChange: (mf, reason) => { var _a; (_a = this.virtualKeyboard) === null || _a === void 0 ? void 0 : _a.executeCommand([ 'onUndoStateChanged', this.canUndo(), this.canRedo(), ]); this.options.onUndoStateDidChange(mf, reason); }, }); } snapshotAndCoalesce() { this.undoManager.snapshotAndCoalesce({ ...this.options, onUndoStateDidChange: (mf, reason) => { var _a; (_a = this.virtualKeyboard) === null || _a === void 0 ? void 0 : _a.executeCommand([ 'onUndoStateChanged', this.canUndo(), this.canRedo(), ]); this.options.onUndoStateDidChange(mf, reason); }, }); } getUndoRecord() { return this.undoManager.save(); } restoreToUndoRecord(s) { this.undoManager.restore(s, { ...this.options, suppressChangeNotifications: true, }); } undo() { return this.undoManager.undo({ ...this.options, onUndoStateDidChange: (mf, reason) => { var _a; (_a = this.virtualKeyboard) === null || _a === void 0 ? void 0 : _a.executeCommand([ 'onUndoStateChanged', this.canUndo(), this.canRedo(), ]); this.options.onUndoStateDidChange(mf, reason); }, }); } redo() { return this.undoManager.redo({ ...this.options, onUndoStateDidChange: (mf, reason) => { var _a; (_a = this.virtualKeyboard) === null || _a === void 0 ? void 0 : _a.executeCommand([ 'onUndoStateChanged', this.canUndo(), this.canRedo(), ]); this.options.onUndoStateDidChange(mf, reason); }, }); } _onSelectionDidChange() { var _a, _b; // Keep the content of the textarea in sync wiht the selection. // This will allow cut/copy to work. this.keyboardDelegate.setValue(this.model.getValue(this.model.selection, 'latex-expanded')); const selectedAtoms = this.model.getAtoms(this.model.selection); if (selectedAtoms.length === 1 && selectedAtoms[0].type === 'placeholder') { const placeholder = selectedAtoms[0]; if (this.model.mathfield._placeholders.has(placeholder.placeholderId)) { (_a = this.model.mathfield._placeholders .get(placeholder.placeholderId)) === null || _a === void 0 ? void 0 : _a.field.focus(); } } // Adjust mode { const cursor = this.model.at(this.model.position); const newMode = (_b = cursor.mode) !== null && _b !== void 0 ? _b : effectiveMode(this.options); if (this.mode !== newMode) { if (this.mode === 'latex') { complete(this, 'accept', { mode: newMode }); this.model.position = this.model.offsetOf(cursor); } else this.switchMode(newMode); } } // Invoke client listeners, if provided. if (typeof this.options.onSelectionDidChange === 'function') this.options.onSelectionDidChange(this); } onFocus() { var _a, _b; if (this.focusBlurInProgress || !this.blurred) return; this.focusBlurInProgress = true; this.blurred = false; this.keyboardDelegate.focus(); (_a = this.virtualKeyboard) === null || _a === void 0 ? void 0 : _a.setOptions(this.options); (_b = this.virtualKeyboard) === null || _b === void 0 ? void 0 : _b.enable(); if (this.options.virtualKeyboardMode === 'onfocus') this.executeCommand('showVirtualKeyboard'); updatePopoverPosition(this); render(this, { interactive: true }); if (typeof this.options.onFocus === 'function') this.options.onFocus(this); // Save the current value. // It will be compared in `onBlur()` to see if the // `onCommit` listener needs to be invoked. This // mimic the `<input>` and `<textarea>` behavior this.valueOnFocus = this.model.getValue(); this.focusBlurInProgress = false; } onBlur() { var _a, _b; if (this.focusBlurInProgress || this.blurred) return; this.focusBlurInProgress = true; this.blurred = true; this.ariaLiveText.textContent = ''; complete(this, 'accept'); if (typeof this.options.onCommit === 'function' && this.model.getValue() !== this.valueOnFocus) this.options.onCommit(this); if (!((_a = window.mathlive) === null || _a === void 0 ? void 0 : _a.sharedVirtualKeyboard) && /onfocus|manual/.test(this.options.virtualKeyboardMode)) this.executeCommand('hideVirtualKeyboard'); (_b = this.virtualKeyboard) === null || _b === void 0 ? void 0 : _b.disable(); if (typeof this.options.onBlur === 'function') this.options.onBlur(this); requestUpdate(this); this.focusBlurInProgress = false; } onCompositionStart(_composition) { // Clear the selection if there is one this.model.deleteAtoms(range(this.model.selection)); requestAnimationFrame(() => { render(this); // Recalculate the position of the caret // Synchronize the location and style of textarea // so that the IME candidate window can align with the composition const caretPoint = getCaretPoint(this.field); if (!caretPoint) return; this.keyboardDelegate.moveTo(caretPoint.x, caretPoint.y); }); } onCompositionUpdate(composition) { updateComposition(this.model, composition); requestUpdate(this); } onCompositionEnd(composition) { removeComposition(this.model); onTypedText(this, composition, { simulateKeystroke: true, }); } onResize() { updatePopoverPosition(this); } onWheel(ev) { const wheelDelta = 5 * ev.deltaX; if (!Number.isFinite(wheelDelta) || wheelDelta === 0) return; const field = this.field; if (wheelDelta < 0 && field.scrollLeft === 0) return; if (wheelDelta > 0 && field.offsetWidth + field.scrollLeft >= field.scrollWidth) return; field.scrollBy({ top: 0, left: wheelDelta }); ev.preventDefault(); ev.stopPropagation(); } } var _a, _b; // // Note: the `position: relative` is required to fix https://github.com/arnog/mathlive/issues/971 // const MATHFIELD_TEMPLATE = document.createElement('template'); MATHFIELD_TEMPLATE.innerHTML = `<style> :host { display: block; position: relative; overflow: hidden auto;} :host([hidden]) { display: none; } :host([disabled]) { opacity: .5; } :host(:focus), :host(:focus-within) { outline: Highlight auto 1px; /* For Firefox */ outline: -webkit-focus-ring-color auto 1px; } :host([readonly]), :host([read-only]) { outline: none; } </style> <div></div><slot style="display:none"></slot>`; // // Deferred State // // Methods such as `setOptions()` or `getOptions()` could be called before // the element has been connected (i.e. `mf = new MathfieldElement(); mf.setOptions()`...) // and therefore before the mathfield instance has been created. // So we'll stash any deferred operations on options (and value) here, and // will apply them to the element when it gets connected to the DOM. // const gDeferredState = new WeakMap(); /** * The `MathfieldElement` class provides special properties and * methods to control the display and behavior of `<math-field>` * elements. * * It inherits many useful properties and methods from [[`HTMLElement`]] such * as `style`, `tabIndex`, `addEventListener()`, `getAttribute()`, etc... * * To create a new `MathfieldElement`: * * ```javascript * // 1. Create a new MathfieldElement * const mfe = new MathfieldElement(); * // 2. Attach it to the DOM * document.body.appendChild(mfe); * ``` * * The `MathfieldElement` constructor has an optional argument of * [[`MathfieldOptions`]] to configure the element. The options can also * be modified later: * * ```javascript * // Setting options during construction * const mfe = new MathfieldElement({smartFence: false}); * // Modifying options after construction * mfe.setOptions({smartFence: true}); * ``` * * ### CSS Variables * * To customize the appearance of the mathfield, declare the following CSS * variables (custom properties) in a ruleset that applies to the mathfield. * * ```css * math-field { * --hue: 10 // Set the highlight color and caret to a reddish hue * } * ``` * * Alternatively you can set these CSS variables programatically: * * ```js * document.body.style.setProperty("--hue", "10"); * ``` * <div class='symbols-table' style='--first-col-width:25ex'> * * | CSS Variable | Usage | * |:---|:---| * | `--hue` | Hue of the highlight color and the caret | * | `--highlight` | Color of the selection | * | `--contains-highlight` | Backround property for items that contain the caret | * | `--highlight-inactive` | Color of the selection, when the mathfield is not focused | * | `--caret` | Color of the caret/insertion point | * | `--primary` | Primary accent color, used for example in the virtual keyboard | * | `--text-font-family` | The font stack used in text mode | * | `--smart-fence-opacity` | Opacity of a smart fence (default is 50%) | * | `--smart-fence-color` | Color of a smart fence (default is current color) | * * </div> * * You can customize the appearance and zindex of the virtual keyboard panel * with some CSS variables associated with a selector that applies to the * virtual keyboard panel container. * * Read more about [customizing the virtual keyboard appearance](https://cortexjs.io/mathlive/guides/virtual-keyboards/#custom-appearance) * * ### CSS Parts * * To style the virtual keyboard toggle, use the `virtual-keyboard-toggle` CSS * part. To use it, define a CSS rule with a `::part()` selector * for example: * ```css * math-field::part(virtual-keyboard-toggle) { * color: red; * } * ``` * * * ### Attributes * * An attribute is a key-value pair set as part of the tag: * * ```html * <math-field locale="fr"></math-field> * ``` * * The supported attributes are listed in the table below with their * corresponding property. * * The property can be changed either directly on the * `MathfieldElement` object, or using `setOptions()` if it is prefixed with * `options.`, for example: * * ```javascript * getElementById('mf').value = '\\sin x'; * getElementById('mf').setOptions({horizontalSpacingScale: 1.1}); * ``` * * The values of attributes and properties are reflected, which means you can change one or the * other, for example: * * ```javascript * getElementById('mf').setAttribute('virtual-keyboard-mode', 'manual'); * console.log(getElementById('mf').getOption('virtualKeyboardMode')); * // Result: "manual" * getElementById('mf').setOptions({virtualKeyboardMode: 'onfocus'); * console.log(getElementById('mf').getAttribute('virtual-keyboard-mode'); * // Result: 'onfocus' * ``` * * An exception is the `value` property, which is not reflected on the `value` * attribute: the `value` attribute remains at its initial value. * * * <div class='symbols-table' style='--first-col-width:32ex'> * * | Attribute | Property | * |:---|:---| * | `disabled` | `disabled` | * | `default-mode` | `options.defaultMode` | * | `fonts-directory` | `options.fontsDirectory` | * | `sounds-directory` | `options.soundsDirectory` | * | `horizontal-spacing-scale` | `options.horizontalSpacingScale` | * | `inline-shortcut-timeout` | `options.inlineShortcutTimeout` | * | `keypress-vibration` | `options.keypressVibration` | * | `keypress-sound` | `options.keypressSound` | * | `plonk-sound` | `options.plonkSound` | * | `letter-shape-style` | `options.letterShapeStyle` | * | `locale` | `options.locale` | * | `math-mode-space` | `options.mathModeSpace` | * | `read-only` | `options.readOnly` | * | `remove-extraneous-parentheses` | `options.removeExtraneousParentheses` | * | `smart-fence` | `options.smartFence` | * | `smart-mode` | `options.smartMode` | * | `smart-superscript` | `options.superscript` | * | `speech-engine` | `options.speechEngine` | * | `speech-engine-rate` | `options.speechEngineRate` | * | `speech-engine-voice` | `options.speechEngineVoice` | * | `text-to-speech-markup` | `options.textToSpeechMarkup` | * | `text-to-speech-rules` | `options.textToSpeechRules` | * | `value` | `value` | * | `virtual-keyboard-layout` | `options.keyboardLayout` | * | `virtual-keyboard-mode` | `options.keyboardMode` | * | `virtual-keyboard-theme` | `options.keyboardTheme` | * | `virtual-keyboards` | `options.keyboards` | * * </div> * * See [[`MathfieldOptions`]] for more details about these options. * * In addition, the following [global attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes) * can also be used: * - `class` * - `data-*` * - `hidden` * - `id` * - `item*` * - `style` * - `tabindex` * * * ### Events * * Listen to these events by using `addEventListener()`. For events with additional * arguments, the arguments are available in `event.detail`. * * <div class='symbols-table' style='--first-col-width:27ex'> * * | Event Name | Description | * |:---|:---| * | `input` | The value of the mathfield has been modified. This happens on almost every keystroke in the mathfield. | * | `change` | The user has committed the value of the mathfield. This happens when the user presses **Return** or leaves the mathfield. | * | `selection-change` | The selection (or caret position) in the mathfield has changed | * | `mode-change` | The mode (`math`, `text`) of the mathfield has changed | * | `undo-state-change` | The state of the undo stack has changed | * | `read-aloud-status-change` | The status of a read aloud operation has changed | * | `virtual-keyboard-toggle` | The visibility of the virtual keyboard panel has changed | * | `blur` | The mathfield is losing focus | * | `focus` | The mathfield is gaining focus | * | `focus-out` | The user is navigating out of the mathfield, typically using the **tab** key<br> `detail: {direction: 'forward' | 'backward' | 'upward' | 'downward'}` **cancellable**| * | `move-out` | The user has pressed an **arrow** key, but there is nowhere to go. This is an opportunity to change the focus to another element if desired. <br> `detail: {direction: 'forward' | 'backward' | 'upward' | 'downward'}` **cancellable**| * | `math-error` | A parsing or configuration error happened <br> `detail: ErrorListener<ParserErrorCode | MathfieldErrorCode>` | * | `keystroke` | The user typed a keystroke with a physical keyboard <br> `detail: {keystroke: string, event: KeyboardEvent}` | * | `mount` | The element has been attached to the DOM | * | `unmount` | The element is about to be removed from the DOM | * * </div> * * @keywords zindex, events, attribute, attributes, property, properties, parts, variables, css, mathfield, mathfieldelement */ class MathfieldElement extends HTMLElement { /** * To create programmatically a new mathfield use: * * ```javascript let mfe = new MathfieldElement(); // Set initial value and options mfe.value = "\\frac{\\sin(x)}{\\cos(x)}"; // Options can be set either as an attribute (for simple options)... mfe.setAttribute('virtual-keyboard-layout', 'dvorak'); // ... or using `setOptions()` mfe.setOptions({ virtualKeyboardMode: 'manual', }); // Attach the element to the DOM document.body.appendChild(mfe); * ``` */ constructor(options) { throwIfNotInBrowser(); super(); this.attachShadow({ mode: 'open' }); this.shadowRoot.append(MATHFIELD_TEMPLATE.content.cloneNode(true)); // When the elements get focused (through tabbing for example) // focus the mathfield const slot = this.shadowRoot.querySelector('slot:not([name])'); this._slotValue = slot .assignedNodes() .map((x) => (x.nodeType === 3 ? x.textContent : '')) .join('') .trim(); // Record the (optional) configuration options, as a deferred state if (options) this.setOptions(options); this.shadowRoot.host.addEventListener('focus', (_event) => { var _a; return (_a = this._mathfield) === null || _a === void 0 ? void 0 : _a.focus(); }, true); this.shadowRoot.host.addEventListener('blur', (_event) => { var _a; return (_a = this._mathfield) === null || _a === void 0 ? void 0 : _a.blur(); }, true); } /** * Private lifecycle hooks * @internal */ static get optionsAttributes() { return { 'default-mode': 'string', 'fonts-directory': 'string', 'sounds-directory': 'string', 'horizontal-spacing-scale': 'string', 'math-mode-space': 'string', 'inline-shortcut-timeout': 'string', 'keypress-vibration': 'on/off', 'keypress-sound': 'string', 'plonk-sound': 'string', 'letter-shape-style': 'string', 'locale': 'string', 'read-only': 'boolean', 'remove-extraneous-parentheses': 'on/off', 'smart-fence': 'on/off', 'smart-mode': 'on/off', 'smart-superscript': 'on/off', 'speech-engine': 'string', 'speech-engine-rate': 'string', 'speech-engine-voice': 'string', 'text-to-speech-markup': 'string', 'text-to-speech-rules': 'string', 'virtual-keyboard-layout': 'string', 'virtual-keyboard-mode': 'string', 'virtual-keyboard-theme': 'string', 'virtual-keyboards': 'string', 'use-shared-virtual-keyboard': 'boolean', 'shared-virtual-keyboard-target-origin': 'string', }; } /** * Custom elements lifecycle hooks * @internal */ static get observedAttributes() { return [ ...Object.keys(MathfieldElement.optionsAttributes), 'disabled', 'readonly', 'read-only', ]; } getPlaceholderField(placeholderId) { var _a; return (_a = this._mathfield) === null || _a === void 0 ? void 0 : _a.getPlaceholderField(placeholderId); } addEventListener(type, listener, options) { return super.addEventListener(type, listener, options); } removeEventListener(type, listener, options) { super.removeEventListener(type, listener, options); } get mode() { var _a, _b; return (_b = (_a = this._mathfield) === null || _a === void 0 ? void 0 : _a.mode) !== null && _b !== void 0 ? _b : 'math'; } set mode(value) { if (!this._mathfield) return; this._mathfield.mode = value; } get computeEngine() { if (!this._mathfield) return undefined; return this._mathfield.computeEngine; } get expression() { if (!this._mathfield) return undefined; return this._mathfield.expression; } getOptions(keys) { if (this._mathfield) return get(this._mathfield.options, keys); if (!gDeferredState.has(this)) return null; return get(update(getDefault(), gDeferredState.get(this).options), keys); } /** * @category Options */ getOption(key) { return this.getOptions([key])[key]; } /** * @category Options */ setOptions(options) { if (this._mathfield) { this._mathfield.setOptions(options); this._mathfield._placeholders.forEach((placeholder) => { placeholder.field.setOptions({ ...options, virtualKeyboardMode: 'onfocus', readOnly: false, }); }); } else if (gDeferredState.has(this)) { const mergedOptions = { ...gDeferredState.get(this).options, ...options, }; gDeferredState.set(this, { ...gDeferredState.get(this), selection: { ranges: mergedOptions.readOnly ? [[0, 0]] : [[0, -1]] }, options: mergedOptions, }); } else { gDeferredState.set(this, { value: undefined, selection: { ranges: [[0, 0]] }, options, }); } // Reflect options to attributes reflectAttributes(this); } /** * @inheritdoc Mathfield.executeCommand */ executeCommand(command) { var _a, _b; return (_b = (_a = this._mathfield) === null || _a === void 0 ? void 0 : _a.executeCommand(command)) !== null && _b !== void 0 ? _b : false; } getValue(arg1, arg2, arg3) { var _a, _b; if (this._mathfield) return this._mathfield.model.getValue(arg1, arg2, arg3); if (gDeferredState.has(this)) { let start; let end; let format = undefined; if (isSelection(arg1)) { [start, end] = arg1.ranges[0]; format = arg2; } else if (isRange(arg1)) { [start, end] = arg1; format = arg2; } else if (isOffset(arg1) && isOffset(arg2)) { start = arg1; end = arg2; format = arg3; } else { start = 0; end = -1; format = arg1; } if ((format === undefined || format === 'latex') && start === 0 && end === -1) return (_b = (_a = gDeferredState.get(this).value) !== null && _a !== void 0 ? _a : this.textContent) !== null && _b !== void 0 ? _b : ''; } return ''; } /** * @inheritdoc Mathfield.setValue * @category Accessing and changing the content */ setValue(value, options) { if (this._mathfield && value !== undefined) { this._mathfield.setValue(value, options); return; } if (gDeferredState.has(this)) { const options = gDeferredState.get(this).options; gDeferredState.set(this, { value, selection: { ranges: options.readOnly ? [[0, 0]] : [[0, -1]], direction: 'forward', }, options, }); return; } const attrOptions = getOptionsFromAttributes(this); gDeferredState.set(this, { value, selection: { ranges: attrOptions.readOnly ? [[0, 0]] : [[0, -1]], direction: 'forward', }, options: attrOptions, }); } /** * @inheritdoc Mathfield.hasFocus * * @category Focus * */ hasFocus() { var _a, _b; return (_b = (_a = this._mathfield) === null || _a === void 0 ? void 0 : _a.hasFocus()) !== null && _b !== void 0 ? _b : false; } get virtualKeyboardState() { var _a, _b; return (_b = (_a = this._mathfield) === null || _a === void 0 ? void 0 : _a.virtualKeyboardState) !== null && _b !== void 0 ? _b : 'hidden'; } set virtualKeyboardState(value) { if (this._mathfield) this._mathfield.virtualKeyboardState = value; } /** * Sets the focus to the mathfield (will respond to keyboard input). * * @category Focus * */ focus() { super.focus(); } /** * Remove the focus from the mathfield (will no longer respond to keyboard * input). * * @category Focus * */ blur() { var _a; (_a = this._mathfield) === null || _a === void 0 ? void 0 : _a.blur(); super.blur(); } /** * Select the content of the mathfield. * @category Selection */ select() { var _a; (_a = this._mathfield) === null || _a === void 0 ? void 0 : _a.select(); } /** * @inheritdoc Mathfield.insert * @category Accessing and changing the content */ insert(s, options) { var _a, _b; return (_b = (_a = this._mathfield) === null || _a === void 0 ? void 0 : _a.insert(s, options)) !== null && _b !== void 0 ? _b : false; } /** * @inheritdoc Mathfield.applyStyle * * @category Accessing and changing the content */ applyStyle(style, options) { var _a; return (_a = this._mathfield) === null || _a === void 0 ? void 0 : _a.applyStyle(style, options); } /** * The bottom location of the caret (insertion point) in viewport * coordinates. * * @category Selection */ get caretPoint() { var _a, _b; return (_b = (_a = this._mathfield) === null || _a === void 0 ? void 0 : _a.getCaretPoint()) !== null && _b !== void 0 ? _b : null; } set caretPoint(point) { var _a; if (!point) return; (_a = this._mathfield) === null || _a === void 0 ? void 0 : _a.setCaretPoint(point.x, point.y); } /** * `x` and `y` are in viewport coordinates. * * Return true if the location of the point is a valid caret location. * * See also [[`caretPoint`]] * @category Selection */ setCaretPoint(x, y) { var _a, _b; return (_b = (_a = this._mathfield) === null || _a === void 0 ? void 0 : _a.setCaretPoint(x, y)) !== null && _b !== void 0 ? _b : false; } /** * Custom elements lifecycle hooks * @internal */ connectedCallback() { var _a, _b; if (!this.hasAttribute('role')) this.setAttribute('role', 'textbox'); this.setAttribute('dir', 'ltr'); if (!this.hasAttribute('aria-label')) this.setAttribute('aria-label', 'math input field'); // NVDA on Firefox seems to require this attribute this.setAttribute('contenteditable', 'true'); this.setAttribute('aria-multiline', 'false'); if (!this.hasAttribute('tabindex')) this.setAttribute('tabindex', '0'); const slot = this.shadowRoot.querySelector('slot:not([name])'); try { this._style = slot .assignedElements() .filter((x) => x.tagName.toLowerCase() === 'style') .map((x) => x.textContent) .join(''); } catch (error) { console.log(error); } // Add shadowed stylesheet if one was provided if (this._style) { const styleElement = document.createElement('style'); styleElement.textContent = this._style; this.shadowRoot.appendChild(styleElement); } // Inline options (as a JSON structure in the markup) try { const json = slot .assignedElements() .filter((x) => x.tagName.toLowerCase() === 'script' && x.type === 'application/json') .map((x) => x.textContent) .join(''); if (json) this.setOptions(JSON.parse(json)); } catch (error) { console.log(error); } let value = ''; // Check if there is a `value` attribute and set the initial value // of the mathfield from it if (this.hasAttribute('value')) value = (_a = this.getAttribute('value')) !== null && _a !== void 0 ? _a : ''; else { value = (_b = slot === null || slot === void 0 ? void 0 : slot.assignedNodes().map((x) => (x.nodeType === 3 ? x.textContent : '')).join('').trim()) !== null && _b !== void 0 ? _b : ''; } this._mathfield = new MathfieldPrivate(this.shadowRoot.querySelector(':host > div'), { onBlur: () => { this.dispatchEvent(new Event('blur', { cancelable: false, bubbles: false, composed: true, })); }, onContentWillChange: (_sender, options) => { return this.dispatchEvent(new InputEvent('beforeinput', { ...options, cancelable: true, bubbles: true, composed: true, })); }, onContentDidChange: (_sender, options) => { this.dispatchEvent(new InputEvent('input', { ...options, cancelable: false, bubbles: true, composed: true, })); }, onError: (err) => { this.dispatchEvent(new CustomEvent('math-error', { detail: { code: err.code, arg: err.arg, latex: err.latex, before: err.before, after: err.after, }, cancelable: false, bubbles: true, composed: true, })); }, onFocus: () => { this.dispatchEvent(new Event('focus', { cancelable: false, bubbles: false, composed: true, })); }, onKeystroke: (_sender, keystroke, ev) => { return this.dispatchEvent(new CustomEvent('keystroke', { detail: { keystroke, event: ev, }, cancelable: true, bubbles: true, composed: true, })); }, onModeChange: (_sender, _mode) => { this.dispatchEvent(new Event('mode-change', { cancelable: false, bubbles: true, composed: true, })); }, onCommit: (_sender) => { // Match the DOM event sent by `<input>`, `<textarea>`, etc... // Sent when the [Return] or [Enter] key is pressed, or on // focus loss if the content has changed. this.dispatchEvent(new Event('change', { cancelable: false, bubbles: true, composed: true, })); }, onMoveOutOf: (_sender, direction) => { return this.dispatchEvent(new CustomEvent('move-out', { detail: { direction }, cancelable: true, bubbles: true, composed: true, })); }, onTabOutOf: (_sender, direction) => { return this.dispatchEvent(new CustomEvent('focus-out', { detail: { direction }, cancelable: true, bubbles: true, composed: true, })); }, onReadAloudStatus: () => { this.dispatchEvent(new Event('read-aloud-status-change', { cancelable: false, bubbles: true, composed: true, })); }, onSelectionDidChange: () => { this.dispatchEvent(new Event('selection-change', { cancelable: false, bubbles: true, composed: true, })); }, onUndoStateDidChange: () => { this.dispatchEvent(new Event('undo-state-change', { cancelable: false, bubbles: true, composed: true, })); }, ...(gDeferredState.has(this) ? gDeferredState.get(this).options : getOptionsFromAttributes(this)), value, }); if (!gDeferredState.has(this)) { this.upgradeProperty('disabled'); this.upgradeProperty('readonly'); for (const attr of Object.keys(MathfieldElement.optionsAttributes)) this.upgradeProperty(toCamelCase(attr)); } // The mathfield creation could have failed if (!this._mathfield || !this._mathfield.model) { this._mathfield = null; return; } if (gDeferredState.has(this)) { this._mathfield.model.deferNotifications({ content: false, selection: false }, () => { const value = gDeferredState.get(this).value; if (value !== undefined) this._mathfield.setValue(value); this._mathfield.model.selection = gDeferredState.get(this).selection; gDeferredState.delete(this); }); } slot.addEventListener('slotchange', (event) => { if (event.target !== slot) return; const value = slot .assignedNodes() .map((x) => (x.nodeType === 3 ? x.textContent : '')) .join('') .trim(); if (value === this._slotValue) return; if (!this._mathfield) this.value = value; else { // Don't suppress notification changes. We need to know // if the value has changed indirectly through slot manipulation this._mathfield.setValue(value); } }); // Notify listeners that we're mounted and ready this.dispatchEvent(new Event('mount', { cancelable: false, bubbles: true, composed: true })); } /** * Custom elements lifecycle hooks * @internal */ disconnectedCallback() { // Notify listeners that we're about to be unmounted this.dispatchEvent(new Event('unmount', { cancelable: false, bubbles: true, composed: true })); if (!this._mathfield) return; // Save the state (in case the element gets reconnected later) const options = get(this._mathfield.options, [ ...Object.keys(MathfieldElement.optionsAttributes).map((x) => toCamelCase(x)), 'onBlur', 'onContentWillChange', 'onContentDidChange', 'onError', 'onFocus', 'onKeystroke', 'onModeChange', 'onCommit', 'onMoveOutOf', 'onTabOutOf', 'onReadAloudStatus', 'onSelectionDidChange', 'onUndoStateDidChange', ]); gDeferredState.set(this, { value: this._mathfield.getValue(), selection: this._mathfield.model.selection, options, }); // Dispose of the mathfield this._mathfield.dispose(); this._mathfield = null; } /** * Private lifecycle hooks * @internal */ upgradeProperty(prop) { if (this.hasOwnProperty(prop)) { const value = this[prop]; // A property may have already been set on the object, before // the element was connected: delete the property (after saving its value) // and use the setter to (re-)set its value. delete this[prop]; if (prop === 'readonly' || prop === 'read-only') prop = 'readOnly'; this[prop] = value; } } /** * Custom elements lifecycle hooks * @internal */ attributeChangedCallback(name, oldValue, newValue) { if (oldValue === newValue) return; const hasValue = newValue !== null; switch (name) { case 'disabled': this.disabled = hasValue; break; case 'read-only': case 'readonly': this.readOnly = hasValue; break; } } get readonly() { return this.hasAttribute('readonly') || this.hasAttribute('read-only'); } set readonly(value) { const isReadonly = Boolean(value); // Note that `readonly` and `disabled` are "boolean attributes" as // per the HTML5 spec. Their value must be the empty string to indicate // a value of true, or they must be absent to indicate a value of false. // https://html.spec.whatwg.org/#boolean-attribute if (isReadonly) { this.setAttribute('readonly', ''); this.setAttribute('disabled', ''); } else { this.removeAttribute('readonly'); this.removeAttribute('read-only'); this.removeAttribute('disabled'); } this.setAttribute('aria-disabled', isReadonly ? 'true' : 'false'); this.setOptions({ readOnly: isReadonly }); } get disabled() { return this.hasAttribute('disabled'); } set disabled(value) { const isDisabled = Boolean(value); if (isDisabled) this.setAttribute('disabled', ''); else this.removeAttribute('disabled'); this.setAttribute('aria-disabled', isDisabled ? 'true' : 'false'); this.setOptions({ readOnly: isDisabled }); } /** * The content of the mathfield as a LaTeX expression. * ```js * document.querySelector('mf').value = '\\frac{1}{\\pi}' * ``` * @category Accessing and changing the content */ get value() { return this.getValue(); } /** * @category Accessing and changing the content */ set value(value) { this.setValue(value); } get defaultMode() { return this.getOption('defaultMode'); } set defaultMode(value) { this.setOptions({ defaultMode: value }); } get fontsDirectory() { return this.getOption('fontsDirectory'); } set fontsDirectory(value) { this.setOptions({ fontsDirectory: value }); } get mathModeSpace() { return this.getOption('fontsDirectory'); } set mathModeSpace(value) { this.setOptions({ mathModeSpace: value }); } get inlineShortcutTimeout() { return this.getOption('inlineShortcutTimeout'); } set inlineShortcutTimeout(value) { this.setOptions({ inlineShortcutTimeout: value }); } get keypressVibration() { return this.getOption('keypressVibration'); } set keypressVibration(value) { this.setOptions({ keypressVibration: value }); } get keypressSound() { return this.getOption('keypressSound'); } set keypressSound(value) { this.setOptions({ keypressSound: value }); } get plonkSound() { var _a; return (_a = this.getOption('plonkSound')) !== null && _a !== void 0 ? _a : null; } set plonkSound(value) { this.setOptions({ plonkSound: value }); } get letterShapeStyle() { return this.getOption('letterShapeStyle'); } set letterShapeStyle(value) { this.setOptions({ letterShapeStyle: value }); } get locale() { return this.getOption('locale'); } set locale(value) { this.setOptions({ locale: value }); } get readOnly() { return this.getOption('readOnly'); } set readOnly(value) { this.setOptions({ readOnly: value }); } get removeExtraneousParentheses() { return this.getOption('removeExtraneousParentheses'); } set removeExtraneousParentheses(value) { this.setOptions({ removeExtraneousParentheses: value }); } get smartFence() { return this.getOption('smartFence'); } set smartFence(value) { this.setOptions({ smartFence: value }); } get smartMode() { return this.getOption('smartMode'); } set smartMode(value) { this.setOptions({ smartMode: value }); } get smartSuperscript() { return this.getOption('smartSuperscript'); } set smartSuperscript(value) { this.setOptions({ smartSuperscript: value }); } get speechEngine() { return this.getOption('speechEngine'); } set speechEngine(value) { this.setOptions({ speechEngine: value }); } get speechEngineRate() { return this.getOption('speechEngineRate'); } set speechEngineRate(value) { this.setOptions({ speechEngineRate: value }); } get speechEngineVoice() { return this.getOption('speechEngineVoice'); } set speechEngineVoice(value) { this.setOptions({ speechEngineVoice: value }); } get textToSpeechMarkup() { return this.getOption('textToSpeechMarkup'); } set textToSpeechMarkup(value) { this.setOptions({ textToSpeechMarkup: value }); } get textToSpeechRules() { return this.getOption('textToSpeechRules'); } set textToSpeechRule(value) { this.setOptions({ textToSpeechRules: value }); } get virtualKeyboardLayout() { return this.getOption('virtualKeyboardLayout'); } set virtualKeyboardLayout(value) { this.setOptions({ virtualKeyboardLayout: value }); } get virtualKeyboardMode() { return this.getOption('virtualKeyboardMode'); } set virtualKeyboardMode(value) { this.setOptions({ virtualKeyboardMode: value }); } get virtualKeyboardTheme() { return this.getOption('virtualKeyboardTheme'); } set virtualKeyboardTheme(value) { this.setOptions({ virtualKeyboardTheme: value }); } get virtualKeyboards() { return this.getOption('virtualKeyboards'); } set virtualKeyboards(value) { this.setOptions({ virtualKeyboards: value }); } get useSharedVirtualKeyboard() { return this.getOption('useSharedVirtualKeyboard'); } set useSharedVirtualKeyboard(value) { this.setOptions({ useSharedVirtualKeyboard: value }); } get sharedVirtualKeyboardTargetOrigin() { return this.getOption('sharedVirtualKeyboardTargetOrigin'); } set sharedVirtualKeyboardTargetOrigin(value) { this.setOptions({ sharedVirtualKeyboardTargetOrigin: value }); } /** * An array of ranges representing the selection. * * It is guaranteed there will be at least one element. If a discontinuous * selection is present, the result will include more than one element. * * @category Selection * */ get selection() { if (this._mathfield) return this._mathfield.model.selection; if (gDeferredState.has(this)) return gDeferredState.get(this).selection; return { ranges: [[0, 0]], direction: 'forward' }; } /** * * @category Selection */ set selection(sel) { if (typeof sel === 'number') sel = { ranges: [[sel, sel]] }; if (this._mathfield) { this._mathfield.model.selection = sel; return; } if (gDeferredState.has(this)) { gDeferredState.set(this, { ...gDeferredState.get(this), selection: sel, }); return; } gDeferredState.set(this, { value: undefined, selection: sel, options: getOptionsFromAttributes(this), }); } /** * The position of the caret/insertion point, from 0 to `lastOffset`. * * @category Selection * */ get position() { if (this._mathfield) return this._mathfield.model.position; if (gDeferredState.has(this)) return gDeferredState.get(this).selection.ranges[0][0]; return 0; } /** * @category Selection */ set position(offset) { if (this._mathfield) this._mathfield.model.position = offset; if (gDeferredState.has(this)) { gDeferredState.set(this, { ...gDeferredState.get(this), selection: { ranges: [[offset, offset]] }, }); return; } gDeferredState.set(this, { value: undefined, selection: { ranges: [[offset, offset]] }, options: getOptionsFromAttributes(this), }); } /** * The depth of an offset represent the depth in the expression tree. */ getOffsetDepth(offset) { var _a, _b; if (this._mathfield) return (_b = ((_a = this._mathfield.model.at(offset)) === null || _a === void 0 ? void 0 : _a.treeDepth) - 2) !== null && _b !== void 0 ? _b : 0; return 0; } /** * The last valid offset. * @category Selection */ get lastOffset() { var _a, _b; return (_b = (_a = this._mathfield) === null || _a === void 0 ? void 0 : _a.model.lastOffset) !== null && _b !== void 0 ? _b : -1; } } function toCamelCase(s) { return s.toLowerCase().replace(/[^a-zA-Z\d]+(.)/g, (m, c) => c.toUpperCase()); } function reflectAttributes(element) { const defaultOptions = getDefault(); const options = element.getOptions(); Object.keys(MathfieldElement.optionsAttributes).forEach((x) => { const prop = toCamelCase(x); if (MathfieldElement.optionsAttributes[x] === 'on/off') { if (defaultOptions[prop] !== options[prop]) element.setAttribute(x, options[prop] ? 'on' : 'off'); else element.removeAttribute(x); } else if (defaultOptions[prop] !== options[prop]) { if (MathfieldElement.optionsAttributes[x] === 'boolean') { if (options[prop]) { // Add attribute element.setAttribute(x, ''); } else { // Remove attribute element.removeAttribute(x); } } else { // Set attribute (as string) if (typeof options[prop] === 'string' || typeof options[prop] === 'number') element.setAttribute(x, options[prop].toString()); } } }); } // Function toKebabCase(s: string): string { // return s // .match( // /[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g // ) // .map((x: string) => x.toLowerCase()) // .join('-'); // } function getOptionsFromAttributes(mfe) { const result = {}; const attribs = MathfieldElement.optionsAttributes; Object.keys(attribs).forEach((x) => { if (mfe.hasAttribute(x)) { const value = mfe.getAttribute(x); if (attribs[x] === 'boolean') result[toCamelCase(x)] = true; else if (attribs[x] === 'on/off') { if (value === 'on') result[toCamelCase(x)] = true; else if (value === 'off') result[toCamelCase(x)] = false; else result[toCamelCase(x)] = undefined; } else if (attribs[x] === 'number') result[toCamelCase(x)] = Number.parseFloat(value !== null && value !== void 0 ? value : '0'); else result[toCamelCase(x)] = value; } else if (attribs[x] === 'boolean') result[toCamelCase(x)] = false; }); return result; } if (isBrowser() && !((_a = window.customElements) === null || _a === void 0 ? void 0 : _a.get('math-field'))) { window.MathfieldElement = MathfieldElement; window.mathlive = { version: '0.77.0' }; (_b = window.customElements) === null || _b === void 0 ? void 0 : _b.define('math-field', MathfieldElement); } /** * Setup the document to use a single shared virtual keyboard amongst * all `<math-field>` instances in the document, including those in _iframes_. * * `makeSharedVirtualKeyboard()` should be called as early as possible, * and before any new mathfield element is created: it doesn't apply * retroactively. * * `<math-field>` elements in an _iframe_ should have the * `use-shared-virtual-keyboard` attribute. * * The shared virtual keyboard coordinates focus between multiple mathfield * elements and renders the virtual keyboard with the options passed by param * of this method. * * Calling `setOptions()` on a mathfield with options related to the keyboard * will affect this shared virtual keyboard instance when the mathfield is * focused. * * @param options Options to configure the shared virtual keyboard. * * ```html * <iframe src="..."> * <!-- The iframe page content --> * <math-field virtual-keyboard-mode="onfocus" use-shared-virtual-keyboard /> * * <script type="module"> * import 'https://unpkg.com/mathlive?module'; * </script> * </iframe> * ``` * * ```javascript * import { makeSharedVirtualKeyboard } from 'https://unpkg.com/mathlive?module'; * * makeSharedVirtualKeyboard(); * ``` * Read more about [sharing virtual keyboards](https://cortexjs.io/mathlive/guides/virtual-keyboards/#shared-virtual-keyboard) * * @keywords create, make, mathfield, iframe */ function makeSharedVirtualKeyboard(options) { var _a, _b; if (!((_a = window.mathlive) === null || _a === void 0 ? void 0 : _a.sharedVirtualKeyboard)) { if ([...document.querySelectorAll('math-field')].some((x) => x.isConnected && x['_mathfield'] && x['_mathfield']['_virtualKeyboard'])) { console.error('ERROR: makeSharedVirtualKeyboard() must be called before any mathfield element is connected to the DOM'); } if (!window.mathlive) window.mathlive = {}; window.mathlive.sharedVirtualKeyboard = new RemoteVirtualKeyboard(options); } return (_b = window.mathlive) === null || _b === void 0 ? void 0 : _b.sharedVirtualKeyboard; } /** * Convert a LaTeX string to a string of HTML markup. * * **(Note)** * * This function does not interact with the DOM. The function does not load * fonts or inject stylesheets in the document. It can be used * on the server side. * * To get the output of this function to correctly display * in a document, use the mathlive static style sheet by adding the following * to the `<head>` of the document: * * ```html * <link rel="stylesheet" href="https://unpkg.com/mathlive/dist/mathlive-static.css" /> * ``` * * --- * * @param text A string of valid LaTeX. It does not have to start * with a mode token such as `$$` or `\(`. * * @param options.mathstyle If `"displaystyle"` the "display" mode of TeX * is used to typeset the formula, which is most appropriate for formulas that are * displayed in a standalone block. * * If `"textstyle"` is used, the "text" mode * of TeX is used, which is most appropriate when displaying math "inline" * with other text (on the same line). * * @param options.macros A dictionary of LaTeX macros * * @param options.onError A function invoked when a syntax error is encountered. * An attempt to recover will be made even when an error is reported. * * @category Converting * @keywords convert, latex, markup */ function convertLatexToMarkup(text, options) { var _a, _b; options = options !== null && options !== void 0 ? options : {}; options.mathstyle = (_a = options.mathstyle) !== null && _a !== void 0 ? _a : 'displaystyle'; let letterShapeStyle = (_b = options.letterShapeStyle) !== null && _b !== void 0 ? _b : 'auto'; if (letterShapeStyle === 'auto') letterShapeStyle = l10n.locale.startsWith('fr') ? 'french' : 'tex'; options.macros = getMacros(options === null || options === void 0 ? void 0 : options.macros); // // 1. Parse the formula and return a tree of atoms, e.g. 'genfrac'. // const root = new Atom('root'); root.body = typeset(parseLatex(text, { parseMode: 'math', macros: options.macros, registers: options.registers, mathstyle: options.mathstyle, onError: options.onError, colorMap: options.colorMap, backgroundColorMap: options.backgroundColorMap, }), { registers: options.registers }); // // 2. Transform the math atoms into elementary boxes // for example from genfrac to VBox. // const box = root.render(new Context({ registers: getDefaultRegisters(), smartFence: false, renderPlaceholder: () => new Box(0xa0, { maxFontSize: 1.0 }), }, { fontSize: DEFAULT_FONT_SIZE, letterShapeStyle: letterShapeStyle, }, options.mathstyle)); if (!box) return ''; // // 3. Adjust to `mord` according to TeX spacing rules // adjustInterAtomSpacing(box); // // 2. Simplify by coalescing adjacent boxes // for example, from <span>1</span><span>2</span> // to <span>12</span> // coalesce(box); // // 4. Wrap the expression with struts // const wrapper = makeStruts(box, { classes: 'ML__mathlive' }); // // 5. Generate markup // return wrapper.toMarkup(); } /** * Convert a LaTeX string to a string of MathML markup. * * @param latex A string of valid LaTeX. It does not have to start * with a mode token such as a `$$` or `\(`. * @param options.generateId If true, add an `"extid"` attribute * to the MathML nodes with a value matching the `atomID`. This can be used * to map items on the screen with their MathML representation or vice-versa. * @param options.onError Callback invoked when an error is encountered while * parsing the input string. * * @category Converting */ function convertLatexToMathMl(latex, options = {}) { options.macros = getMacros(options === null || options === void 0 ? void 0 : options.macros); return atomsToMathML(parseLatex(latex, { parseMode: 'math', args: () => '', macros: options.macros, registers: options.registers, mathstyle: 'displaystyle', onError: options.onError, colorMap: options.colorMap, backgroundColorMap: options.backgroundColorMap, }), options); } /** * Convert a LaTeX string to a textual representation ready to be spoken * * @param latex A string of valid LaTeX. It does not have to start * with a mode token such as a `$$` or `\(`. * * @param options {@inheritDoc TextToSpeechOptions} * @param options.onError Callback invoked when an error is encountered while * parsing the input string. * * @return The spoken representation of the input LaTeX. * @example * console.log(convertLatexToSpeakableText('\\frac{1}{2}')); * // 'half' * @category Converting * @keywords convert, latex, speech, speakable, text, speakable text */ function convertLatexToSpeakableText(latex, options = {}) { options.macros = getMacros(options === null || options === void 0 ? void 0 : options.macros); const atoms = parseLatex(latex, { parseMode: 'math', macros: options.macros, registers: options.registers, mathstyle: 'displaystyle', onError: options.onError, colorMap: options.colorMap, backgroundColorMap: options.backgroundColorMap, }); return atomToSpeakableText(atoms, options); } /** * Transform all the elements in the document body that contain LaTeX code * into typeset math. * * **(Caution):** * This is a very expensive call, as it needs to parse the entire * DOM tree to determine which elements need to be processed. In most cases * this should only be called once per document, once the DOM has been loaded. * * To render a specific element, use {@linkcode renderMathInElement | renderMathInElement()} * --- * * Read {@tutorial getting-started | Getting Started}. * * @example * import { renderMathInDocument } from 'https://unpkg.com/mathlive?module'; * document.addEventListener("load", () => renderMathInDocument()); * * @category Rendering * @keywords render, document, autorender */ function renderMathInDocument(options) { throwIfNotInBrowser(); renderMathInElement(document.body, options); } function getElement(element) { if (typeof element === 'string' && isBrowser()) { const result = document.getElementById(element); if (result === null) throw new Error(`The element with ID "${element}" could not be found.`); return result; } return typeof element === 'string' ? null : element; } /** * Transform all the children of `element` that contain LaTeX code * into typeset math, recursively. * * Read {@tutorial mathfield-getting-started | Getting Started}. * * @param element An HTML DOM element, or a string containing * the ID of an element. * * @category Rendering * @keywords render, element, htmlelement */ function renderMathInElement(element, options) { var _a, _b, _c; const el = getElement(element); if (!el) return; const optionsPrivate = options !== null && options !== void 0 ? options : {}; optionsPrivate.renderToMarkup = (_a = optionsPrivate.renderToMarkup) !== null && _a !== void 0 ? _a : convertLatexToMarkup; optionsPrivate.renderToMathML = (_b = optionsPrivate.renderToMathML) !== null && _b !== void 0 ? _b : convertLatexToMathMl; optionsPrivate.renderToSpeakableText = (_c = optionsPrivate.renderToSpeakableText) !== null && _c !== void 0 ? _c : convertLatexToSpeakableText; autoRenderMathInElement(el, optionsPrivate); } /** * Current version: `0.77.0` * * The version string of the SDK using the [semver](https://semver.org/) convention: * * `MAJOR`.`MINOR`.`PATCH` * * * **`MAJOR`** is incremented for incompatible API changes * * **`MINOR`** is incremented for new features * * **`PATCH`** is incremented for bug fixes */ const version = { mathlive: '0.77.0', computeEngine: Fi, }; /** @internal */ const debug = { latexToAsciiMath, asciiMathToLatex, FUNCTIONS: MathliveDebug.FUNCTIONS, MATH_SYMBOLS: MathliveDebug.MATH_SYMBOLS, TEXT_SYMBOLS: MathliveDebug.TEXT_SYMBOLS, ENVIRONMENTS: MathliveDebug.ENVIRONMENTS, DEFAULT_KEYBINDINGS: MathliveDebug.DEFAULT_KEYBINDINGS, getKeybindingMarkup: MathliveDebug.getKeybindingMarkup, }; exports.ComputeEngine = Mi; exports.MathfieldElement = MathfieldElement; exports.autoRenderMathInElement = autoRenderMathInElement; exports.convertLatexToMarkup = convertLatexToMarkup; exports.convertLatexToMathMl = convertLatexToMathMl; exports.convertLatexToSpeakableText = convertLatexToSpeakableText; exports.debug = debug; exports.getVars = He; exports.isEnvironmentEntry = s; exports.isInfixEntry = i; exports.isMatchfixEntry = t; exports.isPostfixEntry = r; exports.isPrefixEntry = n; exports.isSymbolEntry = e; exports.makeSharedVirtualKeyboard = makeSharedVirtualKeyboard; exports.renderMathInDocument = renderMathInDocument; exports.renderMathInElement = renderMathInElement; exports.version = version; Object.defineProperty(exports, '__esModule', { value: true }); }));