diff --git a/.obsidian/community-plugins.json b/.obsidian/community-plugins.json index 72575c32..2b98cf15 100644 --- a/.obsidian/community-plugins.json +++ b/.obsidian/community-plugins.json @@ -29,5 +29,6 @@ "obsidian-douban-plugin", "obsidian-icon-folder", "better-export-pdf", - "obsidian-calibre-plugin" + "obsidian-calibre-plugin", + "obsidian-enhancing-mindmap" ] \ No newline at end of file diff --git a/.obsidian/plugins/obsidian-enhancing-mindmap/main.js b/.obsidian/plugins/obsidian-enhancing-mindmap/main.js new file mode 100644 index 00000000..902e0f18 --- /dev/null +++ b/.obsidian/plugins/obsidian-enhancing-mindmap/main.js @@ -0,0 +1,37771 @@ +/* +THIS IS A GENERATED/BUNDLED FILE BY ROLLUP +if you want to view the source visit the plugins github repository +*/ + +'use strict'; + +var obsidian = require('obsidian'); + +/****************************************************************************** +Copyright (c) Microsoft Corporation. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. +***************************************************************************** */ + +function __awaiter(thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +} + +typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { + var e = new Error(message); + return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; +}; + +function around(obj, factories) { + const removers = Object.keys(factories).map(key => around1(obj, key, factories[key])); + return removers.length === 1 ? removers[0] : function () { removers.forEach(r => r()); }; +} +function around1(obj, method, createWrapper) { + const original = obj[method], hadOwn = obj.hasOwnProperty(method); + let current = createWrapper(original); + // Let our wrapper inherit static props from the wrapping method, + // and the wrapping method, props from the original method + if (original) + Object.setPrototypeOf(current, original); + Object.setPrototypeOf(wrapper, current); + obj[method] = wrapper; + // Return a callback to allow safe removal + return remove; + function wrapper(...args) { + // If we have been deactivated and are no longer wrapped, remove ourselves + if (current === original && obj[method] === wrapper) + remove(); + return current.apply(this, args); + } + function remove() { + // If no other patches, just do a direct removal + if (obj[method] === wrapper) { + if (hadOwn) + obj[method] = original; + else + delete obj[method]; + } + if (current === original) + return; + // Else pass future calls through, and remove wrapper from the prototype chain + current = original; + Object.setPrototypeOf(wrapper, original || Function); + } +} + +// العربية +var ar = {}; + +// čeština +var cz = {}; + +// Dansk +var da = {}; + +// Deutsch +var de = { + // main.ts + "Open as mindmap board": "Öffne als Mindmap", + "Create new mindmap": "Erstelle neue Mindmap", + "New mindmap board": "Neue Mindmap", + "Untitled mindmap": "Unbenannte Mindmap", + "Open as markdown": "Öffne als Markdown", + //Execute.ts + "Sub title": "Untertitel", + "Export to opml": "Exportiere als opml", + "Export to image": "Exportiere als Bild", + //setting + "Canvas size": "Leinwandgröße", + "Canvas size desc": "Breite und Höhe der Leinwand", + "Canvas background": "Leinwand Hintergrund", + "Canvas background desc": "Hintergrundfarbe der Leinwand", + "Max level of node to markdown head": "Maximale Knotenebene welche eine Überschrift erstellt", + "Max level of node to markdown head desc": "Angeben, welche Knotenebene eine separate Überschrift anstelle eines Listenelements erstellt.", + "Font size": "Schriftgröße", + "Font size desc": "Gebe die Schriftgröße ans", + "Mind map layout direct": "Mindmap Layout", + "Mind map layout direct desc": "Geben Sie die Richtung an, in der die Knoten in Ihrer Mindmap platziert werden.", + "Right": "Rechts", + "Left": "Links", + "Centered": "Zentriert" +}; + +// English +var en = { + // main.ts + "Open as mindmap board": "Open as mindmap board", + "Create new mindmap": "Create new mindmap", + "New mindmap board": "New mindmap board", + "Untitled mindmap": "Untitled mindmap", + "Open as markdown": "Open as markdown", + //Execute.ts + "Sub title": "Sub title", + "Export to opml": "Export to opml", + "Export to image": "Export to image", + //setting + "Canvas size": "Canvas size", + "Canvas size desc": "Width and height of the canvas", + "Canvas background": "Canvas background", + "Canvas background desc": "Background color of canvas", + "Max level of node to markdown head": "Max level of node to create a Heading", + "Max level of node to markdown head desc": "Specify which Node Level creates a seperate Heading instead of a List-Item", + "Font size": "Font Size", + "Font size desc": "Specify the Font Size", + "Mind map layout direct": "Mindmap Layout", + "Mind map layout direct desc": "Specify the direction the nodes are placed in your Mindmap", + "Right": "Right", + "Left": "Left", + "Centered": "Centered", + "Clockwise": "Clockwise", + "Stroke Array": "Stroke array", + "Stroke Array Desc": "Node link color base on this value or random color", + "Save fail": "Save data err", + "Save success": "Save data success", + "Toggle markdown/mindmap": "Toggle to markdown/mindmap mode", + "Copy node": "Copy", + "Paste node": "Paste", + "Export to html": "Export to html" +}; + +// British English +var enGB = {}; + +// Español +var es = {}; + +// français +var fr = {}; + +// हिन्दी +var hi = {}; + +// Bahasa Indonesia +var id = {}; + +// Italiano +var it = {}; + +// 日本語 +var ja = {}; + +// 한국어 +var ko = {}; + +// Nederlands +var nl = {}; + +// Norsk +var no = {}; + +// język polski +var pl = {}; + +// Português +var pt = {}; + +// Português do Brasil +// Brazilian Portuguese +var ptBR = {}; + +// Română +var ro = {}; + +// русский +var ru = {}; + +// Türkçe +var tr = {}; + +// 简体中文 +var zhCN = { + // main.ts + "Open as mindmap board": "打开为思维导图", + "Create new mindmap": "创建新思维导图", + "New mindmap board": "新建思维导图", + "Untitled mindmap": "未命名思维导图", + "Open as markdown": "打开为 Markdown 文件", + //Execute.ts + "Sub title": "新节点", + "Export to opml": "导出至OPML", + "Export to image": "导出至图片", + //setting + "Canvas size": "画布尺寸", + "Canvas size desc": "绘制思维导图画布的宽度和高度", + "Canvas background": "背景颜色", + "Canvas background desc": "画布的背景颜色", + "Max level of node to markdown head": "节点文字转为markdown标题的最大层级", + "Max level of node to markdown head desc": "将小于该层级的节点文字转为markdown标题,最大层级为6,因为HTML标题支持最大为6级", + "Font size": "文字大小", + "Font size desc": "思维导图文字默认大小,单位px", + "Mind map layout direct": "思维导图布局方向", + "Mind map layout direct desc": "思维导图的布局方向,分为向两侧发散、仅右侧、仅左侧三个方向", + // wait to use + "Expand node": "展开节点", + "Collapse node": "收缩节点", + "Only show this node": "仅显示该节点", + "Expand all node": "展开所有节点", + "Collapse all node": "收缩所有节点", + "Add relate link": "添加联系线", + "Remove relate link": "删除联系线", + "Add summary node": "添加归纳", + "Remove summary node": "删除归纳", + "Remove node": "删除节点", + "Only remove this node": "仅删除该节点", + "Add child node": "添加子节点", + "Add brother node": "添加兄弟节点", + "Stroke Array": "颜色组", + "Stroke Array Desc": "节点连线颜色将按照颜色组生成,否则生成随机颜色", + "Save fail": "保存失败", + "Save success": "保存成功", + "Toggle markdown/mindmap": "切换为 markdown 或 mindmap 模式", + "Copy node": "复制", + "Paste node": "粘贴", + "Export to html": "导出为 html" +}; + +// 繁體中文 +var zhTW = {}; + +const localeMap = { + ar, + cs: cz, + da, + de, + en, + "en-gb": enGB, + es, + fr, + hi, + id, + it, + ja, + ko, + nl, + nn: no, + pl, + pt, + "pt-br": ptBR, + ro, + ru, + tr, + "zh-cn": zhCN, + "zh-tw": zhTW, +}; +const locale = localeMap[obsidian.moment.locale()]; +function t(str) { + if (!locale) { + console.error("Error: kanban locale not found", obsidian.moment.locale()); + } + return (locale && locale[str]) || en[str]; +} + +const FRONT_MATTER_REGEX = /^(---)$.+?^(---)$.+?/ims; +const frontMatterKey = 'mindmap-plugin'; +const basicFrontmatter = [ + "---", + "", + `${frontMatterKey}: basic`, + "", + "---", + "", + "", +].join("\n"); + +function keepLastIndex(dom) { + if (window.getSelection) { //ie11 10 9 ff safari + dom.focus(); //ff + var range = window.getSelection(); + range.selectAllChildren(dom); + range.collapseToEnd(); + } + // else if ( document.selection ) { //ie10 9 8 7 6 5 + // var range = document.selection.createRange(); + // range.moveToElementText(dom); + // range.collapse(false); + // range.select(); + // } +} +class Node { + constructor(data, mindMap) { + this.box = { + x: 0, + y: 0, + width: 0, + height: 0 + }; + this.isExpand = true; + this.isSelect = false; + this.children = []; + this.isHide = false; + this.isEdit = false; + this._barDom = null; + this.data = data; + this.mindmap = mindMap; + this.initDom(); + } + getId() { + return this.data.id; + } + initDom() { + this.containEl = document.createElement('div'); + this.containEl.classList.add('mm-node'); + this.containEl.setAttribute('contentEditable', 'false'); + this.containEl.setAttribute('tabIndex', '-1'); + this.containEl.setAttribute('data-id', this.data.id); + this.containEl.setAttribute('draggable', 'false'); + this.contentEl = document.createElement('div'); + this.contentEl.classList.add('mm-node-content'); + this.containEl.appendChild(this.contentEl); + //this.containEl.textContent = this.data.text; + this.initNodeBar(); + if (this.data.isRoot) { + this.containEl.classList.add('mm-root'); + this.isRoot = true; + } + this.parseText(); + } + initNodeBar() { + this._barDom = document.createElement('div'); + this._barDom.classList.add('mm-node-bar'); + this.containEl.appendChild(this._barDom); + } + parseText() { + if (this.data.text.length === 0) { + this.data.text = "Sub title"; + } + obsidian.MarkdownRenderer.renderMarkdown(this.data.text, this.contentEl, this.mindmap.path || "", null).then(() => { + this.data.mdText = this.contentEl.innerHTML; + this.refreshBox(); + this.mindmap && this.mindmap.emit('initNode', {}); + this._delay(); + }); + } + _delay() { + //parse md + this.contentEl.findAll(".internal-embed").forEach((el) => __awaiter(this, void 0, void 0, function* () { + const src = el.getAttribute("src"); + if (typeof src === 'string') { + var pathObj = obsidian.parseLinktext(src); + var fileData = ''; + if (this.mindmap && this.mindmap.view) { + var f = this.mindmap.view.app.metadataCache.getFirstLinkpathDest(pathObj.path, this.mindmap.path); + if (f instanceof obsidian.TFile && f.extension === 'md') { + fileData = yield this.mindmap.view.app.vault.adapter.read(f.path); + var markdownEmbed = document.createElement('div'); + markdownEmbed.classList.add('markdown-embed'); + // var markdownHead = document.createElement('div'); + // markdownHead.classList.add('markdown-embed-title'); + // markdownHead.innerText=f.basename; + markdownEmbed.setAttribute('data-name', f.path); + var markdownContent = document.createElement('div'); + markdownContent.classList.add('markdown-embed-content'); + var markdownPreview = document.createElement('div'); + markdownPreview.classList.add('markdown-preview-view'); + markdownContent.appendChild(markdownPreview); + var markdownLink = document.createElement('div'); + markdownLink.classList.add('markdown-embed-link'); + markdownLink.setAttribute('aria-label', 'Open link'); + markdownLink.innerHTML = ``; + el.appendChild(markdownEmbed); + // markdownEmbed.appendChild(markdownHead); + markdownEmbed.appendChild(markdownContent); + markdownEmbed.appendChild(markdownLink); + if (pathObj.subpath) { + var metacache = this.mindmap.view.app.metadataCache.getFileCache(f); + var t = obsidian.resolveSubpath(metacache, pathObj.subpath); + // console.log(t); + if (t && t.start && t.end) { + var md = fileData.substring(t.start.offset, t.end.offset); + // console.log(md) + } + else if (t && t.start && !t.end) { + var md = fileData.substr(t.start.offset); + } + else { + var md = fileData || ''; + } + } + else { + var md = fileData || ''; + } + if (md) { + obsidian.MarkdownRenderer.renderMarkdown(md, markdownPreview, this.mindmap.path || "", null).then(() => { + // this.data.mdText = this.editDom.innerHTML; + this.refreshBox(); + //this._delay(); + this.mindmap && this.mindmap.emit('renderEditNode', { node: this }); + }); + } + } + } + } + })); + //parse image + setTimeout(() => { + this.contentEl.findAll(".internal-embed").forEach((el) => { + var _a; + const src = el.getAttribute("src"); + const target = typeof src === "string" && + this.mindmap && ((_a = this.mindmap.view) === null || _a === void 0 ? void 0 : _a.app.metadataCache.getFirstLinkpathDest(src, this.mindmap.path)); + if (target instanceof obsidian.TFile && target.extension !== "md" && this.mindmap) { + el.innerText = ""; + el.createEl("img", { attr: { src: this.mindmap.view.app.vault.getResourcePath(target) } }, (img) => { + if (el.hasAttribute("width")) + img.setAttribute("width", el.getAttribute("width")); + if (el.hasAttribute("alt")) + img.setAttribute("alt", el.getAttribute("alt")); + }); + el.addClasses(["image-embed", "is-loaded"]); + } + }); + //Possible causes of delay,code mathjax + var dom = this.contentEl.querySelector('code') || this.contentEl.querySelector('.MathJax'); + if (dom) { + setTimeout(() => { + this.clearCacheData(); + this.refreshBox(); + this.mindmap && this.mindmap.emit('renderEditNode', {}); + }, 100); + } + //image + this.contentEl.querySelectorAll('img').forEach(element => { + element.onload = () => { + this.clearCacheData(); + this.refreshBox(); + this.mindmap && this.mindmap.emit('renderEditNode', {}); + }; + element.onerror = () => { + this.clearCacheData(); + this.refreshBox(); + this.mindmap && this.mindmap.emit('renderEditNode', {}); + }; + element.setAttribute('draggble', 'false'); + }); + }, 100); + } + select() { + this.isSelect = true; + this.containEl.setAttribute('draggable', 'true'); + Object.assign(window, { + myNode: this + }); + if (!this.containEl.classList.contains('mm-node-select')) { + this.containEl.classList.add('mm-node-select'); + } + this.mindmap.selectNode = this; + } + unSelect() { + this.isSelect = false; + this.containEl.setAttribute('draggable', 'false'); + if (this.containEl.classList.contains('mm-node-select')) { + this.containEl.classList.remove('mm-node-select'); + } + } + edit() { + this.contentEl.innerText = ''; + this._oldText = this.data.text; + //var _t = this.data.text.replace(/\r\n/g,"
") + // _t = _t.replace(/\n/g,"
"); + // console.log(_t); + this.contentEl.innerText = this.data.text; + this.contentEl.setAttribute('contentEditable', 'true'); + this.contentEl.focus(); + this.mindmap.editNode = this; + this.isEdit = true; + keepLastIndex(this.contentEl); + if (this.contentEl.innerText == t('Sub title')) { + this.selectText(); + } + if (!this.containEl.classList.contains('mm-edit-node')) { + this.containEl.classList.add('mm-edit-node'); + } + } + selectText() { + var text = this.contentEl; + // if (document.body.createTextRange) { + // var range = document.body.createTextRange(); + // range.moveToElementText(text); + // range.select(); + // } + if (window.getSelection) { + var selection = window.getSelection(); + var range = document.createRange(); + range.selectNodeContents(text); + selection.removeAllRanges(); + selection.addRange(range); + } + } + cancelEdit() { + var text = this.contentEl.innerText.trim() || ''; + if (text.length == 0) { + text = this._oldText; + } + this.data.text = text; + this.contentEl.innerText = ''; + obsidian.MarkdownRenderer.renderMarkdown(text, this.contentEl, this.mindmap.path || "", null).then(() => { + this.data.mdText = this.contentEl.innerHTML; + this.refreshBox(); + this._delay(); + }); + if (text != this._oldText) { + this.mindmap.execute('changeNodeText', { + node: this, + text, + oldText: this._oldText + }); + } + this.contentEl.setAttribute('contentEditable', 'false'); + this.isEdit = false; + if (this.containEl.classList.contains('mm-edit-node')) { + this.containEl.classList.remove('mm-edit-node'); + } + } + getLevel() { + var level = 0, parent = this.parent; + if (this == this.mindmap.root) { + return level; + } + level++; + while (parent && parent != this.mindmap.root) { + level++; + parent = parent.parent; + } + return level; + } + getChildren() { + return this.children; + } + setPosition(x, y) { + this.box.x = x; + this.box.y = y; + this.containEl.style.left = x + 'px'; + this.containEl.style.top = y + 'px'; + } + getPosition() { + return { + x: this.box.x, + y: this.box.y + }; + } + move(dx, dy) { + var p = this.getPosition(); + this.setPosition(p.x + dx, p.y + dy); + } + getData() { + return JSON.parse(JSON.stringify(this.data)); + } + refreshBox() { + this.box = this.getDomBox(); + } + getBox() { + return Object.assign({}, this.box); + } + getCBox() { + return Object.assign({}, this.box); + } + getDomBox() { + var t = parseInt(this.containEl.style.top); + var l = parseInt(this.containEl.style.left); + var w = Math.ceil(this.contentEl.offsetWidth); + var h = Math.ceil(this.contentEl.offsetHeight); + return { + x: l, + y: t, + width: w, + height: h, + th: 0, + bh: 0 + }; + } + getShowNodeList() { + var list = []; + (function getList(node) { + if (node.isShow()) { + list.push(node); + } + node.children.forEach((n) => { + getList(n); + }); + })(this); + return list; + } + getSiblings() { + if (this.parent) { + return this.parent.children.filter(item => item != this); + } + else { + return []; + } + } + isLeaf() { + return !this.children.length; + } + isShow() { + return !this.isHide; + } + show() { + this.containEl.style.display = "block"; + this.isHide = false; + } + hide() { + this.containEl.style.display = "none"; + this.isHide = true; + } + clearCacheData() { + var anchor = this; + while (anchor) { + anchor.boundingRect = null; + anchor = anchor.parent; + } + } + addChild(node, i) { + if (this.children.indexOf(node) == -1) { + if (i > -1) { + if (i > this.children.length) + i = this.children.length; + this.children.splice(i, 0, node); + } + else { + this.children.push(node); + } + node.parent = this; + } + } + removeChild(child) { + var index = this.children.indexOf(child); + if (index > -1) { + this.children.splice(index, 1); + } + return index; + } + setText(text) { + this.data.text = text; + this.contentEl.innerHTML = ''; + this.parseText(); + } + expand() { + this.isExpand = true; + function show(node) { + node.show(); + node.refreshBox(); + node.boundingRect = null; + if (node.isExpand) { + node.children.forEach(c => { + show(c); + }); + } + } + show(this); + if (this.containEl.classList.contains('mm-node-collapse')) { + this.containEl.classList.remove('mm-node-collapse'); + } + } + collapse() { + this.isExpand = false; + function hide(node) { + node.hide(); + if (node.isExpand) { + node.children.forEach(c => { + hide(c); + }); + } + } + this.children.forEach((c) => { + hide(c); + }); + if (!this.containEl.classList.contains('mm-node-collapse')) { + this.containEl.classList.add('mm-node-collapse'); + } + } +} + +var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; + +function createCommonjsModule(fn) { + var module = { exports: {} }; + return fn(module, module.exports), module.exports; +} + +function commonjsRequire (path) { + throw new Error('Could not dynamically require "' + path + '". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.'); +} + +var randomColor = createCommonjsModule(function (module, exports) { +(function(root, factory) { + + // Support CommonJS + { + var randomColor = factory(); + + // Support NodeJS & Component, which allow module.exports to be a function + if (module && module.exports) { + exports = module.exports = randomColor; + } + + // Support CommonJS 1.1.1 spec + exports.randomColor = randomColor; + + // Support AMD + } + +}(commonjsGlobal, function() { + + // Seed to get repeatable colors + var seed = null; + + // Shared color dictionary + var colorDictionary = {}; + + // Populate the color dictionary + loadColorBounds(); + + // check if a range is taken + var colorRanges = []; + + var randomColor = function (options) { + + options = options || {}; + + // Check if there is a seed and ensure it's an + // integer. Otherwise, reset the seed value. + if (options.seed !== undefined && options.seed !== null && options.seed === parseInt(options.seed, 10)) { + seed = options.seed; + + // A string was passed as a seed + } else if (typeof options.seed === 'string') { + seed = stringToInteger(options.seed); + + // Something was passed as a seed but it wasn't an integer or string + } else if (options.seed !== undefined && options.seed !== null) { + throw new TypeError('The seed value must be an integer or string'); + + // No seed, reset the value outside. + } else { + seed = null; + } + + var H,S,B; + + // Check if we need to generate multiple colors + if (options.count !== null && options.count !== undefined) { + + var totalColors = options.count, + colors = []; + // Value false at index i means the range i is not taken yet. + for (var i = 0; i < options.count; i++) { + colorRanges.push(false); + } + options.count = null; + + while (totalColors > colors.length) { + + var color = randomColor(options); + + if (seed !== null) { + options.seed = seed; + } + + colors.push(color); + } + + options.count = totalColors; + + return colors; + } + + // First we pick a hue (H) + H = pickHue(options); + + // Then use H to determine saturation (S) + S = pickSaturation(H, options); + + // Then use S and H to determine brightness (B). + B = pickBrightness(H, S, options); + + // Then we return the HSB color in the desired format + return setFormat([H,S,B], options); + }; + + function pickHue(options) { + if (colorRanges.length > 0) { + var hueRange = getRealHueRange(options.hue); + + var hue = randomWithin(hueRange); + + //Each of colorRanges.length ranges has a length equal approximatelly one step + var step = (hueRange[1] - hueRange[0]) / colorRanges.length; + + var j = parseInt((hue - hueRange[0]) / step); + + //Check if the range j is taken + if (colorRanges[j] === true) { + j = (j + 2) % colorRanges.length; + } + else { + colorRanges[j] = true; + } + + var min = (hueRange[0] + j * step) % 359, + max = (hueRange[0] + (j + 1) * step) % 359; + + hueRange = [min, max]; + + hue = randomWithin(hueRange); + + if (hue < 0) {hue = 360 + hue;} + return hue + } + else { + var hueRange = getHueRange(options.hue); + + hue = randomWithin(hueRange); + // Instead of storing red as two seperate ranges, + // we group them, using negative numbers + if (hue < 0) { + hue = 360 + hue; + } + + return hue; + } + } + + function pickSaturation (hue, options) { + + if (options.hue === 'monochrome') { + return 0; + } + + if (options.luminosity === 'random') { + return randomWithin([0,100]); + } + + var saturationRange = getSaturationRange(hue); + + var sMin = saturationRange[0], + sMax = saturationRange[1]; + + switch (options.luminosity) { + + case 'bright': + sMin = 55; + break; + + case 'dark': + sMin = sMax - 10; + break; + + case 'light': + sMax = 55; + break; + } + + return randomWithin([sMin, sMax]); + + } + + function pickBrightness (H, S, options) { + + var bMin = getMinimumBrightness(H, S), + bMax = 100; + + switch (options.luminosity) { + + case 'dark': + bMax = bMin + 20; + break; + + case 'light': + bMin = (bMax + bMin)/2; + break; + + case 'random': + bMin = 0; + bMax = 100; + break; + } + + return randomWithin([bMin, bMax]); + } + + function setFormat (hsv, options) { + + switch (options.format) { + + case 'hsvArray': + return hsv; + + case 'hslArray': + return HSVtoHSL(hsv); + + case 'hsl': + var hsl = HSVtoHSL(hsv); + return 'hsl('+hsl[0]+', '+hsl[1]+'%, '+hsl[2]+'%)'; + + case 'hsla': + var hslColor = HSVtoHSL(hsv); + var alpha = options.alpha || Math.random(); + return 'hsla('+hslColor[0]+', '+hslColor[1]+'%, '+hslColor[2]+'%, ' + alpha + ')'; + + case 'rgbArray': + return HSVtoRGB(hsv); + + case 'rgb': + var rgb = HSVtoRGB(hsv); + return 'rgb(' + rgb.join(', ') + ')'; + + case 'rgba': + var rgbColor = HSVtoRGB(hsv); + var alpha = options.alpha || Math.random(); + return 'rgba(' + rgbColor.join(', ') + ', ' + alpha + ')'; + + default: + return HSVtoHex(hsv); + } + + } + + function getMinimumBrightness(H, S) { + + var lowerBounds = getColorInfo(H).lowerBounds; + + for (var i = 0; i < lowerBounds.length - 1; i++) { + + var s1 = lowerBounds[i][0], + v1 = lowerBounds[i][1]; + + var s2 = lowerBounds[i+1][0], + v2 = lowerBounds[i+1][1]; + + if (S >= s1 && S <= s2) { + + var m = (v2 - v1)/(s2 - s1), + b = v1 - m*s1; + + return m*S + b; + } + + } + + return 0; + } + + function getHueRange (colorInput) { + + if (typeof parseInt(colorInput) === 'number') { + + var number = parseInt(colorInput); + + if (number < 360 && number > 0) { + return [number, number]; + } + + } + + if (typeof colorInput === 'string') { + + if (colorDictionary[colorInput]) { + var color = colorDictionary[colorInput]; + if (color.hueRange) {return color.hueRange;} + } else if (colorInput.match(/^#?([0-9A-F]{3}|[0-9A-F]{6})$/i)) { + var hue = HexToHSB(colorInput)[0]; + return [ hue, hue ]; + } + } + + return [0,360]; + + } + + function getSaturationRange (hue) { + return getColorInfo(hue).saturationRange; + } + + function getColorInfo (hue) { + + // Maps red colors to make picking hue easier + if (hue >= 334 && hue <= 360) { + hue-= 360; + } + + for (var colorName in colorDictionary) { + var color = colorDictionary[colorName]; + if (color.hueRange && + hue >= color.hueRange[0] && + hue <= color.hueRange[1]) { + return colorDictionary[colorName]; + } + } return 'Color not found'; + } + + function randomWithin (range) { + if (seed === null) { + //generate random evenly destinct number from : https://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/ + var golden_ratio = 0.618033988749895; + var r=Math.random(); + r += golden_ratio; + r %= 1; + return Math.floor(range[0] + r*(range[1] + 1 - range[0])); + } else { + //Seeded random algorithm from http://indiegamr.com/generate-repeatable-random-numbers-in-js/ + var max = range[1] || 1; + var min = range[0] || 0; + seed = (seed * 9301 + 49297) % 233280; + var rnd = seed / 233280.0; + return Math.floor(min + rnd * (max - min)); +} + } + + function HSVtoHex (hsv){ + + var rgb = HSVtoRGB(hsv); + + function componentToHex(c) { + var hex = c.toString(16); + return hex.length == 1 ? '0' + hex : hex; + } + + var hex = '#' + componentToHex(rgb[0]) + componentToHex(rgb[1]) + componentToHex(rgb[2]); + + return hex; + + } + + function defineColor (name, hueRange, lowerBounds) { + + var sMin = lowerBounds[0][0], + sMax = lowerBounds[lowerBounds.length - 1][0], + + bMin = lowerBounds[lowerBounds.length - 1][1], + bMax = lowerBounds[0][1]; + + colorDictionary[name] = { + hueRange: hueRange, + lowerBounds: lowerBounds, + saturationRange: [sMin, sMax], + brightnessRange: [bMin, bMax] + }; + + } + + function loadColorBounds () { + + defineColor( + 'monochrome', + null, + [[0,0],[100,0]] + ); + + defineColor( + 'red', + [-26,18], + [[20,100],[30,92],[40,89],[50,85],[60,78],[70,70],[80,60],[90,55],[100,50]] + ); + + defineColor( + 'orange', + [18,46], + [[20,100],[30,93],[40,88],[50,86],[60,85],[70,70],[100,70]] + ); + + defineColor( + 'yellow', + [46,62], + [[25,100],[40,94],[50,89],[60,86],[70,84],[80,82],[90,80],[100,75]] + ); + + defineColor( + 'green', + [62,178], + [[30,100],[40,90],[50,85],[60,81],[70,74],[80,64],[90,50],[100,40]] + ); + + defineColor( + 'blue', + [178, 257], + [[20,100],[30,86],[40,80],[50,74],[60,60],[70,52],[80,44],[90,39],[100,35]] + ); + + defineColor( + 'purple', + [257, 282], + [[20,100],[30,87],[40,79],[50,70],[60,65],[70,59],[80,52],[90,45],[100,42]] + ); + + defineColor( + 'pink', + [282, 334], + [[20,100],[30,90],[40,86],[60,84],[80,80],[90,75],[100,73]] + ); + + } + + function HSVtoRGB (hsv) { + + // this doesn't work for the values of 0 and 360 + // here's the hacky fix + var h = hsv[0]; + if (h === 0) {h = 1;} + if (h === 360) {h = 359;} + + // Rebase the h,s,v values + h = h/360; + var s = hsv[1]/100, + v = hsv[2]/100; + + var h_i = Math.floor(h*6), + f = h * 6 - h_i, + p = v * (1 - s), + q = v * (1 - f*s), + t = v * (1 - (1 - f)*s), + r = 256, + g = 256, + b = 256; + + switch(h_i) { + case 0: r = v; g = t; b = p; break; + case 1: r = q; g = v; b = p; break; + case 2: r = p; g = v; b = t; break; + case 3: r = p; g = q; b = v; break; + case 4: r = t; g = p; b = v; break; + case 5: r = v; g = p; b = q; break; + } + + var result = [Math.floor(r*255), Math.floor(g*255), Math.floor(b*255)]; + return result; + } + + function HexToHSB (hex) { + hex = hex.replace(/^#/, ''); + hex = hex.length === 3 ? hex.replace(/(.)/g, '$1$1') : hex; + + var red = parseInt(hex.substr(0, 2), 16) / 255, + green = parseInt(hex.substr(2, 2), 16) / 255, + blue = parseInt(hex.substr(4, 2), 16) / 255; + + var cMax = Math.max(red, green, blue), + delta = cMax - Math.min(red, green, blue), + saturation = cMax ? (delta / cMax) : 0; + + switch (cMax) { + case red: return [ 60 * (((green - blue) / delta) % 6) || 0, saturation, cMax ]; + case green: return [ 60 * (((blue - red) / delta) + 2) || 0, saturation, cMax ]; + case blue: return [ 60 * (((red - green) / delta) + 4) || 0, saturation, cMax ]; + } + } + + function HSVtoHSL (hsv) { + var h = hsv[0], + s = hsv[1]/100, + v = hsv[2]/100, + k = (2-s)*v; + + return [ + h, + Math.round(s*v / (k<1 ? k : 2-k) * 10000) / 100, + k/2 * 100 + ]; + } + + function stringToInteger (string) { + var total = 0; + for (var i = 0; i !== string.length; i++) { + if (total >= Number.MAX_SAFE_INTEGER) break; + total += string.charCodeAt(i); + } + return total + } + + // get The range of given hue when options.count!=0 + function getRealHueRange(colorHue) + { if (!isNaN(colorHue)) { + var number = parseInt(colorHue); + + if (number < 360 && number > 0) { + return getColorInfo(colorHue).hueRange + } + } + else if (typeof colorHue === 'string') { + + if (colorDictionary[colorHue]) { + var color = colorDictionary[colorHue]; + + if (color.hueRange) { + return color.hueRange + } + } else if (colorHue.match(/^#?([0-9A-F]{3}|[0-9A-F]{6})$/i)) { + var hue = HexToHSB(colorHue)[0]; + return getColorInfo(hue).hueRange + } + } + + return [0,360] +} + return randomColor; +})); +}); + +class Layout { + constructor(node, direct, colors) { + this.layoutName = 'mindmap'; + this.direct = ''; + this.levelDis = 40; + this.nodeDis = 8; + this.firstLevelDis = 80; + this.firstNodeDis = 20; + this.isCache = true; + this.lefts = []; + this.rights = []; + this.colors = []; + this.lineWidth = 2; + this.root = node || null; + this.mind = (node === null || node === void 0 ? void 0 : node.mindmap) || null; + this.direct = direct || 'mindmap'; + this.colors = colors || []; + if (!this.svgDom) + this.svgDom = this.mind.edgeGroup.group(); + this.layout(); + } + setDirect() { + var me = this; + var len = this.root.children.length; + var root = this.root; + this.rights = []; + this.lefts = []; + if (this.direct == 'right') { + this.rights = root.children; + this.rights.forEach(r => { + this._setDirect(r, 'right'); + }); + } + else if (this.direct == 'left') { + this.lefts = root.children; + this.lefts.forEach(r => { + this._setDirect(r, 'left'); + }); + } + else if (this.direct == 'clockwise') { + root.children.forEach(function (child, i) { + if (i < len / 2) { + me.rights.push(child); + me._setDirect(child, 'right'); + } + else { + me.lefts.push(child); + me._setDirect(child, 'left'); + } + }); + me.lefts.reverse(); + } + else { + root.children.forEach(function (child, i) { + if (i < len / 2) { + me.rights.push(child); + me._setDirect(child, 'right'); + } + else { + me.lefts.push(child); + me._setDirect(child, 'left'); + } + }); + } + } + _setDirect(n, direct) { + n.stroke = ''; + n.direct = direct; + var flag = n.containEl.classList.contains('mm-node-second'); + n.containEl.setAttribute('class', ''); + n.containEl.classList.add('mm-node'); + n.containEl.classList.add('mm-node-' + direct); + if (n.isLeaf() && !n.containEl.classList.contains('mm-node-leaf')) { + n.containEl.classList.add('mm-node-leaf'); + } + else { + if (n.containEl.classList.contains('mm-node-leaf')) { + n.containEl.classList.remove('mm-node-leaf'); + } + } + if (n.getLevel() == 1) { + n.containEl.classList.add('mm-node-second'); + n.refreshBox(); + } + if (flag) { + n.refreshBox(); + } + if (!n.isExpand) { + n.containEl.classList.add('mm-node-collapse'); + } + n.children.forEach(c => { + this._setDirect(c, direct); + }); + } + layout(node, direct) { + if (node) + this.root = node; + if (direct) + this.direct = direct; + this.setDirect(); + if (this.direct == 'right') { + this.layoutRight(); + } + else if (this.direct == 'left') { + this.layoutLeft(); + } + else { + this.layoutMindMap(); + } + this._dolayout(); + // balance layout + this._doRefresh(); + this.createLink(); + } + layoutMindMap() { + this.layoutRight(); + this.layoutLeft(); + } + layoutRight(arr, parent) { + var rights = arr || this.rights; + var p = parent || this.root; + var pos = p.getPosition(); + var box = p.getBox(); + var plevel = p.getLevel(); + if (plevel == 0) { + var dis = this.firstLevelDis; + var nodeDis = this.firstNodeDis; + } + else { + var dis = this.levelDis; + var nodeDis = this.nodeDis; + } + var { disHeight, height } = this._getNodesHeight(rights); + var h = height / rights.length; + if (plevel == 0) { + var next = [parseInt(pos.x + box.width + dis + ''), parseInt(pos.y + box.height / 2 - disHeight / 2 + '')]; + } + else if (plevel == 1) { + var next = [parseInt(pos.x + box.width + dis + ''), parseInt(pos.y + box.height / 2 - disHeight / 2 - h / 2 - this.lineWidth / 2 + '')]; + } + else { + var next = [parseInt(pos.x + box.width + dis + ''), parseInt(pos.y + box.height - disHeight / 2 - h / 2 + '')]; + } + rights.forEach((child) => { + child.setPosition(next[0], next[1]); + var childBox = child.getBox(); + next[1] += parseInt(childBox.height + nodeDis + ''); + }); + rights.forEach((item) => { + if (item.isExpand) { + this.layoutRight(item.children, item); + } + }); + } + layoutLeft(arr, parent) { + var lefts = arr || this.lefts; + var p = parent || this.root; + var pos = p.getPosition(); + var box = p.getBox(); + var plevel = p.getLevel(); + if (plevel == 0) { + var dis = this.firstLevelDis; + var nodeDis = this.firstNodeDis; + } + else { + var dis = this.levelDis; + var nodeDis = this.nodeDis; + } + var { disHeight, height } = this._getNodesHeight(lefts); + var h = height / lefts.length; + if (plevel == 0) { + var next = [parseInt(pos.x - dis + ''), parseInt(pos.y + box.height / 2 - disHeight / 2 + '')]; + } + else if (plevel == 1) { + var next = [parseInt(pos.x - dis + ''), parseInt(pos.y + box.height / 2 - disHeight / 2 - h / 2 - this.lineWidth / 2 + '')]; + } + else { + var next = [parseInt(pos.x - dis + ''), parseInt(pos.y + box.height - disHeight / 2 - h / 2 + '')]; + } + // var next = [parseInt(pos.x - dis+''), parseInt(pos.y - disHeight / 2 +'')]; + lefts.forEach((child) => { + var childBox = child.getBox(); + child.setPosition(parseInt(next[0] - childBox.width + ''), parseInt(next[1] + '')); + next[1] += childBox.height + nodeDis; + }); + lefts.forEach((item) => { + if (item.isExpand) { + this.layoutLeft(item.children, item); + } + }); + } + _getNodesHeight(nodes) { + if (nodes[0] && nodes[0].getLevel() == 1) { + var dis = this.firstNodeDis; + } + else { + var dis = this.nodeDis; + } + var h = 0; + var h1 = 0; + if (nodes.length == 1) { + var h = nodes[0].getBox().height; + return { + disHeight: h, + height: h + }; + } + nodes.forEach((item, index) => { + var height = item.getBox().height; + h += height; + h1 += height; + if (index != nodes.length - 1) { + h += dis; + } + }); + return { + disHeight: h, + height: h1 + }; + } + _doRefresh() { + var root = this.root; + var rootPos = root.getPosition(); + var rootBox = root.getBox(); + var center = rootPos.y + rootBox.height / 2; + var rights = this.rights; + var lefts = this.lefts; + if (rights.length >= 2) { + var firstNode = rights[0]; + var lastNode = rights[rights.length - 1]; + var firstPos = firstNode.getPosition(); + var lastPos = lastNode.getPosition(); + var lastBox = lastNode.getBox(); + var dis1 = lastPos.y + lastBox.height - center; + var dis2 = center - firstPos.y; + if (Math.abs(dis1) != Math.abs(dis2)) { + var dy = Math.abs(Math.abs(dis1) - Math.abs(dis2)) / 2 + ''; + if (Math.abs(dis1) > Math.abs(dis2)) { + rights.forEach(c => { + this.moveNode(c, 0, -parseInt(dy)); + }); + } + else { + rights.forEach(c => { + this.moveNode(c, 0, parseInt(dy)); + }); + } + } + } + if (lefts.length >= 2) { + var firstNode = lefts[0]; + var lastNode = lefts[lefts.length - 1]; + var firstPos = firstNode.getPosition(); + var lastPos = lastNode.getPosition(); + var lastBox = lastNode.getBox(); + var dis1 = lastPos.y + lastBox.height - center; + var dis2 = center - firstPos.y; + if (Math.abs(dis1) != Math.abs(dis2)) { + var dy = Math.abs(Math.abs(dis1) - Math.abs(dis2)) / 2 + ''; + if (Math.abs(dis1) > Math.abs(dis2)) { + lefts.forEach(c => { + this.moveNode(c, 0, -parseInt(dy)); + }); + } + else { + lefts.forEach(c => { + this.moveNode(c, 0, parseInt(dy)); + }); + } + } + } + } + moveNode(node, dx, dy) { + node.move(dx, dy); + node && node.children && node.children.forEach((child) => { + this.moveNode(child, dx, dy); + }); + } + //fix 0.5 line width bug + linePoint(arr, lineWidth) { + let num = 0; + var func; + if (lineWidth % 2 == 1) { + num = 0.5; + func = parseInt; + } + else { + func = Math.ceil; + } + var p = arr.map((item) => { + return [func(item[0]) + num, func(item[1]) + num]; + }); + return p; + } + _dolayout() { + var me = this; + var mind = this.mind; + mind.traverseDF((n) => { + if (n == me.root) + return; + if (!n.isExpand) { + return; + } + var box = n.getCBox(); + if (me.isCache && n.boundingRect) { + var cbox = n.boundingRect; + } + else { + var list = n.getShowNodeList(); + if (list.length) { + cbox = mind.getBoundingRect(list); + } + } + if (!cbox) + return; + if (n.boundingRect && me.isCache) { + var topDy = cbox.topDy, downDy = cbox.downDy; + } + else { + topDy = Math.abs(cbox.y - box.y); + downDy = Math.abs(cbox.y + cbox.height - box.y - box.height); + cbox.topDy = topDy; + cbox.downDy = downDy; + n.boundingRect = cbox; + } + me._adjustNode(n, 0, topDy, downDy); + }); + } + _adjustNode(node, dx, dy1, dy2) { + if (!node) { + return; + } + var direct = node.direct; + if (node && node != this.root) { + var sibs = node.getSiblings(); + var pos = node.getPosition(); + sibs.forEach((sib) => { + if (sib.direct == direct) { + var sibPos = sib.getPosition(); + if (sibPos.y > pos.y) { + this.moveNode(sib, dx, dy2); + } + else { + this.moveNode(sib, dx, -dy1); + } + } + }); + } + } + refresh(node, direct) { + this.layout(node, direct); + } + createLink() { + var me = this; + this.svgDom && this.svgDom.clear(); + if (this.root.getChildren().length == 0) { + return; + } + var dis = this.levelDis; + var root = this.root; + var lineWidth = this.lineWidth; + var rootLevel = this.root.getLevel(); + function createLine(node) { + if (!node.isExpand) { + return; + } + var children = node.getChildren(); + var pos = node.getPosition(); + var box = node.getBox(); + box.height = box.height + lineWidth; + var level = node.getLevel(); + children.length && children.forEach(function (child) { + var direct = child.direct; + var childPos = child.getPosition(); + var childBox = Object.assign({}, child.getBox()); + childBox.height = childBox.height + lineWidth; + let _stroke = node.stroke ? node.stroke : (child.stroke ? child.stroke : randomColor()); + if (!child.stroke) { + child.stroke = _stroke; + } + child._barDom.style.backgroundColor = _stroke; + child._barDom.style.borderColor = _stroke; + if (level == rootLevel) { + var from = { + x: pos.x + box.width / 2, + y: pos.y + box.height / 2 + }; + } + else if (level == 1 + rootLevel) { + if (direct == 'right') { + from = { + x: pos.x + box.width, + y: pos.y + box.height / 2 + }; + } + else { + from = { + x: pos.x, + y: pos.y + box.height / 2 + }; + } + } + else { + if (direct == 'right') { + from = { + x: pos.x + box.width, + y: pos.y + box.height + }; + } + else { + from = { + x: pos.x, + y: pos.y + box.height + }; + } + } + if (level == rootLevel) { + if (direct == 'right') { + var to = { + x: childPos.x, + y: childBox.height / 2 + childPos.y + }; + } + else { + to = { + x: childPos.x + childBox.width, + y: childBox.height / 2 + childPos.y + }; + } + } + else { + if (direct == 'right') { + to = { + x: childPos.x, + y: childBox.height + childPos.y + }; + } + else { + to = { + x: childPos.x + childBox.width, + y: childBox.height + childPos.y + }; + } + } + if (lineWidth % 2 == 1) { + var x1 = parseInt(from.x + '') - 0.5; + var x2 = parseInt(to.x + '') - 0.5; + var y1 = parseInt(from.y + '') - 0.5; + var y2 = parseInt(to.y + '') - 0.5; + } + else { + var x1 = parseInt(from.x + ''); + var y1 = parseInt(from.y + ''); + var x2 = parseInt(to.x + ''); + var y2 = parseInt(to.y + ''); + } + if (level == rootLevel) { + var line1 = me.svgDom.path().stroke({ + color: _stroke, + width: lineWidth + 1, + linecap: 'round', + linejoin: 'round' + }).fill('none'); + } + else { + var line1 = me.svgDom.path().stroke({ + color: _stroke, + width: lineWidth, + linecap: 'round', + linejoin: 'round' + }).fill('none'); + } + if (lineWidth % 2 == 1) { + var x11 = parseInt(childPos.x + '') - 0.5; + var x22 = parseInt(childPos.x + childBox.width + '') - 0.5; + var y11 = y2; + var y22 = y2; + } + else { + var x11 = parseInt(childPos.x + ''); + var y11 = parseInt(childBox.height + childPos.y + ''); + var x22 = parseInt(childPos.x + childBox.width + ''); + var y22 = parseInt(childBox.height + childPos.y + ''); + } + if (level == rootLevel) { + var cpx1 = parseInt(from.x + '') + (to.x - from.x) / 9; + var cpy1 = parseInt(from.y + '') + (to.y - from.y) / 9 * 8; + var cpx2 = parseInt(from.x + (to.x - from.x) / 9 * 8 + ''); + var cpy2 = parseInt(to.y + ''); + var pathStr = `M${x1} ${y1} C ${cpx1} ${cpy1}, ${cpx2} ${cpy2}, ${x2} ${y2}`; + line1.plot(pathStr); + } + else { + me.svgDom.line(x11, y11, x22, y22).stroke({ + color: _stroke, + width: lineWidth, + linecap: 'miter', + linejoin: 'miter' + }).fill('none'); + //var c = parseInt((to.y - from.y) / 6+''); + var cpx11 = { + x: from.x + dis / 2, + y: from.y + }; + var cpx12 = { + x: from.x + dis / 2, + y: to.y + }; + if (direct == 'left') { + cpx11.x = from.x - dis / 2; + cpx12.x = from.x - dis / 2; + } + cpx11.x = parseInt(cpx11.x + ''); + cpx11.y = parseInt(cpx11.y + ''); + cpx12.x = parseInt(cpx12.x + ''); + cpx12.y = parseInt(cpx12.y + ''); + var path = `M${x1} ${y1} C ${cpx11.x} ${cpx11.y}, ${cpx12.x} ${cpx12.y}, ${x2} ${y2}`; + line1.plot(path); + } + createLine(child); + }); + } + //Set Node link Color + this.root.children.forEach((c, i) => { + c.stroke = this.colors[i] || randomColor(); + }); + createLine(root); + } +} + +/*! +* svg.js - A lightweight library for manipulating and animating SVG. +* @version 2.7.1 +* https://svgdotjs.github.io/ +* +* @copyright Wout Fierens +* @license MIT +* +* BUILT: Fri Nov 30 2018 10:01:55 GMT+0100 (GMT+01:00) +*/ + +var svg = createCommonjsModule(function (module, exports) { +(function(root, factory) { + /* istanbul ignore next */ + { + module.exports = root.document ? factory(root, root.document) : function(w){ return factory(w, w.document) }; + } +}(typeof window !== "undefined" ? window : commonjsGlobal, function(window, document) { + +// Find global reference - uses 'this' by default when available, +// falls back to 'window' otherwise (for bundlers like Webpack) +var globalRef = (typeof this !== "undefined") ? this : window; + +// The main wrapping element +var SVG = globalRef.SVG = function(element) { + if (SVG.supported) { + element = new SVG.Doc(element); + + if(!SVG.parser.draw) + SVG.prepare(); + + return element + } +}; + +// Default namespaces +SVG.ns = 'http://www.w3.org/2000/svg'; +SVG.xmlns = 'http://www.w3.org/2000/xmlns/'; +SVG.xlink = 'http://www.w3.org/1999/xlink'; +SVG.svgjs = 'http://svgjs.com/svgjs'; + +// Svg support test +SVG.supported = (function() { + return !! document.createElementNS && + !! document.createElementNS(SVG.ns,'svg').createSVGRect +})(); + +// Don't bother to continue if SVG is not supported +if (!SVG.supported) return false + +// Element id sequence +SVG.did = 1000; + +// Get next named element id +SVG.eid = function(name) { + return 'Svgjs' + capitalize(name) + (SVG.did++) +}; + +// Method for element creation +SVG.create = function(name) { + // create element + var element = document.createElementNS(this.ns, name); + + // apply unique id + element.setAttribute('id', this.eid(name)); + + return element +}; + +// Method for extending objects +SVG.extend = function() { + var modules, methods, key, i; + + // Get list of modules + modules = [].slice.call(arguments); + + // Get object with extensions + methods = modules.pop(); + + for (i = modules.length - 1; i >= 0; i--) + if (modules[i]) + for (key in methods) + modules[i].prototype[key] = methods[key]; + + // Make sure SVG.Set inherits any newly added methods + if (SVG.Set && SVG.Set.inherit) + SVG.Set.inherit(); +}; + +// Invent new element +SVG.invent = function(config) { + // Create element initializer + var initializer = typeof config.create == 'function' ? + config.create : + function() { + this.constructor.call(this, SVG.create(config.create)); + }; + + // Inherit prototype + if (config.inherit) + initializer.prototype = new config.inherit; + + // Extend with methods + if (config.extend) + SVG.extend(initializer, config.extend); + + // Attach construct method to parent + if (config.construct) + SVG.extend(config.parent || SVG.Container, config.construct); + + return initializer +}; + +// Adopt existing svg elements +SVG.adopt = function(node) { + // check for presence of node + if (!node) return null + + // make sure a node isn't already adopted + if (node.instance) return node.instance + + // initialize variables + var element; + + // adopt with element-specific settings + if (node.nodeName == 'svg') + element = node.parentNode instanceof window.SVGElement ? new SVG.Nested : new SVG.Doc; + else if (node.nodeName == 'linearGradient') + element = new SVG.Gradient('linear'); + else if (node.nodeName == 'radialGradient') + element = new SVG.Gradient('radial'); + else if (SVG[capitalize(node.nodeName)]) + element = new SVG[capitalize(node.nodeName)]; + else + element = new SVG.Element(node); + + // ensure references + element.type = node.nodeName; + element.node = node; + node.instance = element; + + // SVG.Class specific preparations + if (element instanceof SVG.Doc) + element.namespace().defs(); + + // pull svgjs data from the dom (getAttributeNS doesn't work in html5) + element.setData(JSON.parse(node.getAttribute('svgjs:data')) || {}); + + return element +}; + +// Initialize parsing element +SVG.prepare = function() { + // Select document body and create invisible svg element + var body = document.getElementsByTagName('body')[0] + , draw = (body ? new SVG.Doc(body) : SVG.adopt(document.documentElement).nested()).size(2, 0); + + // Create parser object + SVG.parser = { + body: body || document.documentElement + , draw: draw.style('opacity:0;position:absolute;left:-100%;top:-100%;overflow:hidden').attr('focusable', 'false').node + , poly: draw.polyline().node + , path: draw.path().node + , native: SVG.create('svg') + }; +}; + +SVG.parser = { + native: SVG.create('svg') +}; + +document.addEventListener('DOMContentLoaded', function() { + if(!SVG.parser.draw) + SVG.prepare(); +}, false); + +// Storage for regular expressions +SVG.regex = { + // Parse unit value + numberAndUnit: /^([+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?)([a-z%]*)$/i + + // Parse hex value +, hex: /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i + + // Parse rgb value +, rgb: /rgb\((\d+),(\d+),(\d+)\)/ + + // Parse reference id +, reference: /#([a-z0-9\-_]+)/i + + // splits a transformation chain +, transforms: /\)\s*,?\s*/ + + // Whitespace +, whitespace: /\s/g + + // Test hex value +, isHex: /^#[a-f0-9]{3,6}$/i + + // Test rgb value +, isRgb: /^rgb\(/ + + // Test css declaration +, isCss: /[^:]+:[^;]+;?/ + + // Test for blank string +, isBlank: /^(\s+)?$/ + + // Test for numeric string +, isNumber: /^[+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i + + // Test for percent value +, isPercent: /^-?[\d\.]+%$/ + + // Test for image url +, isImage: /\.(jpg|jpeg|png|gif|svg)(\?[^=]+.*)?/i + + // split at whitespace and comma +, delimiter: /[\s,]+/ + + // The following regex are used to parse the d attribute of a path + + // Matches all hyphens which are not after an exponent +, hyphen: /([^e])\-/gi + + // Replaces and tests for all path letters +, pathLetters: /[MLHVCSQTAZ]/gi + + // yes we need this one, too +, isPathLetter: /[MLHVCSQTAZ]/i + + // matches 0.154.23.45 +, numbersWithDots: /((\d?\.\d+(?:e[+-]?\d+)?)((?:\.\d+(?:e[+-]?\d+)?)+))+/gi + + // matches . +, dots: /\./g +}; + +SVG.utils = { + // Map function + map: function(array, block) { + var i + , il = array.length + , result = []; + + for (i = 0; i < il; i++) + result.push(block(array[i])); + + return result + } + + // Filter function +, filter: function(array, block) { + var i + , il = array.length + , result = []; + + for (i = 0; i < il; i++) + if (block(array[i])) + result.push(array[i]); + + return result + } + + // Degrees to radians +, radians: function(d) { + return d % 360 * Math.PI / 180 + } + + // Radians to degrees +, degrees: function(r) { + return r * 180 / Math.PI % 360 + } + +, filterSVGElements: function(nodes) { + return this.filter( nodes, function(el) { return el instanceof window.SVGElement }) + } + +}; + +SVG.defaults = { + // Default attribute values + attrs: { + // fill and stroke + 'fill-opacity': 1 + , 'stroke-opacity': 1 + , 'stroke-width': 0 + , 'stroke-linejoin': 'miter' + , 'stroke-linecap': 'butt' + , fill: '#000000' + , stroke: '#000000' + , opacity: 1 + // position + , x: 0 + , y: 0 + , cx: 0 + , cy: 0 + // size + , width: 0 + , height: 0 + // radius + , r: 0 + , rx: 0 + , ry: 0 + // gradient + , offset: 0 + , 'stop-opacity': 1 + , 'stop-color': '#000000' + // text + , 'font-size': 16 + , 'font-family': 'Helvetica, Arial, sans-serif' + , 'text-anchor': 'start' + } + +}; +// Module for color convertions +SVG.Color = function(color) { + var match; + + // initialize defaults + this.r = 0; + this.g = 0; + this.b = 0; + + if(!color) return + + // parse color + if (typeof color === 'string') { + if (SVG.regex.isRgb.test(color)) { + // get rgb values + match = SVG.regex.rgb.exec(color.replace(SVG.regex.whitespace,'')); + + // parse numeric values + this.r = parseInt(match[1]); + this.g = parseInt(match[2]); + this.b = parseInt(match[3]); + + } else if (SVG.regex.isHex.test(color)) { + // get hex values + match = SVG.regex.hex.exec(fullHex(color)); + + // parse numeric values + this.r = parseInt(match[1], 16); + this.g = parseInt(match[2], 16); + this.b = parseInt(match[3], 16); + + } + + } else if (typeof color === 'object') { + this.r = color.r; + this.g = color.g; + this.b = color.b; + + } + +}; + +SVG.extend(SVG.Color, { + // Default to hex conversion + toString: function() { + return this.toHex() + } + // Build hex value +, toHex: function() { + return '#' + + compToHex(this.r) + + compToHex(this.g) + + compToHex(this.b) + } + // Build rgb value +, toRgb: function() { + return 'rgb(' + [this.r, this.g, this.b].join() + ')' + } + // Calculate true brightness +, brightness: function() { + return (this.r / 255 * 0.30) + + (this.g / 255 * 0.59) + + (this.b / 255 * 0.11) + } + // Make color morphable +, morph: function(color) { + this.destination = new SVG.Color(color); + + return this + } + // Get morphed color at given position +, at: function(pos) { + // make sure a destination is defined + if (!this.destination) return this + + // normalise pos + pos = pos < 0 ? 0 : pos > 1 ? 1 : pos; + + // generate morphed color + return new SVG.Color({ + r: ~~(this.r + (this.destination.r - this.r) * pos) + , g: ~~(this.g + (this.destination.g - this.g) * pos) + , b: ~~(this.b + (this.destination.b - this.b) * pos) + }) + } + +}); + +// Testers + +// Test if given value is a color string +SVG.Color.test = function(color) { + color += ''; + return SVG.regex.isHex.test(color) + || SVG.regex.isRgb.test(color) +}; + +// Test if given value is a rgb object +SVG.Color.isRgb = function(color) { + return color && typeof color.r == 'number' + && typeof color.g == 'number' + && typeof color.b == 'number' +}; + +// Test if given value is a color +SVG.Color.isColor = function(color) { + return SVG.Color.isRgb(color) || SVG.Color.test(color) +}; +// Module for array conversion +SVG.Array = function(array, fallback) { + array = (array || []).valueOf(); + + // if array is empty and fallback is provided, use fallback + if (array.length == 0 && fallback) + array = fallback.valueOf(); + + // parse array + this.value = this.parse(array); +}; + +SVG.extend(SVG.Array, { + // Make array morphable + morph: function(array) { + this.destination = this.parse(array); + + // normalize length of arrays + if (this.value.length != this.destination.length) { + var lastValue = this.value[this.value.length - 1] + , lastDestination = this.destination[this.destination.length - 1]; + + while(this.value.length > this.destination.length) + this.destination.push(lastDestination); + while(this.value.length < this.destination.length) + this.value.push(lastValue); + } + + return this + } + // Clean up any duplicate points +, settle: function() { + // find all unique values + for (var i = 0, il = this.value.length, seen = []; i < il; i++) + if (seen.indexOf(this.value[i]) == -1) + seen.push(this.value[i]); + + // set new value + return this.value = seen + } + // Get morphed array at given position +, at: function(pos) { + // make sure a destination is defined + if (!this.destination) return this + + // generate morphed array + for (var i = 0, il = this.value.length, array = []; i < il; i++) + array.push(this.value[i] + (this.destination[i] - this.value[i]) * pos); + + return new SVG.Array(array) + } + // Convert array to string +, toString: function() { + return this.value.join(' ') + } + // Real value +, valueOf: function() { + return this.value + } + // Parse whitespace separated string +, parse: function(array) { + array = array.valueOf(); + + // if already is an array, no need to parse it + if (Array.isArray(array)) return array + + return this.split(array) + } + // Strip unnecessary whitespace +, split: function(string) { + return string.trim().split(SVG.regex.delimiter).map(parseFloat) + } + // Reverse array +, reverse: function() { + this.value.reverse(); + + return this + } +, clone: function() { + var clone = new this.constructor(); + clone.value = array_clone(this.value); + return clone + } +}); +// Poly points array +SVG.PointArray = function(array, fallback) { + SVG.Array.call(this, array, fallback || [[0,0]]); +}; + +// Inherit from SVG.Array +SVG.PointArray.prototype = new SVG.Array; +SVG.PointArray.prototype.constructor = SVG.PointArray; + +SVG.extend(SVG.PointArray, { + // Convert array to string + toString: function() { + // convert to a poly point string + for (var i = 0, il = this.value.length, array = []; i < il; i++) + array.push(this.value[i].join(',')); + + return array.join(' ') + } + // Convert array to line object +, toLine: function() { + return { + x1: this.value[0][0] + , y1: this.value[0][1] + , x2: this.value[1][0] + , y2: this.value[1][1] + } + } + // Get morphed array at given position +, at: function(pos) { + // make sure a destination is defined + if (!this.destination) return this + + // generate morphed point string + for (var i = 0, il = this.value.length, array = []; i < il; i++) + array.push([ + this.value[i][0] + (this.destination[i][0] - this.value[i][0]) * pos + , this.value[i][1] + (this.destination[i][1] - this.value[i][1]) * pos + ]); + + return new SVG.PointArray(array) + } + // Parse point string and flat array +, parse: function(array) { + var points = []; + + array = array.valueOf(); + + // if it is an array + if (Array.isArray(array)) { + // and it is not flat, there is no need to parse it + if(Array.isArray(array[0])) { + // make sure to use a clone + return array.map(function (el) { return el.slice() }) + } else if (array[0].x != null){ + // allow point objects to be passed + return array.map(function (el) { return [el.x, el.y] }) + } + } else { // Else, it is considered as a string + // parse points + array = array.trim().split(SVG.regex.delimiter).map(parseFloat); + } + + // validate points - https://svgwg.org/svg2-draft/shapes.html#DataTypePoints + // Odd number of coordinates is an error. In such cases, drop the last odd coordinate. + if (array.length % 2 !== 0) array.pop(); + + // wrap points in two-tuples and parse points as floats + for(var i = 0, len = array.length; i < len; i = i + 2) + points.push([ array[i], array[i+1] ]); + + return points + } + // Move point string +, move: function(x, y) { + var box = this.bbox(); + + // get relative offset + x -= box.x; + y -= box.y; + + // move every point + if (!isNaN(x) && !isNaN(y)) + for (var i = this.value.length - 1; i >= 0; i--) + this.value[i] = [this.value[i][0] + x, this.value[i][1] + y]; + + return this + } + // Resize poly string +, size: function(width, height) { + var i, box = this.bbox(); + + // recalculate position of all points according to new size + for (i = this.value.length - 1; i >= 0; i--) { + if(box.width) this.value[i][0] = ((this.value[i][0] - box.x) * width) / box.width + box.x; + if(box.height) this.value[i][1] = ((this.value[i][1] - box.y) * height) / box.height + box.y; + } + + return this + } + // Get bounding box of points +, bbox: function() { + SVG.parser.poly.setAttribute('points', this.toString()); + + return SVG.parser.poly.getBBox() + } +}); + +var pathHandlers = { + M: function(c, p, p0) { + p.x = p0.x = c[0]; + p.y = p0.y = c[1]; + + return ['M', p.x, p.y] + }, + L: function(c, p) { + p.x = c[0]; + p.y = c[1]; + return ['L', c[0], c[1]] + }, + H: function(c, p) { + p.x = c[0]; + return ['H', c[0]] + }, + V: function(c, p) { + p.y = c[0]; + return ['V', c[0]] + }, + C: function(c, p) { + p.x = c[4]; + p.y = c[5]; + return ['C', c[0], c[1], c[2], c[3], c[4], c[5]] + }, + S: function(c, p) { + p.x = c[2]; + p.y = c[3]; + return ['S', c[0], c[1], c[2], c[3]] + }, + Q: function(c, p) { + p.x = c[2]; + p.y = c[3]; + return ['Q', c[0], c[1], c[2], c[3]] + }, + T: function(c, p) { + p.x = c[0]; + p.y = c[1]; + return ['T', c[0], c[1]] + }, + Z: function(c, p, p0) { + p.x = p0.x; + p.y = p0.y; + return ['Z'] + }, + A: function(c, p) { + p.x = c[5]; + p.y = c[6]; + return ['A', c[0], c[1], c[2], c[3], c[4], c[5], c[6]] + } +}; + +var mlhvqtcsa = 'mlhvqtcsaz'.split(''); + +for(var i = 0, il = mlhvqtcsa.length; i < il; ++i){ + pathHandlers[mlhvqtcsa[i]] = (function(i){ + return function(c, p, p0) { + if(i == 'H') c[0] = c[0] + p.x; + else if(i == 'V') c[0] = c[0] + p.y; + else if(i == 'A'){ + c[5] = c[5] + p.x, + c[6] = c[6] + p.y; + } + else + for(var j = 0, jl = c.length; j < jl; ++j) { + c[j] = c[j] + (j%2 ? p.y : p.x); + } + + return pathHandlers[i](c, p, p0) + } + })(mlhvqtcsa[i].toUpperCase()); +} + +// Path points array +SVG.PathArray = function(array, fallback) { + SVG.Array.call(this, array, fallback || [['M', 0, 0]]); +}; + +// Inherit from SVG.Array +SVG.PathArray.prototype = new SVG.Array; +SVG.PathArray.prototype.constructor = SVG.PathArray; + +SVG.extend(SVG.PathArray, { + // Convert array to string + toString: function() { + return arrayToString(this.value) + } + // Move path string +, move: function(x, y) { + // get bounding box of current situation + var box = this.bbox(); + + // get relative offset + x -= box.x; + y -= box.y; + + if (!isNaN(x) && !isNaN(y)) { + // move every point + for (var l, i = this.value.length - 1; i >= 0; i--) { + l = this.value[i][0]; + + if (l == 'M' || l == 'L' || l == 'T') { + this.value[i][1] += x; + this.value[i][2] += y; + + } else if (l == 'H') { + this.value[i][1] += x; + + } else if (l == 'V') { + this.value[i][1] += y; + + } else if (l == 'C' || l == 'S' || l == 'Q') { + this.value[i][1] += x; + this.value[i][2] += y; + this.value[i][3] += x; + this.value[i][4] += y; + + if (l == 'C') { + this.value[i][5] += x; + this.value[i][6] += y; + } + + } else if (l == 'A') { + this.value[i][6] += x; + this.value[i][7] += y; + } + + } + } + + return this + } + // Resize path string +, size: function(width, height) { + // get bounding box of current situation + var i, l, box = this.bbox(); + + // recalculate position of all points according to new size + for (i = this.value.length - 1; i >= 0; i--) { + l = this.value[i][0]; + + if (l == 'M' || l == 'L' || l == 'T') { + this.value[i][1] = ((this.value[i][1] - box.x) * width) / box.width + box.x; + this.value[i][2] = ((this.value[i][2] - box.y) * height) / box.height + box.y; + + } else if (l == 'H') { + this.value[i][1] = ((this.value[i][1] - box.x) * width) / box.width + box.x; + + } else if (l == 'V') { + this.value[i][1] = ((this.value[i][1] - box.y) * height) / box.height + box.y; + + } else if (l == 'C' || l == 'S' || l == 'Q') { + this.value[i][1] = ((this.value[i][1] - box.x) * width) / box.width + box.x; + this.value[i][2] = ((this.value[i][2] - box.y) * height) / box.height + box.y; + this.value[i][3] = ((this.value[i][3] - box.x) * width) / box.width + box.x; + this.value[i][4] = ((this.value[i][4] - box.y) * height) / box.height + box.y; + + if (l == 'C') { + this.value[i][5] = ((this.value[i][5] - box.x) * width) / box.width + box.x; + this.value[i][6] = ((this.value[i][6] - box.y) * height) / box.height + box.y; + } + + } else if (l == 'A') { + // resize radii + this.value[i][1] = (this.value[i][1] * width) / box.width; + this.value[i][2] = (this.value[i][2] * height) / box.height; + + // move position values + this.value[i][6] = ((this.value[i][6] - box.x) * width) / box.width + box.x; + this.value[i][7] = ((this.value[i][7] - box.y) * height) / box.height + box.y; + } + + } + + return this + } + // Test if the passed path array use the same path data commands as this path array +, equalCommands: function(pathArray) { + var i, il, equalCommands; + + pathArray = new SVG.PathArray(pathArray); + + equalCommands = this.value.length === pathArray.value.length; + for(i = 0, il = this.value.length; equalCommands && i < il; i++) { + equalCommands = this.value[i][0] === pathArray.value[i][0]; + } + + return equalCommands + } + // Make path array morphable +, morph: function(pathArray) { + pathArray = new SVG.PathArray(pathArray); + + if(this.equalCommands(pathArray)) { + this.destination = pathArray; + } else { + this.destination = null; + } + + return this + } + // Get morphed path array at given position +, at: function(pos) { + // make sure a destination is defined + if (!this.destination) return this + + var sourceArray = this.value + , destinationArray = this.destination.value + , array = [], pathArray = new SVG.PathArray() + , i, il, j, jl; + + // Animate has specified in the SVG spec + // See: https://www.w3.org/TR/SVG11/paths.html#PathElement + for (i = 0, il = sourceArray.length; i < il; i++) { + array[i] = [sourceArray[i][0]]; + for(j = 1, jl = sourceArray[i].length; j < jl; j++) { + array[i][j] = sourceArray[i][j] + (destinationArray[i][j] - sourceArray[i][j]) * pos; + } + // For the two flags of the elliptical arc command, the SVG spec say: + // Flags and booleans are interpolated as fractions between zero and one, with any non-zero value considered to be a value of one/true + // Elliptical arc command as an array followed by corresponding indexes: + // ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y] + // 0 1 2 3 4 5 6 7 + if(array[i][0] === 'A') { + array[i][4] = +(array[i][4] != 0); + array[i][5] = +(array[i][5] != 0); + } + } + + // Directly modify the value of a path array, this is done this way for performance + pathArray.value = array; + return pathArray + } + // Absolutize and parse path to array +, parse: function(array) { + // if it's already a patharray, no need to parse it + if (array instanceof SVG.PathArray) return array.valueOf() + + // prepare for parsing + var s, arr + , paramCnt = { 'M':2, 'L':2, 'H':1, 'V':1, 'C':6, 'S':4, 'Q':4, 'T':2, 'A':7, 'Z':0 }; + + if(typeof array == 'string'){ + + array = array + .replace(SVG.regex.numbersWithDots, pathRegReplace) // convert 45.123.123 to 45.123 .123 + .replace(SVG.regex.pathLetters, ' $& ') // put some room between letters and numbers + .replace(SVG.regex.hyphen, '$1 -') // add space before hyphen + .trim() // trim + .split(SVG.regex.delimiter); // split into array + + }else { + array = array.reduce(function(prev, curr){ + return [].concat.call(prev, curr) + }, []); + } + + // array now is an array containing all parts of a path e.g. ['M', '0', '0', 'L', '30', '30' ...] + var arr = [] + , p = new SVG.Point() + , p0 = new SVG.Point() + , index = 0 + , len = array.length; + + do{ + // Test if we have a path letter + if(SVG.regex.isPathLetter.test(array[index])){ + s = array[index]; + ++index; + // If last letter was a move command and we got no new, it defaults to [L]ine + }else if(s == 'M'){ + s = 'L'; + }else if(s == 'm'){ + s = 'l'; + } + + arr.push(pathHandlers[s].call(null, + array.slice(index, (index = index + paramCnt[s.toUpperCase()])).map(parseFloat), + p, p0 + ) + ); + + }while(len > index) + + return arr + + } + // Get bounding box of path +, bbox: function() { + SVG.parser.path.setAttribute('d', this.toString()); + + return SVG.parser.path.getBBox() + } + +}); + +// Module for unit convertions +SVG.Number = SVG.invent({ + // Initialize + create: function(value, unit) { + // initialize defaults + this.value = 0; + this.unit = unit || ''; + + // parse value + if (typeof value === 'number') { + // ensure a valid numeric value + this.value = isNaN(value) ? 0 : !isFinite(value) ? (value < 0 ? -3.4e+38 : +3.4e+38) : value; + + } else if (typeof value === 'string') { + unit = value.match(SVG.regex.numberAndUnit); + + if (unit) { + // make value numeric + this.value = parseFloat(unit[1]); + + // normalize + if (unit[5] == '%') + this.value /= 100; + else if (unit[5] == 's') + this.value *= 1000; + + // store unit + this.unit = unit[5]; + } + + } else { + if (value instanceof SVG.Number) { + this.value = value.valueOf(); + this.unit = value.unit; + } + } + + } + // Add methods +, extend: { + // Stringalize + toString: function() { + return ( + this.unit == '%' ? + ~~(this.value * 1e8) / 1e6: + this.unit == 's' ? + this.value / 1e3 : + this.value + ) + this.unit + } + , toJSON: function() { + return this.toString() + } + , // Convert to primitive + valueOf: function() { + return this.value + } + // Add number + , plus: function(number) { + number = new SVG.Number(number); + return new SVG.Number(this + number, this.unit || number.unit) + } + // Subtract number + , minus: function(number) { + number = new SVG.Number(number); + return new SVG.Number(this - number, this.unit || number.unit) + } + // Multiply number + , times: function(number) { + number = new SVG.Number(number); + return new SVG.Number(this * number, this.unit || number.unit) + } + // Divide number + , divide: function(number) { + number = new SVG.Number(number); + return new SVG.Number(this / number, this.unit || number.unit) + } + // Convert to different unit + , to: function(unit) { + var number = new SVG.Number(this); + + if (typeof unit === 'string') + number.unit = unit; + + return number + } + // Make number morphable + , morph: function(number) { + this.destination = new SVG.Number(number); + + if(number.relative) { + this.destination.value += this.value; + } + + return this + } + // Get morphed number at given position + , at: function(pos) { + // Make sure a destination is defined + if (!this.destination) return this + + // Generate new morphed number + return new SVG.Number(this.destination) + .minus(this) + .times(pos) + .plus(this) + } + + } +}); + + +SVG.Element = SVG.invent({ + // Initialize node + create: function(node) { + // make stroke value accessible dynamically + this._stroke = SVG.defaults.attrs.stroke; + this._event = null; + this._events = {}; + + // initialize data object + this.dom = {}; + + // create circular reference + if (this.node = node) { + this.type = node.nodeName; + this.node.instance = this; + this._events = node._events || {}; + + // store current attribute value + this._stroke = node.getAttribute('stroke') || this._stroke; + } + } + + // Add class methods +, extend: { + // Move over x-axis + x: function(x) { + return this.attr('x', x) + } + // Move over y-axis + , y: function(y) { + return this.attr('y', y) + } + // Move by center over x-axis + , cx: function(x) { + return x == null ? this.x() + this.width() / 2 : this.x(x - this.width() / 2) + } + // Move by center over y-axis + , cy: function(y) { + return y == null ? this.y() + this.height() / 2 : this.y(y - this.height() / 2) + } + // Move element to given x and y values + , move: function(x, y) { + return this.x(x).y(y) + } + // Move element by its center + , center: function(x, y) { + return this.cx(x).cy(y) + } + // Set width of element + , width: function(width) { + return this.attr('width', width) + } + // Set height of element + , height: function(height) { + return this.attr('height', height) + } + // Set element size to given width and height + , size: function(width, height) { + var p = proportionalSize(this, width, height); + + return this + .width(new SVG.Number(p.width)) + .height(new SVG.Number(p.height)) + } + // Clone element + , clone: function(parent) { + // write dom data to the dom so the clone can pickup the data + this.writeDataToDom(); + + // clone element and assign new id + var clone = assignNewId(this.node.cloneNode(true)); + + // insert the clone in the given parent or after myself + if(parent) parent.add(clone); + else this.after(clone); + + return clone + } + // Remove element + , remove: function() { + if (this.parent()) + this.parent().removeElement(this); + + return this + } + // Replace element + , replace: function(element) { + this.after(element).remove(); + + return element + } + // Add element to given container and return self + , addTo: function(parent) { + return parent.put(this) + } + // Add element to given container and return container + , putIn: function(parent) { + return parent.add(this) + } + // Get / set id + , id: function(id) { + return this.attr('id', id) + } + // Checks whether the given point inside the bounding box of the element + , inside: function(x, y) { + var box = this.bbox(); + + return x > box.x + && y > box.y + && x < box.x + box.width + && y < box.y + box.height + } + // Show element + , show: function() { + return this.style('display', '') + } + // Hide element + , hide: function() { + return this.style('display', 'none') + } + // Is element visible? + , visible: function() { + return this.style('display') != 'none' + } + // Return id on string conversion + , toString: function() { + return this.attr('id') + } + // Return array of classes on the node + , classes: function() { + var attr = this.attr('class'); + + return attr == null ? [] : attr.trim().split(SVG.regex.delimiter) + } + // Return true if class exists on the node, false otherwise + , hasClass: function(name) { + return this.classes().indexOf(name) != -1 + } + // Add class to the node + , addClass: function(name) { + if (!this.hasClass(name)) { + var array = this.classes(); + array.push(name); + this.attr('class', array.join(' ')); + } + + return this + } + // Remove class from the node + , removeClass: function(name) { + if (this.hasClass(name)) { + this.attr('class', this.classes().filter(function(c) { + return c != name + }).join(' ')); + } + + return this + } + // Toggle the presence of a class on the node + , toggleClass: function(name) { + return this.hasClass(name) ? this.removeClass(name) : this.addClass(name) + } + // Get referenced element form attribute value + , reference: function(attr) { + return SVG.get(this.attr(attr)) + } + // Returns the parent element instance + , parent: function(type) { + var parent = this; + + // check for parent + if(!parent.node.parentNode) return null + + // get parent element + parent = SVG.adopt(parent.node.parentNode); + + if(!type) return parent + + // loop trough ancestors if type is given + while(parent && parent.node instanceof window.SVGElement){ + if(typeof type === 'string' ? parent.matches(type) : parent instanceof type) return parent + if(!parent.node.parentNode || parent.node.parentNode.nodeName == '#document' || parent.node.parentNode.nodeName == '#document-fragment') return null // #759, #720 + parent = SVG.adopt(parent.node.parentNode); + } + } + // Get parent document + , doc: function() { + return this instanceof SVG.Doc ? this : this.parent(SVG.Doc) + } + // return array of all ancestors of given type up to the root svg + , parents: function(type) { + var parents = [], parent = this; + + do{ + parent = parent.parent(type); + if(!parent || !parent.node) break + + parents.push(parent); + } while(parent.parent) + + return parents + } + // matches the element vs a css selector + , matches: function(selector){ + return matches(this.node, selector) + } + // Returns the svg node to call native svg methods on it + , native: function() { + return this.node + } + // Import raw svg + , svg: function(svg) { + // create temporary holder + var well = document.createElement('svg'); + + // act as a setter if svg is given + if (svg && this instanceof SVG.Parent) { + // dump raw svg + well.innerHTML = '' + svg.replace(/\n/, '').replace(/<([\w:-]+)([^<]+?)\/>/g, '<$1$2>') + ''; + + // transplant nodes + for (var i = 0, il = well.firstChild.childNodes.length; i < il; i++) + this.node.appendChild(well.firstChild.firstChild); + + // otherwise act as a getter + } else { + // create a wrapping svg element in case of partial content + well.appendChild(svg = document.createElement('svg')); + + // write svgjs data to the dom + this.writeDataToDom(); + + // insert a copy of this node + svg.appendChild(this.node.cloneNode(true)); + + // return target element + return well.innerHTML.replace(/^/, '').replace(/<\/svg>$/, '') + } + + return this + } + // write svgjs data to the dom + , writeDataToDom: function() { + + // dump variables recursively + if(this.each || this.lines){ + var fn = this.each ? this : this.lines(); + fn.each(function(){ + this.writeDataToDom(); + }); + } + + // remove previously set data + this.node.removeAttribute('svgjs:data'); + + if(Object.keys(this.dom).length) + this.node.setAttribute('svgjs:data', JSON.stringify(this.dom)); // see #428 + + return this + } + // set given data to the elements data property + , setData: function(o){ + this.dom = o; + return this + } + , is: function(obj){ + return is(this, obj) + } + } +}); + +SVG.easing = { + '-': function(pos){return pos} +, '<>':function(pos){return -Math.cos(pos * Math.PI) / 2 + 0.5} +, '>': function(pos){return Math.sin(pos * Math.PI / 2)} +, '<': function(pos){return -Math.cos(pos * Math.PI / 2) + 1} +}; + +SVG.morph = function(pos){ + return function(from, to) { + return new SVG.MorphObj(from, to).at(pos) + } +}; + +SVG.Situation = SVG.invent({ + + create: function(o){ + this.init = false; + this.reversed = false; + this.reversing = false; + + this.duration = new SVG.Number(o.duration).valueOf(); + this.delay = new SVG.Number(o.delay).valueOf(); + + this.start = +new Date() + this.delay; + this.finish = this.start + this.duration; + this.ease = o.ease; + + // this.loop is incremented from 0 to this.loops + // it is also incremented when in an infinite loop (when this.loops is true) + this.loop = 0; + this.loops = false; + + this.animations = { + // functionToCall: [list of morphable objects] + // e.g. move: [SVG.Number, SVG.Number] + }; + + this.attrs = { + // holds all attributes which are not represented from a function svg.js provides + // e.g. someAttr: SVG.Number + }; + + this.styles = { + // holds all styles which should be animated + // e.g. fill-color: SVG.Color + }; + + this.transforms = [ + // holds all transformations as transformation objects + // e.g. [SVG.Rotate, SVG.Translate, SVG.Matrix] + ]; + + this.once = { + // functions to fire at a specific position + // e.g. "0.5": function foo(){} + }; + + } + +}); + + +SVG.FX = SVG.invent({ + + create: function(element) { + this._target = element; + this.situations = []; + this.active = false; + this.situation = null; + this.paused = false; + this.lastPos = 0; + this.pos = 0; + // The absolute position of an animation is its position in the context of its complete duration (including delay and loops) + // When performing a delay, absPos is below 0 and when performing a loop, its value is above 1 + this.absPos = 0; + this._speed = 1; + } + +, extend: { + + /** + * sets or returns the target of this animation + * @param o object || number In case of Object it holds all parameters. In case of number its the duration of the animation + * @param ease function || string Function which should be used for easing or easing keyword + * @param delay Number indicating the delay before the animation starts + * @return target || this + */ + animate: function(o, ease, delay){ + + if(typeof o == 'object'){ + ease = o.ease; + delay = o.delay; + o = o.duration; + } + + var situation = new SVG.Situation({ + duration: o || 1000, + delay: delay || 0, + ease: SVG.easing[ease || '-'] || ease + }); + + this.queue(situation); + + return this + } + + /** + * sets a delay before the next element of the queue is called + * @param delay Duration of delay in milliseconds + * @return this.target() + */ + , delay: function(delay){ + // The delay is performed by an empty situation with its duration + // attribute set to the duration of the delay + var situation = new SVG.Situation({ + duration: delay, + delay: 0, + ease: SVG.easing['-'] + }); + + return this.queue(situation) + } + + /** + * sets or returns the target of this animation + * @param null || target SVG.Element which should be set as new target + * @return target || this + */ + , target: function(target){ + if(target && target instanceof SVG.Element){ + this._target = target; + return this + } + + return this._target + } + + // returns the absolute position at a given time + , timeToAbsPos: function(timestamp){ + return (timestamp - this.situation.start) / (this.situation.duration/this._speed) + } + + // returns the timestamp from a given absolute positon + , absPosToTime: function(absPos){ + return this.situation.duration/this._speed * absPos + this.situation.start + } + + // starts the animationloop + , startAnimFrame: function(){ + this.stopAnimFrame(); + this.animationFrame = window.requestAnimationFrame(function(){ this.step(); }.bind(this)); + } + + // cancels the animationframe + , stopAnimFrame: function(){ + window.cancelAnimationFrame(this.animationFrame); + } + + // kicks off the animation - only does something when the queue is currently not active and at least one situation is set + , start: function(){ + // dont start if already started + if(!this.active && this.situation){ + this.active = true; + this.startCurrent(); + } + + return this + } + + // start the current situation + , startCurrent: function(){ + this.situation.start = +new Date + this.situation.delay/this._speed; + this.situation.finish = this.situation.start + this.situation.duration/this._speed; + return this.initAnimations().step() + } + + /** + * adds a function / Situation to the animation queue + * @param fn function / situation to add + * @return this + */ + , queue: function(fn){ + if(typeof fn == 'function' || fn instanceof SVG.Situation) + this.situations.push(fn); + + if(!this.situation) this.situation = this.situations.shift(); + + return this + } + + /** + * pulls next element from the queue and execute it + * @return this + */ + , dequeue: function(){ + // stop current animation + this.stop(); + + // get next animation from queue + this.situation = this.situations.shift(); + + if(this.situation){ + if(this.situation instanceof SVG.Situation) { + this.start(); + } else { + // If it is not a SVG.Situation, then it is a function, we execute it + this.situation.call(this); + } + } + + return this + } + + // updates all animations to the current state of the element + // this is important when one property could be changed from another property + , initAnimations: function() { + var i, j, source; + var s = this.situation; + + if(s.init) return this + + for(i in s.animations){ + source = this.target()[i](); + + if(!Array.isArray(source)) { + source = [source]; + } + + if(!Array.isArray(s.animations[i])) { + s.animations[i] = [s.animations[i]]; + } + + //if(s.animations[i].length > source.length) { + // source.concat = source.concat(s.animations[i].slice(source.length, s.animations[i].length)) + //} + + for(j = source.length; j--;) { + // The condition is because some methods return a normal number instead + // of a SVG.Number + if(s.animations[i][j] instanceof SVG.Number) + source[j] = new SVG.Number(source[j]); + + s.animations[i][j] = source[j].morph(s.animations[i][j]); + } + } + + for(i in s.attrs){ + s.attrs[i] = new SVG.MorphObj(this.target().attr(i), s.attrs[i]); + } + + for(i in s.styles){ + s.styles[i] = new SVG.MorphObj(this.target().style(i), s.styles[i]); + } + + s.initialTransformation = this.target().matrixify(); + + s.init = true; + return this + } + , clearQueue: function(){ + this.situations = []; + return this + } + , clearCurrent: function(){ + this.situation = null; + return this + } + /** stops the animation immediately + * @param jumpToEnd A Boolean indicating whether to complete the current animation immediately. + * @param clearQueue A Boolean indicating whether to remove queued animation as well. + * @return this + */ + , stop: function(jumpToEnd, clearQueue){ + var active = this.active; + this.active = false; + + if(clearQueue){ + this.clearQueue(); + } + + if(jumpToEnd && this.situation){ + // initialize the situation if it was not + !active && this.startCurrent(); + this.atEnd(); + } + + this.stopAnimFrame(); + + return this.clearCurrent() + } + + /** resets the element to the state where the current element has started + * @return this + */ + , reset: function(){ + if(this.situation){ + var temp = this.situation; + this.stop(); + this.situation = temp; + this.atStart(); + } + return this + } + + // Stop the currently-running animation, remove all queued animations, and complete all animations for the element. + , finish: function(){ + + this.stop(true, false); + + while(this.dequeue().situation && this.stop(true, false)); + + this.clearQueue().clearCurrent(); + + return this + } + + // set the internal animation pointer at the start position, before any loops, and updates the visualisation + , atStart: function() { + return this.at(0, true) + } + + // set the internal animation pointer at the end position, after all the loops, and updates the visualisation + , atEnd: function() { + if (this.situation.loops === true) { + // If in a infinite loop, we end the current iteration + this.situation.loops = this.situation.loop + 1; + } + + if(typeof this.situation.loops == 'number') { + // If performing a finite number of loops, we go after all the loops + return this.at(this.situation.loops, true) + } else { + // If no loops, we just go at the end + return this.at(1, true) + } + } + + // set the internal animation pointer to the specified position and updates the visualisation + // if isAbsPos is true, pos is treated as an absolute position + , at: function(pos, isAbsPos){ + var durDivSpd = this.situation.duration/this._speed; + + this.absPos = pos; + // If pos is not an absolute position, we convert it into one + if (!isAbsPos) { + if (this.situation.reversed) this.absPos = 1 - this.absPos; + this.absPos += this.situation.loop; + } + + this.situation.start = +new Date - this.absPos * durDivSpd; + this.situation.finish = this.situation.start + durDivSpd; + + return this.step(true) + } + + /** + * sets or returns the speed of the animations + * @param speed null || Number The new speed of the animations + * @return Number || this + */ + , speed: function(speed){ + if (speed === 0) return this.pause() + + if (speed) { + this._speed = speed; + // We use an absolute position here so that speed can affect the delay before the animation + return this.at(this.absPos, true) + } else return this._speed + } + + // Make loopable + , loop: function(times, reverse) { + var c = this.last(); + + // store total loops + c.loops = (times != null) ? times : true; + c.loop = 0; + + if(reverse) c.reversing = true; + return this + } + + // pauses the animation + , pause: function(){ + this.paused = true; + this.stopAnimFrame(); + + return this + } + + // unpause the animation + , play: function(){ + if(!this.paused) return this + this.paused = false; + // We use an absolute position here so that the delay before the animation can be paused + return this.at(this.absPos, true) + } + + /** + * toggle or set the direction of the animation + * true sets direction to backwards while false sets it to forwards + * @param reversed Boolean indicating whether to reverse the animation or not (default: toggle the reverse status) + * @return this + */ + , reverse: function(reversed){ + var c = this.last(); + + if(typeof reversed == 'undefined') c.reversed = !c.reversed; + else c.reversed = reversed; + + return this + } + + + /** + * returns a float from 0-1 indicating the progress of the current animation + * @param eased Boolean indicating whether the returned position should be eased or not + * @return number + */ + , progress: function(easeIt){ + return easeIt ? this.situation.ease(this.pos) : this.pos + } + + /** + * adds a callback function which is called when the current animation is finished + * @param fn Function which should be executed as callback + * @return number + */ + , after: function(fn){ + var c = this.last() + , wrapper = function wrapper(e){ + if(e.detail.situation == c){ + fn.call(this, c); + this.off('finished.fx', wrapper); // prevent memory leak + } + }; + + this.target().on('finished.fx', wrapper); + + return this._callStart() + } + + // adds a callback which is called whenever one animation step is performed + , during: function(fn){ + var c = this.last() + , wrapper = function(e){ + if(e.detail.situation == c){ + fn.call(this, e.detail.pos, SVG.morph(e.detail.pos), e.detail.eased, c); + } + }; + + // see above + this.target().off('during.fx', wrapper).on('during.fx', wrapper); + + this.after(function(){ + this.off('during.fx', wrapper); + }); + + return this._callStart() + } + + // calls after ALL animations in the queue are finished + , afterAll: function(fn){ + var wrapper = function wrapper(e){ + fn.call(this); + this.off('allfinished.fx', wrapper); + }; + + // see above + this.target().off('allfinished.fx', wrapper).on('allfinished.fx', wrapper); + + return this._callStart() + } + + // calls on every animation step for all animations + , duringAll: function(fn){ + var wrapper = function(e){ + fn.call(this, e.detail.pos, SVG.morph(e.detail.pos), e.detail.eased, e.detail.situation); + }; + + this.target().off('during.fx', wrapper).on('during.fx', wrapper); + + this.afterAll(function(){ + this.off('during.fx', wrapper); + }); + + return this._callStart() + } + + , last: function(){ + return this.situations.length ? this.situations[this.situations.length-1] : this.situation + } + + // adds one property to the animations + , add: function(method, args, type){ + this.last()[type || 'animations'][method] = args; + return this._callStart() + } + + /** perform one step of the animation + * @param ignoreTime Boolean indicating whether to ignore time and use position directly or recalculate position based on time + * @return this + */ + , step: function(ignoreTime){ + + // convert current time to an absolute position + if(!ignoreTime) this.absPos = this.timeToAbsPos(+new Date); + + // This part convert an absolute position to a position + if(this.situation.loops !== false) { + var absPos, absPosInt, lastLoop; + + // If the absolute position is below 0, we just treat it as if it was 0 + absPos = Math.max(this.absPos, 0); + absPosInt = Math.floor(absPos); + + if(this.situation.loops === true || absPosInt < this.situation.loops) { + this.pos = absPos - absPosInt; + lastLoop = this.situation.loop; + this.situation.loop = absPosInt; + } else { + this.absPos = this.situation.loops; + this.pos = 1; + // The -1 here is because we don't want to toggle reversed when all the loops have been completed + lastLoop = this.situation.loop - 1; + this.situation.loop = this.situation.loops; + } + + if(this.situation.reversing) { + // Toggle reversed if an odd number of loops as occured since the last call of step + this.situation.reversed = this.situation.reversed != Boolean((this.situation.loop - lastLoop) % 2); + } + + } else { + // If there are no loop, the absolute position must not be above 1 + this.absPos = Math.min(this.absPos, 1); + this.pos = this.absPos; + } + + // while the absolute position can be below 0, the position must not be below 0 + if(this.pos < 0) this.pos = 0; + + if(this.situation.reversed) this.pos = 1 - this.pos; + + + // apply easing + var eased = this.situation.ease(this.pos); + + // call once-callbacks + for(var i in this.situation.once){ + if(i > this.lastPos && i <= eased){ + this.situation.once[i].call(this.target(), this.pos, eased); + delete this.situation.once[i]; + } + } + + // fire during callback with position, eased position and current situation as parameter + if(this.active) this.target().fire('during', {pos: this.pos, eased: eased, fx: this, situation: this.situation}); + + // the user may call stop or finish in the during callback + // so make sure that we still have a valid situation + if(!this.situation){ + return this + } + + // apply the actual animation to every property + this.eachAt(); + + // do final code when situation is finished + if((this.pos == 1 && !this.situation.reversed) || (this.situation.reversed && this.pos == 0)){ + + // stop animation callback + this.stopAnimFrame(); + + // fire finished callback with current situation as parameter + this.target().fire('finished', {fx:this, situation: this.situation}); + + if(!this.situations.length){ + this.target().fire('allfinished'); + + // Recheck the length since the user may call animate in the afterAll callback + if(!this.situations.length){ + this.target().off('.fx'); // there shouldnt be any binding left, but to make sure... + this.active = false; + } + } + + // start next animation + if(this.active) this.dequeue(); + else this.clearCurrent(); + + }else if(!this.paused && this.active){ + // we continue animating when we are not at the end + this.startAnimFrame(); + } + + // save last eased position for once callback triggering + this.lastPos = eased; + return this + + } + + // calculates the step for every property and calls block with it + , eachAt: function(){ + var i, len, at, self = this, target = this.target(), s = this.situation; + + // apply animations which can be called trough a method + for(i in s.animations){ + + at = [].concat(s.animations[i]).map(function(el){ + return typeof el !== 'string' && el.at ? el.at(s.ease(self.pos), self.pos) : el + }); + + target[i].apply(target, at); + + } + + // apply animation which has to be applied with attr() + for(i in s.attrs){ + + at = [i].concat(s.attrs[i]).map(function(el){ + return typeof el !== 'string' && el.at ? el.at(s.ease(self.pos), self.pos) : el + }); + + target.attr.apply(target, at); + + } + + // apply animation which has to be applied with style() + for(i in s.styles){ + + at = [i].concat(s.styles[i]).map(function(el){ + return typeof el !== 'string' && el.at ? el.at(s.ease(self.pos), self.pos) : el + }); + + target.style.apply(target, at); + + } + + // animate initialTransformation which has to be chained + if(s.transforms.length){ + + // get initial initialTransformation + at = s.initialTransformation; + for(i = 0, len = s.transforms.length; i < len; i++){ + + // get next transformation in chain + var a = s.transforms[i]; + + // multiply matrix directly + if(a instanceof SVG.Matrix){ + + if(a.relative){ + at = at.multiply(new SVG.Matrix().morph(a).at(s.ease(this.pos))); + }else { + at = at.morph(a).at(s.ease(this.pos)); + } + continue + } + + // when transformation is absolute we have to reset the needed transformation first + if(!a.relative) + a.undo(at.extract()); + + // and reapply it after + at = at.multiply(a.at(s.ease(this.pos))); + + } + + // set new matrix on element + target.matrix(at); + } + + return this + + } + + + // adds an once-callback which is called at a specific position and never again + , once: function(pos, fn, isEased){ + var c = this.last(); + if(!isEased) pos = c.ease(pos); + + c.once[pos] = fn; + + return this + } + + , _callStart: function() { + setTimeout(function(){this.start();}.bind(this), 0); + return this + } + + } + +, parent: SVG.Element + + // Add method to parent elements +, construct: { + // Get fx module or create a new one, then animate with given duration and ease + animate: function(o, ease, delay) { + return (this.fx || (this.fx = new SVG.FX(this))).animate(o, ease, delay) + } + , delay: function(delay){ + return (this.fx || (this.fx = new SVG.FX(this))).delay(delay) + } + , stop: function(jumpToEnd, clearQueue) { + if (this.fx) + this.fx.stop(jumpToEnd, clearQueue); + + return this + } + , finish: function() { + if (this.fx) + this.fx.finish(); + + return this + } + // Pause current animation + , pause: function() { + if (this.fx) + this.fx.pause(); + + return this + } + // Play paused current animation + , play: function() { + if (this.fx) + this.fx.play(); + + return this + } + // Set/Get the speed of the animations + , speed: function(speed) { + if (this.fx) + if (speed == null) + return this.fx.speed() + else + this.fx.speed(speed); + + return this + } + } + +}); + +// MorphObj is used whenever no morphable object is given +SVG.MorphObj = SVG.invent({ + + create: function(from, to){ + // prepare color for morphing + if(SVG.Color.isColor(to)) return new SVG.Color(from).morph(to) + // check if we have a list of values + if(SVG.regex.delimiter.test(from)) { + // prepare path for morphing + if(SVG.regex.pathLetters.test(from)) return new SVG.PathArray(from).morph(to) + // prepare value list for morphing + else return new SVG.Array(from).morph(to) + } + // prepare number for morphing + if(SVG.regex.numberAndUnit.test(to)) return new SVG.Number(from).morph(to) + + // prepare for plain morphing + this.value = from; + this.destination = to; + } + +, extend: { + at: function(pos, real){ + return real < 1 ? this.value : this.destination + }, + + valueOf: function(){ + return this.value + } + } + +}); + +SVG.extend(SVG.FX, { + // Add animatable attributes + attr: function(a, v, relative) { + // apply attributes individually + if (typeof a == 'object') { + for (var key in a) + this.attr(key, a[key]); + + } else { + this.add(a, v, 'attrs'); + } + + return this + } + // Add animatable styles +, style: function(s, v) { + if (typeof s == 'object') + for (var key in s) + this.style(key, s[key]); + + else + this.add(s, v, 'styles'); + + return this + } + // Animatable x-axis +, x: function(x, relative) { + if(this.target() instanceof SVG.G){ + this.transform({x:x}, relative); + return this + } + + var num = new SVG.Number(x); + num.relative = relative; + return this.add('x', num) + } + // Animatable y-axis +, y: function(y, relative) { + if(this.target() instanceof SVG.G){ + this.transform({y:y}, relative); + return this + } + + var num = new SVG.Number(y); + num.relative = relative; + return this.add('y', num) + } + // Animatable center x-axis +, cx: function(x) { + return this.add('cx', new SVG.Number(x)) + } + // Animatable center y-axis +, cy: function(y) { + return this.add('cy', new SVG.Number(y)) + } + // Add animatable move +, move: function(x, y) { + return this.x(x).y(y) + } + // Add animatable center +, center: function(x, y) { + return this.cx(x).cy(y) + } + // Add animatable size +, size: function(width, height) { + if (this.target() instanceof SVG.Text) { + // animate font size for Text elements + this.attr('font-size', width); + + } else { + // animate bbox based size for all other elements + var box; + + if(!width || !height){ + box = this.target().bbox(); + } + + if(!width){ + width = box.width / box.height * height; + } + + if(!height){ + height = box.height / box.width * width; + } + + this.add('width' , new SVG.Number(width)) + .add('height', new SVG.Number(height)); + + } + + return this + } + // Add animatable width +, width: function(width) { + return this.add('width', new SVG.Number(width)) + } + // Add animatable height +, height: function(height) { + return this.add('height', new SVG.Number(height)) + } + // Add animatable plot +, plot: function(a, b, c, d) { + // Lines can be plotted with 4 arguments + if(arguments.length == 4) { + return this.plot([a, b, c, d]) + } + + return this.add('plot', new (this.target().morphArray)(a)) + } + // Add leading method +, leading: function(value) { + return this.target().leading ? + this.add('leading', new SVG.Number(value)) : + this + } + // Add animatable viewbox +, viewbox: function(x, y, width, height) { + if (this.target() instanceof SVG.Container) { + this.add('viewbox', new SVG.ViewBox(x, y, width, height)); + } + + return this + } +, update: function(o) { + if (this.target() instanceof SVG.Stop) { + if (typeof o == 'number' || o instanceof SVG.Number) { + return this.update({ + offset: arguments[0] + , color: arguments[1] + , opacity: arguments[2] + }) + } + + if (o.opacity != null) this.attr('stop-opacity', o.opacity); + if (o.color != null) this.attr('stop-color', o.color); + if (o.offset != null) this.attr('offset', o.offset); + } + + return this + } +}); + +SVG.Box = SVG.invent({ + create: function(x, y, width, height) { + if (typeof x == 'object' && !(x instanceof SVG.Element)) { + // chromes getBoundingClientRect has no x and y property + return SVG.Box.call(this, x.left != null ? x.left : x.x , x.top != null ? x.top : x.y, x.width, x.height) + } else if (arguments.length == 4) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + // add center, right, bottom... + fullBox(this); + } +, extend: { + // Merge rect box with another, return a new instance + merge: function(box) { + var b = new this.constructor(); + + // merge boxes + b.x = Math.min(this.x, box.x); + b.y = Math.min(this.y, box.y); + b.width = Math.max(this.x + this.width, box.x + box.width) - b.x; + b.height = Math.max(this.y + this.height, box.y + box.height) - b.y; + + return fullBox(b) + } + + , transform: function(m) { + var xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, bbox; + + var pts = [ + new SVG.Point(this.x, this.y), + new SVG.Point(this.x2, this.y), + new SVG.Point(this.x, this.y2), + new SVG.Point(this.x2, this.y2) + ]; + + pts.forEach(function(p) { + p = p.transform(m); + xMin = Math.min(xMin,p.x); + xMax = Math.max(xMax,p.x); + yMin = Math.min(yMin,p.y); + yMax = Math.max(yMax,p.y); + }); + + bbox = new this.constructor(); + bbox.x = xMin; + bbox.width = xMax-xMin; + bbox.y = yMin; + bbox.height = yMax-yMin; + + fullBox(bbox); + + return bbox + } + } +}); + +SVG.BBox = SVG.invent({ + // Initialize + create: function(element) { + SVG.Box.apply(this, [].slice.call(arguments)); + + // get values if element is given + if (element instanceof SVG.Element) { + var box; + + // yes this is ugly, but Firefox can be a pain when it comes to elements that are not yet rendered + try { + + if (!document.documentElement.contains){ + // This is IE - it does not support contains() for top-level SVGs + var topParent = element.node; + while (topParent.parentNode){ + topParent = topParent.parentNode; + } + if (topParent != document) throw new Exception('Element not in the dom') + } else { + // the element is NOT in the dom, throw error + if(!document.documentElement.contains(element.node)) throw new Exception('Element not in the dom') + } + + // find native bbox + box = element.node.getBBox(); + } catch(e) { + if(element instanceof SVG.Shape){ + var clone = element.clone(SVG.parser.draw.instance).show(); + box = clone.node.getBBox(); + clone.remove(); + }else { + box = { + x: element.node.clientLeft + , y: element.node.clientTop + , width: element.node.clientWidth + , height: element.node.clientHeight + }; + } + } + + SVG.Box.call(this, box); + } + + } + + // Define ancestor +, inherit: SVG.Box + + // Define Parent +, parent: SVG.Element + + // Constructor +, construct: { + // Get bounding box + bbox: function() { + return new SVG.BBox(this) + } + } + +}); + +SVG.BBox.prototype.constructor = SVG.BBox; + + +SVG.extend(SVG.Element, { + tbox: function(){ + console.warn('Use of TBox is deprecated and mapped to RBox. Use .rbox() instead.'); + return this.rbox(this.doc()) + } +}); + +SVG.RBox = SVG.invent({ + // Initialize + create: function(element) { + SVG.Box.apply(this, [].slice.call(arguments)); + + if (element instanceof SVG.Element) { + SVG.Box.call(this, element.node.getBoundingClientRect()); + } + } + +, inherit: SVG.Box + + // define Parent +, parent: SVG.Element + +, extend: { + addOffset: function() { + // offset by window scroll position, because getBoundingClientRect changes when window is scrolled + this.x += window.pageXOffset; + this.y += window.pageYOffset; + return this + } + } + + // Constructor +, construct: { + // Get rect box + rbox: function(el) { + if (el) return new SVG.RBox(this).transform(el.screenCTM().inverse()) + return new SVG.RBox(this).addOffset() + } + } + +}); + +SVG.RBox.prototype.constructor = SVG.RBox; + +SVG.Matrix = SVG.invent({ + // Initialize + create: function(source) { + var i, base = arrayToMatrix([1, 0, 0, 1, 0, 0]); + + // ensure source as object + source = source instanceof SVG.Element ? + source.matrixify() : + typeof source === 'string' ? + arrayToMatrix(source.split(SVG.regex.delimiter).map(parseFloat)) : + arguments.length == 6 ? + arrayToMatrix([].slice.call(arguments)) : + Array.isArray(source) ? + arrayToMatrix(source) : + typeof source === 'object' ? + source : base; + + // merge source + for (i = abcdef.length - 1; i >= 0; --i) + this[abcdef[i]] = source[abcdef[i]] != null ? + source[abcdef[i]] : base[abcdef[i]]; + } + + // Add methods +, extend: { + // Extract individual transformations + extract: function() { + // find delta transform points + var px = deltaTransformPoint(this, 0, 1) + , py = deltaTransformPoint(this, 1, 0) + , skewX = 180 / Math.PI * Math.atan2(px.y, px.x) - 90; + + return { + // translation + x: this.e + , y: this.f + , transformedX:(this.e * Math.cos(skewX * Math.PI / 180) + this.f * Math.sin(skewX * Math.PI / 180)) / Math.sqrt(this.a * this.a + this.b * this.b) + , transformedY:(this.f * Math.cos(skewX * Math.PI / 180) + this.e * Math.sin(-skewX * Math.PI / 180)) / Math.sqrt(this.c * this.c + this.d * this.d) + // skew + , skewX: -skewX + , skewY: 180 / Math.PI * Math.atan2(py.y, py.x) + // scale + , scaleX: Math.sqrt(this.a * this.a + this.b * this.b) + , scaleY: Math.sqrt(this.c * this.c + this.d * this.d) + // rotation + , rotation: skewX + , a: this.a + , b: this.b + , c: this.c + , d: this.d + , e: this.e + , f: this.f + , matrix: new SVG.Matrix(this) + } + } + // Clone matrix + , clone: function() { + return new SVG.Matrix(this) + } + // Morph one matrix into another + , morph: function(matrix) { + // store new destination + this.destination = new SVG.Matrix(matrix); + + return this + } + // Get morphed matrix at a given position + , at: function(pos) { + // make sure a destination is defined + if (!this.destination) return this + + // calculate morphed matrix at a given position + var matrix = new SVG.Matrix({ + a: this.a + (this.destination.a - this.a) * pos + , b: this.b + (this.destination.b - this.b) * pos + , c: this.c + (this.destination.c - this.c) * pos + , d: this.d + (this.destination.d - this.d) * pos + , e: this.e + (this.destination.e - this.e) * pos + , f: this.f + (this.destination.f - this.f) * pos + }); + + return matrix + } + // Multiplies by given matrix + , multiply: function(matrix) { + return new SVG.Matrix(this.native().multiply(parseMatrix(matrix).native())) + } + // Inverses matrix + , inverse: function() { + return new SVG.Matrix(this.native().inverse()) + } + // Translate matrix + , translate: function(x, y) { + return new SVG.Matrix(this.native().translate(x || 0, y || 0)) + } + // Scale matrix + , scale: function(x, y, cx, cy) { + // support uniformal scale + if (arguments.length == 1) { + y = x; + } else if (arguments.length == 3) { + cy = cx; + cx = y; + y = x; + } + + return this.around(cx, cy, new SVG.Matrix(x, 0, 0, y, 0, 0)) + } + // Rotate matrix + , rotate: function(r, cx, cy) { + // convert degrees to radians + r = SVG.utils.radians(r); + + return this.around(cx, cy, new SVG.Matrix(Math.cos(r), Math.sin(r), -Math.sin(r), Math.cos(r), 0, 0)) + } + // Flip matrix on x or y, at a given offset + , flip: function(a, o) { + return a == 'x' ? + this.scale(-1, 1, o, 0) : + a == 'y' ? + this.scale(1, -1, 0, o) : + this.scale(-1, -1, a, o != null ? o : a) + } + // Skew + , skew: function(x, y, cx, cy) { + // support uniformal skew + if (arguments.length == 1) { + y = x; + } else if (arguments.length == 3) { + cy = cx; + cx = y; + y = x; + } + + // convert degrees to radians + x = SVG.utils.radians(x); + y = SVG.utils.radians(y); + + return this.around(cx, cy, new SVG.Matrix(1, Math.tan(y), Math.tan(x), 1, 0, 0)) + } + // SkewX + , skewX: function(x, cx, cy) { + return this.skew(x, 0, cx, cy) + } + // SkewY + , skewY: function(y, cx, cy) { + return this.skew(0, y, cx, cy) + } + // Transform around a center point + , around: function(cx, cy, matrix) { + return this + .multiply(new SVG.Matrix(1, 0, 0, 1, cx || 0, cy || 0)) + .multiply(matrix) + .multiply(new SVG.Matrix(1, 0, 0, 1, -cx || 0, -cy || 0)) + } + // Convert to native SVGMatrix + , native: function() { + // create new matrix + var matrix = SVG.parser.native.createSVGMatrix(); + + // update with current values + for (var i = abcdef.length - 1; i >= 0; i--) + matrix[abcdef[i]] = this[abcdef[i]]; + + return matrix + } + // Convert matrix to string + , toString: function() { + // Construct the matrix directly, avoid values that are too small + return 'matrix(' + float32String(this.a) + ',' + float32String(this.b) + + ',' + float32String(this.c) + ',' + float32String(this.d) + + ',' + float32String(this.e) + ',' + float32String(this.f) + + ')' + } + } + + // Define parent +, parent: SVG.Element + + // Add parent method +, construct: { + // Get current matrix + ctm: function() { + return new SVG.Matrix(this.node.getCTM()) + }, + // Get current screen matrix + screenCTM: function() { + /* https://bugzilla.mozilla.org/show_bug.cgi?id=1344537 + This is needed because FF does not return the transformation matrix + for the inner coordinate system when getScreenCTM() is called on nested svgs. + However all other Browsers do that */ + if(this instanceof SVG.Nested) { + var rect = this.rect(1,1); + var m = rect.node.getScreenCTM(); + rect.remove(); + return new SVG.Matrix(m) + } + return new SVG.Matrix(this.node.getScreenCTM()) + } + + } + +}); + +SVG.Point = SVG.invent({ + // Initialize + create: function(x,y) { + var source, base = {x:0, y:0}; + + // ensure source as object + source = Array.isArray(x) ? + {x:x[0], y:x[1]} : + typeof x === 'object' ? + {x:x.x, y:x.y} : + x != null ? + {x:x, y:(y != null ? y : x)} : base; // If y has no value, then x is used has its value + + // merge source + this.x = source.x; + this.y = source.y; + } + + // Add methods +, extend: { + // Clone point + clone: function() { + return new SVG.Point(this) + } + // Morph one point into another + , morph: function(x, y) { + // store new destination + this.destination = new SVG.Point(x, y); + + return this + } + // Get morphed point at a given position + , at: function(pos) { + // make sure a destination is defined + if (!this.destination) return this + + // calculate morphed matrix at a given position + var point = new SVG.Point({ + x: this.x + (this.destination.x - this.x) * pos + , y: this.y + (this.destination.y - this.y) * pos + }); + + return point + } + // Convert to native SVGPoint + , native: function() { + // create new point + var point = SVG.parser.native.createSVGPoint(); + + // update with current values + point.x = this.x; + point.y = this.y; + + return point + } + // transform point with matrix + , transform: function(matrix) { + return new SVG.Point(this.native().matrixTransform(matrix.native())) + } + + } + +}); + +SVG.extend(SVG.Element, { + + // Get point + point: function(x, y) { + return new SVG.Point(x,y).transform(this.screenCTM().inverse()); + } + +}); + +SVG.extend(SVG.Element, { + // Set svg element attribute + attr: function(a, v, n) { + // act as full getter + if (a == null) { + // get an object of attributes + a = {}; + v = this.node.attributes; + for (n = v.length - 1; n >= 0; n--) + a[v[n].nodeName] = SVG.regex.isNumber.test(v[n].nodeValue) ? parseFloat(v[n].nodeValue) : v[n].nodeValue; + + return a + + } else if (typeof a == 'object') { + // apply every attribute individually if an object is passed + for (v in a) this.attr(v, a[v]); + + } else if (v === null) { + // remove value + this.node.removeAttribute(a); + + } else if (v == null) { + // act as a getter if the first and only argument is not an object + v = this.node.getAttribute(a); + return v == null ? + SVG.defaults.attrs[a] : + SVG.regex.isNumber.test(v) ? + parseFloat(v) : v + + } else { + // BUG FIX: some browsers will render a stroke if a color is given even though stroke width is 0 + if (a == 'stroke-width') + this.attr('stroke', parseFloat(v) > 0 ? this._stroke : null); + else if (a == 'stroke') + this._stroke = v; + + // convert image fill and stroke to patterns + if (a == 'fill' || a == 'stroke') { + if (SVG.regex.isImage.test(v)) + v = this.doc().defs().image(v, 0, 0); + + if (v instanceof SVG.Image) + v = this.doc().defs().pattern(0, 0, function() { + this.add(v); + }); + } + + // ensure correct numeric values (also accepts NaN and Infinity) + if (typeof v === 'number') + v = new SVG.Number(v); + + // ensure full hex color + else if (SVG.Color.isColor(v)) + v = new SVG.Color(v); + + // parse array values + else if (Array.isArray(v)) + v = new SVG.Array(v); + + // if the passed attribute is leading... + if (a == 'leading') { + // ... call the leading method instead + if (this.leading) + this.leading(v); + } else { + // set given attribute on node + typeof n === 'string' ? + this.node.setAttributeNS(n, a, v.toString()) : + this.node.setAttribute(a, v.toString()); + } + + // rebuild if required + if (this.rebuild && (a == 'font-size' || a == 'x')) + this.rebuild(a, v); + } + + return this + } +}); +SVG.extend(SVG.Element, { + // Add transformations + transform: function(o, relative) { + // get target in case of the fx module, otherwise reference this + var target = this + , matrix, bbox; + + // act as a getter + if (typeof o !== 'object') { + // get current matrix + matrix = new SVG.Matrix(target).extract(); + + return typeof o === 'string' ? matrix[o] : matrix + } + + // get current matrix + matrix = new SVG.Matrix(target); + + // ensure relative flag + relative = !!relative || !!o.relative; + + // act on matrix + if (o.a != null) { + matrix = relative ? + // relative + matrix.multiply(new SVG.Matrix(o)) : + // absolute + new SVG.Matrix(o); + + // act on rotation + } else if (o.rotation != null) { + // ensure centre point + ensureCentre(o, target); + + // apply transformation + matrix = relative ? + // relative + matrix.rotate(o.rotation, o.cx, o.cy) : + // absolute + matrix.rotate(o.rotation - matrix.extract().rotation, o.cx, o.cy); + + // act on scale + } else if (o.scale != null || o.scaleX != null || o.scaleY != null) { + // ensure centre point + ensureCentre(o, target); + + // ensure scale values on both axes + o.scaleX = o.scale != null ? o.scale : o.scaleX != null ? o.scaleX : 1; + o.scaleY = o.scale != null ? o.scale : o.scaleY != null ? o.scaleY : 1; + + if (!relative) { + // absolute; multiply inversed values + var e = matrix.extract(); + o.scaleX = o.scaleX * 1 / e.scaleX; + o.scaleY = o.scaleY * 1 / e.scaleY; + } + + matrix = matrix.scale(o.scaleX, o.scaleY, o.cx, o.cy); + + // act on skew + } else if (o.skew != null || o.skewX != null || o.skewY != null) { + // ensure centre point + ensureCentre(o, target); + + // ensure skew values on both axes + o.skewX = o.skew != null ? o.skew : o.skewX != null ? o.skewX : 0; + o.skewY = o.skew != null ? o.skew : o.skewY != null ? o.skewY : 0; + + if (!relative) { + // absolute; reset skew values + var e = matrix.extract(); + matrix = matrix.multiply(new SVG.Matrix().skew(e.skewX, e.skewY, o.cx, o.cy).inverse()); + } + + matrix = matrix.skew(o.skewX, o.skewY, o.cx, o.cy); + + // act on flip + } else if (o.flip) { + if(o.flip == 'x' || o.flip == 'y') { + o.offset = o.offset == null ? target.bbox()['c' + o.flip] : o.offset; + } else { + if(o.offset == null) { + bbox = target.bbox(); + o.flip = bbox.cx; + o.offset = bbox.cy; + } else { + o.flip = o.offset; + } + } + + matrix = new SVG.Matrix().flip(o.flip, o.offset); + + // act on translate + } else if (o.x != null || o.y != null) { + if (relative) { + // relative + matrix = matrix.translate(o.x, o.y); + } else { + // absolute + if (o.x != null) matrix.e = o.x; + if (o.y != null) matrix.f = o.y; + } + } + + return this.attr('transform', matrix) + } +}); + +SVG.extend(SVG.FX, { + transform: function(o, relative) { + // get target in case of the fx module, otherwise reference this + var target = this.target() + , matrix, bbox; + + // act as a getter + if (typeof o !== 'object') { + // get current matrix + matrix = new SVG.Matrix(target).extract(); + + return typeof o === 'string' ? matrix[o] : matrix + } + + // ensure relative flag + relative = !!relative || !!o.relative; + + // act on matrix + if (o.a != null) { + matrix = new SVG.Matrix(o); + + // act on rotation + } else if (o.rotation != null) { + // ensure centre point + ensureCentre(o, target); + + // apply transformation + matrix = new SVG.Rotate(o.rotation, o.cx, o.cy); + + // act on scale + } else if (o.scale != null || o.scaleX != null || o.scaleY != null) { + // ensure centre point + ensureCentre(o, target); + + // ensure scale values on both axes + o.scaleX = o.scale != null ? o.scale : o.scaleX != null ? o.scaleX : 1; + o.scaleY = o.scale != null ? o.scale : o.scaleY != null ? o.scaleY : 1; + + matrix = new SVG.Scale(o.scaleX, o.scaleY, o.cx, o.cy); + + // act on skew + } else if (o.skewX != null || o.skewY != null) { + // ensure centre point + ensureCentre(o, target); + + // ensure skew values on both axes + o.skewX = o.skewX != null ? o.skewX : 0; + o.skewY = o.skewY != null ? o.skewY : 0; + + matrix = new SVG.Skew(o.skewX, o.skewY, o.cx, o.cy); + + // act on flip + } else if (o.flip) { + if(o.flip == 'x' || o.flip == 'y') { + o.offset = o.offset == null ? target.bbox()['c' + o.flip] : o.offset; + } else { + if(o.offset == null) { + bbox = target.bbox(); + o.flip = bbox.cx; + o.offset = bbox.cy; + } else { + o.flip = o.offset; + } + } + + matrix = new SVG.Matrix().flip(o.flip, o.offset); + + // act on translate + } else if (o.x != null || o.y != null) { + matrix = new SVG.Translate(o.x, o.y); + } + + if(!matrix) return this + + matrix.relative = relative; + + this.last().transforms.push(matrix); + + return this._callStart() + } +}); + +SVG.extend(SVG.Element, { + // Reset all transformations + untransform: function() { + return this.attr('transform', null) + }, + // merge the whole transformation chain into one matrix and returns it + matrixify: function() { + + var matrix = (this.attr('transform') || '') + // split transformations + .split(SVG.regex.transforms).slice(0,-1).map(function(str){ + // generate key => value pairs + var kv = str.trim().split('('); + return [kv[0], kv[1].split(SVG.regex.delimiter).map(function(str){ return parseFloat(str) })] + }) + // merge every transformation into one matrix + .reduce(function(matrix, transform){ + + if(transform[0] == 'matrix') return matrix.multiply(arrayToMatrix(transform[1])) + return matrix[transform[0]].apply(matrix, transform[1]) + + }, new SVG.Matrix()); + + return matrix + }, + // add an element to another parent without changing the visual representation on the screen + toParent: function(parent) { + if(this == parent) return this + var ctm = this.screenCTM(); + var pCtm = parent.screenCTM().inverse(); + + this.addTo(parent).untransform().transform(pCtm.multiply(ctm)); + + return this + }, + // same as above with parent equals root-svg + toDoc: function() { + return this.toParent(this.doc()) + } + +}); + +SVG.Transformation = SVG.invent({ + + create: function(source, inversed){ + + if(arguments.length > 1 && typeof inversed != 'boolean'){ + return this.constructor.call(this, [].slice.call(arguments)) + } + + if(Array.isArray(source)){ + for(var i = 0, len = this.arguments.length; i < len; ++i){ + this[this.arguments[i]] = source[i]; + } + } else if(typeof source == 'object'){ + for(var i = 0, len = this.arguments.length; i < len; ++i){ + this[this.arguments[i]] = source[this.arguments[i]]; + } + } + + this.inversed = false; + + if(inversed === true){ + this.inversed = true; + } + + } + +, extend: { + + arguments: [] + , method: '' + + , at: function(pos){ + + var params = []; + + for(var i = 0, len = this.arguments.length; i < len; ++i){ + params.push(this[this.arguments[i]]); + } + + var m = this._undo || new SVG.Matrix(); + + m = new SVG.Matrix().morph(SVG.Matrix.prototype[this.method].apply(m, params)).at(pos); + + return this.inversed ? m.inverse() : m + + } + + , undo: function(o){ + for(var i = 0, len = this.arguments.length; i < len; ++i){ + o[this.arguments[i]] = typeof this[this.arguments[i]] == 'undefined' ? 0 : o[this.arguments[i]]; + } + + // The method SVG.Matrix.extract which was used before calling this + // method to obtain a value for the parameter o doesn't return a cx and + // a cy so we use the ones that were provided to this object at its creation + o.cx = this.cx; + o.cy = this.cy; + + this._undo = new SVG[capitalize(this.method)](o, true).at(1); + + return this + } + + } + +}); + +SVG.Translate = SVG.invent({ + + parent: SVG.Matrix +, inherit: SVG.Transformation + +, create: function(source, inversed){ + this.constructor.apply(this, [].slice.call(arguments)); + } + +, extend: { + arguments: ['transformedX', 'transformedY'] + , method: 'translate' + } + +}); + +SVG.Rotate = SVG.invent({ + + parent: SVG.Matrix +, inherit: SVG.Transformation + +, create: function(source, inversed){ + this.constructor.apply(this, [].slice.call(arguments)); + } + +, extend: { + arguments: ['rotation', 'cx', 'cy'] + , method: 'rotate' + , at: function(pos){ + var m = new SVG.Matrix().rotate(new SVG.Number().morph(this.rotation - (this._undo ? this._undo.rotation : 0)).at(pos), this.cx, this.cy); + return this.inversed ? m.inverse() : m + } + , undo: function(o){ + this._undo = o; + return this + } + } + +}); + +SVG.Scale = SVG.invent({ + + parent: SVG.Matrix +, inherit: SVG.Transformation + +, create: function(source, inversed){ + this.constructor.apply(this, [].slice.call(arguments)); + } + +, extend: { + arguments: ['scaleX', 'scaleY', 'cx', 'cy'] + , method: 'scale' + } + +}); + +SVG.Skew = SVG.invent({ + + parent: SVG.Matrix +, inherit: SVG.Transformation + +, create: function(source, inversed){ + this.constructor.apply(this, [].slice.call(arguments)); + } + +, extend: { + arguments: ['skewX', 'skewY', 'cx', 'cy'] + , method: 'skew' + } + +}); + +SVG.extend(SVG.Element, { + // Dynamic style generator + style: function(s, v) { + if (arguments.length == 0) { + // get full style + return this.node.style.cssText || '' + + } else if (arguments.length < 2) { + // apply every style individually if an object is passed + if (typeof s == 'object') { + for (v in s) this.style(v, s[v]); + + } else if (SVG.regex.isCss.test(s)) { + // parse css string + s = s.split(/\s*;\s*/) + // filter out suffix ; and stuff like ;; + .filter(function(e) { return !!e }) + .map(function(e){ return e.split(/\s*:\s*/) }); + + // apply every definition individually + while (v = s.pop()) { + this.style(v[0], v[1]); + } + } else { + // act as a getter if the first and only argument is not an object + return this.node.style[camelCase(s)] + } + + } else { + this.node.style[camelCase(s)] = v === null || SVG.regex.isBlank.test(v) ? '' : v; + } + + return this + } +}); +SVG.Parent = SVG.invent({ + // Initialize node + create: function(element) { + this.constructor.call(this, element); + } + + // Inherit from +, inherit: SVG.Element + + // Add class methods +, extend: { + // Returns all child elements + children: function() { + return SVG.utils.map(SVG.utils.filterSVGElements(this.node.childNodes), function(node) { + return SVG.adopt(node) + }) + } + // Add given element at a position + , add: function(element, i) { + if (i == null) + this.node.appendChild(element.node); + else if (element.node != this.node.childNodes[i]) + this.node.insertBefore(element.node, this.node.childNodes[i]); + + return this + } + // Basically does the same as `add()` but returns the added element instead + , put: function(element, i) { + this.add(element, i); + return element + } + // Checks if the given element is a child + , has: function(element) { + return this.index(element) >= 0 + } + // Gets index of given element + , index: function(element) { + return [].slice.call(this.node.childNodes).indexOf(element.node) + } + // Get a element at the given index + , get: function(i) { + return SVG.adopt(this.node.childNodes[i]) + } + // Get first child + , first: function() { + return this.get(0) + } + // Get the last child + , last: function() { + return this.get(this.node.childNodes.length - 1) + } + // Iterates over all children and invokes a given block + , each: function(block, deep) { + var i, il + , children = this.children(); + + for (i = 0, il = children.length; i < il; i++) { + if (children[i] instanceof SVG.Element) + block.apply(children[i], [i, children]); + + if (deep && (children[i] instanceof SVG.Container)) + children[i].each(block, deep); + } + + return this + } + // Remove a given child + , removeElement: function(element) { + this.node.removeChild(element.node); + + return this + } + // Remove all elements in this container + , clear: function() { + // remove children + while(this.node.hasChildNodes()) + this.node.removeChild(this.node.lastChild); + + // remove defs reference + delete this._defs; + + return this + } + , // Get defs + defs: function() { + return this.doc().defs() + } + } + +}); + +SVG.extend(SVG.Parent, { + + ungroup: function(parent, depth) { + if(depth === 0 || this instanceof SVG.Defs || this.node == SVG.parser.draw) return this + + parent = parent || (this instanceof SVG.Doc ? this : this.parent(SVG.Parent)); + depth = depth || Infinity; + + this.each(function(){ + if(this instanceof SVG.Defs) return this + if(this instanceof SVG.Parent) return this.ungroup(parent, depth-1) + return this.toParent(parent) + }); + + this.node.firstChild || this.remove(); + + return this + }, + + flatten: function(parent, depth) { + return this.ungroup(parent, depth) + } + +}); +SVG.Container = SVG.invent({ + // Initialize node + create: function(element) { + this.constructor.call(this, element); + } + + // Inherit from +, inherit: SVG.Parent + +}); + +SVG.ViewBox = SVG.invent({ + + create: function(source) { + var base = [0, 0, 0, 0]; + + var x, y, width, height, view, we, he + , wm = 1 // width multiplier + , hm = 1 // height multiplier + , reg = /[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?/gi; + + if(source instanceof SVG.Element){ + + we = source; + he = source; + view = (source.attr('viewBox') || '').match(reg); + source.bbox; + + // get dimensions of current node + width = new SVG.Number(source.width()); + height = new SVG.Number(source.height()); + + // find nearest non-percentual dimensions + while (width.unit == '%') { + wm *= width.value; + width = new SVG.Number(we instanceof SVG.Doc ? we.parent().offsetWidth : we.parent().width()); + we = we.parent(); + } + while (height.unit == '%') { + hm *= height.value; + height = new SVG.Number(he instanceof SVG.Doc ? he.parent().offsetHeight : he.parent().height()); + he = he.parent(); + } + + // ensure defaults + this.x = 0; + this.y = 0; + this.width = width * wm; + this.height = height * hm; + this.zoom = 1; + + if (view) { + // get width and height from viewbox + x = parseFloat(view[0]); + y = parseFloat(view[1]); + width = parseFloat(view[2]); + height = parseFloat(view[3]); + + // calculate zoom accoring to viewbox + this.zoom = ((this.width / this.height) > (width / height)) ? + this.height / height : + this.width / width; + + // calculate real pixel dimensions on parent SVG.Doc element + this.x = x; + this.y = y; + this.width = width; + this.height = height; + + } + + }else { + + // ensure source as object + source = typeof source === 'string' ? + source.match(reg).map(function(el){ return parseFloat(el) }) : + Array.isArray(source) ? + source : + typeof source == 'object' ? + [source.x, source.y, source.width, source.height] : + arguments.length == 4 ? + [].slice.call(arguments) : + base; + + this.x = source[0]; + this.y = source[1]; + this.width = source[2]; + this.height = source[3]; + } + + + } + +, extend: { + + toString: function() { + return this.x + ' ' + this.y + ' ' + this.width + ' ' + this.height + } + , morph: function(x, y, width, height){ + this.destination = new SVG.ViewBox(x, y, width, height); + return this + } + + , at: function(pos) { + + if(!this.destination) return this + + return new SVG.ViewBox([ + this.x + (this.destination.x - this.x) * pos + , this.y + (this.destination.y - this.y) * pos + , this.width + (this.destination.width - this.width) * pos + , this.height + (this.destination.height - this.height) * pos + ]) + + } + + } + + // Define parent +, parent: SVG.Container + + // Add parent method +, construct: { + + // get/set viewbox + viewbox: function(x, y, width, height) { + if (arguments.length == 0) + // act as a getter if there are no arguments + return new SVG.ViewBox(this) + + // otherwise act as a setter + return this.attr('viewBox', new SVG.ViewBox(x, y, width, height)) + } + + } + +}) +// Add events to elements + +;[ 'click', + 'dblclick', + 'mousedown', + 'mouseup', + 'mouseover', + 'mouseout', + 'mousemove', + 'mouseenter', + 'mouseleave', + 'touchstart', + 'touchmove', + 'touchleave', + 'touchend', + 'touchcancel' ].forEach(function (event) { + // add event to SVG.Element + SVG.Element.prototype[event] = function (f) { + // bind event to element rather than element node + if (f == null) { + SVG.off(this, event); + } else { + SVG.on(this, event, f); + } + return this + }; + }); + +SVG.listenerId = 0; + +// Add event binder in the SVG namespace +SVG.on = function (node, events, listener, binding, options) { + var l = listener.bind(binding || node); + var n = node instanceof SVG.Element ? node.node : node; + + // ensure instance object for nodes which are not adopted + n.instance = n.instance || {_events: {}}; + + var bag = n.instance._events; + + // add id to listener + if (!listener._svgjsListenerId) { listener._svgjsListenerId = ++SVG.listenerId; } + + events.split(SVG.regex.delimiter).forEach(function (event) { + var ev = event.split('.')[0]; + var ns = event.split('.')[1] || '*'; + + // ensure valid object + bag[ev] = bag[ev] || {}; + bag[ev][ns] = bag[ev][ns] || {}; + + // reference listener + bag[ev][ns][listener._svgjsListenerId] = l; + + // add listener + n.addEventListener(ev, l, options || false); + }); +}; + +// Add event unbinder in the SVG namespace +SVG.off = function (node, events, listener, options) { + var n = node instanceof SVG.Element ? node.node : node; + if (!n.instance) return + + // listener can be a function or a number + if (typeof listener === 'function') { + listener = listener._svgjsListenerId; + if (!listener) return + } + + var bag = n.instance._events + + ;(events || '').split(SVG.regex.delimiter).forEach(function (event) { + var ev = event && event.split('.')[0]; + var ns = event && event.split('.')[1]; + var namespace, l; + + if (listener) { + // remove listener reference + if (bag[ev] && bag[ev][ns || '*']) { + // removeListener + n.removeEventListener(ev, bag[ev][ns || '*'][listener], options || false); + + delete bag[ev][ns || '*'][listener]; + } + } else if (ev && ns) { + // remove all listeners for a namespaced event + if (bag[ev] && bag[ev][ns]) { + for (l in bag[ev][ns]) { SVG.off(n, [ev, ns].join('.'), l); } + + delete bag[ev][ns]; + } + } else if (ns) { + // remove all listeners for a specific namespace + for (event in bag) { + for (namespace in bag[event]) { + if (ns === namespace) { SVG.off(n, [event, ns].join('.')); } + } + } + } else if (ev) { + // remove all listeners for the event + if (bag[ev]) { + for (namespace in bag[ev]) { SVG.off(n, [ev, namespace].join('.')); } + + delete bag[ev]; + } + } else { + // remove all listeners on a given node + for (event in bag) { SVG.off(n, event); } + + n.instance._events = {}; + } + }); +}; + +SVG.extend(SVG.Element, { + // Bind given event to listener + on: function (event, listener, binding, options) { + SVG.on(this, event, listener, binding, options); + return this + }, + // Unbind event from listener + off: function (event, listener) { + SVG.off(this.node, event, listener); + return this + }, + fire: function (event, data) { + // Dispatch event + if (event instanceof window.Event) { + this.node.dispatchEvent(event); + } else { + this.node.dispatchEvent(event = new SVG.CustomEvent(event, {detail: data, cancelable: true})); + } + this._event = event; + return this + }, + event: function() { + return this._event + } +}); + + +SVG.Defs = SVG.invent({ + // Initialize node + create: 'defs' + + // Inherit from +, inherit: SVG.Container + +}); +SVG.G = SVG.invent({ + // Initialize node + create: 'g' + + // Inherit from +, inherit: SVG.Container + + // Add class methods +, extend: { + // Move over x-axis + x: function(x) { + return x == null ? this.transform('x') : this.transform({ x: x - this.x() }, true) + } + // Move over y-axis + , y: function(y) { + return y == null ? this.transform('y') : this.transform({ y: y - this.y() }, true) + } + // Move by center over x-axis + , cx: function(x) { + return x == null ? this.gbox().cx : this.x(x - this.gbox().width / 2) + } + // Move by center over y-axis + , cy: function(y) { + return y == null ? this.gbox().cy : this.y(y - this.gbox().height / 2) + } + , gbox: function() { + + var bbox = this.bbox() + , trans = this.transform(); + + bbox.x += trans.x; + bbox.x2 += trans.x; + bbox.cx += trans.x; + + bbox.y += trans.y; + bbox.y2 += trans.y; + bbox.cy += trans.y; + + return bbox + } + } + + // Add parent method +, construct: { + // Create a group element + group: function() { + return this.put(new SVG.G) + } + } +}); + +SVG.Doc = SVG.invent({ + // Initialize node + create: function(element) { + if (element) { + // ensure the presence of a dom element + element = typeof element == 'string' ? + document.getElementById(element) : + element; + + // If the target is an svg element, use that element as the main wrapper. + // This allows svg.js to work with svg documents as well. + if (element.nodeName == 'svg') { + this.constructor.call(this, element); + } else { + this.constructor.call(this, SVG.create('svg')); + element.appendChild(this.node); + this.size('100%', '100%'); + } + + // set svg element attributes and ensure defs node + this.namespace().defs(); + } + } + + // Inherit from +, inherit: SVG.Container + + // Add class methods +, extend: { + // Add namespaces + namespace: function() { + return this + .attr({ xmlns: SVG.ns, version: '1.1' }) + .attr('xmlns:xlink', SVG.xlink, SVG.xmlns) + .attr('xmlns:svgjs', SVG.svgjs, SVG.xmlns) + } + // Creates and returns defs element + , defs: function() { + if (!this._defs) { + var defs; + + // Find or create a defs element in this instance + if (defs = this.node.getElementsByTagName('defs')[0]) + this._defs = SVG.adopt(defs); + else + this._defs = new SVG.Defs; + + // Make sure the defs node is at the end of the stack + this.node.appendChild(this._defs.node); + } + + return this._defs + } + // custom parent method + , parent: function() { + if(!this.node.parentNode || this.node.parentNode.nodeName == '#document' || this.node.parentNode.nodeName == '#document-fragment') return null + return this.node.parentNode + } + // Fix for possible sub-pixel offset. See: + // https://bugzilla.mozilla.org/show_bug.cgi?id=608812 + , spof: function() { + var pos = this.node.getScreenCTM(); + + if (pos) + this + .style('left', (-pos.e % 1) + 'px') + .style('top', (-pos.f % 1) + 'px'); + + return this + } + + // Removes the doc from the DOM + , remove: function() { + if(this.parent()) { + this.parent().removeChild(this.node); + } + + return this + } + , clear: function() { + // remove children + while(this.node.hasChildNodes()) + this.node.removeChild(this.node.lastChild); + + // remove defs reference + delete this._defs; + + // add back parser + if(!SVG.parser.draw.parentNode) + this.node.appendChild(SVG.parser.draw); + + return this + } + , clone: function (parent) { + // write dom data to the dom so the clone can pickup the data + this.writeDataToDom(); + + // get reference to node + var node = this.node; + + // clone element and assign new id + var clone = assignNewId(node.cloneNode(true)); + + // insert the clone in the given parent or after myself + if(parent) { + (parent.node || parent).appendChild(clone.node); + } else { + node.parentNode.insertBefore(clone.node, node.nextSibling); + } + + return clone + } + } + +}); + +// ### This module adds backward / forward functionality to elements. + +// +SVG.extend(SVG.Element, { + // Get all siblings, including myself + siblings: function() { + return this.parent().children() + } + // Get the curent position siblings +, position: function() { + return this.parent().index(this) + } + // Get the next element (will return null if there is none) +, next: function() { + return this.siblings()[this.position() + 1] + } + // Get the next element (will return null if there is none) +, previous: function() { + return this.siblings()[this.position() - 1] + } + // Send given element one step forward +, forward: function() { + var i = this.position() + 1 + , p = this.parent(); + + // move node one step forward + p.removeElement(this).add(this, i); + + // make sure defs node is always at the top + if (p instanceof SVG.Doc) + p.node.appendChild(p.defs().node); + + return this + } + // Send given element one step backward +, backward: function() { + var i = this.position(); + + if (i > 0) + this.parent().removeElement(this).add(this, i - 1); + + return this + } + // Send given element all the way to the front +, front: function() { + var p = this.parent(); + + // Move node forward + p.node.appendChild(this.node); + + // Make sure defs node is always at the top + if (p instanceof SVG.Doc) + p.node.appendChild(p.defs().node); + + return this + } + // Send given element all the way to the back +, back: function() { + if (this.position() > 0) + this.parent().removeElement(this).add(this, 0); + + return this + } + // Inserts a given element before the targeted element +, before: function(element) { + element.remove(); + + var i = this.position(); + + this.parent().add(element, i); + + return this + } + // Insters a given element after the targeted element +, after: function(element) { + element.remove(); + + var i = this.position(); + + this.parent().add(element, i + 1); + + return this + } + +}); +SVG.Mask = SVG.invent({ + // Initialize node + create: function() { + this.constructor.call(this, SVG.create('mask')); + + // keep references to masked elements + this.targets = []; + } + + // Inherit from +, inherit: SVG.Container + + // Add class methods +, extend: { + // Unmask all masked elements and remove itself + remove: function() { + // unmask all targets + for (var i = this.targets.length - 1; i >= 0; i--) + if (this.targets[i]) + this.targets[i].unmask(); + this.targets = []; + + // remove mask from parent + SVG.Element.prototype.remove.call(this); + + return this + } + } + + // Add parent method +, construct: { + // Create masking element + mask: function() { + return this.defs().put(new SVG.Mask) + } + } +}); + + +SVG.extend(SVG.Element, { + // Distribute mask to svg element + maskWith: function(element) { + // use given mask or create a new one + this.masker = element instanceof SVG.Mask ? element : this.parent().mask().add(element); + + // store reverence on self in mask + this.masker.targets.push(this); + + // apply mask + return this.attr('mask', 'url("#' + this.masker.attr('id') + '")') + } + // Unmask element +, unmask: function() { + delete this.masker; + return this.attr('mask', null) + } + +}); + +SVG.ClipPath = SVG.invent({ + // Initialize node + create: function() { + this.constructor.call(this, SVG.create('clipPath')); + + // keep references to clipped elements + this.targets = []; + } + + // Inherit from +, inherit: SVG.Container + + // Add class methods +, extend: { + // Unclip all clipped elements and remove itself + remove: function() { + // unclip all targets + for (var i = this.targets.length - 1; i >= 0; i--) + if (this.targets[i]) + this.targets[i].unclip(); + this.targets = []; + + // remove clipPath from parent + this.parent().removeElement(this); + + return this + } + } + + // Add parent method +, construct: { + // Create clipping element + clip: function() { + return this.defs().put(new SVG.ClipPath) + } + } +}); + +// +SVG.extend(SVG.Element, { + // Distribute clipPath to svg element + clipWith: function(element) { + // use given clip or create a new one + this.clipper = element instanceof SVG.ClipPath ? element : this.parent().clip().add(element); + + // store reverence on self in mask + this.clipper.targets.push(this); + + // apply mask + return this.attr('clip-path', 'url("#' + this.clipper.attr('id') + '")') + } + // Unclip element +, unclip: function() { + delete this.clipper; + return this.attr('clip-path', null) + } + +}); +SVG.Gradient = SVG.invent({ + // Initialize node + create: function(type) { + this.constructor.call(this, SVG.create(type + 'Gradient')); + + // store type + this.type = type; + } + + // Inherit from +, inherit: SVG.Container + + // Add class methods +, extend: { + // Add a color stop + at: function(offset, color, opacity) { + return this.put(new SVG.Stop).update(offset, color, opacity) + } + // Update gradient + , update: function(block) { + // remove all stops + this.clear(); + + // invoke passed block + if (typeof block == 'function') + block.call(this, this); + + return this + } + // Return the fill id + , fill: function() { + return 'url(#' + this.id() + ')' + } + // Alias string convertion to fill + , toString: function() { + return this.fill() + } + // custom attr to handle transform + , attr: function(a, b, c) { + if(a == 'transform') a = 'gradientTransform'; + return SVG.Container.prototype.attr.call(this, a, b, c) + } + } + + // Add parent method +, construct: { + // Create gradient element in defs + gradient: function(type, block) { + return this.defs().gradient(type, block) + } + } +}); + +// Add animatable methods to both gradient and fx module +SVG.extend(SVG.Gradient, SVG.FX, { + // From position + from: function(x, y) { + return (this._target || this).type == 'radial' ? + this.attr({ fx: new SVG.Number(x), fy: new SVG.Number(y) }) : + this.attr({ x1: new SVG.Number(x), y1: new SVG.Number(y) }) + } + // To position +, to: function(x, y) { + return (this._target || this).type == 'radial' ? + this.attr({ cx: new SVG.Number(x), cy: new SVG.Number(y) }) : + this.attr({ x2: new SVG.Number(x), y2: new SVG.Number(y) }) + } +}); + +// Base gradient generation +SVG.extend(SVG.Defs, { + // define gradient + gradient: function(type, block) { + return this.put(new SVG.Gradient(type)).update(block) + } + +}); + +SVG.Stop = SVG.invent({ + // Initialize node + create: 'stop' + + // Inherit from +, inherit: SVG.Element + + // Add class methods +, extend: { + // add color stops + update: function(o) { + if (typeof o == 'number' || o instanceof SVG.Number) { + o = { + offset: arguments[0] + , color: arguments[1] + , opacity: arguments[2] + }; + } + + // set attributes + if (o.opacity != null) this.attr('stop-opacity', o.opacity); + if (o.color != null) this.attr('stop-color', o.color); + if (o.offset != null) this.attr('offset', new SVG.Number(o.offset)); + + return this + } + } + +}); + +SVG.Pattern = SVG.invent({ + // Initialize node + create: 'pattern' + + // Inherit from +, inherit: SVG.Container + + // Add class methods +, extend: { + // Return the fill id + fill: function() { + return 'url(#' + this.id() + ')' + } + // Update pattern by rebuilding + , update: function(block) { + // remove content + this.clear(); + + // invoke passed block + if (typeof block == 'function') + block.call(this, this); + + return this + } + // Alias string convertion to fill + , toString: function() { + return this.fill() + } + // custom attr to handle transform + , attr: function(a, b, c) { + if(a == 'transform') a = 'patternTransform'; + return SVG.Container.prototype.attr.call(this, a, b, c) + } + + } + + // Add parent method +, construct: { + // Create pattern element in defs + pattern: function(width, height, block) { + return this.defs().pattern(width, height, block) + } + } +}); + +SVG.extend(SVG.Defs, { + // Define gradient + pattern: function(width, height, block) { + return this.put(new SVG.Pattern).update(block).attr({ + x: 0 + , y: 0 + , width: width + , height: height + , patternUnits: 'userSpaceOnUse' + }) + } + +}); +SVG.Shape = SVG.invent({ + // Initialize node + create: function(element) { + this.constructor.call(this, element); + } + + // Inherit from +, inherit: SVG.Element + +}); + +SVG.Bare = SVG.invent({ + // Initialize + create: function(element, inherit) { + // construct element + this.constructor.call(this, SVG.create(element)); + + // inherit custom methods + if (inherit) + for (var method in inherit.prototype) + if (typeof inherit.prototype[method] === 'function') + this[method] = inherit.prototype[method]; + } + + // Inherit from +, inherit: SVG.Element + + // Add methods +, extend: { + // Insert some plain text + words: function(text) { + // remove contents + while (this.node.hasChildNodes()) + this.node.removeChild(this.node.lastChild); + + // create text node + this.node.appendChild(document.createTextNode(text)); + + return this + } + } +}); + + +SVG.extend(SVG.Parent, { + // Create an element that is not described by SVG.js + element: function(element, inherit) { + return this.put(new SVG.Bare(element, inherit)) + } +}); + +SVG.Symbol = SVG.invent({ + // Initialize node + create: 'symbol' + + // Inherit from +, inherit: SVG.Container + +, construct: { + // create symbol + symbol: function() { + return this.put(new SVG.Symbol) + } + } +}); + +SVG.Use = SVG.invent({ + // Initialize node + create: 'use' + + // Inherit from +, inherit: SVG.Shape + + // Add class methods +, extend: { + // Use element as a reference + element: function(element, file) { + // Set lined element + return this.attr('href', (file || '') + '#' + element, SVG.xlink) + } + } + + // Add parent method +, construct: { + // Create a use element + use: function(element, file) { + return this.put(new SVG.Use).element(element, file) + } + } +}); +SVG.Rect = SVG.invent({ + // Initialize node + create: 'rect' + + // Inherit from +, inherit: SVG.Shape + + // Add parent method +, construct: { + // Create a rect element + rect: function(width, height) { + return this.put(new SVG.Rect()).size(width, height) + } + } +}); +SVG.Circle = SVG.invent({ + // Initialize node + create: 'circle' + + // Inherit from +, inherit: SVG.Shape + + // Add parent method +, construct: { + // Create circle element, based on ellipse + circle: function(size) { + return this.put(new SVG.Circle).rx(new SVG.Number(size).divide(2)).move(0, 0) + } + } +}); + +SVG.extend(SVG.Circle, SVG.FX, { + // Radius x value + rx: function(rx) { + return this.attr('r', rx) + } + // Alias radius x value +, ry: function(ry) { + return this.rx(ry) + } +}); + +SVG.Ellipse = SVG.invent({ + // Initialize node + create: 'ellipse' + + // Inherit from +, inherit: SVG.Shape + + // Add parent method +, construct: { + // Create an ellipse + ellipse: function(width, height) { + return this.put(new SVG.Ellipse).size(width, height).move(0, 0) + } + } +}); + +SVG.extend(SVG.Ellipse, SVG.Rect, SVG.FX, { + // Radius x value + rx: function(rx) { + return this.attr('rx', rx) + } + // Radius y value +, ry: function(ry) { + return this.attr('ry', ry) + } +}); + +// Add common method +SVG.extend(SVG.Circle, SVG.Ellipse, { + // Move over x-axis + x: function(x) { + return x == null ? this.cx() - this.rx() : this.cx(x + this.rx()) + } + // Move over y-axis + , y: function(y) { + return y == null ? this.cy() - this.ry() : this.cy(y + this.ry()) + } + // Move by center over x-axis + , cx: function(x) { + return x == null ? this.attr('cx') : this.attr('cx', x) + } + // Move by center over y-axis + , cy: function(y) { + return y == null ? this.attr('cy') : this.attr('cy', y) + } + // Set width of element + , width: function(width) { + return width == null ? this.rx() * 2 : this.rx(new SVG.Number(width).divide(2)) + } + // Set height of element + , height: function(height) { + return height == null ? this.ry() * 2 : this.ry(new SVG.Number(height).divide(2)) + } + // Custom size function + , size: function(width, height) { + var p = proportionalSize(this, width, height); + + return this + .rx(new SVG.Number(p.width).divide(2)) + .ry(new SVG.Number(p.height).divide(2)) + } +}); +SVG.Line = SVG.invent({ + // Initialize node + create: 'line' + + // Inherit from +, inherit: SVG.Shape + + // Add class methods +, extend: { + // Get array + array: function() { + return new SVG.PointArray([ + [ this.attr('x1'), this.attr('y1') ] + , [ this.attr('x2'), this.attr('y2') ] + ]) + } + // Overwrite native plot() method + , plot: function(x1, y1, x2, y2) { + if (x1 == null) + return this.array() + else if (typeof y1 !== 'undefined') + x1 = { x1: x1, y1: y1, x2: x2, y2: y2 }; + else + x1 = new SVG.PointArray(x1).toLine(); + + return this.attr(x1) + } + // Move by left top corner + , move: function(x, y) { + return this.attr(this.array().move(x, y).toLine()) + } + // Set element size to given width and height + , size: function(width, height) { + var p = proportionalSize(this, width, height); + + return this.attr(this.array().size(p.width, p.height).toLine()) + } + } + + // Add parent method +, construct: { + // Create a line element + line: function(x1, y1, x2, y2) { + // make sure plot is called as a setter + // x1 is not necessarily a number, it can also be an array, a string and a SVG.PointArray + return SVG.Line.prototype.plot.apply( + this.put(new SVG.Line) + , x1 != null ? [x1, y1, x2, y2] : [0, 0, 0, 0] + ) + } + } +}); + +SVG.Polyline = SVG.invent({ + // Initialize node + create: 'polyline' + + // Inherit from +, inherit: SVG.Shape + + // Add parent method +, construct: { + // Create a wrapped polyline element + polyline: function(p) { + // make sure plot is called as a setter + return this.put(new SVG.Polyline).plot(p || new SVG.PointArray) + } + } +}); + +SVG.Polygon = SVG.invent({ + // Initialize node + create: 'polygon' + + // Inherit from +, inherit: SVG.Shape + + // Add parent method +, construct: { + // Create a wrapped polygon element + polygon: function(p) { + // make sure plot is called as a setter + return this.put(new SVG.Polygon).plot(p || new SVG.PointArray) + } + } +}); + +// Add polygon-specific functions +SVG.extend(SVG.Polyline, SVG.Polygon, { + // Get array + array: function() { + return this._array || (this._array = new SVG.PointArray(this.attr('points'))) + } + // Plot new path +, plot: function(p) { + return (p == null) ? + this.array() : + this.clear().attr('points', typeof p == 'string' ? p : (this._array = new SVG.PointArray(p))) + } + // Clear array cache +, clear: function() { + delete this._array; + return this + } + // Move by left top corner +, move: function(x, y) { + return this.attr('points', this.array().move(x, y)) + } + // Set element size to given width and height +, size: function(width, height) { + var p = proportionalSize(this, width, height); + + return this.attr('points', this.array().size(p.width, p.height)) + } + +}); + +// unify all point to point elements +SVG.extend(SVG.Line, SVG.Polyline, SVG.Polygon, { + // Define morphable array + morphArray: SVG.PointArray + // Move by left top corner over x-axis +, x: function(x) { + return x == null ? this.bbox().x : this.move(x, this.bbox().y) + } + // Move by left top corner over y-axis +, y: function(y) { + return y == null ? this.bbox().y : this.move(this.bbox().x, y) + } + // Set width of element +, width: function(width) { + var b = this.bbox(); + + return width == null ? b.width : this.size(width, b.height) + } + // Set height of element +, height: function(height) { + var b = this.bbox(); + + return height == null ? b.height : this.size(b.width, height) + } +}); +SVG.Path = SVG.invent({ + // Initialize node + create: 'path' + + // Inherit from +, inherit: SVG.Shape + + // Add class methods +, extend: { + // Define morphable array + morphArray: SVG.PathArray + // Get array + , array: function() { + return this._array || (this._array = new SVG.PathArray(this.attr('d'))) + } + // Plot new path + , plot: function(d) { + return (d == null) ? + this.array() : + this.clear().attr('d', typeof d == 'string' ? d : (this._array = new SVG.PathArray(d))) + } + // Clear array cache + , clear: function() { + delete this._array; + return this + } + // Move by left top corner + , move: function(x, y) { + return this.attr('d', this.array().move(x, y)) + } + // Move by left top corner over x-axis + , x: function(x) { + return x == null ? this.bbox().x : this.move(x, this.bbox().y) + } + // Move by left top corner over y-axis + , y: function(y) { + return y == null ? this.bbox().y : this.move(this.bbox().x, y) + } + // Set element size to given width and height + , size: function(width, height) { + var p = proportionalSize(this, width, height); + + return this.attr('d', this.array().size(p.width, p.height)) + } + // Set width of element + , width: function(width) { + return width == null ? this.bbox().width : this.size(width, this.bbox().height) + } + // Set height of element + , height: function(height) { + return height == null ? this.bbox().height : this.size(this.bbox().width, height) + } + + } + + // Add parent method +, construct: { + // Create a wrapped path element + path: function(d) { + // make sure plot is called as a setter + return this.put(new SVG.Path).plot(d || new SVG.PathArray) + } + } +}); + +SVG.Image = SVG.invent({ + // Initialize node + create: 'image' + + // Inherit from +, inherit: SVG.Shape + + // Add class methods +, extend: { + // (re)load image + load: function(url) { + if (!url) return this + + var self = this + , img = new window.Image(); + + // preload image + SVG.on(img, 'load', function() { + SVG.off(img); + + var p = self.parent(SVG.Pattern); + + if(p === null) return + + // ensure image size + if (self.width() == 0 && self.height() == 0) + self.size(img.width, img.height); + + // ensure pattern size if not set + if (p && p.width() == 0 && p.height() == 0) + p.size(self.width(), self.height()); + + // callback + if (typeof self._loaded === 'function') + self._loaded.call(self, { + width: img.width + , height: img.height + , ratio: img.width / img.height + , url: url + }); + }); + + SVG.on(img, 'error', function(e){ + SVG.off(img); + + if (typeof self._error === 'function'){ + self._error.call(self, e); + } + }); + + return this.attr('href', (img.src = this.src = url), SVG.xlink) + } + // Add loaded callback + , loaded: function(loaded) { + this._loaded = loaded; + return this + } + + , error: function(error) { + this._error = error; + return this + } + } + + // Add parent method +, construct: { + // create image element, load image and set its size + image: function(source, width, height) { + return this.put(new SVG.Image).load(source).size(width || 0, height || width || 0) + } + } + +}); +SVG.Text = SVG.invent({ + // Initialize node + create: function() { + this.constructor.call(this, SVG.create('text')); + + this.dom.leading = new SVG.Number(1.3); // store leading value for rebuilding + this._rebuild = true; // enable automatic updating of dy values + this._build = false; // disable build mode for adding multiple lines + + // set default font + this.attr('font-family', SVG.defaults.attrs['font-family']); + } + + // Inherit from +, inherit: SVG.Shape + + // Add class methods +, extend: { + // Move over x-axis + x: function(x) { + // act as getter + if (x == null) + return this.attr('x') + + return this.attr('x', x) + } + // Move over y-axis + , y: function(y) { + var oy = this.attr('y') + , o = typeof oy === 'number' ? oy - this.bbox().y : 0; + + // act as getter + if (y == null) + return typeof oy === 'number' ? oy - o : oy + + return this.attr('y', typeof y.valueOf() === 'number' ? y + o : y) + } + // Move center over x-axis + , cx: function(x) { + return x == null ? this.bbox().cx : this.x(x - this.bbox().width / 2) + } + // Move center over y-axis + , cy: function(y) { + return y == null ? this.bbox().cy : this.y(y - this.bbox().height / 2) + } + // Set the text content + , text: function(text) { + // act as getter + if (typeof text === 'undefined'){ + var text = ''; + var children = this.node.childNodes; + for(var i = 0, len = children.length; i < len; ++i){ + + // add newline if its not the first child and newLined is set to true + if(i != 0 && children[i].nodeType != 3 && SVG.adopt(children[i]).dom.newLined == true){ + text += '\n'; + } + + // add content of this node + text += children[i].textContent; + } + + return text + } + + // remove existing content + this.clear().build(true); + + if (typeof text === 'function') { + // call block + text.call(this, this); + + } else { + // store text and make sure text is not blank + text = text.split('\n'); + + // build new lines + for (var i = 0, il = text.length; i < il; i++) + this.tspan(text[i]).newLine(); + } + + // disable build mode and rebuild lines + return this.build(false).rebuild() + } + // Set font size + , size: function(size) { + return this.attr('font-size', size).rebuild() + } + // Set / get leading + , leading: function(value) { + // act as getter + if (value == null) + return this.dom.leading + + // act as setter + this.dom.leading = new SVG.Number(value); + + return this.rebuild() + } + // Get all the first level lines + , lines: function() { + var node = (this.textPath && this.textPath() || this).node; + + // filter tspans and map them to SVG.js instances + var lines = SVG.utils.map(SVG.utils.filterSVGElements(node.childNodes), function(el){ + return SVG.adopt(el) + }); + + // return an instance of SVG.set + return new SVG.Set(lines) + } + // Rebuild appearance type + , rebuild: function(rebuild) { + // store new rebuild flag if given + if (typeof rebuild == 'boolean') + this._rebuild = rebuild; + + // define position of all lines + if (this._rebuild) { + var self = this + , blankLineOffset = 0 + , dy = this.dom.leading * new SVG.Number(this.attr('font-size')); + + this.lines().each(function() { + if (this.dom.newLined) { + if (!self.textPath()) + this.attr('x', self.attr('x')); + if(this.text() == '\n') { + blankLineOffset += dy; + }else { + this.attr('dy', dy + blankLineOffset); + blankLineOffset = 0; + } + } + }); + + this.fire('rebuild'); + } + + return this + } + // Enable / disable build mode + , build: function(build) { + this._build = !!build; + return this + } + // overwrite method from parent to set data properly + , setData: function(o){ + this.dom = o; + this.dom.leading = new SVG.Number(o.leading || 1.3); + return this + } + } + + // Add parent method +, construct: { + // Create text element + text: function(text) { + return this.put(new SVG.Text).text(text) + } + // Create plain text element + , plain: function(text) { + return this.put(new SVG.Text).plain(text) + } + } + +}); + +SVG.Tspan = SVG.invent({ + // Initialize node + create: 'tspan' + + // Inherit from +, inherit: SVG.Shape + + // Add class methods +, extend: { + // Set text content + text: function(text) { + if(text == null) return this.node.textContent + (this.dom.newLined ? '\n' : '') + + typeof text === 'function' ? text.call(this, this) : this.plain(text); + + return this + } + // Shortcut dx + , dx: function(dx) { + return this.attr('dx', dx) + } + // Shortcut dy + , dy: function(dy) { + return this.attr('dy', dy) + } + // Create new line + , newLine: function() { + // fetch text parent + var t = this.parent(SVG.Text); + + // mark new line + this.dom.newLined = true; + + // apply new hy¡n + return this.dy(t.dom.leading * t.attr('font-size')).attr('x', t.x()) + } + } + +}); + +SVG.extend(SVG.Text, SVG.Tspan, { + // Create plain text node + plain: function(text) { + // clear if build mode is disabled + if (this._build === false) + this.clear(); + + // create text node + this.node.appendChild(document.createTextNode(text)); + + return this + } + // Create a tspan +, tspan: function(text) { + var node = (this.textPath && this.textPath() || this).node + , tspan = new SVG.Tspan; + + // clear if build mode is disabled + if (this._build === false) + this.clear(); + + // add new tspan + node.appendChild(tspan.node); + + return tspan.text(text) + } + // Clear all lines +, clear: function() { + var node = (this.textPath && this.textPath() || this).node; + + // remove existing child nodes + while (node.hasChildNodes()) + node.removeChild(node.lastChild); + + return this + } + // Get length of text element +, length: function() { + return this.node.getComputedTextLength() + } +}); + +SVG.TextPath = SVG.invent({ + // Initialize node + create: 'textPath' + + // Inherit from +, inherit: SVG.Parent + + // Define parent class +, parent: SVG.Text + + // Add parent method +, construct: { + morphArray: SVG.PathArray + // Create path for text to run on + , path: function(d) { + // create textPath element + var path = new SVG.TextPath + , track = this.doc().defs().path(d); + + // move lines to textpath + while (this.node.hasChildNodes()) + path.node.appendChild(this.node.firstChild); + + // add textPath element as child node + this.node.appendChild(path.node); + + // link textPath to path and add content + path.attr('href', '#' + track, SVG.xlink); + + return this + } + // return the array of the path track element + , array: function() { + var track = this.track(); + + return track ? track.array() : null + } + // Plot path if any + , plot: function(d) { + var track = this.track() + , pathArray = null; + + if (track) { + pathArray = track.plot(d); + } + + return (d == null) ? pathArray : this + } + // Get the path track element + , track: function() { + var path = this.textPath(); + + if (path) + return path.reference('href') + } + // Get the textPath child + , textPath: function() { + if (this.node.firstChild && this.node.firstChild.nodeName == 'textPath') + return SVG.adopt(this.node.firstChild) + } + } +}); + +SVG.Nested = SVG.invent({ + // Initialize node + create: function() { + this.constructor.call(this, SVG.create('svg')); + + this.style('overflow', 'visible'); + } + + // Inherit from +, inherit: SVG.Container + + // Add parent method +, construct: { + // Create nested svg document + nested: function() { + return this.put(new SVG.Nested) + } + } +}); +SVG.A = SVG.invent({ + // Initialize node + create: 'a' + + // Inherit from +, inherit: SVG.Container + + // Add class methods +, extend: { + // Link url + to: function(url) { + return this.attr('href', url, SVG.xlink) + } + // Link show attribute + , show: function(target) { + return this.attr('show', target, SVG.xlink) + } + // Link target attribute + , target: function(target) { + return this.attr('target', target) + } + } + + // Add parent method +, construct: { + // Create a hyperlink element + link: function(url) { + return this.put(new SVG.A).to(url) + } + } +}); + +SVG.extend(SVG.Element, { + // Create a hyperlink element + linkTo: function(url) { + var link = new SVG.A; + + if (typeof url == 'function') + url.call(link, link); + else + link.to(url); + + return this.parent().put(link).put(this) + } + +}); +SVG.Marker = SVG.invent({ + // Initialize node + create: 'marker' + + // Inherit from +, inherit: SVG.Container + + // Add class methods +, extend: { + // Set width of element + width: function(width) { + return this.attr('markerWidth', width) + } + // Set height of element + , height: function(height) { + return this.attr('markerHeight', height) + } + // Set marker refX and refY + , ref: function(x, y) { + return this.attr('refX', x).attr('refY', y) + } + // Update marker + , update: function(block) { + // remove all content + this.clear(); + + // invoke passed block + if (typeof block == 'function') + block.call(this, this); + + return this + } + // Return the fill id + , toString: function() { + return 'url(#' + this.id() + ')' + } + } + + // Add parent method +, construct: { + marker: function(width, height, block) { + // Create marker element in defs + return this.defs().marker(width, height, block) + } + } + +}); + +SVG.extend(SVG.Defs, { + // Create marker + marker: function(width, height, block) { + // Set default viewbox to match the width and height, set ref to cx and cy and set orient to auto + return this.put(new SVG.Marker) + .size(width, height) + .ref(width / 2, height / 2) + .viewbox(0, 0, width, height) + .attr('orient', 'auto') + .update(block) + } + +}); + +SVG.extend(SVG.Line, SVG.Polyline, SVG.Polygon, SVG.Path, { + // Create and attach markers + marker: function(marker, width, height, block) { + var attr = ['marker']; + + // Build attribute name + if (marker != 'all') attr.push(marker); + attr = attr.join('-'); + + // Set marker attribute + marker = arguments[1] instanceof SVG.Marker ? + arguments[1] : + this.doc().marker(width, height, block); + + return this.attr(attr, marker) + } + +}); +// Define list of available attributes for stroke and fill +var sugar = { + stroke: ['color', 'width', 'opacity', 'linecap', 'linejoin', 'miterlimit', 'dasharray', 'dashoffset'] +, fill: ['color', 'opacity', 'rule'] +, prefix: function(t, a) { + return a == 'color' ? t : t + '-' + a + } +} + +// Add sugar for fill and stroke +;['fill', 'stroke'].forEach(function(m) { + var i, extension = {}; + + extension[m] = function(o) { + if (typeof o == 'undefined') + return this + if (typeof o == 'string' || SVG.Color.isRgb(o) || (o && typeof o.fill === 'function')) + this.attr(m, o); + + else + // set all attributes from sugar.fill and sugar.stroke list + for (i = sugar[m].length - 1; i >= 0; i--) + if (o[sugar[m][i]] != null) + this.attr(sugar.prefix(m, sugar[m][i]), o[sugar[m][i]]); + + return this + }; + + SVG.extend(SVG.Element, SVG.FX, extension); + +}); + +SVG.extend(SVG.Element, SVG.FX, { + // Map rotation to transform + rotate: function(d, cx, cy) { + return this.transform({ rotation: d, cx: cx, cy: cy }) + } + // Map skew to transform +, skew: function(x, y, cx, cy) { + return arguments.length == 1 || arguments.length == 3 ? + this.transform({ skew: x, cx: y, cy: cx }) : + this.transform({ skewX: x, skewY: y, cx: cx, cy: cy }) + } + // Map scale to transform +, scale: function(x, y, cx, cy) { + return arguments.length == 1 || arguments.length == 3 ? + this.transform({ scale: x, cx: y, cy: cx }) : + this.transform({ scaleX: x, scaleY: y, cx: cx, cy: cy }) + } + // Map translate to transform +, translate: function(x, y) { + return this.transform({ x: x, y: y }) + } + // Map flip to transform +, flip: function(a, o) { + o = typeof a == 'number' ? a : o; + return this.transform({ flip: a || 'both', offset: o }) + } + // Map matrix to transform +, matrix: function(m) { + return this.attr('transform', new SVG.Matrix(arguments.length == 6 ? [].slice.call(arguments) : m)) + } + // Opacity +, opacity: function(value) { + return this.attr('opacity', value) + } + // Relative move over x axis +, dx: function(x) { + return this.x(new SVG.Number(x).plus(this instanceof SVG.FX ? 0 : this.x()), true) + } + // Relative move over y axis +, dy: function(y) { + return this.y(new SVG.Number(y).plus(this instanceof SVG.FX ? 0 : this.y()), true) + } + // Relative move over x and y axes +, dmove: function(x, y) { + return this.dx(x).dy(y) + } +}); + +SVG.extend(SVG.Rect, SVG.Ellipse, SVG.Circle, SVG.Gradient, SVG.FX, { + // Add x and y radius + radius: function(x, y) { + var type = (this._target || this).type; + return type == 'radial' || type == 'circle' ? + this.attr('r', new SVG.Number(x)) : + this.rx(x).ry(y == null ? x : y) + } +}); + +SVG.extend(SVG.Path, { + // Get path length + length: function() { + return this.node.getTotalLength() + } + // Get point at length +, pointAt: function(length) { + return this.node.getPointAtLength(length) + } +}); + +SVG.extend(SVG.Parent, SVG.Text, SVG.Tspan, SVG.FX, { + // Set font + font: function(a, v) { + if (typeof a == 'object') { + for (v in a) this.font(v, a[v]); + } + + return a == 'leading' ? + this.leading(v) : + a == 'anchor' ? + this.attr('text-anchor', v) : + a == 'size' || a == 'family' || a == 'weight' || a == 'stretch' || a == 'variant' || a == 'style' ? + this.attr('font-'+ a, v) : + this.attr(a, v) + } +}); + +SVG.Set = SVG.invent({ + // Initialize + create: function(members) { + if (members instanceof SVG.Set) { + this.members = members.members.slice(); + } else { + Array.isArray(members) ? this.members = members : this.clear(); + } + } + + // Add class methods +, extend: { + // Add element to set + add: function() { + var i, il, elements = [].slice.call(arguments); + + for (i = 0, il = elements.length; i < il; i++) + this.members.push(elements[i]); + + return this + } + // Remove element from set + , remove: function(element) { + var i = this.index(element); + + // remove given child + if (i > -1) + this.members.splice(i, 1); + + return this + } + // Iterate over all members + , each: function(block) { + for (var i = 0, il = this.members.length; i < il; i++) + block.apply(this.members[i], [i, this.members]); + + return this + } + // Restore to defaults + , clear: function() { + // initialize store + this.members = []; + + return this + } + // Get the length of a set + , length: function() { + return this.members.length + } + // Checks if a given element is present in set + , has: function(element) { + return this.index(element) >= 0 + } + // retuns index of given element in set + , index: function(element) { + return this.members.indexOf(element) + } + // Get member at given index + , get: function(i) { + return this.members[i] + } + // Get first member + , first: function() { + return this.get(0) + } + // Get last member + , last: function() { + return this.get(this.members.length - 1) + } + // Default value + , valueOf: function() { + return this.members + } + // Get the bounding box of all members included or empty box if set has no items + , bbox: function(){ + // return an empty box of there are no members + if (this.members.length == 0) + return new SVG.RBox() + + // get the first rbox and update the target bbox + var rbox = this.members[0].rbox(this.members[0].doc()); + + this.each(function() { + // user rbox for correct position and visual representation + rbox = rbox.merge(this.rbox(this.doc())); + }); + + return rbox + } + } + + // Add parent method +, construct: { + // Create a new set + set: function(members) { + return new SVG.Set(members) + } + } +}); + +SVG.FX.Set = SVG.invent({ + // Initialize node + create: function(set) { + // store reference to set + this.set = set; + } + +}); + +// Alias methods +SVG.Set.inherit = function() { + var m + , methods = []; + + // gather shape methods + for(var m in SVG.Shape.prototype) + if (typeof SVG.Shape.prototype[m] == 'function' && typeof SVG.Set.prototype[m] != 'function') + methods.push(m); + + // apply shape aliasses + methods.forEach(function(method) { + SVG.Set.prototype[method] = function() { + for (var i = 0, il = this.members.length; i < il; i++) + if (this.members[i] && typeof this.members[i][method] == 'function') + this.members[i][method].apply(this.members[i], arguments); + + return method == 'animate' ? (this.fx || (this.fx = new SVG.FX.Set(this))) : this + }; + }); + + // clear methods for the next round + methods = []; + + // gather fx methods + for(var m in SVG.FX.prototype) + if (typeof SVG.FX.prototype[m] == 'function' && typeof SVG.FX.Set.prototype[m] != 'function') + methods.push(m); + + // apply fx aliasses + methods.forEach(function(method) { + SVG.FX.Set.prototype[method] = function() { + for (var i = 0, il = this.set.members.length; i < il; i++) + this.set.members[i].fx[method].apply(this.set.members[i].fx, arguments); + + return this + }; + }); +}; + + +SVG.extend(SVG.Element, { + // Store data values on svg nodes + data: function(a, v, r) { + if (typeof a == 'object') { + for (v in a) + this.data(v, a[v]); + + } else if (arguments.length < 2) { + try { + return JSON.parse(this.attr('data-' + a)) + } catch(e) { + return this.attr('data-' + a) + } + + } else { + this.attr( + 'data-' + a + , v === null ? + null : + r === true || typeof v === 'string' || typeof v === 'number' ? + v : + JSON.stringify(v) + ); + } + + return this + } +}); +SVG.extend(SVG.Element, { + // Remember arbitrary data + remember: function(k, v) { + // remember every item in an object individually + if (typeof arguments[0] == 'object') + for (var v in k) + this.remember(v, k[v]); + + // retrieve memory + else if (arguments.length == 1) + return this.memory()[k] + + // store memory + else + this.memory()[k] = v; + + return this + } + + // Erase a given memory +, forget: function() { + if (arguments.length == 0) + this._memory = {}; + else + for (var i = arguments.length - 1; i >= 0; i--) + delete this.memory()[arguments[i]]; + + return this + } + + // Initialize or return local memory object +, memory: function() { + return this._memory || (this._memory = {}) + } + +}); +// Method for getting an element by id +SVG.get = function(id) { + var node = document.getElementById(idFromReference(id) || id); + return SVG.adopt(node) +}; + +// Select elements by query string +SVG.select = function(query, parent) { + return new SVG.Set( + SVG.utils.map((parent || document).querySelectorAll(query), function(node) { + return SVG.adopt(node) + }) + ) +}; + +SVG.extend(SVG.Parent, { + // Scoped select method + select: function(query) { + return SVG.select(query, this.node) + } + +}); +function pathRegReplace(a, b, c, d) { + return c + d.replace(SVG.regex.dots, ' .') +} + +// creates deep clone of array +function array_clone(arr){ + var clone = arr.slice(0); + for(var i = clone.length; i--;){ + if(Array.isArray(clone[i])){ + clone[i] = array_clone(clone[i]); + } + } + return clone +} + +// tests if a given element is instance of an object +function is(el, obj){ + return el instanceof obj +} + +// tests if a given selector matches an element +function matches(el, selector) { + return (el.matches || el.matchesSelector || el.msMatchesSelector || el.mozMatchesSelector || el.webkitMatchesSelector || el.oMatchesSelector).call(el, selector); +} + +// Convert dash-separated-string to camelCase +function camelCase(s) { + return s.toLowerCase().replace(/-(.)/g, function(m, g) { + return g.toUpperCase() + }) +} + +// Capitalize first letter of a string +function capitalize(s) { + return s.charAt(0).toUpperCase() + s.slice(1) +} + +// Ensure to six-based hex +function fullHex(hex) { + return hex.length == 4 ? + [ '#', + hex.substring(1, 2), hex.substring(1, 2) + , hex.substring(2, 3), hex.substring(2, 3) + , hex.substring(3, 4), hex.substring(3, 4) + ].join('') : hex +} + +// Component to hex value +function compToHex(comp) { + var hex = comp.toString(16); + return hex.length == 1 ? '0' + hex : hex +} + +// Calculate proportional width and height values when necessary +function proportionalSize(element, width, height) { + if (width == null || height == null) { + var box = element.bbox(); + + if (width == null) + width = box.width / box.height * height; + else if (height == null) + height = box.height / box.width * width; + } + + return { + width: width + , height: height + } +} + +// Delta transform point +function deltaTransformPoint(matrix, x, y) { + return { + x: x * matrix.a + y * matrix.c + 0 + , y: x * matrix.b + y * matrix.d + 0 + } +} + +// Map matrix array to object +function arrayToMatrix(a) { + return { a: a[0], b: a[1], c: a[2], d: a[3], e: a[4], f: a[5] } +} + +// Parse matrix if required +function parseMatrix(matrix) { + if (!(matrix instanceof SVG.Matrix)) + matrix = new SVG.Matrix(matrix); + + return matrix +} + +// Add centre point to transform object +function ensureCentre(o, target) { + o.cx = o.cx == null ? target.bbox().cx : o.cx; + o.cy = o.cy == null ? target.bbox().cy : o.cy; +} + +// PathArray Helpers +function arrayToString(a) { + for (var i = 0, il = a.length, s = ''; i < il; i++) { + s += a[i][0]; + + if (a[i][1] != null) { + s += a[i][1]; + + if (a[i][2] != null) { + s += ' '; + s += a[i][2]; + + if (a[i][3] != null) { + s += ' '; + s += a[i][3]; + s += ' '; + s += a[i][4]; + + if (a[i][5] != null) { + s += ' '; + s += a[i][5]; + s += ' '; + s += a[i][6]; + + if (a[i][7] != null) { + s += ' '; + s += a[i][7]; + } + } + } + } + } + } + + return s + ' ' +} + +// Deep new id assignment +function assignNewId(node) { + // do the same for SVG child nodes as well + for (var i = node.childNodes.length - 1; i >= 0; i--) + if (node.childNodes[i] instanceof window.SVGElement) + assignNewId(node.childNodes[i]); + + return SVG.adopt(node).id(SVG.eid(node.nodeName)) +} + +// Add more bounding box properties +function fullBox(b) { + if (b.x == null) { + b.x = 0; + b.y = 0; + b.width = 0; + b.height = 0; + } + + b.w = b.width; + b.h = b.height; + b.x2 = b.x + b.width; + b.y2 = b.y + b.height; + b.cx = b.x + b.width / 2; + b.cy = b.y + b.height / 2; + + return b +} + +// Get id from reference string +function idFromReference(url) { + var m = (url || '').toString().match(SVG.regex.reference); + + if (m) return m[1] +} + +// If values like 1e-88 are passed, this is not a valid 32 bit float, +// but in those cases, we are so close to 0 that 0 works well! +function float32String(v) { + return Math.abs(v) > 1e-37 ? v : 0 +} + +// Create matrix array for looping +var abcdef = 'abcdef'.split(''); + +// Add CustomEvent to IE9 and IE10 +if (typeof window.CustomEvent !== 'function') { + // Code from: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent + var CustomEventPoly = function(event, options) { + options = options || { bubbles: false, cancelable: false, detail: undefined }; + var e = document.createEvent('CustomEvent'); + e.initCustomEvent(event, options.bubbles, options.cancelable, options.detail); + return e + }; + + CustomEventPoly.prototype = window.Event.prototype; + + SVG.CustomEvent = CustomEventPoly; +} else { + SVG.CustomEvent = window.CustomEvent; +} + +// requestAnimationFrame / cancelAnimationFrame Polyfill with fallback based on Paul Irish +(function(w) { + var lastTime = 0; + var vendors = ['moz', 'webkit']; + + for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { + w.requestAnimationFrame = w[vendors[x] + 'RequestAnimationFrame']; + w.cancelAnimationFrame = w[vendors[x] + 'CancelAnimationFrame'] || + w[vendors[x] + 'CancelRequestAnimationFrame']; + } + + w.requestAnimationFrame = w.requestAnimationFrame || + function(callback) { + var currTime = new Date().getTime(); + var timeToCall = Math.max(0, 16 - (currTime - lastTime)); + + var id = w.setTimeout(function() { + callback(currTime + timeToCall); + }, timeToCall); + + lastTime = currTime + timeToCall; + return id + }; + + w.cancelAnimationFrame = w.cancelAnimationFrame || w.clearTimeout; + +}(window)); + +return SVG + +})); +}); + +class History { + constructor(limit) { + this.limit = 50; + this.undos = []; + this.redos = []; + this.saveCommand = null; + this.limit = limit || 50; + } + execute(command) { + this.clearRedo(); + command.execute(); + var length = this.undos.length; + if (length >= this.limit) { + this.undos.shift(); + } + this.undos.push(command); + this.change(command); + } + undo() { + if (this.canUndo()) { + var command = this.undos.pop(); + this.redos.push(command); + command.undo(); + this.change(command); + } + } + canUndo() { + return !!this.undos.length; + } + redo() { + if (this.canRedo()) { + var command = this.redos.pop(); + this.undos.push(command); + command.redo(); + this.change(command); + } + } + canRedo() { + return !!this.redos.length; + } + save() { + this.saveCommand = this.undos[this.undos.length - 1]; + } + dirty() { + return this.saveCommand != this.undos[this.undos.length - 1]; + } + clearRedo() { + this.redos = []; + } + clear() { + this.undos = []; + this.redos = []; + this.saveCommand = null; + this.change(); + } + change(cmd) { + } +} + +class Command { + constructor(name) { + this.name = name; + } + execute() { } + undo() { } + redo() { + this.execute(); + } + refresh(mind) { + var m = mind || this.mind; + if (m) { + m.emit('renderEditNode', {}); + m.emit('mindMapChange', {}); + } + } +} +class AddNode extends Command { + constructor(node, parent, mind) { + super('addNode'); + this.parent = null; + this.mind = null; + this.index = -1; + this.node = node; + this.parent = parent; + this.mind = mind || this.node.mindmap; + } + execute() { + if (this.index > -1) { + this.mind.addNode(this.node, this.parent, this.index); //add node to position of parent children + } + else { + this.mind.addNode(this.node, this.parent); + } + this.node.refreshBox(); + this.refresh(); + this.mind.clearSelectNode(); + setTimeout(() => { + this.node.select(); + this.node.edit(); + }, 0); + } + undo() { + var p = this.node.parent; + this.index = this.mind.removeNode(this.node); + this.mind.clearSelectNode(); + setTimeout(() => { + this.refresh(); + p && p.select(); + }, 0); + } +} +class RemoveNode extends Command { + constructor(node, mind) { + super('removeNode'); + this.parent = null; + this.mind = null; + this.index = -1; + this.node = node; + this.parent = this.node.parent || null; + this.mind = mind || this.node.mindmap; + } + execute() { + this.node.clearCacheData(); + this.mind.clearSelectNode(); + this.index = this.mind.removeNode(this.node); + this.refresh(); + this.parent && this.parent.select(); + } + undo() { + this.mind.addNode(this.node, this.parent, this.index); + this.node.clearCacheData(); + this.node.refreshBox(); + this.mind.clearSelectNode(); + this.refresh(); + setTimeout(() => { + this.node.select(); + }, 0); + } +} +class ChangeNodeText extends Command { + constructor(node, oldText, text) { + super('changeNodeText'); + this.node = node; + this.oldText = oldText; + this.text = text; + this.isFirst = true; + } + execute() { + if (!this.isFirst) { + this.node.setText(this.text); + } + this.node.refreshBox(); + this.node.clearCacheData(); + this.refresh(this.node.mindmap); + } + undo() { + this.node.setText(this.oldText); + this.node.clearCacheData(); + this.node.refreshBox(); + this.refresh(this.node.mindmap); + this.isFirst = false; + } +} +class MoveNode extends Command { + constructor(data) { + super('moveNode'); + this.data = {}; + this.index = -1; + this.data = data; + if (this.data.type.indexOf('child') > -1) { + this.node = this.data.node; + this.oldParent = this.data.oldParent; + this.parent = this.data.parent; + } + else { + this.node = this.data.node; + this.oldParent = this.node.parent; + this.dropNode = this.data.dropNode; + this.newParent = this.dropNode.parent; + this.type = this.data.direct; + } + } + execute() { + this.node.mindmap.clearSelectNode(); + if (this.data.type.indexOf('child') > -1) { + if (this.oldParent) { + this.index = this.oldParent.removeChild(this.node); + } + this.parent.addChild(this.node); + this.node.mindmap.traverseBF((n) => { + n.boundingRect = null; + n.stroke = ''; + }, this.node); + this.node.clearCacheData(); + this.oldParent.clearCacheData(); + this.refresh(this.node.mindmap); + this.node.select(); + } + else { + if (this.oldParent) { + this.index = this.oldParent.removeChild(this.node); + } + this.node.mindmap.traverseBF((n) => { + n.boundingRect = null; + n.stroke = ''; + }, this.node); + this.oldParent.clearCacheData(); + var dropNodeIndex = this.newParent.children.indexOf(this.dropNode); + if (this.type == 'top' || this.type == 'left') { + this.newParent.addChild(this.node, dropNodeIndex); + } + else { + this.newParent.addChild(this.node, dropNodeIndex + 1); + } + this.node.clearCacheData(); + this.refresh(this.node.mindmap); + this.node.select(); + } + } + undo() { + this.node.mindmap.clearSelectNode(); + if (this.data.type.indexOf('child') > -1) { + this.parent.removeChild(this.node); + if (this.oldParent) { + this.oldParent.addChild(this.node, this.index); + } + this.node.mindmap.traverseBF((n) => { + n.boundingRect = null; + n.stroke = ''; + }, this.node); + this.parent.clearCacheData(); + this.node.clearCacheData(); + this.refresh(this.node.mindmap); + this.node.select(); + } + else { + this.newParent.removeChild(this.node); + this.dropNode.clearCacheData(); + this.oldParent.addChild(this.node, this.index); + this.node.clearCacheData(); + this.refresh(this.node.mindmap); + this.node.select(); + } + } +} +class MovePos extends Command { + constructor(node, oldPos, newPos) { + super('movePos'); + this.node = node; + this.oldPos = oldPos; + this.newPos = newPos; + } + execute() { + this.node.setPosition(this.newPos.x, this.newPos.y); + this.refresh(this.node.mindmap); + } + undo() { + this.node.setPosition(this.oldPos.x, this.oldPos.y); + this.refresh(this.node.mindmap); + } +} +class CollapseNode extends Command { + constructor(node) { + super('collapseNOde'); + this.node = node; + this.node.mindmap.clearSelectNode(); + this.node.refreshBox(); + } + execute() { + this.node.clearCacheData(); + this.node.collapse(); + this.refresh(this.node.mindmap); + this.node.select(); + } + undo() { + this.node.clearCacheData(); + this.node.expand(); + this.refresh(this.node.mindmap); + this.node.select(); + } +} +class ExpandNode extends Command { + constructor(node) { + super('collapseNOde'); + this.node = node; + this.node.mindmap.clearSelectNode(); + this.node.refreshBox(); + } + execute() { + this.node.clearCacheData(); + this.node.expand(); + this.refresh(this.node.mindmap); + this.node.select(); + } + undo() { + this.node.clearCacheData(); + this.node.collapse(); + this.refresh(this.node.mindmap); + this.node.select(); + } +} +class PasteNode extends Command { + constructor(node, data) { + super('copyNode'); + this.waitCollapse = []; + this.node = node; + this.data = data; + this.mind = this.node.mindmap; + this.waitCollapse = []; + } + execute() { + this.paste(); + } + undo() { + if (this.firstNode) { + this.mind.removeNode(this.firstNode); + this.node.clearCacheData(); + // this.updateItems(this.node); + this.refresh(this.node.mindmap); + } + } + paste() { + this.data.forEach((d, i) => { + var n = new Node(d, this.mind); + n.mindmap = this.mind; + if (!d.isExpand) { + this.waitCollapse.push(n); + } + if (i == 0) { + n.data.pid = this.node.getId(); + this.mind.addNode(n, this.node); + this.firstNode = n; + n.setPosition(0, 0); + n.refreshBox(); + } + else { + var parent = this.mind.getNodeById(d.pid); + if (parent) { + this.mind.addNode(n, parent); + n.setPosition(0, 0); + n.refreshBox(); + } + } + if (i == this.data.length - 1) { + n.clearCacheData(); + this.refresh(this.mind); + } + }); + } +} + +class Exec { + constructor() { + this.history = new History(50); + } + execute(name, data) { + switch (name) { + case 'addChildNode': + case 'addSiblingNode': + if (data) { + var d = { + id: uuid(), + text: data.text || t('Sub title') + }; + var parent = data.parent; + var node = new Node(d, parent.mindmap); + this.history.execute(new AddNode(node, data.parent, parent.mindmap)); + } + break; + case 'deleteNodeAndChild': + if (data) { + var node = data.node; + this.history.execute(new RemoveNode(node, node.mindmap)); + } + break; + case 'deleteNodeExcludeChild': + break; + case 'changeNodeText': + if (data) { + this.history.execute(new ChangeNodeText(data.node, data.oldText, data.text)); + } + break; + case 'moveNode': + if (data) { + this.history.execute(new MoveNode(data)); + } + break; + case 'movePosition': + if (data) { + this.history.execute(new MovePos(data.node, data.oldPos, data.newPos)); + } + break; + case 'expandNode': + if (data) { + this.history.execute(new ExpandNode(data.node)); + } + break; + case 'collapseNode': + if (data) { + this.history.execute(new CollapseNode(data.node)); + } + break; + case 'pasteNode': + this.history.execute(new PasteNode(data.node, data.data)); + break; + } + } + undo() { + this.history.undo(); + } + redo() { + this.history.redo(); + } +} + +var mind = {}; +var color = ['#fda16c', '#74bdf7', '#71FF5E', 'orange', '#D4D4AA', 'yellow']; +var canvasWidth = 8000; +var canvasHeight = 8000; +const importXmind = function (data) { + mind = { + "theme": 'blue', + "mindData": [], + "induceData": [], + "wireFrameData": [], + "relateLink": [], + "background": '', + "relateLinkData": [], + "calloutData": [], + 'marks': [], + }; + var mainlist = []; + transferData(data.rootTopic, null, mainlist, true); + var root = mainlist[0]; + data.rootTopic.children && data.rootTopic.children.detached && data.rootTopic.children.detached.forEach(d => { + var list = []; + transferData(d, root.id, list); + mainlist = mainlist.concat(list); + }); + mind.mindData.push(mainlist); + mind.basicData = transferListToData(mainlist); + data.relationships && data.relationships.forEach(rl => { + var obj = { + startNodeId: rl.end1Id, + endNodeId: rl.end2Id, + nodeData: { + 'text': '', + 'nodeType': 'relateLink', + 'backgroundColor': '#f06' + } + }; + if (rl.title) { + obj.nodeData.text = rl.title; + } + mind.relateLinkData.push(obj); + }); + return mind; +}; +var task = { + 'task-start': '0', + 'task-oct': '10', + 'task-3oct': '30', + 'task-5oct': '50', + 'task-7oct': '70', + 'task-9oct': '90', + 'task-done': '100', +}; +function transferListToData(list) { + var obj = {}; + var root = null; + function transfer(data, i) { + var nodeData = { + id: data.id, + text: data.text, + children: [], + expanded: true + }; + if (i == 0) { + nodeData.main = true; + nodeData.isRoot = true; + root = nodeData; + } + return nodeData; + } + list.forEach((n, i) => { + var data = transfer(n, i); + if (!obj[n.id]) { + obj[n.id] = data; + } + if (obj[n.pid]) { + obj[n.pid].children.push(data); + } + }); + return root; +} +function transferData(data, parentId, list, mainFlag) { + var text = ''; + if (data.title) { + text = data.title; + } + var node = { + id: data.id, + pid: parentId, + text: text, + // percent:data.data.progress<=8?data.data.progress*10:'', + // priority:data.data.priority, + //link:data.href, + note: '', + //tag:data.data.resource + marks: [], + // resource:data.data.resource, + expanded: true, + image: '', + imageName: '', + x: 0, + y: 0 + }; + if (!parentId) { + node.layout = {}; + node.layout.layoutName = "mindmap"; + node.layout.direct = "right"; + if (mainFlag) { + node.isRoot = true; + node.main = true; + node.x = canvasWidth / 2; + node.y = canvasHeight / 2; + } + } + if (data.href) { + node.link = data.href; + } + if (data.notes) { + node.note = data.notes.plain.content.trim(); + } + // if(data.image){ + // node.isImageNode=true; + // node.image=''; + // node.imageName=data.image.src.replace('xap:resources/',''); + // } + if (data.labels) { + node.marks = []; + data.labels.forEach((lab) => { + node.marks.push({ + id: uuid(), + text: lab, + fill: color[parseInt(Math.random() * color.length + '')] + }); + }); + } + if (data.markers) { + data.markers.forEach((item) => { + if (item.markerId.indexOf('priority') > -1) { + node.priority = item.markerId.split('-')[1]; + } + if (item.markerId.indexOf('task') > -1) { + node.percent = task[item.markerId]; + } + }); + } + list.push(node); + data.children && data.children.attached && data.children.attached.forEach(c => { + transferData(c, data.id, list); + }); + //induce + data.summaries && data.summaries.forEach((sum) => { + var r = sum.range.substring(1, sum.range.length - 1); + // console.log(r,'induce'); + var s = r.split(',')[0]; + var e = r.split(',')[1]; + var induceData = { + induceData: { + nodeId: sum.topicId, + range: r, + id: sum.id, + } + }; + data.children.attached.forEach((c, i) => { + if (i == s) { + induceData.induceData.nodeId = c.id; + } + if (i == e) { + induceData.induceData.endNodeId = c.id; + } + }); + var mData = []; + var summary = data.children.summary; + var sumData = summary.filter((item) => { + return item.id == sum.topicId; + })[0]; + transferData(sumData, null, mData); + mData[0].nodeType = 'induce'; + induceData.mindData = mData; + mind.induceData.push(induceData); + }); + //wireframe + data.boundaries && data.boundaries.forEach(bum => { + var r = bum.range.substring(1, bum.range.length - 1); + var s = r.split(',')[0]; + var e = r.split(',')[1]; + var wf = { + stroke: "rgb(206, 214, 218)", + fill: "transparent", + lineDash: [6, 2], + radius: 10, + data: { + text: '', + nodeType: 'wireFrame' + } + }; + if (bum.title) { + wf.data = { + text: bum.title, + nodeType: 'wireFrame' + }; + } + data.children.attached.forEach((c, i) => { + if (i == s) { + wf.nodeId = c.id; + } + if (i == e) { + wf.endNodeId = c.id; + } + }); + wf.range = r; + mind.wireFrameData.push(wf); + }); + //collout + data.children && data.children.callout && data.children.callout.forEach(c => { + var callout = { + nodeId: data.id, + color: '#f06', + rootData: { + text: c.title, + id: c.id + } + }; + mind.calloutData.push(callout); + }); +} + +/*! + +JSZip v3.10.1 - A JavaScript class for generating and reading zip files + + +(c) 2009-2016 Stuart Knightley +Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip/main/LICENSE.markdown. + +JSZip uses the library pako released under the MIT license : +https://github.com/nodeca/pako/blob/main/LICENSE +*/ + +var jszip_min = createCommonjsModule(function (module, exports) { +!function(e){module.exports=e();}(function(){return function s(a,o,h){function u(r,e){if(!o[r]){if(!a[r]){var t="function"==typeof commonjsRequire&&commonjsRequire;if(!e&&t)return t(r,!0);if(l)return l(r,!0);var n=new Error("Cannot find module '"+r+"'");throw n.code="MODULE_NOT_FOUND",n}var i=o[r]={exports:{}};a[r][0].call(i.exports,function(e){var t=a[r][1][e];return u(t||e)},i,i.exports,s,a,o,h);}return o[r].exports}for(var l="function"==typeof commonjsRequire&&commonjsRequire,e=0;e>2,s=(3&t)<<4|r>>4,a=1>6:64,o=2>4,r=(15&i)<<4|(s=p.indexOf(e.charAt(o++)))>>2,n=(3&s)<<6|(a=p.indexOf(e.charAt(o++))),l[h++]=t,64!==s&&(l[h++]=r),64!==a&&(l[h++]=n);return l};},{"./support":30,"./utils":32}],2:[function(e,t,r){var n=e("./external"),i=e("./stream/DataWorker"),s=e("./stream/Crc32Probe"),a=e("./stream/DataLengthProbe");function o(e,t,r,n,i){this.compressedSize=e,this.uncompressedSize=t,this.crc32=r,this.compression=n,this.compressedContent=i;}o.prototype={getContentWorker:function(){var e=new i(n.Promise.resolve(this.compressedContent)).pipe(this.compression.uncompressWorker()).pipe(new a("data_length")),t=this;return e.on("end",function(){if(this.streamInfo.data_length!==t.uncompressedSize)throw new Error("Bug : uncompressed data size mismatch")}),e},getCompressedWorker:function(){return new i(n.Promise.resolve(this.compressedContent)).withStreamInfo("compressedSize",this.compressedSize).withStreamInfo("uncompressedSize",this.uncompressedSize).withStreamInfo("crc32",this.crc32).withStreamInfo("compression",this.compression)}},o.createWorkerFrom=function(e,t,r){return e.pipe(new s).pipe(new a("uncompressedSize")).pipe(t.compressWorker(r)).pipe(new a("compressedSize")).withStreamInfo("compression",t)},t.exports=o;},{"./external":6,"./stream/Crc32Probe":25,"./stream/DataLengthProbe":26,"./stream/DataWorker":27}],3:[function(e,t,r){var n=e("./stream/GenericWorker");r.STORE={magic:"\0\0",compressWorker:function(){return new n("STORE compression")},uncompressWorker:function(){return new n("STORE decompression")}},r.DEFLATE=e("./flate");},{"./flate":7,"./stream/GenericWorker":28}],4:[function(e,t,r){var n=e("./utils");var o=function(){for(var e,t=[],r=0;r<256;r++){e=r;for(var n=0;n<8;n++)e=1&e?3988292384^e>>>1:e>>>1;t[r]=e;}return t}();t.exports=function(e,t){return void 0!==e&&e.length?"string"!==n.getTypeOf(e)?function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a>>8^i[255&(e^t[a])];return -1^e}(0|t,e,e.length,0):function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a>>8^i[255&(e^t.charCodeAt(a))];return -1^e}(0|t,e,e.length,0):0};},{"./utils":32}],5:[function(e,t,r){r.base64=!1,r.binary=!1,r.dir=!1,r.createFolders=!0,r.date=null,r.compression=null,r.compressionOptions=null,r.comment=null,r.unixPermissions=null,r.dosPermissions=null;},{}],6:[function(e,t,r){var n=null;n="undefined"!=typeof Promise?Promise:e("lie"),t.exports={Promise:n};},{lie:37}],7:[function(e,t,r){var n="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Uint32Array,i=e("pako"),s=e("./utils"),a=e("./stream/GenericWorker"),o=n?"uint8array":"array";function h(e,t){a.call(this,"FlateWorker/"+e),this._pako=null,this._pakoAction=e,this._pakoOptions=t,this.meta={};}r.magic="\b\0",s.inherits(h,a),h.prototype.processChunk=function(e){this.meta=e.meta,null===this._pako&&this._createPako(),this._pako.push(s.transformTo(o,e.data),!1);},h.prototype.flush=function(){a.prototype.flush.call(this),null===this._pako&&this._createPako(),this._pako.push([],!0);},h.prototype.cleanUp=function(){a.prototype.cleanUp.call(this),this._pako=null;},h.prototype._createPako=function(){this._pako=new i[this._pakoAction]({raw:!0,level:this._pakoOptions.level||-1});var t=this;this._pako.onData=function(e){t.push({data:e,meta:t.meta});};},r.compressWorker=function(e){return new h("Deflate",e)},r.uncompressWorker=function(){return new h("Inflate",{})};},{"./stream/GenericWorker":28,"./utils":32,pako:38}],8:[function(e,t,r){function A(e,t){var r,n="";for(r=0;r>>=8;return n}function n(e,t,r,n,i,s){var a,o,h=e.file,u=e.compression,l=s!==O.utf8encode,f=I.transformTo("string",s(h.name)),c=I.transformTo("string",O.utf8encode(h.name)),d=h.comment,p=I.transformTo("string",s(d)),m=I.transformTo("string",O.utf8encode(d)),_=c.length!==h.name.length,g=m.length!==d.length,b="",v="",y="",w=h.dir,k=h.date,x={crc32:0,compressedSize:0,uncompressedSize:0};t&&!r||(x.crc32=e.crc32,x.compressedSize=e.compressedSize,x.uncompressedSize=e.uncompressedSize);var S=0;t&&(S|=8),l||!_&&!g||(S|=2048);var z=0,C=0;w&&(z|=16),"UNIX"===i?(C=798,z|=function(e,t){var r=e;return e||(r=t?16893:33204),(65535&r)<<16}(h.unixPermissions,w)):(C=20,z|=function(e){return 63&(e||0)}(h.dosPermissions)),a=k.getUTCHours(),a<<=6,a|=k.getUTCMinutes(),a<<=5,a|=k.getUTCSeconds()/2,o=k.getUTCFullYear()-1980,o<<=4,o|=k.getUTCMonth()+1,o<<=5,o|=k.getUTCDate(),_&&(v=A(1,1)+A(B(f),4)+c,b+="up"+A(v.length,2)+v),g&&(y=A(1,1)+A(B(p),4)+m,b+="uc"+A(y.length,2)+y);var E="";return E+="\n\0",E+=A(S,2),E+=u.magic,E+=A(a,2),E+=A(o,2),E+=A(x.crc32,4),E+=A(x.compressedSize,4),E+=A(x.uncompressedSize,4),E+=A(f.length,2),E+=A(b.length,2),{fileRecord:R.LOCAL_FILE_HEADER+E+f+b,dirRecord:R.CENTRAL_FILE_HEADER+A(C,2)+E+A(p.length,2)+"\0\0\0\0"+A(z,4)+A(n,4)+f+b+p}}var I=e("../utils"),i=e("../stream/GenericWorker"),O=e("../utf8"),B=e("../crc32"),R=e("../signature");function s(e,t,r,n){i.call(this,"ZipFileWorker"),this.bytesWritten=0,this.zipComment=t,this.zipPlatform=r,this.encodeFileName=n,this.streamFiles=e,this.accumulate=!1,this.contentBuffer=[],this.dirRecords=[],this.currentSourceOffset=0,this.entriesCount=0,this.currentFile=null,this._sources=[];}I.inherits(s,i),s.prototype.push=function(e){var t=e.meta.percent||0,r=this.entriesCount,n=this._sources.length;this.accumulate?this.contentBuffer.push(e):(this.bytesWritten+=e.data.length,i.prototype.push.call(this,{data:e.data,meta:{currentFile:this.currentFile,percent:r?(t+100*(r-n-1))/r:100}}));},s.prototype.openedSource=function(e){this.currentSourceOffset=this.bytesWritten,this.currentFile=e.file.name;var t=this.streamFiles&&!e.file.dir;if(t){var r=n(e,t,!1,this.currentSourceOffset,this.zipPlatform,this.encodeFileName);this.push({data:r.fileRecord,meta:{percent:0}});}else this.accumulate=!0;},s.prototype.closedSource=function(e){this.accumulate=!1;var t=this.streamFiles&&!e.file.dir,r=n(e,t,!0,this.currentSourceOffset,this.zipPlatform,this.encodeFileName);if(this.dirRecords.push(r.dirRecord),t)this.push({data:function(e){return R.DATA_DESCRIPTOR+A(e.crc32,4)+A(e.compressedSize,4)+A(e.uncompressedSize,4)}(e),meta:{percent:100}});else for(this.push({data:r.fileRecord,meta:{percent:0}});this.contentBuffer.length;)this.push(this.contentBuffer.shift());this.currentFile=null;},s.prototype.flush=function(){for(var e=this.bytesWritten,t=0;t=this.index;t--)r=(r<<8)+this.byteAt(t);return this.index+=e,r},readString:function(e){return n.transformTo("string",this.readData(e))},readData:function(){},lastIndexOfSignature:function(){},readAndCheckSignature:function(){},readDate:function(){var e=this.readInt(4);return new Date(Date.UTC(1980+(e>>25&127),(e>>21&15)-1,e>>16&31,e>>11&31,e>>5&63,(31&e)<<1))}},t.exports=i;},{"../utils":32}],19:[function(e,t,r){var n=e("./Uint8ArrayReader");function i(e){n.call(this,e);}e("../utils").inherits(i,n),i.prototype.readData=function(e){this.checkOffset(e);var t=this.data.slice(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i;},{"../utils":32,"./Uint8ArrayReader":21}],20:[function(e,t,r){var n=e("./DataReader");function i(e){n.call(this,e);}e("../utils").inherits(i,n),i.prototype.byteAt=function(e){return this.data.charCodeAt(this.zero+e)},i.prototype.lastIndexOfSignature=function(e){return this.data.lastIndexOf(e)-this.zero},i.prototype.readAndCheckSignature=function(e){return e===this.readData(4)},i.prototype.readData=function(e){this.checkOffset(e);var t=this.data.slice(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i;},{"../utils":32,"./DataReader":18}],21:[function(e,t,r){var n=e("./ArrayReader");function i(e){n.call(this,e);}e("../utils").inherits(i,n),i.prototype.readData=function(e){if(this.checkOffset(e),0===e)return new Uint8Array(0);var t=this.data.subarray(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i;},{"../utils":32,"./ArrayReader":17}],22:[function(e,t,r){var n=e("../utils"),i=e("../support"),s=e("./ArrayReader"),a=e("./StringReader"),o=e("./NodeBufferReader"),h=e("./Uint8ArrayReader");t.exports=function(e){var t=n.getTypeOf(e);return n.checkSupport(t),"string"!==t||i.uint8array?"nodebuffer"===t?new o(e):i.uint8array?new h(n.transformTo("uint8array",e)):new s(n.transformTo("array",e)):new a(e)};},{"../support":30,"../utils":32,"./ArrayReader":17,"./NodeBufferReader":19,"./StringReader":20,"./Uint8ArrayReader":21}],23:[function(e,t,r){r.LOCAL_FILE_HEADER="PK",r.CENTRAL_FILE_HEADER="PK",r.CENTRAL_DIRECTORY_END="PK",r.ZIP64_CENTRAL_DIRECTORY_LOCATOR="PK",r.ZIP64_CENTRAL_DIRECTORY_END="PK",r.DATA_DESCRIPTOR="PK\b";},{}],24:[function(e,t,r){var n=e("./GenericWorker"),i=e("../utils");function s(e){n.call(this,"ConvertWorker to "+e),this.destType=e;}i.inherits(s,n),s.prototype.processChunk=function(e){this.push({data:i.transformTo(this.destType,e.data),meta:e.meta});},t.exports=s;},{"../utils":32,"./GenericWorker":28}],25:[function(e,t,r){var n=e("./GenericWorker"),i=e("../crc32");function s(){n.call(this,"Crc32Probe"),this.withStreamInfo("crc32",0);}e("../utils").inherits(s,n),s.prototype.processChunk=function(e){this.streamInfo.crc32=i(e.data,this.streamInfo.crc32||0),this.push(e);},t.exports=s;},{"../crc32":4,"../utils":32,"./GenericWorker":28}],26:[function(e,t,r){var n=e("../utils"),i=e("./GenericWorker");function s(e){i.call(this,"DataLengthProbe for "+e),this.propName=e,this.withStreamInfo(e,0);}n.inherits(s,i),s.prototype.processChunk=function(e){if(e){var t=this.streamInfo[this.propName]||0;this.streamInfo[this.propName]=t+e.data.length;}i.prototype.processChunk.call(this,e);},t.exports=s;},{"../utils":32,"./GenericWorker":28}],27:[function(e,t,r){var n=e("../utils"),i=e("./GenericWorker");function s(e){i.call(this,"DataWorker");var t=this;this.dataIsReady=!1,this.index=0,this.max=0,this.data=null,this.type="",this._tickScheduled=!1,e.then(function(e){t.dataIsReady=!0,t.data=e,t.max=e&&e.length||0,t.type=n.getTypeOf(e),t.isPaused||t._tickAndRepeat();},function(e){t.error(e);});}n.inherits(s,i),s.prototype.cleanUp=function(){i.prototype.cleanUp.call(this),this.data=null;},s.prototype.resume=function(){return !!i.prototype.resume.call(this)&&(!this._tickScheduled&&this.dataIsReady&&(this._tickScheduled=!0,n.delay(this._tickAndRepeat,[],this)),!0)},s.prototype._tickAndRepeat=function(){this._tickScheduled=!1,this.isPaused||this.isFinished||(this._tick(),this.isFinished||(n.delay(this._tickAndRepeat,[],this),this._tickScheduled=!0));},s.prototype._tick=function(){if(this.isPaused||this.isFinished)return !1;var e=null,t=Math.min(this.max,this.index+16384);if(this.index>=this.max)return this.end();switch(this.type){case"string":e=this.data.substring(this.index,t);break;case"uint8array":e=this.data.subarray(this.index,t);break;case"array":case"nodebuffer":e=this.data.slice(this.index,t);}return this.index=t,this.push({data:e,meta:{percent:this.max?this.index/this.max*100:0}})},t.exports=s;},{"../utils":32,"./GenericWorker":28}],28:[function(e,t,r){function n(e){this.name=e||"default",this.streamInfo={},this.generatedError=null,this.extraStreamInfo={},this.isPaused=!0,this.isFinished=!1,this.isLocked=!1,this._listeners={data:[],end:[],error:[]},this.previous=null;}n.prototype={push:function(e){this.emit("data",e);},end:function(){if(this.isFinished)return !1;this.flush();try{this.emit("end"),this.cleanUp(),this.isFinished=!0;}catch(e){this.emit("error",e);}return !0},error:function(e){return !this.isFinished&&(this.isPaused?this.generatedError=e:(this.isFinished=!0,this.emit("error",e),this.previous&&this.previous.error(e),this.cleanUp()),!0)},on:function(e,t){return this._listeners[e].push(t),this},cleanUp:function(){this.streamInfo=this.generatedError=this.extraStreamInfo=null,this._listeners=[];},emit:function(e,t){if(this._listeners[e])for(var r=0;r "+e:e}},t.exports=n;},{}],29:[function(e,t,r){var h=e("../utils"),i=e("./ConvertWorker"),s=e("./GenericWorker"),u=e("../base64"),n=e("../support"),a=e("../external"),o=null;if(n.nodestream)try{o=e("../nodejs/NodejsStreamOutputAdapter");}catch(e){}function l(e,o){return new a.Promise(function(t,r){var n=[],i=e._internalType,s=e._outputType,a=e._mimeType;e.on("data",function(e,t){n.push(e),o&&o(t);}).on("error",function(e){n=[],r(e);}).on("end",function(){try{var e=function(e,t,r){switch(e){case"blob":return h.newBlob(h.transformTo("arraybuffer",t),r);case"base64":return u.encode(t);default:return h.transformTo(e,t)}}(s,function(e,t){var r,n=0,i=null,s=0;for(r=0;r>>6:(r<65536?t[s++]=224|r>>>12:(t[s++]=240|r>>>18,t[s++]=128|r>>>12&63),t[s++]=128|r>>>6&63),t[s++]=128|63&r);return t}(e)},s.utf8decode=function(e){return h.nodebuffer?o.transformTo("nodebuffer",e).toString("utf-8"):function(e){var t,r,n,i,s=e.length,a=new Array(2*s);for(t=r=0;t>10&1023,a[r++]=56320|1023&n);}return a.length!==r&&(a.subarray?a=a.subarray(0,r):a.length=r),o.applyFromCharCode(a)}(e=o.transformTo(h.uint8array?"uint8array":"array",e))},o.inherits(a,n),a.prototype.processChunk=function(e){var t=o.transformTo(h.uint8array?"uint8array":"array",e.data);if(this.leftOver&&this.leftOver.length){if(h.uint8array){var r=t;(t=new Uint8Array(r.length+this.leftOver.length)).set(this.leftOver,0),t.set(r,this.leftOver.length);}else t=this.leftOver.concat(t);this.leftOver=null;}var n=function(e,t){var r;for((t=t||e.length)>e.length&&(t=e.length),r=t-1;0<=r&&128==(192&e[r]);)r--;return r<0?t:0===r?t:r+u[e[r]]>t?r:t}(t),i=t;n!==t.length&&(h.uint8array?(i=t.subarray(0,n),this.leftOver=t.subarray(n,t.length)):(i=t.slice(0,n),this.leftOver=t.slice(n,t.length))),this.push({data:s.utf8decode(i),meta:e.meta});},a.prototype.flush=function(){this.leftOver&&this.leftOver.length&&(this.push({data:s.utf8decode(this.leftOver),meta:{}}),this.leftOver=null);},s.Utf8DecodeWorker=a,o.inherits(l,n),l.prototype.processChunk=function(e){this.push({data:s.utf8encode(e.data),meta:e.meta});},s.Utf8EncodeWorker=l;},{"./nodejsUtils":14,"./stream/GenericWorker":28,"./support":30,"./utils":32}],32:[function(e,t,a){var o=e("./support"),h=e("./base64"),r=e("./nodejsUtils"),u=e("./external");function n(e){return e}function l(e,t){for(var r=0;r>8;this.dir=!!(16&this.externalFileAttributes),0==e&&(this.dosPermissions=63&this.externalFileAttributes),3==e&&(this.unixPermissions=this.externalFileAttributes>>16&65535),this.dir||"/"!==this.fileNameStr.slice(-1)||(this.dir=!0);},parseZIP64ExtraField:function(){if(this.extraFields[1]){var e=n(this.extraFields[1].value);this.uncompressedSize===s.MAX_VALUE_32BITS&&(this.uncompressedSize=e.readInt(8)),this.compressedSize===s.MAX_VALUE_32BITS&&(this.compressedSize=e.readInt(8)),this.localHeaderOffset===s.MAX_VALUE_32BITS&&(this.localHeaderOffset=e.readInt(8)),this.diskNumberStart===s.MAX_VALUE_32BITS&&(this.diskNumberStart=e.readInt(4));}},readExtraFields:function(e){var t,r,n,i=e.index+this.extraFieldsLength;for(this.extraFields||(this.extraFields={});e.index+4>>6:(r<65536?t[s++]=224|r>>>12:(t[s++]=240|r>>>18,t[s++]=128|r>>>12&63),t[s++]=128|r>>>6&63),t[s++]=128|63&r);return t},r.buf2binstring=function(e){return l(e,e.length)},r.binstring2buf=function(e){for(var t=new h.Buf8(e.length),r=0,n=t.length;r>10&1023,o[n++]=56320|1023&i);}return l(o,n)},r.utf8border=function(e,t){var r;for((t=t||e.length)>e.length&&(t=e.length),r=t-1;0<=r&&128==(192&e[r]);)r--;return r<0?t:0===r?t:r+u[e[r]]>t?r:t};},{"./common":41}],43:[function(e,t,r){t.exports=function(e,t,r,n){for(var i=65535&e|0,s=e>>>16&65535|0,a=0;0!==r;){for(r-=a=2e3>>1:e>>>1;t[r]=e;}return t}();t.exports=function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a>>8^i[255&(e^t[a])];return -1^e};},{}],46:[function(e,t,r){var h,c=e("../utils/common"),u=e("./trees"),d=e("./adler32"),p=e("./crc32"),n=e("./messages"),l=0,f=4,m=0,_=-2,g=-1,b=4,i=2,v=8,y=9,s=286,a=30,o=19,w=2*s+1,k=15,x=3,S=258,z=S+x+1,C=42,E=113,A=1,I=2,O=3,B=4;function R(e,t){return e.msg=n[t],t}function T(e){return (e<<1)-(4e.avail_out&&(r=e.avail_out),0!==r&&(c.arraySet(e.output,t.pending_buf,t.pending_out,r,e.next_out),e.next_out+=r,t.pending_out+=r,e.total_out+=r,e.avail_out-=r,t.pending-=r,0===t.pending&&(t.pending_out=0));}function N(e,t){u._tr_flush_block(e,0<=e.block_start?e.block_start:-1,e.strstart-e.block_start,t),e.block_start=e.strstart,F(e.strm);}function U(e,t){e.pending_buf[e.pending++]=t;}function P(e,t){e.pending_buf[e.pending++]=t>>>8&255,e.pending_buf[e.pending++]=255&t;}function L(e,t){var r,n,i=e.max_chain_length,s=e.strstart,a=e.prev_length,o=e.nice_match,h=e.strstart>e.w_size-z?e.strstart-(e.w_size-z):0,u=e.window,l=e.w_mask,f=e.prev,c=e.strstart+S,d=u[s+a-1],p=u[s+a];e.prev_length>=e.good_match&&(i>>=2),o>e.lookahead&&(o=e.lookahead);do{if(u[(r=t)+a]===p&&u[r+a-1]===d&&u[r]===u[s]&&u[++r]===u[s+1]){s+=2,r++;do{}while(u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&sh&&0!=--i);return a<=e.lookahead?a:e.lookahead}function j(e){var t,r,n,i,s,a,o,h,u,l,f=e.w_size;do{if(i=e.window_size-e.lookahead-e.strstart,e.strstart>=f+(f-z)){for(c.arraySet(e.window,e.window,f,f,0),e.match_start-=f,e.strstart-=f,e.block_start-=f,t=r=e.hash_size;n=e.head[--t],e.head[t]=f<=n?n-f:0,--r;);for(t=r=f;n=e.prev[--t],e.prev[t]=f<=n?n-f:0,--r;);i+=f;}if(0===e.strm.avail_in)break;if(a=e.strm,o=e.window,h=e.strstart+e.lookahead,u=i,l=void 0,l=a.avail_in,u=x)for(s=e.strstart-e.insert,e.ins_h=e.window[s],e.ins_h=(e.ins_h<=x&&(e.ins_h=(e.ins_h<=x)if(n=u._tr_tally(e,e.strstart-e.match_start,e.match_length-x),e.lookahead-=e.match_length,e.match_length<=e.max_lazy_match&&e.lookahead>=x){for(e.match_length--;e.strstart++,e.ins_h=(e.ins_h<=x&&(e.ins_h=(e.ins_h<=x&&e.match_length<=e.prev_length){for(i=e.strstart+e.lookahead-x,n=u._tr_tally(e,e.strstart-1-e.prev_match,e.prev_length-x),e.lookahead-=e.prev_length-1,e.prev_length-=2;++e.strstart<=i&&(e.ins_h=(e.ins_h<e.pending_buf_size-5&&(r=e.pending_buf_size-5);;){if(e.lookahead<=1){if(j(e),0===e.lookahead&&t===l)return A;if(0===e.lookahead)break}e.strstart+=e.lookahead,e.lookahead=0;var n=e.block_start+r;if((0===e.strstart||e.strstart>=n)&&(e.lookahead=e.strstart-n,e.strstart=n,N(e,!1),0===e.strm.avail_out))return A;if(e.strstart-e.block_start>=e.w_size-z&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=0,t===f?(N(e,!0),0===e.strm.avail_out?O:B):(e.strstart>e.block_start&&(N(e,!1),e.strm.avail_out),A)}),new M(4,4,8,4,Z),new M(4,5,16,8,Z),new M(4,6,32,32,Z),new M(4,4,16,16,W),new M(8,16,32,32,W),new M(8,16,128,128,W),new M(8,32,128,256,W),new M(32,128,258,1024,W),new M(32,258,258,4096,W)],r.deflateInit=function(e,t){return Y(e,t,v,15,8,0)},r.deflateInit2=Y,r.deflateReset=K,r.deflateResetKeep=G,r.deflateSetHeader=function(e,t){return e&&e.state?2!==e.state.wrap?_:(e.state.gzhead=t,m):_},r.deflate=function(e,t){var r,n,i,s;if(!e||!e.state||5>8&255),U(n,n.gzhead.time>>16&255),U(n,n.gzhead.time>>24&255),U(n,9===n.level?2:2<=n.strategy||n.level<2?4:0),U(n,255&n.gzhead.os),n.gzhead.extra&&n.gzhead.extra.length&&(U(n,255&n.gzhead.extra.length),U(n,n.gzhead.extra.length>>8&255)),n.gzhead.hcrc&&(e.adler=p(e.adler,n.pending_buf,n.pending,0)),n.gzindex=0,n.status=69):(U(n,0),U(n,0),U(n,0),U(n,0),U(n,0),U(n,9===n.level?2:2<=n.strategy||n.level<2?4:0),U(n,3),n.status=E);else {var a=v+(n.w_bits-8<<4)<<8;a|=(2<=n.strategy||n.level<2?0:n.level<6?1:6===n.level?2:3)<<6,0!==n.strstart&&(a|=32),a+=31-a%31,n.status=E,P(n,a),0!==n.strstart&&(P(n,e.adler>>>16),P(n,65535&e.adler)),e.adler=1;}if(69===n.status)if(n.gzhead.extra){for(i=n.pending;n.gzindex<(65535&n.gzhead.extra.length)&&(n.pending!==n.pending_buf_size||(n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),F(e),i=n.pending,n.pending!==n.pending_buf_size));)U(n,255&n.gzhead.extra[n.gzindex]),n.gzindex++;n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),n.gzindex===n.gzhead.extra.length&&(n.gzindex=0,n.status=73);}else n.status=73;if(73===n.status)if(n.gzhead.name){i=n.pending;do{if(n.pending===n.pending_buf_size&&(n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),F(e),i=n.pending,n.pending===n.pending_buf_size)){s=1;break}s=n.gzindexi&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),0===s&&(n.gzindex=0,n.status=91);}else n.status=91;if(91===n.status)if(n.gzhead.comment){i=n.pending;do{if(n.pending===n.pending_buf_size&&(n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),F(e),i=n.pending,n.pending===n.pending_buf_size)){s=1;break}s=n.gzindexi&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),0===s&&(n.status=103);}else n.status=103;if(103===n.status&&(n.gzhead.hcrc?(n.pending+2>n.pending_buf_size&&F(e),n.pending+2<=n.pending_buf_size&&(U(n,255&e.adler),U(n,e.adler>>8&255),e.adler=0,n.status=E)):n.status=E),0!==n.pending){if(F(e),0===e.avail_out)return n.last_flush=-1,m}else if(0===e.avail_in&&T(t)<=T(r)&&t!==f)return R(e,-5);if(666===n.status&&0!==e.avail_in)return R(e,-5);if(0!==e.avail_in||0!==n.lookahead||t!==l&&666!==n.status){var o=2===n.strategy?function(e,t){for(var r;;){if(0===e.lookahead&&(j(e),0===e.lookahead)){if(t===l)return A;break}if(e.match_length=0,r=u._tr_tally(e,0,e.window[e.strstart]),e.lookahead--,e.strstart++,r&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=0,t===f?(N(e,!0),0===e.strm.avail_out?O:B):e.last_lit&&(N(e,!1),0===e.strm.avail_out)?A:I}(n,t):3===n.strategy?function(e,t){for(var r,n,i,s,a=e.window;;){if(e.lookahead<=S){if(j(e),e.lookahead<=S&&t===l)return A;if(0===e.lookahead)break}if(e.match_length=0,e.lookahead>=x&&0e.lookahead&&(e.match_length=e.lookahead);}if(e.match_length>=x?(r=u._tr_tally(e,1,e.match_length-x),e.lookahead-=e.match_length,e.strstart+=e.match_length,e.match_length=0):(r=u._tr_tally(e,0,e.window[e.strstart]),e.lookahead--,e.strstart++),r&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=0,t===f?(N(e,!0),0===e.strm.avail_out?O:B):e.last_lit&&(N(e,!1),0===e.strm.avail_out)?A:I}(n,t):h[n.level].func(n,t);if(o!==O&&o!==B||(n.status=666),o===A||o===O)return 0===e.avail_out&&(n.last_flush=-1),m;if(o===I&&(1===t?u._tr_align(n):5!==t&&(u._tr_stored_block(n,0,0,!1),3===t&&(D(n.head),0===n.lookahead&&(n.strstart=0,n.block_start=0,n.insert=0))),F(e),0===e.avail_out))return n.last_flush=-1,m}return t!==f?m:n.wrap<=0?1:(2===n.wrap?(U(n,255&e.adler),U(n,e.adler>>8&255),U(n,e.adler>>16&255),U(n,e.adler>>24&255),U(n,255&e.total_in),U(n,e.total_in>>8&255),U(n,e.total_in>>16&255),U(n,e.total_in>>24&255)):(P(n,e.adler>>>16),P(n,65535&e.adler)),F(e),0=r.w_size&&(0===s&&(D(r.head),r.strstart=0,r.block_start=0,r.insert=0),u=new c.Buf8(r.w_size),c.arraySet(u,t,l-r.w_size,r.w_size,0),t=u,l=r.w_size),a=e.avail_in,o=e.next_in,h=e.input,e.avail_in=l,e.next_in=0,e.input=t,j(r);r.lookahead>=x;){for(n=r.strstart,i=r.lookahead-(x-1);r.ins_h=(r.ins_h<>>=y=v>>>24,p-=y,0===(y=v>>>16&255))C[s++]=65535&v;else {if(!(16&y)){if(0==(64&y)){v=m[(65535&v)+(d&(1<>>=y,p-=y),p<15&&(d+=z[n++]<>>=y=v>>>24,p-=y,!(16&(y=v>>>16&255))){if(0==(64&y)){v=_[(65535&v)+(d&(1<>>=y,p-=y,(y=s-a)>3,d&=(1<<(p-=w<<3))-1,e.next_in=n,e.next_out=s,e.avail_in=n>>24&255)+(e>>>8&65280)+((65280&e)<<8)+((255&e)<<24)}function s(){this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new I.Buf16(320),this.work=new I.Buf16(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0;}function a(e){var t;return e&&e.state?(t=e.state,e.total_in=e.total_out=t.total=0,e.msg="",t.wrap&&(e.adler=1&t.wrap),t.mode=P,t.last=0,t.havedict=0,t.dmax=32768,t.head=null,t.hold=0,t.bits=0,t.lencode=t.lendyn=new I.Buf32(n),t.distcode=t.distdyn=new I.Buf32(i),t.sane=1,t.back=-1,N):U}function o(e){var t;return e&&e.state?((t=e.state).wsize=0,t.whave=0,t.wnext=0,a(e)):U}function h(e,t){var r,n;return e&&e.state?(n=e.state,t<0?(r=0,t=-t):(r=1+(t>>4),t<48&&(t&=15)),t&&(t<8||15=s.wsize?(I.arraySet(s.window,t,r-s.wsize,s.wsize,0),s.wnext=0,s.whave=s.wsize):(n<(i=s.wsize-s.wnext)&&(i=n),I.arraySet(s.window,t,r-n,i,s.wnext),(n-=i)?(I.arraySet(s.window,t,r-n,n,0),s.wnext=n,s.whave=s.wsize):(s.wnext+=i,s.wnext===s.wsize&&(s.wnext=0),s.whave>>8&255,r.check=B(r.check,E,2,0),l=u=0,r.mode=2;break}if(r.flags=0,r.head&&(r.head.done=!1),!(1&r.wrap)||(((255&u)<<8)+(u>>8))%31){e.msg="incorrect header check",r.mode=30;break}if(8!=(15&u)){e.msg="unknown compression method",r.mode=30;break}if(l-=4,k=8+(15&(u>>>=4)),0===r.wbits)r.wbits=k;else if(k>r.wbits){e.msg="invalid window size",r.mode=30;break}r.dmax=1<>8&1),512&r.flags&&(E[0]=255&u,E[1]=u>>>8&255,r.check=B(r.check,E,2,0)),l=u=0,r.mode=3;case 3:for(;l<32;){if(0===o)break e;o--,u+=n[s++]<>>8&255,E[2]=u>>>16&255,E[3]=u>>>24&255,r.check=B(r.check,E,4,0)),l=u=0,r.mode=4;case 4:for(;l<16;){if(0===o)break e;o--,u+=n[s++]<>8),512&r.flags&&(E[0]=255&u,E[1]=u>>>8&255,r.check=B(r.check,E,2,0)),l=u=0,r.mode=5;case 5:if(1024&r.flags){for(;l<16;){if(0===o)break e;o--,u+=n[s++]<>>8&255,r.check=B(r.check,E,2,0)),l=u=0;}else r.head&&(r.head.extra=null);r.mode=6;case 6:if(1024&r.flags&&(o<(d=r.length)&&(d=o),d&&(r.head&&(k=r.head.extra_len-r.length,r.head.extra||(r.head.extra=new Array(r.head.extra_len)),I.arraySet(r.head.extra,n,s,d,k)),512&r.flags&&(r.check=B(r.check,n,d,s)),o-=d,s+=d,r.length-=d),r.length))break e;r.length=0,r.mode=7;case 7:if(2048&r.flags){if(0===o)break e;for(d=0;k=n[s+d++],r.head&&k&&r.length<65536&&(r.head.name+=String.fromCharCode(k)),k&&d>9&1,r.head.done=!0),e.adler=r.check=0,r.mode=12;break;case 10:for(;l<32;){if(0===o)break e;o--,u+=n[s++]<>>=7&l,l-=7&l,r.mode=27;break}for(;l<3;){if(0===o)break e;o--,u+=n[s++]<>>=1)){case 0:r.mode=14;break;case 1:if(j(r),r.mode=20,6!==t)break;u>>>=2,l-=2;break e;case 2:r.mode=17;break;case 3:e.msg="invalid block type",r.mode=30;}u>>>=2,l-=2;break;case 14:for(u>>>=7&l,l-=7&l;l<32;){if(0===o)break e;o--,u+=n[s++]<>>16^65535)){e.msg="invalid stored block lengths",r.mode=30;break}if(r.length=65535&u,l=u=0,r.mode=15,6===t)break e;case 15:r.mode=16;case 16:if(d=r.length){if(o>>=5,l-=5,r.ndist=1+(31&u),u>>>=5,l-=5,r.ncode=4+(15&u),u>>>=4,l-=4,286>>=3,l-=3;}for(;r.have<19;)r.lens[A[r.have++]]=0;if(r.lencode=r.lendyn,r.lenbits=7,S={bits:r.lenbits},x=T(0,r.lens,0,19,r.lencode,0,r.work,S),r.lenbits=S.bits,x){e.msg="invalid code lengths set",r.mode=30;break}r.have=0,r.mode=19;case 19:for(;r.have>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>>=_,l-=_,r.lens[r.have++]=b;else {if(16===b){for(z=_+2;l>>=_,l-=_,0===r.have){e.msg="invalid bit length repeat",r.mode=30;break}k=r.lens[r.have-1],d=3+(3&u),u>>>=2,l-=2;}else if(17===b){for(z=_+3;l>>=_)),u>>>=3,l-=3;}else {for(z=_+7;l>>=_)),u>>>=7,l-=7;}if(r.have+d>r.nlen+r.ndist){e.msg="invalid bit length repeat",r.mode=30;break}for(;d--;)r.lens[r.have++]=k;}}if(30===r.mode)break;if(0===r.lens[256]){e.msg="invalid code -- missing end-of-block",r.mode=30;break}if(r.lenbits=9,S={bits:r.lenbits},x=T(D,r.lens,0,r.nlen,r.lencode,0,r.work,S),r.lenbits=S.bits,x){e.msg="invalid literal/lengths set",r.mode=30;break}if(r.distbits=6,r.distcode=r.distdyn,S={bits:r.distbits},x=T(F,r.lens,r.nlen,r.ndist,r.distcode,0,r.work,S),r.distbits=S.bits,x){e.msg="invalid distances set",r.mode=30;break}if(r.mode=20,6===t)break e;case 20:r.mode=21;case 21:if(6<=o&&258<=h){e.next_out=a,e.avail_out=h,e.next_in=s,e.avail_in=o,r.hold=u,r.bits=l,R(e,c),a=e.next_out,i=e.output,h=e.avail_out,s=e.next_in,n=e.input,o=e.avail_in,u=r.hold,l=r.bits,12===r.mode&&(r.back=-1);break}for(r.back=0;g=(C=r.lencode[u&(1<>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>v)])>>>16&255,b=65535&C,!(v+(_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>>=v,l-=v,r.back+=v;}if(u>>>=_,l-=_,r.back+=_,r.length=b,0===g){r.mode=26;break}if(32&g){r.back=-1,r.mode=12;break}if(64&g){e.msg="invalid literal/length code",r.mode=30;break}r.extra=15&g,r.mode=22;case 22:if(r.extra){for(z=r.extra;l>>=r.extra,l-=r.extra,r.back+=r.extra;}r.was=r.length,r.mode=23;case 23:for(;g=(C=r.distcode[u&(1<>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>v)])>>>16&255,b=65535&C,!(v+(_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>>=v,l-=v,r.back+=v;}if(u>>>=_,l-=_,r.back+=_,64&g){e.msg="invalid distance code",r.mode=30;break}r.offset=b,r.extra=15&g,r.mode=24;case 24:if(r.extra){for(z=r.extra;l>>=r.extra,l-=r.extra,r.back+=r.extra;}if(r.offset>r.dmax){e.msg="invalid distance too far back",r.mode=30;break}r.mode=25;case 25:if(0===h)break e;if(d=c-h,r.offset>d){if((d=r.offset-d)>r.whave&&r.sane){e.msg="invalid distance too far back",r.mode=30;break}p=d>r.wnext?(d-=r.wnext,r.wsize-d):r.wnext-d,d>r.length&&(d=r.length),m=r.window;}else m=i,p=a-r.offset,d=r.length;for(hd?(m=R[T+a[v]],A[I+a[v]]):(m=96,0),h=1<>S)+(u-=h)]=p<<24|m<<16|_|0,0!==u;);for(h=1<>=1;if(0!==h?(E&=h-1,E+=h):E=0,v++,0==--O[b]){if(b===w)break;b=t[r+a[v]];}if(k>>7)]}function U(e,t){e.pending_buf[e.pending++]=255&t,e.pending_buf[e.pending++]=t>>>8&255;}function P(e,t,r){e.bi_valid>d-r?(e.bi_buf|=t<>d-e.bi_valid,e.bi_valid+=r-d):(e.bi_buf|=t<>>=1,r<<=1,0<--t;);return r>>>1}function Z(e,t,r){var n,i,s=new Array(g+1),a=0;for(n=1;n<=g;n++)s[n]=a=a+r[n-1]<<1;for(i=0;i<=t;i++){var o=e[2*i+1];0!==o&&(e[2*i]=j(s[o]++,o));}}function W(e){var t;for(t=0;t>1;1<=r;r--)G(e,s,r);for(i=h;r=e.heap[1],e.heap[1]=e.heap[e.heap_len--],G(e,s,1),n=e.heap[1],e.heap[--e.heap_max]=r,e.heap[--e.heap_max]=n,s[2*i]=s[2*r]+s[2*n],e.depth[i]=(e.depth[r]>=e.depth[n]?e.depth[r]:e.depth[n])+1,s[2*r+1]=s[2*n+1]=i,e.heap[1]=i++,G(e,s,1),2<=e.heap_len;);e.heap[--e.heap_max]=e.heap[1],function(e,t){var r,n,i,s,a,o,h=t.dyn_tree,u=t.max_code,l=t.stat_desc.static_tree,f=t.stat_desc.has_stree,c=t.stat_desc.extra_bits,d=t.stat_desc.extra_base,p=t.stat_desc.max_length,m=0;for(s=0;s<=g;s++)e.bl_count[s]=0;for(h[2*e.heap[e.heap_max]+1]=0,r=e.heap_max+1;r<_;r++)p<(s=h[2*h[2*(n=e.heap[r])+1]+1]+1)&&(s=p,m++),h[2*n+1]=s,u>=7;n>>=1)if(1&r&&0!==e.dyn_ltree[2*t])return o;if(0!==e.dyn_ltree[18]||0!==e.dyn_ltree[20]||0!==e.dyn_ltree[26])return h;for(t=32;t>>3,(s=e.static_len+3+7>>>3)<=i&&(i=s)):i=s=r+5,r+4<=i&&-1!==t?J(e,t,r,n):4===e.strategy||s===i?(P(e,2+(n?1:0),3),K(e,z,C)):(P(e,4+(n?1:0),3),function(e,t,r,n){var i;for(P(e,t-257,5),P(e,r-1,5),P(e,n-4,4),i=0;i>>8&255,e.pending_buf[e.d_buf+2*e.last_lit+1]=255&t,e.pending_buf[e.l_buf+e.last_lit]=255&r,e.last_lit++,0===t?e.dyn_ltree[2*r]++:(e.matches++,t--,e.dyn_ltree[2*(A[r]+u+1)]++,e.dyn_dtree[2*N(t)]++),e.last_lit===e.lit_bufsize-1},r._tr_align=function(e){P(e,2,3),L(e,m,z),function(e){16===e.bi_valid?(U(e,e.bi_buf),e.bi_buf=0,e.bi_valid=0):8<=e.bi_valid&&(e.pending_buf[e.pending++]=255&e.bi_buf,e.bi_buf>>=8,e.bi_valid-=8);}(e);};},{"../utils/common":41}],53:[function(e,t,r){t.exports=function(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0;};},{}],54:[function(e,t,r){(function(e){!function(r,n){if(!r.setImmediate){var i,s,t,a,o=1,h={},u=!1,l=r.document,e=Object.getPrototypeOf&&Object.getPrototypeOf(r);e=e&&e.setTimeout?e:r,i="[object process]"==={}.toString.call(r.process)?function(e){process.nextTick(function(){c(e);});}:function(){if(r.postMessage&&!r.importScripts){var e=!0,t=r.onmessage;return r.onmessage=function(){e=!1;},r.postMessage("","*"),r.onmessage=t,e}}()?(a="setImmediate$"+Math.random()+"$",r.addEventListener?r.addEventListener("message",d,!1):r.attachEvent("onmessage",d),function(e){r.postMessage(a+e,"*");}):r.MessageChannel?((t=new MessageChannel).port1.onmessage=function(e){c(e.data);},function(e){t.port2.postMessage(e);}):l&&"onreadystatechange"in l.createElement("script")?(s=l.documentElement,function(e){var t=l.createElement("script");t.onreadystatechange=function(){c(e),t.onreadystatechange=null,s.removeChild(t),t=null;},s.appendChild(t);}):function(e){setTimeout(c,0,e);},e.setImmediate=function(e){"function"!=typeof e&&(e=new Function(""+e));for(var t=new Array(arguments.length-1),r=0;r { + initNode(dd, false, n); + }); + } + } + initNode(data, true); + if (waitCollapseNodes.length) { + waitCollapseNodes.forEach(n => { + n.collapse(); + }); + } + } + traverseBF(callback, node) { + var array = []; + array.push(node || this.root); + var currentNode = array.shift(); + while (currentNode) { + for (let i = 0, len = currentNode.children.length; i < len; i++) { + array.push(currentNode.children[i]); + } + callback(currentNode); + currentNode = array.shift(); + } + } + traverseDF(callback, node, cbFirst) { + function recurse(currentNode) { + if (currentNode) { + if (cbFirst) { + callback(currentNode); + } + if (currentNode.children) { + for (var i = 0, length = currentNode.children.length; i < length; i++) { + recurse(currentNode.children[i]); + } + } + if (!cbFirst) { + callback(currentNode); + } + } + } + recurse(node || this.root); + } + getNodeById(id) { + var snode = null; + this.traverseDF((n) => { + if (n.getId() == id) { + snode = n; + } + }); + return snode; + } + clearSelectNode() { + if (this.selectNode) { + this.selectNode.unSelect(); + this.selectNode = null; + } + if (this.editNode) { + if (this.editNode.isEdit) { + this.editNode.cancelEdit(); + } + this.editNode = null; + } + } + initEvent() { + this.appEl.addEventListener('click', this.appClickFn); + this.appEl.addEventListener('mouseover', this.appMouseOverFn); + this.appEl.addEventListener('dblclick', this.appDblclickFn); + this.appEl.addEventListener('dragstart', this.appDragstart); + this.appEl.addEventListener('dragover', this.appDragover); + this.appEl.addEventListener('dragend', this.appDragend); + this.appEl.addEventListener('drop', this.appDrop); + document.addEventListener('keyup', this.appKeyup); + document.addEventListener('keydown', this.appKeydown); + document.addEventListener('compositionstart', this.compositionStart); + document.addEventListener('compositionend', this.compositionEnd); + document.body.addEventListener('mousewheel', this.appMousewheel); + if (obsidian.Platform.isDesktop) { + this.appEl.addEventListener('mousedown', this.appMouseDown); + this.appEl.addEventListener('mouseup', this.appMouseUp); + } + this.appEl.addEventListener('mousemove', this.appMouseMove); + //custom event + this.on('initNode', this.initNode); + this.on('renderEditNode', this.renderEditNode); + this.on('mindMapChange', this.mindMapChange); + } + removeEvent() { + this.appEl.removeEventListener('click', this.appClickFn); + this.appEl.removeEventListener('dragstart', this.appDragstart); + this.appEl.removeEventListener('dragover', this.appDragover); + this.appEl.removeEventListener('dragend', this.appDragend); + this.appEl.removeEventListener('dblClick', this.appDblclickFn); + this.appEl.removeEventListener('mouseover', this.appMouseOverFn); + this.appEl.removeEventListener('drop', this.appDrop); + document.removeEventListener('keyup', this.appKeyup); + document.removeEventListener('keydown', this.appKeydown); + document.removeEventListener('compositionstart', this.compositionStart); + document.removeEventListener('compositionend', this.compositionEnd); + document.body.removeEventListener('mousewheel', this.appMousewheel); + if (obsidian.Platform.isDesktop) { + this.appEl.removeEventListener('mousedown', this.appMouseDown); + this.appEl.removeEventListener('mouseup', this.appMouseUp); + } + this.appEl.removeEventListener('mousemove', this.appMouseMove); + this.off('initNode', this.initNode); + this.off('renderEditNode', this.renderEditNode); + this.off('mindMapChange', this.mindMapChange); + } + initNode(evt) { + this._tempNum++; + //console.log(this._nodeNum,this._tempNum); + if (this._tempNum == this._nodeNum) { + this.refresh(); + this.center(); + } + } + renderEditNode(evt) { + var node = evt.detail.node || null; + node === null || node === void 0 ? void 0 : node.clearCacheData(); + this.refresh(); + } + mindMapChange() { + var _a; + //console.log(this.view) + (_a = this.view) === null || _a === void 0 ? void 0 : _a.mindMapChange(); + } + appKeydown(e) { + var keyCode = e.keyCode || e.which || e.charCode; + var ctrlKey = e.ctrlKey || e.metaKey; + var shiftKey = e.shiftKey; + if (!ctrlKey && !shiftKey) { + //enter + if (keyCode == 13 || e.key == 'Enter') { + var node = this.selectNode; + if (node && !node.isEdit) { + e.preventDefault(); + e.stopPropagation(); + // node.edit(); + // this._menuDom.style.display = 'none'; + if (!node.isExpand) { + node.expand(); + } + if (!node.parent) + return; + node.mindmap.execute('addSiblingNode', { + parent: node.parent + }); + this._menuDom.style.display = 'none'; + } + else if (node && node.isEdit) { + if (this.isComposing || e.isComposing || e.key === 'Process' || e.keyCode === 229) { + return; + } + else { + node.cancelEdit(); + node.select(); + node.mindmap.editNode = null; + } + } + } + // tab + if (keyCode == 9 || keyCode == 45) { + e.preventDefault(); + e.stopPropagation(); + } + if (keyCode == 32) { + var node = this.selectNode; + if (node && !node.isEdit) { + e.preventDefault(); + e.stopPropagation(); + node.edit(); + this._menuDom.style.display = 'none'; + } + } + } + // if (!ctrlKey && shiftKey) { + // //enter > add node + // if (keyCode == 13 || e.key =='Enter') { + // var node = this.selectNode; + // if (node && !node.isEdit) { + // e.preventDefault(); + // e.stopPropagation(); + // if (!node.isExpand) { + // node.expand(); + // } + // if (!node.parent) return; + // node.mindmap.execute('addSiblingNode', { + // parent: node.parent + // }); + // this._menuDom.style.display='none'; + // } + // } + // } + if (ctrlKey && !shiftKey) { + //ctrl + y + if (keyCode == 89) { + e.preventDefault(); + e.stopPropagation(); + this.redo(); + } + //ctrl + z + if (keyCode == 90) { + e.preventDefault(); + e.stopPropagation(); + this.undo(); + } + } + } + compositionStart(e) { + this.isComposing = true; + } + compositionEnd(e) { + this.isComposing = false; + } + appKeyup(e) { + var keyCode = e.keyCode || e.which || e.charCode; + var ctrlKey = e.ctrlKey || e.metaKey; + var shiftKey = e.shiftKey; + if (!ctrlKey && !shiftKey) { + // //enter + // if (keyCode == 13 || e.key =='Enter') { + // var node = this.selectNode; + // if (node && !node.isEdit) { + // e.preventDefault(); + // e.stopPropagation(); + // if (!node.isExpand) { + // node.expand(); + // } + // if (!node.parent) return; + // node.mindmap.execute('addSiblingNode', { + // parent: node.parent + // }); + // this._menuDom.style.display='none'; + // } else if (node && node.isEdit) { + // if (this.isComposing || e.isComposing || e.key === 'Process' || e.keyCode === 229) { + // return + // } else { + // node.cancelEdit(); + // node.select(); + // node.mindmap.editNode=null; + // } + // } + // } + //delete + if (keyCode == 46 || e.key == 'Delete' || e.key == 'Backspace') { + var node = this.selectNode; + if (node && !node.isEdit) { + e.preventDefault(); + e.stopPropagation(); + node.mindmap.execute("deleteNodeAndChild", { node }); + this._menuDom.style.display = 'none'; + } + } + //tab + if (keyCode == 9 || keyCode == 45 || e.key == 'Tab') { + e.preventDefault(); + e.stopPropagation(); + var node = this.selectNode; + if (node && !node.isEdit) { + if (!node.isExpand) { + node.expand(); + } + node.mindmap.execute("addChildNode", { parent: node }); + this._menuDom.style.display = 'none'; + } + else if (node && node.isEdit) { + node.cancelEdit(); + node.select(); + node.mindmap.editNode = null; + } + } + // up + if (keyCode == 38 || e.key == 'ArrowUp') { + var node = this.selectNode; + if (node && !node.isEdit) { + this._selectNode(node, "up"); + } + } + if (keyCode == 40 || e.key == 'ArrowDown') { + var node = this.selectNode; + if (node && !node.isEdit) { + this._selectNode(node, "down"); + } + } + if (keyCode == 39 || e.key == 'ArrowRight') { + var node = this.selectNode; + if (node && !node.isEdit) { + this._selectNode(node, "right"); + } + } + if (keyCode == 37 || e.key == 'ArrowLeft') { + var node = this.selectNode; + if (node && !node.isEdit) { + this._selectNode(node, "left"); + } + } + } + if (ctrlKey && !shiftKey) { + //ctr + / toggle expand node + if (keyCode == 191) { + var node = this.selectNode; + if (node && !node.isEdit) { + if (node.isExpand) { + node.mindmap.execute('collapseNode', { + node + }); + } + else { + node.mindmap.execute('expandNode', { + node + }); + } + } + } + // ctrl + E center + if (keyCode == 69) { + this.center(); + } + } + } + _selectNode(node, direct) { + if (!node) { + return; + } + var minDis; + var waitNode = null; + var pos = node.getPosition(); + var mind = this; + mind.traverseDF((n) => { + var p = n.getPosition(); + var dx = Math.abs(p.x - pos.x); + var dy = Math.abs(p.y - pos.y); + var dis = Math.sqrt(dx * dx + dy * dy); + switch (direct) { + case "right": + if (p.x > pos.x) { + if (minDis) { + if (minDis > dis) { + minDis = dis; + waitNode = n; + } + } + else { + minDis = dis; + waitNode = n; + } + } + break; + case "left": + if (p.x < pos.x) { + if (minDis) { + if (minDis > dis) { + minDis = dis; + waitNode = n; + } + } + else { + minDis = dis; + waitNode = n; + } + } + break; + case "up": + if (p.y < pos.y) { + if (minDis) { + if (minDis > dis) { + minDis = dis; + waitNode = n; + } + } + else { + minDis = dis; + waitNode = n; + } + } + break; + case "down": + if (p.y > pos.y) { + if (minDis) { + if (minDis > dis) { + minDis = dis; + waitNode = n; + } + } + else { + minDis = dis; + waitNode = n; + } + } + break; + } + }); + if (waitNode) { + mind.clearSelectNode(); + waitNode.select(); + } + } + appClickFn(evt) { + var _a; + var targetEl = evt.target; + if (targetEl) { + if (targetEl.tagName == 'A' && targetEl.hasClass("internal-link")) { + evt.preventDefault(); + var targetEl = evt.target; + var href = targetEl.getAttr("href"); + if (href) { + this.view.app.workspace.openLinkText(href, this.view.file.path, evt.ctrlKey || evt.metaKey); + } + } + if (targetEl.hasClass('mm-node-bar')) { + evt.preventDefault(); + evt.stopPropagation(); + var id = targetEl.closest('.mm-node').getAttribute('data-id'); + var node = this.getNodeById(id); + if (node.isExpand) { + node.mindmap.execute('collapseNode', { + node + }); + } + else { + node.mindmap.execute('expandNode', { + node + }); + } + return; + } + if (targetEl.closest('.mm-node-menu')) { + if (targetEl.closest('.mm-icon-add-node')) { + var selectNode = this.selectNode; + if (selectNode) { + selectNode.mindmap.execute("addChildNode", { parent: selectNode }); + this._menuDom.style.display = 'none'; + } + } + if (targetEl.closest('.mm-icon-delete-node')) { + var selectNode = this.selectNode; + if (selectNode) { + selectNode.mindmap.execute("deleteNodeAndChild", { node: selectNode }); + this._menuDom.style.display = 'none'; + } + } + return; + } + if (targetEl.closest('.mm-node')) { + var id = targetEl.closest('.mm-node').getAttribute('data-id'); + var node = this.getNodeById(id); + if (!node.isSelect) { + this.clearSelectNode(); + this.selectNode = node; + (_a = this.selectNode) === null || _a === void 0 ? void 0 : _a.select(); + this._menuDom.style.display = 'block'; + var box = this.selectNode.getBox(); + this._menuDom.style.left = `${box.x + box.width + 10}px`; + this._menuDom.style.top = `${box.y + box.height / 2 - 14}px`; + } + } + else { + this.clearSelectNode(); + this._menuDom.style.display = 'none'; + } + } + } + appDragstart(evt) { + evt.stopPropagation(); + this.startX = evt.pageX; + this.startY = evt.pageY; + if (evt.target instanceof HTMLElement) { + if (evt.target.closest('.mm-node')) { + var id = evt.target.closest('.mm-node').getAttribute('data-id'); + this._dragNode = this.getNodeById(id); + this.drag = true; + } + } + } + appDragend(evt) { + this.drag = false; + this._indicateDom.style.display = 'none'; + this._menuDom.style.display = 'none'; + } + appDragover(evt) { + evt.preventDefault(); + evt.stopPropagation(); + var target = evt.target; + var x = evt.pageX; + var y = evt.pageY; + if (this.drag) { + this.dx = x - this.startX; + this.dx = y - this.startY; + } + if (target.closest('.mm-node')) { + var nodeId = target.closest('.mm-node').getAttribute('data-id'); + var node = this.getNodeById(nodeId); + var box = node.getBox(); + this._dragType = this._getDragType(node, x, y); + this._indicateDom.style.display = 'block'; + this._indicateDom.style.left = box.x + box.width / 2 - 40 / 2 + 'px'; + this._indicateDom.style.top = box.y - 90 + 'px'; + this._indicateDom.className = 'mm-node-layout-indicate'; + if (this._dragType == 'top') { + this._indicateDom.classList.add('mm-arrow-top'); + } + else if (this._dragType == 'down') { + this._indicateDom.classList.add('mm-arrow-down'); + } + else if (this._dragType == 'left') { + this._indicateDom.classList.add('mm-arrow-left'); + } + else if (this._dragType == 'right') { + this._indicateDom.classList.add('mm-arrow-right'); + } + else { + this._indicateDom.classList.add('drag-type'); + var arr = this._dragType.split('-'); + if (arr[1]) { + this._indicateDom.classList.add('mm-arrow-' + arr[1]); + } + else { + this._indicateDom.classList.add('mm-arrow-right'); + } + } + } + else { + this._indicateDom.style.display = 'none'; + } + } + _getDragType(node, x, y) { + if (!node) + return; + var box = node.contentEl.getBoundingClientRect(); + box.x = box.x; + box.y = box.y; + var direct = node.direct; + switch (direct) { + case 'right': + if (y < box.y + box.height / 2 && x < box.x + box.width / 4 * 3) { + return 'top'; + } + if (y > box.y + box.height / 2 && x < box.x + box.width / 4 * 3) { + return 'down'; + } + return 'child-right'; + case 'left': + if (y < box.y + box.height / 2 && x > box.x + box.width / 4) { + return 'top'; + } + if (y > box.y + box.height / 2 && x > box.x + box.width / 4) { + return 'down'; + } + return 'child-left'; + case 'top': + case 'up': + if (x < box.x + box.width / 4) { + return 'left'; + } + if (x > box.x + box.width / 4 * 3) { + return 'right'; + } + return 'child-top'; + case 'down': + case 'bottom': + if (x < box.x + box.width / 4) { + return 'left'; + } + if (x > box.x + box.width / 4 * 3) { + return 'right'; + } + return 'child-down'; + default: + return 'child'; + } + } + appDrop(evt) { + if (evt.target instanceof HTMLElement) { + if (evt.target.closest('.mm-node')) { + evt.preventDefault(); + var dropNodeId = evt.target.closest('.mm-node').getAttribute('data-id'); + var dropNode = this.getNodeById(dropNodeId); + if (this._dragNode.isRoot) ; + else { + this.moveNode(this._dragNode, dropNode, this._dragType); + } + } + } + var files = evt.dataTransfer.files; + if (files.length) { + var f = files[0]; + if (f.name.toLowerCase().endsWith('.xmind')) { + try { + var me = this; + var reader = new FileReader(); + reader.onload = () => { + jszip_min.loadAsync(reader.result).then((e) => { + var files = e.files; + for (var k in files) { + if (k == "content.json") { + files[k].async("text").then((res) => { + var mindData = JSON.parse(res); + var data = importXmind(mindData[0]); + me.clearNode(); + me.data = data.basicData; + me.init(); + setTimeout(() => { + me.center(); + me.mindMapChange(); + }, 100); + }); + } + } + }); + }; + reader.readAsArrayBuffer(f); + } + catch (err) { + new obsidian.Notice('Parse xmind error'); + } + } + } + this._indicateDom.style.display = 'none'; + this._menuDom.style.display = 'none'; + } + appMouseOverFn(evt) { + const targetEl = evt.target; + if (targetEl.tagName !== "A") + return; + if (targetEl.hasClass("internal-link")) { + this.view.app.workspace.trigger("hover-link", { + event: evt, + source: frontMatterKey, + hoverParent: this.view, + targetEl, + linktext: targetEl.getAttr("href"), + sourcePath: this.view.file.path, + }); + } + } + appMouseMove(evt) { + const targetEl = evt.target; + this.scalePointer = []; + this.scalePointer.push(evt.offsetX, evt.offsetY); + if (targetEl.closest('.mm-node')) { + var id = targetEl.closest('.mm-node').getAttribute('data-id'); + var node = this.getNodeById(id); + if (node) { + var box = node.getBox(); + this.scalePointer = []; + this.scalePointer.push(box.x + box.width / 2, box.y + box.height / 2); + } + } + else { + if (this.drag) { + this.containerEL.scrollLeft = this._left - (evt.pageX - this.startX); + this.containerEL.scrollTop = this._top - (evt.pageY - this.startY); + } + } + } + appMouseDown(evt) { + const targetEl = evt.target; + if (!targetEl.closest('.mm-node')) { + this.drag = true; + this.startX = evt.pageX; + this.startY = evt.pageY; + this._left = this.containerEL.scrollLeft; + this._top = this.containerEL.scrollTop; + } + } + appMouseUp(evt) { + this.drag = false; + } + appDblclickFn(evt) { + var _a; + if (evt.target instanceof HTMLElement) { + if (evt.target.hasClass('mm-node-bar')) { + evt.preventDefault(); + evt.stopPropagation(); + return; + } + if (evt.target.closest('.mm-node') instanceof HTMLElement) { + var id = evt.target.closest('.mm-node').getAttribute('data-id'); + this.selectNode = this.getNodeById(id); + if (!this.editNode || (this.editNode && this.editNode != this.selectNode)) { + (_a = this.selectNode) === null || _a === void 0 ? void 0 : _a.edit(); + this.editNode = this.selectNode; + this._menuDom.style.display = 'none'; + } + } + } + } + appMousewheel(evt) { + // if(!evt) evt = window.event; + var ctrlKey = evt.ctrlKey || evt.metaKey; + var delta; + if (evt.wheelDelta) { + //IE、chrome -120 + delta = evt.wheelDelta / 120; + } + else if (evt.detail) { + //FF 3 + delta = -evt.detail / 3; + } + if (delta) { + if (delta < 0) { + if (ctrlKey) { + this.setScale("down"); + } + } + else { + if (ctrlKey) { + this.setScale("up"); + } + } + } + } + clearNode() { + var _a; + //delete node + this.traverseBF((n) => { + this.contentEL.removeChild(n.containEl); + }); + //delete line + if (this.mmLayout) { + (_a = this.mmLayout.svgDom) === null || _a === void 0 ? void 0 : _a.clear(); + } + } + clear() { + var _a; + this.clearNode(); + this.removeEvent(); + (_a = this.draw) === null || _a === void 0 ? void 0 : _a.clear(); + } + //get node list rect point + getBoundingRect(list) { + var box = { + x: 0, + y: 0, + width: 0, + height: 0, + right: 0, + bottom: 0 + }; + list.forEach((item, i) => { + var b = item.getBox(); + // console.log(b.x,b.y); + if (i == 0) { + box.x = b.x; + box.y = b.y; + box.right = b.x + b.width; + box.bottom = b.y + b.height; + } + else { + if (b.x < box.x) { + box.x = b.x; + } + if (b.y < box.y) { + box.y = b.y; + } + if (b.x + b.width > box.right) { + box.right = b.x + b.width; + } + if (b.y + b.height > box.bottom) { + box.bottom = b.y + b.height; + } + } + }); + box.width = box.right - box.x; + box.height = box.bottom - box.y; + return box; + } + moveNode(dragNode, dropNode, type) { + if (dragNode == dropNode || dragNode.isRoot) { + return; + } + var flag = false; + var p = dropNode.parent; + while (p) { + if (p == dragNode) { + flag = true; + break; + } + p = p.parent; + } + if (flag) { //parent can not change to child + return; + } + dropNode.clearCacheData(); + dragNode.clearCacheData(); + if (!dropNode.isExpand) { + dropNode.expand(); + } + if (type == 'top' || type == 'left' || type == 'down' || type == 'right') { + this.execute('moveNode', { type: 'siblings', node: dragNode, oldParent: dragNode.parent, dropNode, direct: type }); + } + else if (type.indexOf('child') > -1) { + var typeArr = type.split('-'); + if (typeArr[1]) { + this.execute('moveNode', { type: 'child', node: dragNode, oldParent: dragNode.parent, parent: dropNode, direct: typeArr[1] }); + } + else { + this.execute('moveNode', { type: 'child', node: dragNode, oldParent: dragNode.parent, parent: dropNode }); + } + } + // this.execute('moveNode', { type: 'child', node: dragNode, oldParent: dragNode.parent, parent: dropNode }) + } + //execute cmd , store history + execute(name, data) { + this.exec.execute(name, data); + } + undo() { + this.exec.undo(); + } + redo() { + this.exec.redo(); + } + addNode(node, parent, index = -1) { + if (parent) { + parent.addChild(node, index); + if (parent.direct) { + node.direct = parent.direct; + } + this._addNodeDom(node); + node.clearCacheData(); + } + } + _addNodeDom(node) { + this.traverseBF((n) => { + if (!this.contentEL.contains(n.containEl)) { + this.contentEL.appendChild(n.containEl); + } + }, node); + } + removeNode(node) { + if (node.parent) { + var p = node.parent; + var i = node.parent.removeChild(node); + this._removeChildDom(node); + p.clearCacheData(); + return i; + } + else { + this._removeChildDom(node); + return -1; + } + } + _removeChildDom(node) { + this.traverseBF((n) => { + if (this.contentEL.contains(n.containEl)) { + this.contentEL.removeChild(n.containEl); + } + }, node); + } + //layout + layout() { + if (!this.mmLayout) { + this.mmLayout = new Layout(this.root, this.setting.layoutDirect || 'mind map', this.colors); + return; + } + this.mmLayout.layout(this.root, this.setting.layoutDirect || this.mmLayout.direct || 'mind map'); + } + refresh() { + this.layout(); + } + emit(name, data) { + var evt = new CustomEvent(name, { + detail: data || {} + }); + this.appEl.dispatchEvent(evt); + } + on(name, fn) { + this.appEl.addEventListener(name, fn); + } + off(name, fn) { + if (name && fn) { + this.appEl.removeEventListener(name, fn); + } + } + center() { + this._setMindScalePointer(); + var oldScale = this.mindScale; + this.scale(100); + var w = this.containerEL.clientWidth; + var h = this.containerEL.clientHeight; + this.containerEL.scrollTop = this.setting.canvasSize / 2 - h / 2 - 60; + this.containerEL.scrollLeft = this.setting.canvasSize / 2 - w / 2 + 30; + this.scale(oldScale); + } + _setMindScalePointer() { + this.scalePointer = []; + var root = this.root; + if (root) { + var rbox = root.getBox(); + this.scalePointer.push(rbox.x + rbox.width / 2, rbox.y + rbox.height / 2); + } + } + getMarkdown() { + var md = ''; + var level = this.setting.headLevel; + this.traverseDF((n) => { + var l = n.getLevel() + 1; + var hPrefix = '', space = ''; + if (l > 1) { + hPrefix = '\n'; + } + const ending = n.isExpand ? '' : ` ^${n.getId()}`; + if (n.getLevel() < level) { + for (let i = 0; i < l; i++) { + hPrefix += '#'; + } + md += (hPrefix + ' '); + md += n.getData().text.trim() + ending + '\n'; + } + else { + for (var i = 0; i < n.getLevel() - level; i++) { + space += '\t'; + } + var text = n.getData().text.trim(); + if (text) { + var textArr = text.split('\n'); + var lineLength = textArr.length; + if (lineLength == 1) { + md += `${space}- ${text}${ending}\n`; + } + else if (lineLength > 1) { + //code + if (text.startsWith('```')) { + md += '\n'; + md += `${space}-\n`; + textArr.forEach((t, i) => { + md += `${space} ${t.trim()}${i === textArr.length - 1 ? ending : ''}\n`; + }); + md += '\n'; + } + else { + //text + md += `${space}- `; + textArr.forEach((t, i) => { + var contentText = "void"; + if (t.trim().length > 0) { + contentText = t.trim(); + } + if (i > 0) { + md += `${space}${contentText}${i === textArr.length - 1 ? ending : ''}\n`; + } + else { + md += `${contentText}\n`; + } + }); + } + } + } + else { + for (var i = 0; i < n.getLevel() - level; i++) { + space += '\t'; + } + md += `${space}-\n`; + } + } + }, this.root, true); + return md.trim(); + } + scale(num) { + if (num < 20) { + num = 20; + } + if (num > 300) { + num = 300; + } + this.mindScale = num; + if (this.scalePointer.length) { + this.appEl.style.transformOrigin = `${this.scalePointer[0]}px ${this.scalePointer[1]}px`; + this.appEl.style.transform = "scale(" + this.mindScale / 100 + ")"; + } + else { + this.appEl.style.transform = "scale(" + this.mindScale / 100 + ")"; + } + } + setScale(type) { + if (type == "up") { + var n = this.mindScale + 10; + } + else { + var n = this.mindScale - 10; + } + this.scale(n); + if (this.timeOut) { + clearTimeout(this.timeOut); + } + this.timeOut = setTimeout(() => { + new obsidian.Notice(`${n} %`); + }, 600); + } + copyNode(node) { + var n = node || this.selectNode; + if (n) { + var data = []; + function copyNode(n, pid) { + var d = n.getData(); + d.id = uuid(); + d.pid = pid; + data.push({ + id: d.id, + text: d.text, + pid: pid, + isExpand: d.isExpand, + note: d.note + }); + n.children.forEach((c) => { + copyNode(c, d.id); + }); + } + copyNode(n, null); + var _data = { + type: 'copyNode', + text: data + }; + return JSON.stringify(_data); + } + else { + return ''; + } + } + pasteNode(text) { + var node = this.selectNode; + if (text) { + try { + var json = JSON.parse(text); + if (json.type && json.type == 'copyNode') { + var data = json.text; + if (!node.isExpand) { + node.expand(); + node.clearCacheData(); + } + this.execute('pasteNode', { + node: node, + data: data + }); + navigator.clipboard.writeText(''); + } + } + catch (err) { + console.log(err); + } + } + } +} + +var katex = createCommonjsModule(function (module, exports) { +(function webpackUniversalModuleDefinition(root, factory) { + module.exports = factory(); +})((typeof self !== 'undefined' ? self : commonjsGlobal), function() { +return /******/ (function() { // webpackBootstrap +/******/ // The require scope +/******/ var __webpack_require__ = {}; +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/define property getters */ +/******/ !function() { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = function(exports, definition) { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ }(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ !function() { +/******/ __webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }; +/******/ }(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; + +// EXPORTS +__webpack_require__.d(__webpack_exports__, { + "default": function() { return /* binding */ katex_webpack; } +}); + + +/** + * This is the ParseError class, which is the main error thrown by KaTeX + * functions when something has gone wrong. This is used to distinguish internal + * errors from errors in the expression that the user provided. + * + * If possible, a caller should provide a Token or ParseNode with information + * about where in the source string the problem occurred. + */ +var ParseError = // Error position based on passed-in Token or ParseNode. +function ParseError(message, // The error message +token // An object providing position information +) { + this.position = void 0; + var error = "KaTeX parse error: " + message; + var start; + var loc = token && token.loc; + + if (loc && loc.start <= loc.end) { + // If we have the input and a position, make the error a bit fancier + // Get the input + var input = loc.lexer.input; // Prepend some information + + start = loc.start; + var end = loc.end; + + if (start === input.length) { + error += " at end of input: "; + } else { + error += " at position " + (start + 1) + ": "; + } // Underline token in question using combining underscores + + + var underlined = input.slice(start, end).replace(/[^]/g, "$&\u0332"); // Extract some context from the input and add it to the error + + var left; + + if (start > 15) { + left = "…" + input.slice(start - 15, start); + } else { + left = input.slice(0, start); + } + + var right; + + if (end + 15 < input.length) { + right = input.slice(end, end + 15) + "…"; + } else { + right = input.slice(end); + } + + error += left + underlined + right; + } // Some hackery to make ParseError a prototype of Error + // See http://stackoverflow.com/a/8460753 + + + var self = new Error(error); + self.name = "ParseError"; // $FlowFixMe + + self.__proto__ = ParseError.prototype; // $FlowFixMe + + self.position = start; + return self; +}; // $FlowFixMe More hackery + + +ParseError.prototype.__proto__ = Error.prototype; +/* harmony default export */ var src_ParseError = (ParseError); +/** + * This file contains a list of utility functions which are useful in other + * files. + */ + +/** + * Return whether an element is contained in a list + */ +var contains = function contains(list, elem) { + return list.indexOf(elem) !== -1; +}; +/** + * Provide a default value if a setting is undefined + * NOTE: Couldn't use `T` as the output type due to facebook/flow#5022. + */ + + +var deflt = function deflt(setting, defaultIfUndefined) { + return setting === undefined ? defaultIfUndefined : setting; +}; // hyphenate and escape adapted from Facebook's React under Apache 2 license + + +var uppercase = /([A-Z])/g; + +var hyphenate = function hyphenate(str) { + return str.replace(uppercase, "-$1").toLowerCase(); +}; + +var ESCAPE_LOOKUP = { + "&": "&", + ">": ">", + "<": "<", + "\"": """, + "'": "'" +}; +var ESCAPE_REGEX = /[&><"']/g; +/** + * Escapes text to prevent scripting attacks. + */ + +function utils_escape(text) { + return String(text).replace(ESCAPE_REGEX, function (match) { + return ESCAPE_LOOKUP[match]; + }); +} +/** + * Sometimes we want to pull out the innermost element of a group. In most + * cases, this will just be the group itself, but when ordgroups and colors have + * a single element, we want to pull that out. + */ + + +var getBaseElem = function getBaseElem(group) { + if (group.type === "ordgroup") { + if (group.body.length === 1) { + return getBaseElem(group.body[0]); + } else { + return group; + } + } else if (group.type === "color") { + if (group.body.length === 1) { + return getBaseElem(group.body[0]); + } else { + return group; + } + } else if (group.type === "font") { + return getBaseElem(group.body); + } else { + return group; + } +}; +/** + * TeXbook algorithms often reference "character boxes", which are simply groups + * with a single character in them. To decide if something is a character box, + * we find its innermost group, and see if it is a single character. + */ + + +var isCharacterBox = function isCharacterBox(group) { + var baseElem = getBaseElem(group); // These are all they types of groups which hold single characters + + return baseElem.type === "mathord" || baseElem.type === "textord" || baseElem.type === "atom"; +}; + +var assert = function assert(value) { + if (!value) { + throw new Error('Expected non-null, but got ' + String(value)); + } + + return value; +}; +/** + * Return the protocol of a URL, or "_relative" if the URL does not specify a + * protocol (and thus is relative). + */ + +var protocolFromUrl = function protocolFromUrl(url) { + var protocol = /^\s*([^\\/#]*?)(?::|�*58|�*3a)/i.exec(url); + return protocol != null ? protocol[1] : "_relative"; +}; +/* harmony default export */ var utils = ({ + contains: contains, + deflt: deflt, + escape: utils_escape, + hyphenate: hyphenate, + getBaseElem: getBaseElem, + isCharacterBox: isCharacterBox, + protocolFromUrl: protocolFromUrl +}); +/* eslint no-console:0 */ + +/** + * This is a module for storing settings passed into KaTeX. It correctly handles + * default settings. + */ + + + + +/** + * The main Settings object + * + * The current options stored are: + * - displayMode: Whether the expression should be typeset as inline math + * (false, the default), meaning that the math starts in + * \textstyle and is placed in an inline-block); or as display + * math (true), meaning that the math starts in \displaystyle + * and is placed in a block with vertical margin. + */ +var Settings = /*#__PURE__*/function () { + function Settings(options) { + this.displayMode = void 0; + this.output = void 0; + this.leqno = void 0; + this.fleqn = void 0; + this.throwOnError = void 0; + this.errorColor = void 0; + this.macros = void 0; + this.minRuleThickness = void 0; + this.colorIsTextColor = void 0; + this.strict = void 0; + this.trust = void 0; + this.maxSize = void 0; + this.maxExpand = void 0; + this.globalGroup = void 0; + // allow null options + options = options || {}; + this.displayMode = utils.deflt(options.displayMode, false); + this.output = utils.deflt(options.output, "htmlAndMathml"); + this.leqno = utils.deflt(options.leqno, false); + this.fleqn = utils.deflt(options.fleqn, false); + this.throwOnError = utils.deflt(options.throwOnError, true); + this.errorColor = utils.deflt(options.errorColor, "#cc0000"); + this.macros = options.macros || {}; + this.minRuleThickness = Math.max(0, utils.deflt(options.minRuleThickness, 0)); + this.colorIsTextColor = utils.deflt(options.colorIsTextColor, false); + this.strict = utils.deflt(options.strict, "warn"); + this.trust = utils.deflt(options.trust, false); + this.maxSize = Math.max(0, utils.deflt(options.maxSize, Infinity)); + this.maxExpand = Math.max(0, utils.deflt(options.maxExpand, 1000)); + this.globalGroup = utils.deflt(options.globalGroup, false); + } + /** + * Report nonstrict (non-LaTeX-compatible) input. + * Can safely not be called if `this.strict` is false in JavaScript. + */ + + + var _proto = Settings.prototype; + + _proto.reportNonstrict = function reportNonstrict(errorCode, errorMsg, token) { + var strict = this.strict; + + if (typeof strict === "function") { + // Allow return value of strict function to be boolean or string + // (or null/undefined, meaning no further processing). + strict = strict(errorCode, errorMsg, token); + } + + if (!strict || strict === "ignore") { + return; + } else if (strict === true || strict === "error") { + throw new src_ParseError("LaTeX-incompatible input and strict mode is set to 'error': " + (errorMsg + " [" + errorCode + "]"), token); + } else if (strict === "warn") { + typeof console !== "undefined" && console.warn("LaTeX-incompatible input and strict mode is set to 'warn': " + (errorMsg + " [" + errorCode + "]")); + } else { + // won't happen in type-safe code + typeof console !== "undefined" && console.warn("LaTeX-incompatible input and strict mode is set to " + ("unrecognized '" + strict + "': " + errorMsg + " [" + errorCode + "]")); + } + } + /** + * Check whether to apply strict (LaTeX-adhering) behavior for unusual + * input (like `\\`). Unlike `nonstrict`, will not throw an error; + * instead, "error" translates to a return value of `true`, while "ignore" + * translates to a return value of `false`. May still print a warning: + * "warn" prints a warning and returns `false`. + * This is for the second category of `errorCode`s listed in the README. + */ + ; + + _proto.useStrictBehavior = function useStrictBehavior(errorCode, errorMsg, token) { + var strict = this.strict; + + if (typeof strict === "function") { + // Allow return value of strict function to be boolean or string + // (or null/undefined, meaning no further processing). + // But catch any exceptions thrown by function, treating them + // like "error". + try { + strict = strict(errorCode, errorMsg, token); + } catch (error) { + strict = "error"; + } + } + + if (!strict || strict === "ignore") { + return false; + } else if (strict === true || strict === "error") { + return true; + } else if (strict === "warn") { + typeof console !== "undefined" && console.warn("LaTeX-incompatible input and strict mode is set to 'warn': " + (errorMsg + " [" + errorCode + "]")); + return false; + } else { + // won't happen in type-safe code + typeof console !== "undefined" && console.warn("LaTeX-incompatible input and strict mode is set to " + ("unrecognized '" + strict + "': " + errorMsg + " [" + errorCode + "]")); + return false; + } + } + /** + * Check whether to test potentially dangerous input, and return + * `true` (trusted) or `false` (untrusted). The sole argument `context` + * should be an object with `command` field specifying the relevant LaTeX + * command (as a string starting with `\`), and any other arguments, etc. + * If `context` has a `url` field, a `protocol` field will automatically + * get added by this function (changing the specified object). + */ + ; + + _proto.isTrusted = function isTrusted(context) { + if (context.url && !context.protocol) { + context.protocol = utils.protocolFromUrl(context.url); + } + + var trust = typeof this.trust === "function" ? this.trust(context) : this.trust; + return Boolean(trust); + }; + + return Settings; +}(); +/** + * This file contains information and classes for the various kinds of styles + * used in TeX. It provides a generic `Style` class, which holds information + * about a specific style. It then provides instances of all the different kinds + * of styles possible, and provides functions to move between them and get + * information about them. + */ + +/** + * The main style class. Contains a unique id for the style, a size (which is + * the same for cramped and uncramped version of a style), and a cramped flag. + */ +var Style = /*#__PURE__*/function () { + function Style(id, size, cramped) { + this.id = void 0; + this.size = void 0; + this.cramped = void 0; + this.id = id; + this.size = size; + this.cramped = cramped; + } + /** + * Get the style of a superscript given a base in the current style. + */ + + + var _proto = Style.prototype; + + _proto.sup = function sup() { + return styles[_sup[this.id]]; + } + /** + * Get the style of a subscript given a base in the current style. + */ + ; + + _proto.sub = function sub() { + return styles[_sub[this.id]]; + } + /** + * Get the style of a fraction numerator given the fraction in the current + * style. + */ + ; + + _proto.fracNum = function fracNum() { + return styles[_fracNum[this.id]]; + } + /** + * Get the style of a fraction denominator given the fraction in the current + * style. + */ + ; + + _proto.fracDen = function fracDen() { + return styles[_fracDen[this.id]]; + } + /** + * Get the cramped version of a style (in particular, cramping a cramped style + * doesn't change the style). + */ + ; + + _proto.cramp = function cramp() { + return styles[_cramp[this.id]]; + } + /** + * Get a text or display version of this style. + */ + ; + + _proto.text = function text() { + return styles[_text[this.id]]; + } + /** + * Return true if this style is tightly spaced (scriptstyle/scriptscriptstyle) + */ + ; + + _proto.isTight = function isTight() { + return this.size >= 2; + }; + + return Style; +}(); // Export an interface for type checking, but don't expose the implementation. +// This way, no more styles can be generated. + + +// IDs of the different styles +var D = 0; +var Dc = 1; +var T = 2; +var Tc = 3; +var S = 4; +var Sc = 5; +var SS = 6; +var SSc = 7; // Instances of the different styles + +var styles = [new Style(D, 0, false), new Style(Dc, 0, true), new Style(T, 1, false), new Style(Tc, 1, true), new Style(S, 2, false), new Style(Sc, 2, true), new Style(SS, 3, false), new Style(SSc, 3, true)]; // Lookup tables for switching from one style to another + +var _sup = [S, Sc, S, Sc, SS, SSc, SS, SSc]; +var _sub = [Sc, Sc, Sc, Sc, SSc, SSc, SSc, SSc]; +var _fracNum = [T, Tc, S, Sc, SS, SSc, SS, SSc]; +var _fracDen = [Tc, Tc, Sc, Sc, SSc, SSc, SSc, SSc]; +var _cramp = [Dc, Dc, Tc, Tc, Sc, Sc, SSc, SSc]; +var _text = [D, Dc, T, Tc, T, Tc, T, Tc]; // We only export some of the styles. + +/* harmony default export */ var src_Style = ({ + DISPLAY: styles[D], + TEXT: styles[T], + SCRIPT: styles[S], + SCRIPTSCRIPT: styles[SS] +}); +/* + * This file defines the Unicode scripts and script families that we + * support. To add new scripts or families, just add a new entry to the + * scriptData array below. Adding scripts to the scriptData array allows + * characters from that script to appear in \text{} environments. + */ + +/** + * Each script or script family has a name and an array of blocks. + * Each block is an array of two numbers which specify the start and + * end points (inclusive) of a block of Unicode codepoints. + */ + +/** + * Unicode block data for the families of scripts we support in \text{}. + * Scripts only need to appear here if they do not have font metrics. + */ +var scriptData = [{ + // Latin characters beyond the Latin-1 characters we have metrics for. + // Needed for Czech, Hungarian and Turkish text, for example. + name: 'latin', + blocks: [[0x0100, 0x024f], // Latin Extended-A and Latin Extended-B + [0x0300, 0x036f] // Combining Diacritical marks + ] +}, { + // The Cyrillic script used by Russian and related languages. + // A Cyrillic subset used to be supported as explicitly defined + // symbols in symbols.js + name: 'cyrillic', + blocks: [[0x0400, 0x04ff]] +}, { + // Armenian + name: 'armenian', + blocks: [[0x0530, 0x058F]] +}, { + // The Brahmic scripts of South and Southeast Asia + // Devanagari (0900–097F) + // Bengali (0980–09FF) + // Gurmukhi (0A00–0A7F) + // Gujarati (0A80–0AFF) + // Oriya (0B00–0B7F) + // Tamil (0B80–0BFF) + // Telugu (0C00–0C7F) + // Kannada (0C80–0CFF) + // Malayalam (0D00–0D7F) + // Sinhala (0D80–0DFF) + // Thai (0E00–0E7F) + // Lao (0E80–0EFF) + // Tibetan (0F00–0FFF) + // Myanmar (1000–109F) + name: 'brahmic', + blocks: [[0x0900, 0x109F]] +}, { + name: 'georgian', + blocks: [[0x10A0, 0x10ff]] +}, { + // Chinese and Japanese. + // The "k" in cjk is for Korean, but we've separated Korean out + name: "cjk", + blocks: [[0x3000, 0x30FF], // CJK symbols and punctuation, Hiragana, Katakana + [0x4E00, 0x9FAF], // CJK ideograms + [0xFF00, 0xFF60] // Fullwidth punctuation + // TODO: add halfwidth Katakana and Romanji glyphs + ] +}, { + // Korean + name: 'hangul', + blocks: [[0xAC00, 0xD7AF]] +}]; +/** + * Given a codepoint, return the name of the script or script family + * it is from, or null if it is not part of a known block + */ + +function scriptFromCodepoint(codepoint) { + for (var i = 0; i < scriptData.length; i++) { + var script = scriptData[i]; + + for (var _i = 0; _i < script.blocks.length; _i++) { + var block = script.blocks[_i]; + + if (codepoint >= block[0] && codepoint <= block[1]) { + return script.name; + } + } + } + + return null; +} +/** + * A flattened version of all the supported blocks in a single array. + * This is an optimization to make supportedCodepoint() fast. + */ + +var allBlocks = []; +scriptData.forEach(function (s) { + return s.blocks.forEach(function (b) { + return allBlocks.push.apply(allBlocks, b); + }); +}); +/** + * Given a codepoint, return true if it falls within one of the + * scripts or script families defined above and false otherwise. + * + * Micro benchmarks shows that this is faster than + * /[\u3000-\u30FF\u4E00-\u9FAF\uFF00-\uFF60\uAC00-\uD7AF\u0900-\u109F]/.test() + * in Firefox, Chrome and Node. + */ + +function supportedCodepoint(codepoint) { + for (var i = 0; i < allBlocks.length; i += 2) { + if (codepoint >= allBlocks[i] && codepoint <= allBlocks[i + 1]) { + return true; + } + } + + return false; +} +/** + * This file provides support to domTree.js and delimiter.js. + * It's a storehouse of path geometry for SVG images. + */ +// In all paths below, the viewBox-to-em scale is 1000:1. +var hLinePad = 80; // padding above a sqrt viniculum. Prevents image cropping. +// The viniculum of a \sqrt can be made thicker by a KaTeX rendering option. +// Think of variable extraViniculum as two detours in the SVG path. +// The detour begins at the lower left of the area labeled extraViniculum below. +// The detour proceeds one extraViniculum distance up and slightly to the right, +// displacing the radiused corner between surd and viniculum. The radius is +// traversed as usual, then the detour resumes. It goes right, to the end of +// the very long viniculumn, then down one extraViniculum distance, +// after which it resumes regular path geometry for the radical. + +/* viniculum + / + /▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒←extraViniculum + / █████████████████████←0.04em (40 unit) std viniculum thickness + / / + / / + / /\ + / / surd +*/ + +var sqrtMain = function sqrtMain(extraViniculum, hLinePad) { + // sqrtMain path geometry is from glyph U221A in the font KaTeX Main + return "M95," + (622 + extraViniculum + hLinePad) + "\nc-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14\nc0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54\nc44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10\ns173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429\nc69,-144,104.5,-217.7,106.5,-221\nl" + extraViniculum / 2.075 + " -" + extraViniculum + "\nc5.3,-9.3,12,-14,20,-14\nH400000v" + (40 + extraViniculum) + "H845.2724\ns-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7\nc-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z\nM" + (834 + extraViniculum) + " " + hLinePad + "h400000v" + (40 + extraViniculum) + "h-400000z"; +}; + +var sqrtSize1 = function sqrtSize1(extraViniculum, hLinePad) { + // size1 is from glyph U221A in the font KaTeX_Size1-Regular + return "M263," + (601 + extraViniculum + hLinePad) + "c0.7,0,18,39.7,52,119\nc34,79.3,68.167,158.7,102.5,238c34.3,79.3,51.8,119.3,52.5,120\nc340,-704.7,510.7,-1060.3,512,-1067\nl" + extraViniculum / 2.084 + " -" + extraViniculum + "\nc4.7,-7.3,11,-11,19,-11\nH40000v" + (40 + extraViniculum) + "H1012.3\ns-271.3,567,-271.3,567c-38.7,80.7,-84,175,-136,283c-52,108,-89.167,185.3,-111.5,232\nc-22.3,46.7,-33.8,70.3,-34.5,71c-4.7,4.7,-12.3,7,-23,7s-12,-1,-12,-1\ns-109,-253,-109,-253c-72.7,-168,-109.3,-252,-110,-252c-10.7,8,-22,16.7,-34,26\nc-22,17.3,-33.3,26,-34,26s-26,-26,-26,-26s76,-59,76,-59s76,-60,76,-60z\nM" + (1001 + extraViniculum) + " " + hLinePad + "h400000v" + (40 + extraViniculum) + "h-400000z"; +}; + +var sqrtSize2 = function sqrtSize2(extraViniculum, hLinePad) { + // size2 is from glyph U221A in the font KaTeX_Size2-Regular + return "M983 " + (10 + extraViniculum + hLinePad) + "\nl" + extraViniculum / 3.13 + " -" + extraViniculum + "\nc4,-6.7,10,-10,18,-10 H400000v" + (40 + extraViniculum) + "\nH1013.1s-83.4,268,-264.1,840c-180.7,572,-277,876.3,-289,913c-4.7,4.7,-12.7,7,-24,7\ns-12,0,-12,0c-1.3,-3.3,-3.7,-11.7,-7,-25c-35.3,-125.3,-106.7,-373.3,-214,-744\nc-10,12,-21,25,-33,39s-32,39,-32,39c-6,-5.3,-15,-14,-27,-26s25,-30,25,-30\nc26.7,-32.7,52,-63,76,-91s52,-60,52,-60s208,722,208,722\nc56,-175.3,126.3,-397.3,211,-666c84.7,-268.7,153.8,-488.2,207.5,-658.5\nc53.7,-170.3,84.5,-266.8,92.5,-289.5z\nM" + (1001 + extraViniculum) + " " + hLinePad + "h400000v" + (40 + extraViniculum) + "h-400000z"; +}; + +var sqrtSize3 = function sqrtSize3(extraViniculum, hLinePad) { + // size3 is from glyph U221A in the font KaTeX_Size3-Regular + return "M424," + (2398 + extraViniculum + hLinePad) + "\nc-1.3,-0.7,-38.5,-172,-111.5,-514c-73,-342,-109.8,-513.3,-110.5,-514\nc0,-2,-10.7,14.3,-32,49c-4.7,7.3,-9.8,15.7,-15.5,25c-5.7,9.3,-9.8,16,-12.5,20\ns-5,7,-5,7c-4,-3.3,-8.3,-7.7,-13,-13s-13,-13,-13,-13s76,-122,76,-122s77,-121,77,-121\ns209,968,209,968c0,-2,84.7,-361.7,254,-1079c169.3,-717.3,254.7,-1077.7,256,-1081\nl" + extraViniculum / 4.223 + " -" + extraViniculum + "c4,-6.7,10,-10,18,-10 H400000\nv" + (40 + extraViniculum) + "H1014.6\ns-87.3,378.7,-272.6,1166c-185.3,787.3,-279.3,1182.3,-282,1185\nc-2,6,-10,9,-24,9\nc-8,0,-12,-0.7,-12,-2z M" + (1001 + extraViniculum) + " " + hLinePad + "\nh400000v" + (40 + extraViniculum) + "h-400000z"; +}; + +var sqrtSize4 = function sqrtSize4(extraViniculum, hLinePad) { + // size4 is from glyph U221A in the font KaTeX_Size4-Regular + return "M473," + (2713 + extraViniculum + hLinePad) + "\nc339.3,-1799.3,509.3,-2700,510,-2702 l" + extraViniculum / 5.298 + " -" + extraViniculum + "\nc3.3,-7.3,9.3,-11,18,-11 H400000v" + (40 + extraViniculum) + "H1017.7\ns-90.5,478,-276.2,1466c-185.7,988,-279.5,1483,-281.5,1485c-2,6,-10,9,-24,9\nc-8,0,-12,-0.7,-12,-2c0,-1.3,-5.3,-32,-16,-92c-50.7,-293.3,-119.7,-693.3,-207,-1200\nc0,-1.3,-5.3,8.7,-16,30c-10.7,21.3,-21.3,42.7,-32,64s-16,33,-16,33s-26,-26,-26,-26\ns76,-153,76,-153s77,-151,77,-151c0.7,0.7,35.7,202,105,604c67.3,400.7,102,602.7,104,\n606zM" + (1001 + extraViniculum) + " " + hLinePad + "h400000v" + (40 + extraViniculum) + "H1017.7z"; +}; + +var phasePath = function phasePath(y) { + var x = y / 2; // x coordinate at top of angle + + return "M400000 " + y + " H0 L" + x + " 0 l65 45 L145 " + (y - 80) + " H400000z"; +}; + +var sqrtTall = function sqrtTall(extraViniculum, hLinePad, viewBoxHeight) { + // sqrtTall is from glyph U23B7 in the font KaTeX_Size4-Regular + // One path edge has a variable length. It runs vertically from the viniculumn + // to a point near (14 units) the bottom of the surd. The viniculum + // is normally 40 units thick. So the length of the line in question is: + var vertSegment = viewBoxHeight - 54 - hLinePad - extraViniculum; + return "M702 " + (extraViniculum + hLinePad) + "H400000" + (40 + extraViniculum) + "\nH742v" + vertSegment + "l-4 4-4 4c-.667.7 -2 1.5-4 2.5s-4.167 1.833-6.5 2.5-5.5 1-9.5 1\nh-12l-28-84c-16.667-52-96.667 -294.333-240-727l-212 -643 -85 170\nc-4-3.333-8.333-7.667-13 -13l-13-13l77-155 77-156c66 199.333 139 419.667\n219 661 l218 661zM702 " + hLinePad + "H400000v" + (40 + extraViniculum) + "H742z"; +}; + +var sqrtPath = function sqrtPath(size, extraViniculum, viewBoxHeight) { + extraViniculum = 1000 * extraViniculum; // Convert from document ems to viewBox. + + var path = ""; + + switch (size) { + case "sqrtMain": + path = sqrtMain(extraViniculum, hLinePad); + break; + + case "sqrtSize1": + path = sqrtSize1(extraViniculum, hLinePad); + break; + + case "sqrtSize2": + path = sqrtSize2(extraViniculum, hLinePad); + break; + + case "sqrtSize3": + path = sqrtSize3(extraViniculum, hLinePad); + break; + + case "sqrtSize4": + path = sqrtSize4(extraViniculum, hLinePad); + break; + + case "sqrtTall": + path = sqrtTall(extraViniculum, hLinePad, viewBoxHeight); + } + + return path; +}; +var innerPath = function innerPath(name, height) { + // The inner part of stretchy tall delimiters + switch (name) { + case "\u239C": + return "M291 0 H417 V" + height + " H291z M291 0 H417 V" + height + " H291z"; + + case "\u2223": + return "M145 0 H188 V" + height + " H145z M145 0 H188 V" + height + " H145z"; + + case "\u2225": + return "M145 0 H188 V" + height + " H145z M145 0 H188 V" + height + " H145z" + ("M367 0 H410 V" + height + " H367z M367 0 H410 V" + height + " H367z"); + + case "\u239F": + return "M457 0 H583 V" + height + " H457z M457 0 H583 V" + height + " H457z"; + + case "\u23A2": + return "M319 0 H403 V" + height + " H319z M319 0 H403 V" + height + " H319z"; + + case "\u23A5": + return "M263 0 H347 V" + height + " H263z M263 0 H347 V" + height + " H263z"; + + case "\u23AA": + return "M384 0 H504 V" + height + " H384z M384 0 H504 V" + height + " H384z"; + + case "\u23D0": + return "M312 0 H355 V" + height + " H312z M312 0 H355 V" + height + " H312z"; + + case "\u2016": + return "M257 0 H300 V" + height + " H257z M257 0 H300 V" + height + " H257z" + ("M478 0 H521 V" + height + " H478z M478 0 H521 V" + height + " H478z"); + + default: + return ""; + } +}; +var path = { + // The doubleleftarrow geometry is from glyph U+21D0 in the font KaTeX Main + doubleleftarrow: "M262 157\nl10-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\n 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\n 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\nc2 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\n 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\n-86-123l-10-10h399738v-40H218c328 0 0 0 0 0l-10-8c-26.7-20-65.7-43-117-69 2.7\n-2 6-3.7 10-5 36.7-16 72.3-37.3 107-64l10-8h399782v-40z\nm8 0v40h399730v-40zm0 194v40h399730v-40z", + // doublerightarrow is from glyph U+21D2 in font KaTeX Main + doublerightarrow: "M399738 392l\n-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\n 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\n-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\n-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\n-13h-6c-12 .7-18 4-18 10 0 2 1.7 7 5 15 23.3 46 52 87 86 123l10 10H0v40h399782\nc-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\n-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\n 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\n-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\nc-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\n 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\n 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\n 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\n-45 179-50h399577v120H403c-43.3 7-81 15-113 26-100.7 33-179.7 91-237 174-2.7\n 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\n 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\n 0 2 .3 4 1 18.688 2.7 76 4.3 172 5h399450v120H429l-6-1c-124.688-8-235-61.7\n-331-161C60.687 138.7 32.312 99.3 7 54L0 41V6z", + // overgroup is from the MnSymbol package (public domain) + leftgroup: "M400000 80\nH435C64 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\n 435 0h399565z", + leftgroupunder: "M400000 262\nH435C64 262 168.3 112.6 21 82c-5.9-1.2-18 0-18 0-2 0-3 1-3 3v38c76 158 257 219\n 435 219h399565z", + // Harpoons are from glyph U+21BD in font KaTeX Main + leftharpoon: "M0 267c.7 5.3 3 10 7 14h399993v-40H93c3.3\n-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\n-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\n-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\n 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\n-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\n-10.7 8-12 10-1.3 2-2 5.7-2 11zm100-26v40h399900v-40zM0 435v40h400000v-40z\nm0 0v40h400000v-40z", + leftharpoondown: "M7 241c-4 4-6.333 8.667-7 14 0 5.333.667 9 2 11s5.333\n 5.333 12 10c90.667 54 156 130 196 228 3.333 10.667 6.333 16.333 9 17 2 .667 5\n 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\n-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\n 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\n-2 18-6 2-2.7 1-9.7-3-21-32-87.3-82.7-157.7-152-211l-3-3h399907v-40H7zm93 0\nv40h399900v-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\n-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\n-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\n 71.5 23h399859zM103 281v-40h399897v40z", + leftlinesegment: "M40 281 V428 H0 V94 H40 V241 H400000 v40z\nM40 281 V428 H0 V94 H40 V241 H400000 v40z", + leftmapsto: "M40 281 V448H0V74H40V241H400000v40z\nM40 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\n-.3 23-1 0-1.3-5.3-13.7-16-37-18-35.3-41.3-69-70-101l-7-8h399905v-40H95l7-8\nc28.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\n 68 321 0 361zm0-174v-40h399900v40zm100 154v40h399900v-40z", + longequal: "M0 50 h400000 v40H0z m0 194h40000v40H0z\nM0 50 h400000 v40H0z m0 194h40000v40H0z", + midbrace: "M200428 334\nc-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\n-53.3 86.7-123.7 153-211 199-66.7 36-137.3 56.3-212 62H0V214h199568c178.3-11.7\n 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\n 12 44.7 59.3 101.3 106.3 170 141s145.3 54.3 229 60h199572v120z", + midbraceunder: "M199572 214\nc100.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\n 53.3-86.7 123.7-153 211-199 66.7-36 137.3-56.3 212-62h199568v120H200432c-178.3\n 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\n-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\n-320.3 177.6S193.1 320.6 193.1 249.8c0-71.4 46.9-178.2 319.5-178.2z\nm368.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\n60.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\n-451.1 228.8-386.3 0-452.7-137.5-452.7-228.8 0-92.4 66.4-230 452.7-230z\nm502.4 230c0-111.2-82.4-277.2-502.4-277.2s-504 166-504 277.2\nc0 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\n-480.5 177.6S202.1 320.6 202.1 249.8c0-71.4 70.5-178.2 479.3-178.2z\nm525.8 178.2c0-86.4-86.8-215.4-525.7-215.4-437.9 0-524.7 129-524.7 215.4 0\n85.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\n-707.8 275.8-606 0-710.2-165.8-710.2-275.8C311 218.8 415.2 53 1021.2 53z\nm770.4 277.1c0-131.2-126.4-327.6-770.5-327.6S248.4 198.9 248.4 330.1\nc0 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\n-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\n 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\n 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\n-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\n-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\n 151.7 139 205zm0 0v40h399900v-40z", + rightbrace: "M400000 542l\n-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\ns-117.2-55.8-184.5-66.5c-.7 0-2-.3-4-1-18.7-2.7-76-4.3-172-5H0V214h399571l6 1\nc124.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\n 28.7-117 45-179 50H-300V214h399897c43.3-7 81-15 113-26 100.7-33 179.7-91 237\n-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\n 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\n 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\n-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\n-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\n 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\n-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\n 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\nm0 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\n 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\n-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\n-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\n 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\n 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\n-64.7 57-92 95-27.3 38-48.7 77.7-64 119-3.3 8.7-5 14-5 16zM0 435v40h399900v-40z\nm0-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\n 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\n-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\n 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\nM399960 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\n 1 0 1.3 5.3 13.7 16 37 18 35.3 41.3 69 70 101l7 8H0v40h399905l-7 8c-28.7 32\n-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\n-167z M100 147v40h399900v-40zM0 341v40h399900v-40z", + // twoheadleftarrow is from glyph U+219E in font KaTeX AMS Regular + twoheadleftarrow: "M0 167c68 40\n 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\n-70-101l-7-8h125l9 7c50.7 39.3 85 86 103 140h46c0-4.7-6.3-18.7-19-42-18-35.3\n-40-67.3-66-96l-9-9h399716v-40H284l9-9c26-28.7 48-60.7 66-96 12.7-23.333 19\n-37.333 19-42h-46c-18 54-52.3 100.7-103 140l-9 7H95l7-8c28.7-32 52-65.7 70-101\n 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\nc-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\n 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\n 18 35.3 40 67.3 66 96l9 9H0v40h399716l-9 9c-26 28.7-48 60.7-66 96-12.7 23.333\n-19 37.333-19 42h46c18-54 52.3-100.7 103-140l9-7h125l-7 8c-28.7 32-52 65.7-70\n 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 + tilde1: "M200 55.538c-77 0-168 73.953-177 73.953-3 0-7\n-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\n 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\n 4.35-2 7.613-5 9.788l-19 13.05c-92 63.077-116.937 75.308-183 76.128\n-68.267.847-113-73.952-191-73.952z", + // ditto tilde2, tilde3, & tilde4 + tilde2: "M344 55.266c-142 0-300.638 81.316-311.5 86.418\n-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\n 31.9 283 0 358 0c148 0 188 122 331 122s314-97 326-97c4 0 8 2 10 7l7 21.114\nc1 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\n 181.476 676 181.476c-149 0-189-126.21-332-126.21z", + tilde3: "M786 59C457 59 32 175.242 13 175.242c-6 0-10-3.457\n-11-10.37L.15 138c-1-7 3-12 10-13l19.2-6.4C378.4 40.7 634.3 0 804.3 0c337 0\n 411.8 157 746.8 157 328 0 754-112 773-112 5 0 10 3 11 9l1 14.075c1 8.066-.697\n 16.595-6.697 17.492l-21.052 7.31c-367.9 98.146-609.15 122.696-778.15 122.696\n -338 0-409-156.573-744-156.573z", + tilde4: "M786 58C457 58 32 177.487 13 177.487c-6 0-10-3.345\n-11-10.035L.15 143c-1-7 3-12 10-13l22-6.7C381.2 35 637.15 0 807.15 0c337 0 409\n 177 744 177 328 0 754-127 773-127 5 0 10 3 11 9l1 14.794c1 7.805-3 13.38-9\n 14.495l-20.7 5.574c-366.85 99.79-607.3 139.372-776.3 139.372-338 0-409\n -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\n3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11\n10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63\n-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\n-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59\nH213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359\nc-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\nc-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\n-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\n-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\n-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,\n-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,\n-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,\n-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,\n-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\nc4 -14.7 6 -23 6 -25c0 -7.3 -7 -11 -21 -11c-8 0 -13.2 0.8 -15.5 2.5\nc-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\ns-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\n121.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\ns2.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\nc0 -2 -2 -10.3 -6 -25c-20 -79.3 -65 -146.7 -135 -202l-3 -3h399890z\nM100 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\n-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\n13.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\n-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\n-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\n-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\n151.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\nc1.3,2,5.3,5.3,12,10c90.7,54,156,130,196,228c3.3,10.7,6.3,16.3,9,17\nc2,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\nc-32,-87.3,-82.7,-157.7,-152,-211c0,0,-3,-3,-3,-3l399351,0l0,-40\nc-398570,0,-399437,0,-399437,0z M593 435 v40 H399500 v-40z\nM0 281 v-40 H399908 v40z M0 281 v-40 H399908 v40z", + rightharpoonaboveshortbar: "M0,241 l0,40c399126,0,399993,0,399993,0\nc4.7,-4.7,7,-9.3,7,-14c0,-9.3,-3.7,-15.3,-11,-18c-92.7,-56.7,-159,-133.7,-199,\n-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\nc-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\nM0 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\nc1.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,\n1c0,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,\n-152,-211c0,0,-3,-3,-3,-3l399907,0l0,-40c-399126,0,-399993,0,-399993,0z\nM93 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\nc4.7,-4.7,7,-9.3,7,-14c0,-9.3,-3.7,-15.3,-11,-18c-92.7,-56.7,-159,-133.7,-199,\n-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\nc-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\nM500 241 v40 H399408 v-40z M500 435 v40 H400000 v-40z" +}; + + +/** + * This node represents a document fragment, which contains elements, but when + * placed into the DOM doesn't have any representation itself. It only contains + * children and doesn't have any DOM node properties. + */ +var DocumentFragment = /*#__PURE__*/function () { + // HtmlDomNode + // Never used; needed for satisfying interface. + function DocumentFragment(children) { + this.children = void 0; + this.classes = void 0; + this.height = void 0; + this.depth = void 0; + this.maxFontSize = void 0; + this.style = void 0; + this.children = children; + this.classes = []; + this.height = 0; + this.depth = 0; + this.maxFontSize = 0; + this.style = {}; + } + + var _proto = DocumentFragment.prototype; + + _proto.hasClass = function hasClass(className) { + return utils.contains(this.classes, className); + } + /** Convert the fragment into a node. */ + ; + + _proto.toNode = function toNode() { + var frag = document.createDocumentFragment(); + + for (var i = 0; i < this.children.length; i++) { + frag.appendChild(this.children[i].toNode()); + } + + return frag; + } + /** Convert the fragment into HTML markup. */ + ; + + _proto.toMarkup = function toMarkup() { + var markup = ""; // Simply concatenate the markup for the children together. + + for (var i = 0; i < this.children.length; i++) { + markup += this.children[i].toMarkup(); + } + + return markup; + } + /** + * Converts the math node into a string, similar to innerText. Applies to + * MathDomNode's only. + */ + ; + + _proto.toText = function toText() { + // To avoid this, we would subclass documentFragment separately for + // MathML, but polyfills for subclassing is expensive per PR 1469. + // $FlowFixMe: Only works for ChildType = MathDomNode. + var toText = function toText(child) { + return child.toText(); + }; + + return this.children.map(toText).join(""); + }; + + return DocumentFragment; +}(); +// This file is GENERATED by buildMetrics.sh. DO NOT MODIFY. +/* harmony default export */ var fontMetricsData = ({ + "AMS-Regular": { + "32": [0, 0, 0, 0, 0.25], + "65": [0, 0.68889, 0, 0, 0.72222], + "66": [0, 0.68889, 0, 0, 0.66667], + "67": [0, 0.68889, 0, 0, 0.72222], + "68": [0, 0.68889, 0, 0, 0.72222], + "69": [0, 0.68889, 0, 0, 0.66667], + "70": [0, 0.68889, 0, 0, 0.61111], + "71": [0, 0.68889, 0, 0, 0.77778], + "72": [0, 0.68889, 0, 0, 0.77778], + "73": [0, 0.68889, 0, 0, 0.38889], + "74": [0.16667, 0.68889, 0, 0, 0.5], + "75": [0, 0.68889, 0, 0, 0.77778], + "76": [0, 0.68889, 0, 0, 0.66667], + "77": [0, 0.68889, 0, 0, 0.94445], + "78": [0, 0.68889, 0, 0, 0.72222], + "79": [0.16667, 0.68889, 0, 0, 0.77778], + "80": [0, 0.68889, 0, 0, 0.61111], + "81": [0.16667, 0.68889, 0, 0, 0.77778], + "82": [0, 0.68889, 0, 0, 0.72222], + "83": [0, 0.68889, 0, 0, 0.55556], + "84": [0, 0.68889, 0, 0, 0.66667], + "85": [0, 0.68889, 0, 0, 0.72222], + "86": [0, 0.68889, 0, 0, 0.72222], + "87": [0, 0.68889, 0, 0, 1.0], + "88": [0, 0.68889, 0, 0, 0.72222], + "89": [0, 0.68889, 0, 0, 0.72222], + "90": [0, 0.68889, 0, 0, 0.66667], + "107": [0, 0.68889, 0, 0, 0.55556], + "160": [0, 0, 0, 0, 0.25], + "165": [0, 0.675, 0.025, 0, 0.75], + "174": [0.15559, 0.69224, 0, 0, 0.94666], + "240": [0, 0.68889, 0, 0, 0.55556], + "295": [0, 0.68889, 0, 0, 0.54028], + "710": [0, 0.825, 0, 0, 2.33334], + "732": [0, 0.9, 0, 0, 2.33334], + "770": [0, 0.825, 0, 0, 2.33334], + "771": [0, 0.9, 0, 0, 2.33334], + "989": [0.08167, 0.58167, 0, 0, 0.77778], + "1008": [0, 0.43056, 0.04028, 0, 0.66667], + "8245": [0, 0.54986, 0, 0, 0.275], + "8463": [0, 0.68889, 0, 0, 0.54028], + "8487": [0, 0.68889, 0, 0, 0.72222], + "8498": [0, 0.68889, 0, 0, 0.55556], + "8502": [0, 0.68889, 0, 0, 0.66667], + "8503": [0, 0.68889, 0, 0, 0.44445], + "8504": [0, 0.68889, 0, 0, 0.66667], + "8513": [0, 0.68889, 0, 0, 0.63889], + "8592": [-0.03598, 0.46402, 0, 0, 0.5], + "8594": [-0.03598, 0.46402, 0, 0, 0.5], + "8602": [-0.13313, 0.36687, 0, 0, 1.0], + "8603": [-0.13313, 0.36687, 0, 0, 1.0], + "8606": [0.01354, 0.52239, 0, 0, 1.0], + "8608": [0.01354, 0.52239, 0, 0, 1.0], + "8610": [0.01354, 0.52239, 0, 0, 1.11111], + "8611": [0.01354, 0.52239, 0, 0, 1.11111], + "8619": [0, 0.54986, 0, 0, 1.0], + "8620": [0, 0.54986, 0, 0, 1.0], + "8621": [-0.13313, 0.37788, 0, 0, 1.38889], + "8622": [-0.13313, 0.36687, 0, 0, 1.0], + "8624": [0, 0.69224, 0, 0, 0.5], + "8625": [0, 0.69224, 0, 0, 0.5], + "8630": [0, 0.43056, 0, 0, 1.0], + "8631": [0, 0.43056, 0, 0, 1.0], + "8634": [0.08198, 0.58198, 0, 0, 0.77778], + "8635": [0.08198, 0.58198, 0, 0, 0.77778], + "8638": [0.19444, 0.69224, 0, 0, 0.41667], + "8639": [0.19444, 0.69224, 0, 0, 0.41667], + "8642": [0.19444, 0.69224, 0, 0, 0.41667], + "8643": [0.19444, 0.69224, 0, 0, 0.41667], + "8644": [0.1808, 0.675, 0, 0, 1.0], + "8646": [0.1808, 0.675, 0, 0, 1.0], + "8647": [0.1808, 0.675, 0, 0, 1.0], + "8648": [0.19444, 0.69224, 0, 0, 0.83334], + "8649": [0.1808, 0.675, 0, 0, 1.0], + "8650": [0.19444, 0.69224, 0, 0, 0.83334], + "8651": [0.01354, 0.52239, 0, 0, 1.0], + "8652": [0.01354, 0.52239, 0, 0, 1.0], + "8653": [-0.13313, 0.36687, 0, 0, 1.0], + "8654": [-0.13313, 0.36687, 0, 0, 1.0], + "8655": [-0.13313, 0.36687, 0, 0, 1.0], + "8666": [0.13667, 0.63667, 0, 0, 1.0], + "8667": [0.13667, 0.63667, 0, 0, 1.0], + "8669": [-0.13313, 0.37788, 0, 0, 1.0], + "8672": [-0.064, 0.437, 0, 0, 1.334], + "8674": [-0.064, 0.437, 0, 0, 1.334], + "8705": [0, 0.825, 0, 0, 0.5], + "8708": [0, 0.68889, 0, 0, 0.55556], + "8709": [0.08167, 0.58167, 0, 0, 0.77778], + "8717": [0, 0.43056, 0, 0, 0.42917], + "8722": [-0.03598, 0.46402, 0, 0, 0.5], + "8724": [0.08198, 0.69224, 0, 0, 0.77778], + "8726": [0.08167, 0.58167, 0, 0, 0.77778], + "8733": [0, 0.69224, 0, 0, 0.77778], + "8736": [0, 0.69224, 0, 0, 0.72222], + "8737": [0, 0.69224, 0, 0, 0.72222], + "8738": [0.03517, 0.52239, 0, 0, 0.72222], + "8739": [0.08167, 0.58167, 0, 0, 0.22222], + "8740": [0.25142, 0.74111, 0, 0, 0.27778], + "8741": [0.08167, 0.58167, 0, 0, 0.38889], + "8742": [0.25142, 0.74111, 0, 0, 0.5], + "8756": [0, 0.69224, 0, 0, 0.66667], + "8757": [0, 0.69224, 0, 0, 0.66667], + "8764": [-0.13313, 0.36687, 0, 0, 0.77778], + "8765": [-0.13313, 0.37788, 0, 0, 0.77778], + "8769": [-0.13313, 0.36687, 0, 0, 0.77778], + "8770": [-0.03625, 0.46375, 0, 0, 0.77778], + "8774": [0.30274, 0.79383, 0, 0, 0.77778], + "8776": [-0.01688, 0.48312, 0, 0, 0.77778], + "8778": [0.08167, 0.58167, 0, 0, 0.77778], + "8782": [0.06062, 0.54986, 0, 0, 0.77778], + "8783": [0.06062, 0.54986, 0, 0, 0.77778], + "8785": [0.08198, 0.58198, 0, 0, 0.77778], + "8786": [0.08198, 0.58198, 0, 0, 0.77778], + "8787": [0.08198, 0.58198, 0, 0, 0.77778], + "8790": [0, 0.69224, 0, 0, 0.77778], + "8791": [0.22958, 0.72958, 0, 0, 0.77778], + "8796": [0.08198, 0.91667, 0, 0, 0.77778], + "8806": [0.25583, 0.75583, 0, 0, 0.77778], + "8807": [0.25583, 0.75583, 0, 0, 0.77778], + "8808": [0.25142, 0.75726, 0, 0, 0.77778], + "8809": [0.25142, 0.75726, 0, 0, 0.77778], + "8812": [0.25583, 0.75583, 0, 0, 0.5], + "8814": [0.20576, 0.70576, 0, 0, 0.77778], + "8815": [0.20576, 0.70576, 0, 0, 0.77778], + "8816": [0.30274, 0.79383, 0, 0, 0.77778], + "8817": [0.30274, 0.79383, 0, 0, 0.77778], + "8818": [0.22958, 0.72958, 0, 0, 0.77778], + "8819": [0.22958, 0.72958, 0, 0, 0.77778], + "8822": [0.1808, 0.675, 0, 0, 0.77778], + "8823": [0.1808, 0.675, 0, 0, 0.77778], + "8828": [0.13667, 0.63667, 0, 0, 0.77778], + "8829": [0.13667, 0.63667, 0, 0, 0.77778], + "8830": [0.22958, 0.72958, 0, 0, 0.77778], + "8831": [0.22958, 0.72958, 0, 0, 0.77778], + "8832": [0.20576, 0.70576, 0, 0, 0.77778], + "8833": [0.20576, 0.70576, 0, 0, 0.77778], + "8840": [0.30274, 0.79383, 0, 0, 0.77778], + "8841": [0.30274, 0.79383, 0, 0, 0.77778], + "8842": [0.13597, 0.63597, 0, 0, 0.77778], + "8843": [0.13597, 0.63597, 0, 0, 0.77778], + "8847": [0.03517, 0.54986, 0, 0, 0.77778], + "8848": [0.03517, 0.54986, 0, 0, 0.77778], + "8858": [0.08198, 0.58198, 0, 0, 0.77778], + "8859": [0.08198, 0.58198, 0, 0, 0.77778], + "8861": [0.08198, 0.58198, 0, 0, 0.77778], + "8862": [0, 0.675, 0, 0, 0.77778], + "8863": [0, 0.675, 0, 0, 0.77778], + "8864": [0, 0.675, 0, 0, 0.77778], + "8865": [0, 0.675, 0, 0, 0.77778], + "8872": [0, 0.69224, 0, 0, 0.61111], + "8873": [0, 0.69224, 0, 0, 0.72222], + "8874": [0, 0.69224, 0, 0, 0.88889], + "8876": [0, 0.68889, 0, 0, 0.61111], + "8877": [0, 0.68889, 0, 0, 0.61111], + "8878": [0, 0.68889, 0, 0, 0.72222], + "8879": [0, 0.68889, 0, 0, 0.72222], + "8882": [0.03517, 0.54986, 0, 0, 0.77778], + "8883": [0.03517, 0.54986, 0, 0, 0.77778], + "8884": [0.13667, 0.63667, 0, 0, 0.77778], + "8885": [0.13667, 0.63667, 0, 0, 0.77778], + "8888": [0, 0.54986, 0, 0, 1.11111], + "8890": [0.19444, 0.43056, 0, 0, 0.55556], + "8891": [0.19444, 0.69224, 0, 0, 0.61111], + "8892": [0.19444, 0.69224, 0, 0, 0.61111], + "8901": [0, 0.54986, 0, 0, 0.27778], + "8903": [0.08167, 0.58167, 0, 0, 0.77778], + "8905": [0.08167, 0.58167, 0, 0, 0.77778], + "8906": [0.08167, 0.58167, 0, 0, 0.77778], + "8907": [0, 0.69224, 0, 0, 0.77778], + "8908": [0, 0.69224, 0, 0, 0.77778], + "8909": [-0.03598, 0.46402, 0, 0, 0.77778], + "8910": [0, 0.54986, 0, 0, 0.76042], + "8911": [0, 0.54986, 0, 0, 0.76042], + "8912": [0.03517, 0.54986, 0, 0, 0.77778], + "8913": [0.03517, 0.54986, 0, 0, 0.77778], + "8914": [0, 0.54986, 0, 0, 0.66667], + "8915": [0, 0.54986, 0, 0, 0.66667], + "8916": [0, 0.69224, 0, 0, 0.66667], + "8918": [0.0391, 0.5391, 0, 0, 0.77778], + "8919": [0.0391, 0.5391, 0, 0, 0.77778], + "8920": [0.03517, 0.54986, 0, 0, 1.33334], + "8921": [0.03517, 0.54986, 0, 0, 1.33334], + "8922": [0.38569, 0.88569, 0, 0, 0.77778], + "8923": [0.38569, 0.88569, 0, 0, 0.77778], + "8926": [0.13667, 0.63667, 0, 0, 0.77778], + "8927": [0.13667, 0.63667, 0, 0, 0.77778], + "8928": [0.30274, 0.79383, 0, 0, 0.77778], + "8929": [0.30274, 0.79383, 0, 0, 0.77778], + "8934": [0.23222, 0.74111, 0, 0, 0.77778], + "8935": [0.23222, 0.74111, 0, 0, 0.77778], + "8936": [0.23222, 0.74111, 0, 0, 0.77778], + "8937": [0.23222, 0.74111, 0, 0, 0.77778], + "8938": [0.20576, 0.70576, 0, 0, 0.77778], + "8939": [0.20576, 0.70576, 0, 0, 0.77778], + "8940": [0.30274, 0.79383, 0, 0, 0.77778], + "8941": [0.30274, 0.79383, 0, 0, 0.77778], + "8994": [0.19444, 0.69224, 0, 0, 0.77778], + "8995": [0.19444, 0.69224, 0, 0, 0.77778], + "9416": [0.15559, 0.69224, 0, 0, 0.90222], + "9484": [0, 0.69224, 0, 0, 0.5], + "9488": [0, 0.69224, 0, 0, 0.5], + "9492": [0, 0.37788, 0, 0, 0.5], + "9496": [0, 0.37788, 0, 0, 0.5], + "9585": [0.19444, 0.68889, 0, 0, 0.88889], + "9586": [0.19444, 0.74111, 0, 0, 0.88889], + "9632": [0, 0.675, 0, 0, 0.77778], + "9633": [0, 0.675, 0, 0, 0.77778], + "9650": [0, 0.54986, 0, 0, 0.72222], + "9651": [0, 0.54986, 0, 0, 0.72222], + "9654": [0.03517, 0.54986, 0, 0, 0.77778], + "9660": [0, 0.54986, 0, 0, 0.72222], + "9661": [0, 0.54986, 0, 0, 0.72222], + "9664": [0.03517, 0.54986, 0, 0, 0.77778], + "9674": [0.11111, 0.69224, 0, 0, 0.66667], + "9733": [0.19444, 0.69224, 0, 0, 0.94445], + "10003": [0, 0.69224, 0, 0, 0.83334], + "10016": [0, 0.69224, 0, 0, 0.83334], + "10731": [0.11111, 0.69224, 0, 0, 0.66667], + "10846": [0.19444, 0.75583, 0, 0, 0.61111], + "10877": [0.13667, 0.63667, 0, 0, 0.77778], + "10878": [0.13667, 0.63667, 0, 0, 0.77778], + "10885": [0.25583, 0.75583, 0, 0, 0.77778], + "10886": [0.25583, 0.75583, 0, 0, 0.77778], + "10887": [0.13597, 0.63597, 0, 0, 0.77778], + "10888": [0.13597, 0.63597, 0, 0, 0.77778], + "10889": [0.26167, 0.75726, 0, 0, 0.77778], + "10890": [0.26167, 0.75726, 0, 0, 0.77778], + "10891": [0.48256, 0.98256, 0, 0, 0.77778], + "10892": [0.48256, 0.98256, 0, 0, 0.77778], + "10901": [0.13667, 0.63667, 0, 0, 0.77778], + "10902": [0.13667, 0.63667, 0, 0, 0.77778], + "10933": [0.25142, 0.75726, 0, 0, 0.77778], + "10934": [0.25142, 0.75726, 0, 0, 0.77778], + "10935": [0.26167, 0.75726, 0, 0, 0.77778], + "10936": [0.26167, 0.75726, 0, 0, 0.77778], + "10937": [0.26167, 0.75726, 0, 0, 0.77778], + "10938": [0.26167, 0.75726, 0, 0, 0.77778], + "10949": [0.25583, 0.75583, 0, 0, 0.77778], + "10950": [0.25583, 0.75583, 0, 0, 0.77778], + "10955": [0.28481, 0.79383, 0, 0, 0.77778], + "10956": [0.28481, 0.79383, 0, 0, 0.77778], + "57350": [0.08167, 0.58167, 0, 0, 0.22222], + "57351": [0.08167, 0.58167, 0, 0, 0.38889], + "57352": [0.08167, 0.58167, 0, 0, 0.77778], + "57353": [0, 0.43056, 0.04028, 0, 0.66667], + "57356": [0.25142, 0.75726, 0, 0, 0.77778], + "57357": [0.25142, 0.75726, 0, 0, 0.77778], + "57358": [0.41951, 0.91951, 0, 0, 0.77778], + "57359": [0.30274, 0.79383, 0, 0, 0.77778], + "57360": [0.30274, 0.79383, 0, 0, 0.77778], + "57361": [0.41951, 0.91951, 0, 0, 0.77778], + "57366": [0.25142, 0.75726, 0, 0, 0.77778], + "57367": [0.25142, 0.75726, 0, 0, 0.77778], + "57368": [0.25142, 0.75726, 0, 0, 0.77778], + "57369": [0.25142, 0.75726, 0, 0, 0.77778], + "57370": [0.13597, 0.63597, 0, 0, 0.77778], + "57371": [0.13597, 0.63597, 0, 0, 0.77778] + }, + "Caligraphic-Regular": { + "32": [0, 0, 0, 0, 0.25], + "65": [0, 0.68333, 0, 0.19445, 0.79847], + "66": [0, 0.68333, 0.03041, 0.13889, 0.65681], + "67": [0, 0.68333, 0.05834, 0.13889, 0.52653], + "68": [0, 0.68333, 0.02778, 0.08334, 0.77139], + "69": [0, 0.68333, 0.08944, 0.11111, 0.52778], + "70": [0, 0.68333, 0.09931, 0.11111, 0.71875], + "71": [0.09722, 0.68333, 0.0593, 0.11111, 0.59487], + "72": [0, 0.68333, 0.00965, 0.11111, 0.84452], + "73": [0, 0.68333, 0.07382, 0, 0.54452], + "74": [0.09722, 0.68333, 0.18472, 0.16667, 0.67778], + "75": [0, 0.68333, 0.01445, 0.05556, 0.76195], + "76": [0, 0.68333, 0, 0.13889, 0.68972], + "77": [0, 0.68333, 0, 0.13889, 1.2009], + "78": [0, 0.68333, 0.14736, 0.08334, 0.82049], + "79": [0, 0.68333, 0.02778, 0.11111, 0.79611], + "80": [0, 0.68333, 0.08222, 0.08334, 0.69556], + "81": [0.09722, 0.68333, 0, 0.11111, 0.81667], + "82": [0, 0.68333, 0, 0.08334, 0.8475], + "83": [0, 0.68333, 0.075, 0.13889, 0.60556], + "84": [0, 0.68333, 0.25417, 0, 0.54464], + "85": [0, 0.68333, 0.09931, 0.08334, 0.62583], + "86": [0, 0.68333, 0.08222, 0, 0.61278], + "87": [0, 0.68333, 0.08222, 0.08334, 0.98778], + "88": [0, 0.68333, 0.14643, 0.13889, 0.7133], + "89": [0.09722, 0.68333, 0.08222, 0.08334, 0.66834], + "90": [0, 0.68333, 0.07944, 0.13889, 0.72473], + "160": [0, 0, 0, 0, 0.25] + }, + "Fraktur-Regular": { + "32": [0, 0, 0, 0, 0.25], + "33": [0, 0.69141, 0, 0, 0.29574], + "34": [0, 0.69141, 0, 0, 0.21471], + "38": [0, 0.69141, 0, 0, 0.73786], + "39": [0, 0.69141, 0, 0, 0.21201], + "40": [0.24982, 0.74947, 0, 0, 0.38865], + "41": [0.24982, 0.74947, 0, 0, 0.38865], + "42": [0, 0.62119, 0, 0, 0.27764], + "43": [0.08319, 0.58283, 0, 0, 0.75623], + "44": [0, 0.10803, 0, 0, 0.27764], + "45": [0.08319, 0.58283, 0, 0, 0.75623], + "46": [0, 0.10803, 0, 0, 0.27764], + "47": [0.24982, 0.74947, 0, 0, 0.50181], + "48": [0, 0.47534, 0, 0, 0.50181], + "49": [0, 0.47534, 0, 0, 0.50181], + "50": [0, 0.47534, 0, 0, 0.50181], + "51": [0.18906, 0.47534, 0, 0, 0.50181], + "52": [0.18906, 0.47534, 0, 0, 0.50181], + "53": [0.18906, 0.47534, 0, 0, 0.50181], + "54": [0, 0.69141, 0, 0, 0.50181], + "55": [0.18906, 0.47534, 0, 0, 0.50181], + "56": [0, 0.69141, 0, 0, 0.50181], + "57": [0.18906, 0.47534, 0, 0, 0.50181], + "58": [0, 0.47534, 0, 0, 0.21606], + "59": [0.12604, 0.47534, 0, 0, 0.21606], + "61": [-0.13099, 0.36866, 0, 0, 0.75623], + "63": [0, 0.69141, 0, 0, 0.36245], + "65": [0, 0.69141, 0, 0, 0.7176], + "66": [0, 0.69141, 0, 0, 0.88397], + "67": [0, 0.69141, 0, 0, 0.61254], + "68": [0, 0.69141, 0, 0, 0.83158], + "69": [0, 0.69141, 0, 0, 0.66278], + "70": [0.12604, 0.69141, 0, 0, 0.61119], + "71": [0, 0.69141, 0, 0, 0.78539], + "72": [0.06302, 0.69141, 0, 0, 0.7203], + "73": [0, 0.69141, 0, 0, 0.55448], + "74": [0.12604, 0.69141, 0, 0, 0.55231], + "75": [0, 0.69141, 0, 0, 0.66845], + "76": [0, 0.69141, 0, 0, 0.66602], + "77": [0, 0.69141, 0, 0, 1.04953], + "78": [0, 0.69141, 0, 0, 0.83212], + "79": [0, 0.69141, 0, 0, 0.82699], + "80": [0.18906, 0.69141, 0, 0, 0.82753], + "81": [0.03781, 0.69141, 0, 0, 0.82699], + "82": [0, 0.69141, 0, 0, 0.82807], + "83": [0, 0.69141, 0, 0, 0.82861], + "84": [0, 0.69141, 0, 0, 0.66899], + "85": [0, 0.69141, 0, 0, 0.64576], + "86": [0, 0.69141, 0, 0, 0.83131], + "87": [0, 0.69141, 0, 0, 1.04602], + "88": [0, 0.69141, 0, 0, 0.71922], + "89": [0.18906, 0.69141, 0, 0, 0.83293], + "90": [0.12604, 0.69141, 0, 0, 0.60201], + "91": [0.24982, 0.74947, 0, 0, 0.27764], + "93": [0.24982, 0.74947, 0, 0, 0.27764], + "94": [0, 0.69141, 0, 0, 0.49965], + "97": [0, 0.47534, 0, 0, 0.50046], + "98": [0, 0.69141, 0, 0, 0.51315], + "99": [0, 0.47534, 0, 0, 0.38946], + "100": [0, 0.62119, 0, 0, 0.49857], + "101": [0, 0.47534, 0, 0, 0.40053], + "102": [0.18906, 0.69141, 0, 0, 0.32626], + "103": [0.18906, 0.47534, 0, 0, 0.5037], + "104": [0.18906, 0.69141, 0, 0, 0.52126], + "105": [0, 0.69141, 0, 0, 0.27899], + "106": [0, 0.69141, 0, 0, 0.28088], + "107": [0, 0.69141, 0, 0, 0.38946], + "108": [0, 0.69141, 0, 0, 0.27953], + "109": [0, 0.47534, 0, 0, 0.76676], + "110": [0, 0.47534, 0, 0, 0.52666], + "111": [0, 0.47534, 0, 0, 0.48885], + "112": [0.18906, 0.52396, 0, 0, 0.50046], + "113": [0.18906, 0.47534, 0, 0, 0.48912], + "114": [0, 0.47534, 0, 0, 0.38919], + "115": [0, 0.47534, 0, 0, 0.44266], + "116": [0, 0.62119, 0, 0, 0.33301], + "117": [0, 0.47534, 0, 0, 0.5172], + "118": [0, 0.52396, 0, 0, 0.5118], + "119": [0, 0.52396, 0, 0, 0.77351], + "120": [0.18906, 0.47534, 0, 0, 0.38865], + "121": [0.18906, 0.47534, 0, 0, 0.49884], + "122": [0.18906, 0.47534, 0, 0, 0.39054], + "160": [0, 0, 0, 0, 0.25], + "8216": [0, 0.69141, 0, 0, 0.21471], + "8217": [0, 0.69141, 0, 0, 0.21471], + "58112": [0, 0.62119, 0, 0, 0.49749], + "58113": [0, 0.62119, 0, 0, 0.4983], + "58114": [0.18906, 0.69141, 0, 0, 0.33328], + "58115": [0.18906, 0.69141, 0, 0, 0.32923], + "58116": [0.18906, 0.47534, 0, 0, 0.50343], + "58117": [0, 0.69141, 0, 0, 0.33301], + "58118": [0, 0.62119, 0, 0, 0.33409], + "58119": [0, 0.47534, 0, 0, 0.50073] + }, + "Main-Bold": { + "32": [0, 0, 0, 0, 0.25], + "33": [0, 0.69444, 0, 0, 0.35], + "34": [0, 0.69444, 0, 0, 0.60278], + "35": [0.19444, 0.69444, 0, 0, 0.95833], + "36": [0.05556, 0.75, 0, 0, 0.575], + "37": [0.05556, 0.75, 0, 0, 0.95833], + "38": [0, 0.69444, 0, 0, 0.89444], + "39": [0, 0.69444, 0, 0, 0.31944], + "40": [0.25, 0.75, 0, 0, 0.44722], + "41": [0.25, 0.75, 0, 0, 0.44722], + "42": [0, 0.75, 0, 0, 0.575], + "43": [0.13333, 0.63333, 0, 0, 0.89444], + "44": [0.19444, 0.15556, 0, 0, 0.31944], + "45": [0, 0.44444, 0, 0, 0.38333], + "46": [0, 0.15556, 0, 0, 0.31944], + "47": [0.25, 0.75, 0, 0, 0.575], + "48": [0, 0.64444, 0, 0, 0.575], + "49": [0, 0.64444, 0, 0, 0.575], + "50": [0, 0.64444, 0, 0, 0.575], + "51": [0, 0.64444, 0, 0, 0.575], + "52": [0, 0.64444, 0, 0, 0.575], + "53": [0, 0.64444, 0, 0, 0.575], + "54": [0, 0.64444, 0, 0, 0.575], + "55": [0, 0.64444, 0, 0, 0.575], + "56": [0, 0.64444, 0, 0, 0.575], + "57": [0, 0.64444, 0, 0, 0.575], + "58": [0, 0.44444, 0, 0, 0.31944], + "59": [0.19444, 0.44444, 0, 0, 0.31944], + "60": [0.08556, 0.58556, 0, 0, 0.89444], + "61": [-0.10889, 0.39111, 0, 0, 0.89444], + "62": [0.08556, 0.58556, 0, 0, 0.89444], + "63": [0, 0.69444, 0, 0, 0.54305], + "64": [0, 0.69444, 0, 0, 0.89444], + "65": [0, 0.68611, 0, 0, 0.86944], + "66": [0, 0.68611, 0, 0, 0.81805], + "67": [0, 0.68611, 0, 0, 0.83055], + "68": [0, 0.68611, 0, 0, 0.88194], + "69": [0, 0.68611, 0, 0, 0.75555], + "70": [0, 0.68611, 0, 0, 0.72361], + "71": [0, 0.68611, 0, 0, 0.90416], + "72": [0, 0.68611, 0, 0, 0.9], + "73": [0, 0.68611, 0, 0, 0.43611], + "74": [0, 0.68611, 0, 0, 0.59444], + "75": [0, 0.68611, 0, 0, 0.90138], + "76": [0, 0.68611, 0, 0, 0.69166], + "77": [0, 0.68611, 0, 0, 1.09166], + "78": [0, 0.68611, 0, 0, 0.9], + "79": [0, 0.68611, 0, 0, 0.86388], + "80": [0, 0.68611, 0, 0, 0.78611], + "81": [0.19444, 0.68611, 0, 0, 0.86388], + "82": [0, 0.68611, 0, 0, 0.8625], + "83": [0, 0.68611, 0, 0, 0.63889], + "84": [0, 0.68611, 0, 0, 0.8], + "85": [0, 0.68611, 0, 0, 0.88472], + "86": [0, 0.68611, 0.01597, 0, 0.86944], + "87": [0, 0.68611, 0.01597, 0, 1.18888], + "88": [0, 0.68611, 0, 0, 0.86944], + "89": [0, 0.68611, 0.02875, 0, 0.86944], + "90": [0, 0.68611, 0, 0, 0.70277], + "91": [0.25, 0.75, 0, 0, 0.31944], + "92": [0.25, 0.75, 0, 0, 0.575], + "93": [0.25, 0.75, 0, 0, 0.31944], + "94": [0, 0.69444, 0, 0, 0.575], + "95": [0.31, 0.13444, 0.03194, 0, 0.575], + "97": [0, 0.44444, 0, 0, 0.55902], + "98": [0, 0.69444, 0, 0, 0.63889], + "99": [0, 0.44444, 0, 0, 0.51111], + "100": [0, 0.69444, 0, 0, 0.63889], + "101": [0, 0.44444, 0, 0, 0.52708], + "102": [0, 0.69444, 0.10903, 0, 0.35139], + "103": [0.19444, 0.44444, 0.01597, 0, 0.575], + "104": [0, 0.69444, 0, 0, 0.63889], + "105": [0, 0.69444, 0, 0, 0.31944], + "106": [0.19444, 0.69444, 0, 0, 0.35139], + "107": [0, 0.69444, 0, 0, 0.60694], + "108": [0, 0.69444, 0, 0, 0.31944], + "109": [0, 0.44444, 0, 0, 0.95833], + "110": [0, 0.44444, 0, 0, 0.63889], + "111": [0, 0.44444, 0, 0, 0.575], + "112": [0.19444, 0.44444, 0, 0, 0.63889], + "113": [0.19444, 0.44444, 0, 0, 0.60694], + "114": [0, 0.44444, 0, 0, 0.47361], + "115": [0, 0.44444, 0, 0, 0.45361], + "116": [0, 0.63492, 0, 0, 0.44722], + "117": [0, 0.44444, 0, 0, 0.63889], + "118": [0, 0.44444, 0.01597, 0, 0.60694], + "119": [0, 0.44444, 0.01597, 0, 0.83055], + "120": [0, 0.44444, 0, 0, 0.60694], + "121": [0.19444, 0.44444, 0.01597, 0, 0.60694], + "122": [0, 0.44444, 0, 0, 0.51111], + "123": [0.25, 0.75, 0, 0, 0.575], + "124": [0.25, 0.75, 0, 0, 0.31944], + "125": [0.25, 0.75, 0, 0, 0.575], + "126": [0.35, 0.34444, 0, 0, 0.575], + "160": [0, 0, 0, 0, 0.25], + "163": [0, 0.69444, 0, 0, 0.86853], + "168": [0, 0.69444, 0, 0, 0.575], + "172": [0, 0.44444, 0, 0, 0.76666], + "176": [0, 0.69444, 0, 0, 0.86944], + "177": [0.13333, 0.63333, 0, 0, 0.89444], + "184": [0.17014, 0, 0, 0, 0.51111], + "198": [0, 0.68611, 0, 0, 1.04166], + "215": [0.13333, 0.63333, 0, 0, 0.89444], + "216": [0.04861, 0.73472, 0, 0, 0.89444], + "223": [0, 0.69444, 0, 0, 0.59722], + "230": [0, 0.44444, 0, 0, 0.83055], + "247": [0.13333, 0.63333, 0, 0, 0.89444], + "248": [0.09722, 0.54167, 0, 0, 0.575], + "305": [0, 0.44444, 0, 0, 0.31944], + "338": [0, 0.68611, 0, 0, 1.16944], + "339": [0, 0.44444, 0, 0, 0.89444], + "567": [0.19444, 0.44444, 0, 0, 0.35139], + "710": [0, 0.69444, 0, 0, 0.575], + "711": [0, 0.63194, 0, 0, 0.575], + "713": [0, 0.59611, 0, 0, 0.575], + "714": [0, 0.69444, 0, 0, 0.575], + "715": [0, 0.69444, 0, 0, 0.575], + "728": [0, 0.69444, 0, 0, 0.575], + "729": [0, 0.69444, 0, 0, 0.31944], + "730": [0, 0.69444, 0, 0, 0.86944], + "732": [0, 0.69444, 0, 0, 0.575], + "733": [0, 0.69444, 0, 0, 0.575], + "915": [0, 0.68611, 0, 0, 0.69166], + "916": [0, 0.68611, 0, 0, 0.95833], + "920": [0, 0.68611, 0, 0, 0.89444], + "923": [0, 0.68611, 0, 0, 0.80555], + "926": [0, 0.68611, 0, 0, 0.76666], + "928": [0, 0.68611, 0, 0, 0.9], + "931": [0, 0.68611, 0, 0, 0.83055], + "933": [0, 0.68611, 0, 0, 0.89444], + "934": [0, 0.68611, 0, 0, 0.83055], + "936": [0, 0.68611, 0, 0, 0.89444], + "937": [0, 0.68611, 0, 0, 0.83055], + "8211": [0, 0.44444, 0.03194, 0, 0.575], + "8212": [0, 0.44444, 0.03194, 0, 1.14999], + "8216": [0, 0.69444, 0, 0, 0.31944], + "8217": [0, 0.69444, 0, 0, 0.31944], + "8220": [0, 0.69444, 0, 0, 0.60278], + "8221": [0, 0.69444, 0, 0, 0.60278], + "8224": [0.19444, 0.69444, 0, 0, 0.51111], + "8225": [0.19444, 0.69444, 0, 0, 0.51111], + "8242": [0, 0.55556, 0, 0, 0.34444], + "8407": [0, 0.72444, 0.15486, 0, 0.575], + "8463": [0, 0.69444, 0, 0, 0.66759], + "8465": [0, 0.69444, 0, 0, 0.83055], + "8467": [0, 0.69444, 0, 0, 0.47361], + "8472": [0.19444, 0.44444, 0, 0, 0.74027], + "8476": [0, 0.69444, 0, 0, 0.83055], + "8501": [0, 0.69444, 0, 0, 0.70277], + "8592": [-0.10889, 0.39111, 0, 0, 1.14999], + "8593": [0.19444, 0.69444, 0, 0, 0.575], + "8594": [-0.10889, 0.39111, 0, 0, 1.14999], + "8595": [0.19444, 0.69444, 0, 0, 0.575], + "8596": [-0.10889, 0.39111, 0, 0, 1.14999], + "8597": [0.25, 0.75, 0, 0, 0.575], + "8598": [0.19444, 0.69444, 0, 0, 1.14999], + "8599": [0.19444, 0.69444, 0, 0, 1.14999], + "8600": [0.19444, 0.69444, 0, 0, 1.14999], + "8601": [0.19444, 0.69444, 0, 0, 1.14999], + "8636": [-0.10889, 0.39111, 0, 0, 1.14999], + "8637": [-0.10889, 0.39111, 0, 0, 1.14999], + "8640": [-0.10889, 0.39111, 0, 0, 1.14999], + "8641": [-0.10889, 0.39111, 0, 0, 1.14999], + "8656": [-0.10889, 0.39111, 0, 0, 1.14999], + "8657": [0.19444, 0.69444, 0, 0, 0.70277], + "8658": [-0.10889, 0.39111, 0, 0, 1.14999], + "8659": [0.19444, 0.69444, 0, 0, 0.70277], + "8660": [-0.10889, 0.39111, 0, 0, 1.14999], + "8661": [0.25, 0.75, 0, 0, 0.70277], + "8704": [0, 0.69444, 0, 0, 0.63889], + "8706": [0, 0.69444, 0.06389, 0, 0.62847], + "8707": [0, 0.69444, 0, 0, 0.63889], + "8709": [0.05556, 0.75, 0, 0, 0.575], + "8711": [0, 0.68611, 0, 0, 0.95833], + "8712": [0.08556, 0.58556, 0, 0, 0.76666], + "8715": [0.08556, 0.58556, 0, 0, 0.76666], + "8722": [0.13333, 0.63333, 0, 0, 0.89444], + "8723": [0.13333, 0.63333, 0, 0, 0.89444], + "8725": [0.25, 0.75, 0, 0, 0.575], + "8726": [0.25, 0.75, 0, 0, 0.575], + "8727": [-0.02778, 0.47222, 0, 0, 0.575], + "8728": [-0.02639, 0.47361, 0, 0, 0.575], + "8729": [-0.02639, 0.47361, 0, 0, 0.575], + "8730": [0.18, 0.82, 0, 0, 0.95833], + "8733": [0, 0.44444, 0, 0, 0.89444], + "8734": [0, 0.44444, 0, 0, 1.14999], + "8736": [0, 0.69224, 0, 0, 0.72222], + "8739": [0.25, 0.75, 0, 0, 0.31944], + "8741": [0.25, 0.75, 0, 0, 0.575], + "8743": [0, 0.55556, 0, 0, 0.76666], + "8744": [0, 0.55556, 0, 0, 0.76666], + "8745": [0, 0.55556, 0, 0, 0.76666], + "8746": [0, 0.55556, 0, 0, 0.76666], + "8747": [0.19444, 0.69444, 0.12778, 0, 0.56875], + "8764": [-0.10889, 0.39111, 0, 0, 0.89444], + "8768": [0.19444, 0.69444, 0, 0, 0.31944], + "8771": [0.00222, 0.50222, 0, 0, 0.89444], + "8773": [0.027, 0.638, 0, 0, 0.894], + "8776": [0.02444, 0.52444, 0, 0, 0.89444], + "8781": [0.00222, 0.50222, 0, 0, 0.89444], + "8801": [0.00222, 0.50222, 0, 0, 0.89444], + "8804": [0.19667, 0.69667, 0, 0, 0.89444], + "8805": [0.19667, 0.69667, 0, 0, 0.89444], + "8810": [0.08556, 0.58556, 0, 0, 1.14999], + "8811": [0.08556, 0.58556, 0, 0, 1.14999], + "8826": [0.08556, 0.58556, 0, 0, 0.89444], + "8827": [0.08556, 0.58556, 0, 0, 0.89444], + "8834": [0.08556, 0.58556, 0, 0, 0.89444], + "8835": [0.08556, 0.58556, 0, 0, 0.89444], + "8838": [0.19667, 0.69667, 0, 0, 0.89444], + "8839": [0.19667, 0.69667, 0, 0, 0.89444], + "8846": [0, 0.55556, 0, 0, 0.76666], + "8849": [0.19667, 0.69667, 0, 0, 0.89444], + "8850": [0.19667, 0.69667, 0, 0, 0.89444], + "8851": [0, 0.55556, 0, 0, 0.76666], + "8852": [0, 0.55556, 0, 0, 0.76666], + "8853": [0.13333, 0.63333, 0, 0, 0.89444], + "8854": [0.13333, 0.63333, 0, 0, 0.89444], + "8855": [0.13333, 0.63333, 0, 0, 0.89444], + "8856": [0.13333, 0.63333, 0, 0, 0.89444], + "8857": [0.13333, 0.63333, 0, 0, 0.89444], + "8866": [0, 0.69444, 0, 0, 0.70277], + "8867": [0, 0.69444, 0, 0, 0.70277], + "8868": [0, 0.69444, 0, 0, 0.89444], + "8869": [0, 0.69444, 0, 0, 0.89444], + "8900": [-0.02639, 0.47361, 0, 0, 0.575], + "8901": [-0.02639, 0.47361, 0, 0, 0.31944], + "8902": [-0.02778, 0.47222, 0, 0, 0.575], + "8968": [0.25, 0.75, 0, 0, 0.51111], + "8969": [0.25, 0.75, 0, 0, 0.51111], + "8970": [0.25, 0.75, 0, 0, 0.51111], + "8971": [0.25, 0.75, 0, 0, 0.51111], + "8994": [-0.13889, 0.36111, 0, 0, 1.14999], + "8995": [-0.13889, 0.36111, 0, 0, 1.14999], + "9651": [0.19444, 0.69444, 0, 0, 1.02222], + "9657": [-0.02778, 0.47222, 0, 0, 0.575], + "9661": [0.19444, 0.69444, 0, 0, 1.02222], + "9667": [-0.02778, 0.47222, 0, 0, 0.575], + "9711": [0.19444, 0.69444, 0, 0, 1.14999], + "9824": [0.12963, 0.69444, 0, 0, 0.89444], + "9825": [0.12963, 0.69444, 0, 0, 0.89444], + "9826": [0.12963, 0.69444, 0, 0, 0.89444], + "9827": [0.12963, 0.69444, 0, 0, 0.89444], + "9837": [0, 0.75, 0, 0, 0.44722], + "9838": [0.19444, 0.69444, 0, 0, 0.44722], + "9839": [0.19444, 0.69444, 0, 0, 0.44722], + "10216": [0.25, 0.75, 0, 0, 0.44722], + "10217": [0.25, 0.75, 0, 0, 0.44722], + "10815": [0, 0.68611, 0, 0, 0.9], + "10927": [0.19667, 0.69667, 0, 0, 0.89444], + "10928": [0.19667, 0.69667, 0, 0, 0.89444], + "57376": [0.19444, 0.69444, 0, 0, 0] + }, + "Main-BoldItalic": { + "32": [0, 0, 0, 0, 0.25], + "33": [0, 0.69444, 0.11417, 0, 0.38611], + "34": [0, 0.69444, 0.07939, 0, 0.62055], + "35": [0.19444, 0.69444, 0.06833, 0, 0.94444], + "37": [0.05556, 0.75, 0.12861, 0, 0.94444], + "38": [0, 0.69444, 0.08528, 0, 0.88555], + "39": [0, 0.69444, 0.12945, 0, 0.35555], + "40": [0.25, 0.75, 0.15806, 0, 0.47333], + "41": [0.25, 0.75, 0.03306, 0, 0.47333], + "42": [0, 0.75, 0.14333, 0, 0.59111], + "43": [0.10333, 0.60333, 0.03306, 0, 0.88555], + "44": [0.19444, 0.14722, 0, 0, 0.35555], + "45": [0, 0.44444, 0.02611, 0, 0.41444], + "46": [0, 0.14722, 0, 0, 0.35555], + "47": [0.25, 0.75, 0.15806, 0, 0.59111], + "48": [0, 0.64444, 0.13167, 0, 0.59111], + "49": [0, 0.64444, 0.13167, 0, 0.59111], + "50": [0, 0.64444, 0.13167, 0, 0.59111], + "51": [0, 0.64444, 0.13167, 0, 0.59111], + "52": [0.19444, 0.64444, 0.13167, 0, 0.59111], + "53": [0, 0.64444, 0.13167, 0, 0.59111], + "54": [0, 0.64444, 0.13167, 0, 0.59111], + "55": [0.19444, 0.64444, 0.13167, 0, 0.59111], + "56": [0, 0.64444, 0.13167, 0, 0.59111], + "57": [0, 0.64444, 0.13167, 0, 0.59111], + "58": [0, 0.44444, 0.06695, 0, 0.35555], + "59": [0.19444, 0.44444, 0.06695, 0, 0.35555], + "61": [-0.10889, 0.39111, 0.06833, 0, 0.88555], + "63": [0, 0.69444, 0.11472, 0, 0.59111], + "64": [0, 0.69444, 0.09208, 0, 0.88555], + "65": [0, 0.68611, 0, 0, 0.86555], + "66": [0, 0.68611, 0.0992, 0, 0.81666], + "67": [0, 0.68611, 0.14208, 0, 0.82666], + "68": [0, 0.68611, 0.09062, 0, 0.87555], + "69": [0, 0.68611, 0.11431, 0, 0.75666], + "70": [0, 0.68611, 0.12903, 0, 0.72722], + "71": [0, 0.68611, 0.07347, 0, 0.89527], + "72": [0, 0.68611, 0.17208, 0, 0.8961], + "73": [0, 0.68611, 0.15681, 0, 0.47166], + "74": [0, 0.68611, 0.145, 0, 0.61055], + "75": [0, 0.68611, 0.14208, 0, 0.89499], + "76": [0, 0.68611, 0, 0, 0.69777], + "77": [0, 0.68611, 0.17208, 0, 1.07277], + "78": [0, 0.68611, 0.17208, 0, 0.8961], + "79": [0, 0.68611, 0.09062, 0, 0.85499], + "80": [0, 0.68611, 0.0992, 0, 0.78721], + "81": [0.19444, 0.68611, 0.09062, 0, 0.85499], + "82": [0, 0.68611, 0.02559, 0, 0.85944], + "83": [0, 0.68611, 0.11264, 0, 0.64999], + "84": [0, 0.68611, 0.12903, 0, 0.7961], + "85": [0, 0.68611, 0.17208, 0, 0.88083], + "86": [0, 0.68611, 0.18625, 0, 0.86555], + "87": [0, 0.68611, 0.18625, 0, 1.15999], + "88": [0, 0.68611, 0.15681, 0, 0.86555], + "89": [0, 0.68611, 0.19803, 0, 0.86555], + "90": [0, 0.68611, 0.14208, 0, 0.70888], + "91": [0.25, 0.75, 0.1875, 0, 0.35611], + "93": [0.25, 0.75, 0.09972, 0, 0.35611], + "94": [0, 0.69444, 0.06709, 0, 0.59111], + "95": [0.31, 0.13444, 0.09811, 0, 0.59111], + "97": [0, 0.44444, 0.09426, 0, 0.59111], + "98": [0, 0.69444, 0.07861, 0, 0.53222], + "99": [0, 0.44444, 0.05222, 0, 0.53222], + "100": [0, 0.69444, 0.10861, 0, 0.59111], + "101": [0, 0.44444, 0.085, 0, 0.53222], + "102": [0.19444, 0.69444, 0.21778, 0, 0.4], + "103": [0.19444, 0.44444, 0.105, 0, 0.53222], + "104": [0, 0.69444, 0.09426, 0, 0.59111], + "105": [0, 0.69326, 0.11387, 0, 0.35555], + "106": [0.19444, 0.69326, 0.1672, 0, 0.35555], + "107": [0, 0.69444, 0.11111, 0, 0.53222], + "108": [0, 0.69444, 0.10861, 0, 0.29666], + "109": [0, 0.44444, 0.09426, 0, 0.94444], + "110": [0, 0.44444, 0.09426, 0, 0.64999], + "111": [0, 0.44444, 0.07861, 0, 0.59111], + "112": [0.19444, 0.44444, 0.07861, 0, 0.59111], + "113": [0.19444, 0.44444, 0.105, 0, 0.53222], + "114": [0, 0.44444, 0.11111, 0, 0.50167], + "115": [0, 0.44444, 0.08167, 0, 0.48694], + "116": [0, 0.63492, 0.09639, 0, 0.385], + "117": [0, 0.44444, 0.09426, 0, 0.62055], + "118": [0, 0.44444, 0.11111, 0, 0.53222], + "119": [0, 0.44444, 0.11111, 0, 0.76777], + "120": [0, 0.44444, 0.12583, 0, 0.56055], + "121": [0.19444, 0.44444, 0.105, 0, 0.56166], + "122": [0, 0.44444, 0.13889, 0, 0.49055], + "126": [0.35, 0.34444, 0.11472, 0, 0.59111], + "160": [0, 0, 0, 0, 0.25], + "168": [0, 0.69444, 0.11473, 0, 0.59111], + "176": [0, 0.69444, 0, 0, 0.94888], + "184": [0.17014, 0, 0, 0, 0.53222], + "198": [0, 0.68611, 0.11431, 0, 1.02277], + "216": [0.04861, 0.73472, 0.09062, 0, 0.88555], + "223": [0.19444, 0.69444, 0.09736, 0, 0.665], + "230": [0, 0.44444, 0.085, 0, 0.82666], + "248": [0.09722, 0.54167, 0.09458, 0, 0.59111], + "305": [0, 0.44444, 0.09426, 0, 0.35555], + "338": [0, 0.68611, 0.11431, 0, 1.14054], + "339": [0, 0.44444, 0.085, 0, 0.82666], + "567": [0.19444, 0.44444, 0.04611, 0, 0.385], + "710": [0, 0.69444, 0.06709, 0, 0.59111], + "711": [0, 0.63194, 0.08271, 0, 0.59111], + "713": [0, 0.59444, 0.10444, 0, 0.59111], + "714": [0, 0.69444, 0.08528, 0, 0.59111], + "715": [0, 0.69444, 0, 0, 0.59111], + "728": [0, 0.69444, 0.10333, 0, 0.59111], + "729": [0, 0.69444, 0.12945, 0, 0.35555], + "730": [0, 0.69444, 0, 0, 0.94888], + "732": [0, 0.69444, 0.11472, 0, 0.59111], + "733": [0, 0.69444, 0.11472, 0, 0.59111], + "915": [0, 0.68611, 0.12903, 0, 0.69777], + "916": [0, 0.68611, 0, 0, 0.94444], + "920": [0, 0.68611, 0.09062, 0, 0.88555], + "923": [0, 0.68611, 0, 0, 0.80666], + "926": [0, 0.68611, 0.15092, 0, 0.76777], + "928": [0, 0.68611, 0.17208, 0, 0.8961], + "931": [0, 0.68611, 0.11431, 0, 0.82666], + "933": [0, 0.68611, 0.10778, 0, 0.88555], + "934": [0, 0.68611, 0.05632, 0, 0.82666], + "936": [0, 0.68611, 0.10778, 0, 0.88555], + "937": [0, 0.68611, 0.0992, 0, 0.82666], + "8211": [0, 0.44444, 0.09811, 0, 0.59111], + "8212": [0, 0.44444, 0.09811, 0, 1.18221], + "8216": [0, 0.69444, 0.12945, 0, 0.35555], + "8217": [0, 0.69444, 0.12945, 0, 0.35555], + "8220": [0, 0.69444, 0.16772, 0, 0.62055], + "8221": [0, 0.69444, 0.07939, 0, 0.62055] + }, + "Main-Italic": { + "32": [0, 0, 0, 0, 0.25], + "33": [0, 0.69444, 0.12417, 0, 0.30667], + "34": [0, 0.69444, 0.06961, 0, 0.51444], + "35": [0.19444, 0.69444, 0.06616, 0, 0.81777], + "37": [0.05556, 0.75, 0.13639, 0, 0.81777], + "38": [0, 0.69444, 0.09694, 0, 0.76666], + "39": [0, 0.69444, 0.12417, 0, 0.30667], + "40": [0.25, 0.75, 0.16194, 0, 0.40889], + "41": [0.25, 0.75, 0.03694, 0, 0.40889], + "42": [0, 0.75, 0.14917, 0, 0.51111], + "43": [0.05667, 0.56167, 0.03694, 0, 0.76666], + "44": [0.19444, 0.10556, 0, 0, 0.30667], + "45": [0, 0.43056, 0.02826, 0, 0.35778], + "46": [0, 0.10556, 0, 0, 0.30667], + "47": [0.25, 0.75, 0.16194, 0, 0.51111], + "48": [0, 0.64444, 0.13556, 0, 0.51111], + "49": [0, 0.64444, 0.13556, 0, 0.51111], + "50": [0, 0.64444, 0.13556, 0, 0.51111], + "51": [0, 0.64444, 0.13556, 0, 0.51111], + "52": [0.19444, 0.64444, 0.13556, 0, 0.51111], + "53": [0, 0.64444, 0.13556, 0, 0.51111], + "54": [0, 0.64444, 0.13556, 0, 0.51111], + "55": [0.19444, 0.64444, 0.13556, 0, 0.51111], + "56": [0, 0.64444, 0.13556, 0, 0.51111], + "57": [0, 0.64444, 0.13556, 0, 0.51111], + "58": [0, 0.43056, 0.0582, 0, 0.30667], + "59": [0.19444, 0.43056, 0.0582, 0, 0.30667], + "61": [-0.13313, 0.36687, 0.06616, 0, 0.76666], + "63": [0, 0.69444, 0.1225, 0, 0.51111], + "64": [0, 0.69444, 0.09597, 0, 0.76666], + "65": [0, 0.68333, 0, 0, 0.74333], + "66": [0, 0.68333, 0.10257, 0, 0.70389], + "67": [0, 0.68333, 0.14528, 0, 0.71555], + "68": [0, 0.68333, 0.09403, 0, 0.755], + "69": [0, 0.68333, 0.12028, 0, 0.67833], + "70": [0, 0.68333, 0.13305, 0, 0.65277], + "71": [0, 0.68333, 0.08722, 0, 0.77361], + "72": [0, 0.68333, 0.16389, 0, 0.74333], + "73": [0, 0.68333, 0.15806, 0, 0.38555], + "74": [0, 0.68333, 0.14028, 0, 0.525], + "75": [0, 0.68333, 0.14528, 0, 0.76888], + "76": [0, 0.68333, 0, 0, 0.62722], + "77": [0, 0.68333, 0.16389, 0, 0.89666], + "78": [0, 0.68333, 0.16389, 0, 0.74333], + "79": [0, 0.68333, 0.09403, 0, 0.76666], + "80": [0, 0.68333, 0.10257, 0, 0.67833], + "81": [0.19444, 0.68333, 0.09403, 0, 0.76666], + "82": [0, 0.68333, 0.03868, 0, 0.72944], + "83": [0, 0.68333, 0.11972, 0, 0.56222], + "84": [0, 0.68333, 0.13305, 0, 0.71555], + "85": [0, 0.68333, 0.16389, 0, 0.74333], + "86": [0, 0.68333, 0.18361, 0, 0.74333], + "87": [0, 0.68333, 0.18361, 0, 0.99888], + "88": [0, 0.68333, 0.15806, 0, 0.74333], + "89": [0, 0.68333, 0.19383, 0, 0.74333], + "90": [0, 0.68333, 0.14528, 0, 0.61333], + "91": [0.25, 0.75, 0.1875, 0, 0.30667], + "93": [0.25, 0.75, 0.10528, 0, 0.30667], + "94": [0, 0.69444, 0.06646, 0, 0.51111], + "95": [0.31, 0.12056, 0.09208, 0, 0.51111], + "97": [0, 0.43056, 0.07671, 0, 0.51111], + "98": [0, 0.69444, 0.06312, 0, 0.46], + "99": [0, 0.43056, 0.05653, 0, 0.46], + "100": [0, 0.69444, 0.10333, 0, 0.51111], + "101": [0, 0.43056, 0.07514, 0, 0.46], + "102": [0.19444, 0.69444, 0.21194, 0, 0.30667], + "103": [0.19444, 0.43056, 0.08847, 0, 0.46], + "104": [0, 0.69444, 0.07671, 0, 0.51111], + "105": [0, 0.65536, 0.1019, 0, 0.30667], + "106": [0.19444, 0.65536, 0.14467, 0, 0.30667], + "107": [0, 0.69444, 0.10764, 0, 0.46], + "108": [0, 0.69444, 0.10333, 0, 0.25555], + "109": [0, 0.43056, 0.07671, 0, 0.81777], + "110": [0, 0.43056, 0.07671, 0, 0.56222], + "111": [0, 0.43056, 0.06312, 0, 0.51111], + "112": [0.19444, 0.43056, 0.06312, 0, 0.51111], + "113": [0.19444, 0.43056, 0.08847, 0, 0.46], + "114": [0, 0.43056, 0.10764, 0, 0.42166], + "115": [0, 0.43056, 0.08208, 0, 0.40889], + "116": [0, 0.61508, 0.09486, 0, 0.33222], + "117": [0, 0.43056, 0.07671, 0, 0.53666], + "118": [0, 0.43056, 0.10764, 0, 0.46], + "119": [0, 0.43056, 0.10764, 0, 0.66444], + "120": [0, 0.43056, 0.12042, 0, 0.46389], + "121": [0.19444, 0.43056, 0.08847, 0, 0.48555], + "122": [0, 0.43056, 0.12292, 0, 0.40889], + "126": [0.35, 0.31786, 0.11585, 0, 0.51111], + "160": [0, 0, 0, 0, 0.25], + "168": [0, 0.66786, 0.10474, 0, 0.51111], + "176": [0, 0.69444, 0, 0, 0.83129], + "184": [0.17014, 0, 0, 0, 0.46], + "198": [0, 0.68333, 0.12028, 0, 0.88277], + "216": [0.04861, 0.73194, 0.09403, 0, 0.76666], + "223": [0.19444, 0.69444, 0.10514, 0, 0.53666], + "230": [0, 0.43056, 0.07514, 0, 0.71555], + "248": [0.09722, 0.52778, 0.09194, 0, 0.51111], + "338": [0, 0.68333, 0.12028, 0, 0.98499], + "339": [0, 0.43056, 0.07514, 0, 0.71555], + "710": [0, 0.69444, 0.06646, 0, 0.51111], + "711": [0, 0.62847, 0.08295, 0, 0.51111], + "713": [0, 0.56167, 0.10333, 0, 0.51111], + "714": [0, 0.69444, 0.09694, 0, 0.51111], + "715": [0, 0.69444, 0, 0, 0.51111], + "728": [0, 0.69444, 0.10806, 0, 0.51111], + "729": [0, 0.66786, 0.11752, 0, 0.30667], + "730": [0, 0.69444, 0, 0, 0.83129], + "732": [0, 0.66786, 0.11585, 0, 0.51111], + "733": [0, 0.69444, 0.1225, 0, 0.51111], + "915": [0, 0.68333, 0.13305, 0, 0.62722], + "916": [0, 0.68333, 0, 0, 0.81777], + "920": [0, 0.68333, 0.09403, 0, 0.76666], + "923": [0, 0.68333, 0, 0, 0.69222], + "926": [0, 0.68333, 0.15294, 0, 0.66444], + "928": [0, 0.68333, 0.16389, 0, 0.74333], + "931": [0, 0.68333, 0.12028, 0, 0.71555], + "933": [0, 0.68333, 0.11111, 0, 0.76666], + "934": [0, 0.68333, 0.05986, 0, 0.71555], + "936": [0, 0.68333, 0.11111, 0, 0.76666], + "937": [0, 0.68333, 0.10257, 0, 0.71555], + "8211": [0, 0.43056, 0.09208, 0, 0.51111], + "8212": [0, 0.43056, 0.09208, 0, 1.02222], + "8216": [0, 0.69444, 0.12417, 0, 0.30667], + "8217": [0, 0.69444, 0.12417, 0, 0.30667], + "8220": [0, 0.69444, 0.1685, 0, 0.51444], + "8221": [0, 0.69444, 0.06961, 0, 0.51444], + "8463": [0, 0.68889, 0, 0, 0.54028] + }, + "Main-Regular": { + "32": [0, 0, 0, 0, 0.25], + "33": [0, 0.69444, 0, 0, 0.27778], + "34": [0, 0.69444, 0, 0, 0.5], + "35": [0.19444, 0.69444, 0, 0, 0.83334], + "36": [0.05556, 0.75, 0, 0, 0.5], + "37": [0.05556, 0.75, 0, 0, 0.83334], + "38": [0, 0.69444, 0, 0, 0.77778], + "39": [0, 0.69444, 0, 0, 0.27778], + "40": [0.25, 0.75, 0, 0, 0.38889], + "41": [0.25, 0.75, 0, 0, 0.38889], + "42": [0, 0.75, 0, 0, 0.5], + "43": [0.08333, 0.58333, 0, 0, 0.77778], + "44": [0.19444, 0.10556, 0, 0, 0.27778], + "45": [0, 0.43056, 0, 0, 0.33333], + "46": [0, 0.10556, 0, 0, 0.27778], + "47": [0.25, 0.75, 0, 0, 0.5], + "48": [0, 0.64444, 0, 0, 0.5], + "49": [0, 0.64444, 0, 0, 0.5], + "50": [0, 0.64444, 0, 0, 0.5], + "51": [0, 0.64444, 0, 0, 0.5], + "52": [0, 0.64444, 0, 0, 0.5], + "53": [0, 0.64444, 0, 0, 0.5], + "54": [0, 0.64444, 0, 0, 0.5], + "55": [0, 0.64444, 0, 0, 0.5], + "56": [0, 0.64444, 0, 0, 0.5], + "57": [0, 0.64444, 0, 0, 0.5], + "58": [0, 0.43056, 0, 0, 0.27778], + "59": [0.19444, 0.43056, 0, 0, 0.27778], + "60": [0.0391, 0.5391, 0, 0, 0.77778], + "61": [-0.13313, 0.36687, 0, 0, 0.77778], + "62": [0.0391, 0.5391, 0, 0, 0.77778], + "63": [0, 0.69444, 0, 0, 0.47222], + "64": [0, 0.69444, 0, 0, 0.77778], + "65": [0, 0.68333, 0, 0, 0.75], + "66": [0, 0.68333, 0, 0, 0.70834], + "67": [0, 0.68333, 0, 0, 0.72222], + "68": [0, 0.68333, 0, 0, 0.76389], + "69": [0, 0.68333, 0, 0, 0.68056], + "70": [0, 0.68333, 0, 0, 0.65278], + "71": [0, 0.68333, 0, 0, 0.78472], + "72": [0, 0.68333, 0, 0, 0.75], + "73": [0, 0.68333, 0, 0, 0.36111], + "74": [0, 0.68333, 0, 0, 0.51389], + "75": [0, 0.68333, 0, 0, 0.77778], + "76": [0, 0.68333, 0, 0, 0.625], + "77": [0, 0.68333, 0, 0, 0.91667], + "78": [0, 0.68333, 0, 0, 0.75], + "79": [0, 0.68333, 0, 0, 0.77778], + "80": [0, 0.68333, 0, 0, 0.68056], + "81": [0.19444, 0.68333, 0, 0, 0.77778], + "82": [0, 0.68333, 0, 0, 0.73611], + "83": [0, 0.68333, 0, 0, 0.55556], + "84": [0, 0.68333, 0, 0, 0.72222], + "85": [0, 0.68333, 0, 0, 0.75], + "86": [0, 0.68333, 0.01389, 0, 0.75], + "87": [0, 0.68333, 0.01389, 0, 1.02778], + "88": [0, 0.68333, 0, 0, 0.75], + "89": [0, 0.68333, 0.025, 0, 0.75], + "90": [0, 0.68333, 0, 0, 0.61111], + "91": [0.25, 0.75, 0, 0, 0.27778], + "92": [0.25, 0.75, 0, 0, 0.5], + "93": [0.25, 0.75, 0, 0, 0.27778], + "94": [0, 0.69444, 0, 0, 0.5], + "95": [0.31, 0.12056, 0.02778, 0, 0.5], + "97": [0, 0.43056, 0, 0, 0.5], + "98": [0, 0.69444, 0, 0, 0.55556], + "99": [0, 0.43056, 0, 0, 0.44445], + "100": [0, 0.69444, 0, 0, 0.55556], + "101": [0, 0.43056, 0, 0, 0.44445], + "102": [0, 0.69444, 0.07778, 0, 0.30556], + "103": [0.19444, 0.43056, 0.01389, 0, 0.5], + "104": [0, 0.69444, 0, 0, 0.55556], + "105": [0, 0.66786, 0, 0, 0.27778], + "106": [0.19444, 0.66786, 0, 0, 0.30556], + "107": [0, 0.69444, 0, 0, 0.52778], + "108": [0, 0.69444, 0, 0, 0.27778], + "109": [0, 0.43056, 0, 0, 0.83334], + "110": [0, 0.43056, 0, 0, 0.55556], + "111": [0, 0.43056, 0, 0, 0.5], + "112": [0.19444, 0.43056, 0, 0, 0.55556], + "113": [0.19444, 0.43056, 0, 0, 0.52778], + "114": [0, 0.43056, 0, 0, 0.39167], + "115": [0, 0.43056, 0, 0, 0.39445], + "116": [0, 0.61508, 0, 0, 0.38889], + "117": [0, 0.43056, 0, 0, 0.55556], + "118": [0, 0.43056, 0.01389, 0, 0.52778], + "119": [0, 0.43056, 0.01389, 0, 0.72222], + "120": [0, 0.43056, 0, 0, 0.52778], + "121": [0.19444, 0.43056, 0.01389, 0, 0.52778], + "122": [0, 0.43056, 0, 0, 0.44445], + "123": [0.25, 0.75, 0, 0, 0.5], + "124": [0.25, 0.75, 0, 0, 0.27778], + "125": [0.25, 0.75, 0, 0, 0.5], + "126": [0.35, 0.31786, 0, 0, 0.5], + "160": [0, 0, 0, 0, 0.25], + "163": [0, 0.69444, 0, 0, 0.76909], + "167": [0.19444, 0.69444, 0, 0, 0.44445], + "168": [0, 0.66786, 0, 0, 0.5], + "172": [0, 0.43056, 0, 0, 0.66667], + "176": [0, 0.69444, 0, 0, 0.75], + "177": [0.08333, 0.58333, 0, 0, 0.77778], + "182": [0.19444, 0.69444, 0, 0, 0.61111], + "184": [0.17014, 0, 0, 0, 0.44445], + "198": [0, 0.68333, 0, 0, 0.90278], + "215": [0.08333, 0.58333, 0, 0, 0.77778], + "216": [0.04861, 0.73194, 0, 0, 0.77778], + "223": [0, 0.69444, 0, 0, 0.5], + "230": [0, 0.43056, 0, 0, 0.72222], + "247": [0.08333, 0.58333, 0, 0, 0.77778], + "248": [0.09722, 0.52778, 0, 0, 0.5], + "305": [0, 0.43056, 0, 0, 0.27778], + "338": [0, 0.68333, 0, 0, 1.01389], + "339": [0, 0.43056, 0, 0, 0.77778], + "567": [0.19444, 0.43056, 0, 0, 0.30556], + "710": [0, 0.69444, 0, 0, 0.5], + "711": [0, 0.62847, 0, 0, 0.5], + "713": [0, 0.56778, 0, 0, 0.5], + "714": [0, 0.69444, 0, 0, 0.5], + "715": [0, 0.69444, 0, 0, 0.5], + "728": [0, 0.69444, 0, 0, 0.5], + "729": [0, 0.66786, 0, 0, 0.27778], + "730": [0, 0.69444, 0, 0, 0.75], + "732": [0, 0.66786, 0, 0, 0.5], + "733": [0, 0.69444, 0, 0, 0.5], + "915": [0, 0.68333, 0, 0, 0.625], + "916": [0, 0.68333, 0, 0, 0.83334], + "920": [0, 0.68333, 0, 0, 0.77778], + "923": [0, 0.68333, 0, 0, 0.69445], + "926": [0, 0.68333, 0, 0, 0.66667], + "928": [0, 0.68333, 0, 0, 0.75], + "931": [0, 0.68333, 0, 0, 0.72222], + "933": [0, 0.68333, 0, 0, 0.77778], + "934": [0, 0.68333, 0, 0, 0.72222], + "936": [0, 0.68333, 0, 0, 0.77778], + "937": [0, 0.68333, 0, 0, 0.72222], + "8211": [0, 0.43056, 0.02778, 0, 0.5], + "8212": [0, 0.43056, 0.02778, 0, 1.0], + "8216": [0, 0.69444, 0, 0, 0.27778], + "8217": [0, 0.69444, 0, 0, 0.27778], + "8220": [0, 0.69444, 0, 0, 0.5], + "8221": [0, 0.69444, 0, 0, 0.5], + "8224": [0.19444, 0.69444, 0, 0, 0.44445], + "8225": [0.19444, 0.69444, 0, 0, 0.44445], + "8230": [0, 0.123, 0, 0, 1.172], + "8242": [0, 0.55556, 0, 0, 0.275], + "8407": [0, 0.71444, 0.15382, 0, 0.5], + "8463": [0, 0.68889, 0, 0, 0.54028], + "8465": [0, 0.69444, 0, 0, 0.72222], + "8467": [0, 0.69444, 0, 0.11111, 0.41667], + "8472": [0.19444, 0.43056, 0, 0.11111, 0.63646], + "8476": [0, 0.69444, 0, 0, 0.72222], + "8501": [0, 0.69444, 0, 0, 0.61111], + "8592": [-0.13313, 0.36687, 0, 0, 1.0], + "8593": [0.19444, 0.69444, 0, 0, 0.5], + "8594": [-0.13313, 0.36687, 0, 0, 1.0], + "8595": [0.19444, 0.69444, 0, 0, 0.5], + "8596": [-0.13313, 0.36687, 0, 0, 1.0], + "8597": [0.25, 0.75, 0, 0, 0.5], + "8598": [0.19444, 0.69444, 0, 0, 1.0], + "8599": [0.19444, 0.69444, 0, 0, 1.0], + "8600": [0.19444, 0.69444, 0, 0, 1.0], + "8601": [0.19444, 0.69444, 0, 0, 1.0], + "8614": [0.011, 0.511, 0, 0, 1.0], + "8617": [0.011, 0.511, 0, 0, 1.126], + "8618": [0.011, 0.511, 0, 0, 1.126], + "8636": [-0.13313, 0.36687, 0, 0, 1.0], + "8637": [-0.13313, 0.36687, 0, 0, 1.0], + "8640": [-0.13313, 0.36687, 0, 0, 1.0], + "8641": [-0.13313, 0.36687, 0, 0, 1.0], + "8652": [0.011, 0.671, 0, 0, 1.0], + "8656": [-0.13313, 0.36687, 0, 0, 1.0], + "8657": [0.19444, 0.69444, 0, 0, 0.61111], + "8658": [-0.13313, 0.36687, 0, 0, 1.0], + "8659": [0.19444, 0.69444, 0, 0, 0.61111], + "8660": [-0.13313, 0.36687, 0, 0, 1.0], + "8661": [0.25, 0.75, 0, 0, 0.61111], + "8704": [0, 0.69444, 0, 0, 0.55556], + "8706": [0, 0.69444, 0.05556, 0.08334, 0.5309], + "8707": [0, 0.69444, 0, 0, 0.55556], + "8709": [0.05556, 0.75, 0, 0, 0.5], + "8711": [0, 0.68333, 0, 0, 0.83334], + "8712": [0.0391, 0.5391, 0, 0, 0.66667], + "8715": [0.0391, 0.5391, 0, 0, 0.66667], + "8722": [0.08333, 0.58333, 0, 0, 0.77778], + "8723": [0.08333, 0.58333, 0, 0, 0.77778], + "8725": [0.25, 0.75, 0, 0, 0.5], + "8726": [0.25, 0.75, 0, 0, 0.5], + "8727": [-0.03472, 0.46528, 0, 0, 0.5], + "8728": [-0.05555, 0.44445, 0, 0, 0.5], + "8729": [-0.05555, 0.44445, 0, 0, 0.5], + "8730": [0.2, 0.8, 0, 0, 0.83334], + "8733": [0, 0.43056, 0, 0, 0.77778], + "8734": [0, 0.43056, 0, 0, 1.0], + "8736": [0, 0.69224, 0, 0, 0.72222], + "8739": [0.25, 0.75, 0, 0, 0.27778], + "8741": [0.25, 0.75, 0, 0, 0.5], + "8743": [0, 0.55556, 0, 0, 0.66667], + "8744": [0, 0.55556, 0, 0, 0.66667], + "8745": [0, 0.55556, 0, 0, 0.66667], + "8746": [0, 0.55556, 0, 0, 0.66667], + "8747": [0.19444, 0.69444, 0.11111, 0, 0.41667], + "8764": [-0.13313, 0.36687, 0, 0, 0.77778], + "8768": [0.19444, 0.69444, 0, 0, 0.27778], + "8771": [-0.03625, 0.46375, 0, 0, 0.77778], + "8773": [-0.022, 0.589, 0, 0, 0.778], + "8776": [-0.01688, 0.48312, 0, 0, 0.77778], + "8781": [-0.03625, 0.46375, 0, 0, 0.77778], + "8784": [-0.133, 0.673, 0, 0, 0.778], + "8801": [-0.03625, 0.46375, 0, 0, 0.77778], + "8804": [0.13597, 0.63597, 0, 0, 0.77778], + "8805": [0.13597, 0.63597, 0, 0, 0.77778], + "8810": [0.0391, 0.5391, 0, 0, 1.0], + "8811": [0.0391, 0.5391, 0, 0, 1.0], + "8826": [0.0391, 0.5391, 0, 0, 0.77778], + "8827": [0.0391, 0.5391, 0, 0, 0.77778], + "8834": [0.0391, 0.5391, 0, 0, 0.77778], + "8835": [0.0391, 0.5391, 0, 0, 0.77778], + "8838": [0.13597, 0.63597, 0, 0, 0.77778], + "8839": [0.13597, 0.63597, 0, 0, 0.77778], + "8846": [0, 0.55556, 0, 0, 0.66667], + "8849": [0.13597, 0.63597, 0, 0, 0.77778], + "8850": [0.13597, 0.63597, 0, 0, 0.77778], + "8851": [0, 0.55556, 0, 0, 0.66667], + "8852": [0, 0.55556, 0, 0, 0.66667], + "8853": [0.08333, 0.58333, 0, 0, 0.77778], + "8854": [0.08333, 0.58333, 0, 0, 0.77778], + "8855": [0.08333, 0.58333, 0, 0, 0.77778], + "8856": [0.08333, 0.58333, 0, 0, 0.77778], + "8857": [0.08333, 0.58333, 0, 0, 0.77778], + "8866": [0, 0.69444, 0, 0, 0.61111], + "8867": [0, 0.69444, 0, 0, 0.61111], + "8868": [0, 0.69444, 0, 0, 0.77778], + "8869": [0, 0.69444, 0, 0, 0.77778], + "8872": [0.249, 0.75, 0, 0, 0.867], + "8900": [-0.05555, 0.44445, 0, 0, 0.5], + "8901": [-0.05555, 0.44445, 0, 0, 0.27778], + "8902": [-0.03472, 0.46528, 0, 0, 0.5], + "8904": [0.005, 0.505, 0, 0, 0.9], + "8942": [0.03, 0.903, 0, 0, 0.278], + "8943": [-0.19, 0.313, 0, 0, 1.172], + "8945": [-0.1, 0.823, 0, 0, 1.282], + "8968": [0.25, 0.75, 0, 0, 0.44445], + "8969": [0.25, 0.75, 0, 0, 0.44445], + "8970": [0.25, 0.75, 0, 0, 0.44445], + "8971": [0.25, 0.75, 0, 0, 0.44445], + "8994": [-0.14236, 0.35764, 0, 0, 1.0], + "8995": [-0.14236, 0.35764, 0, 0, 1.0], + "9136": [0.244, 0.744, 0, 0, 0.412], + "9137": [0.244, 0.745, 0, 0, 0.412], + "9651": [0.19444, 0.69444, 0, 0, 0.88889], + "9657": [-0.03472, 0.46528, 0, 0, 0.5], + "9661": [0.19444, 0.69444, 0, 0, 0.88889], + "9667": [-0.03472, 0.46528, 0, 0, 0.5], + "9711": [0.19444, 0.69444, 0, 0, 1.0], + "9824": [0.12963, 0.69444, 0, 0, 0.77778], + "9825": [0.12963, 0.69444, 0, 0, 0.77778], + "9826": [0.12963, 0.69444, 0, 0, 0.77778], + "9827": [0.12963, 0.69444, 0, 0, 0.77778], + "9837": [0, 0.75, 0, 0, 0.38889], + "9838": [0.19444, 0.69444, 0, 0, 0.38889], + "9839": [0.19444, 0.69444, 0, 0, 0.38889], + "10216": [0.25, 0.75, 0, 0, 0.38889], + "10217": [0.25, 0.75, 0, 0, 0.38889], + "10222": [0.244, 0.744, 0, 0, 0.412], + "10223": [0.244, 0.745, 0, 0, 0.412], + "10229": [0.011, 0.511, 0, 0, 1.609], + "10230": [0.011, 0.511, 0, 0, 1.638], + "10231": [0.011, 0.511, 0, 0, 1.859], + "10232": [0.024, 0.525, 0, 0, 1.609], + "10233": [0.024, 0.525, 0, 0, 1.638], + "10234": [0.024, 0.525, 0, 0, 1.858], + "10236": [0.011, 0.511, 0, 0, 1.638], + "10815": [0, 0.68333, 0, 0, 0.75], + "10927": [0.13597, 0.63597, 0, 0, 0.77778], + "10928": [0.13597, 0.63597, 0, 0, 0.77778], + "57376": [0.19444, 0.69444, 0, 0, 0] + }, + "Math-BoldItalic": { + "32": [0, 0, 0, 0, 0.25], + "48": [0, 0.44444, 0, 0, 0.575], + "49": [0, 0.44444, 0, 0, 0.575], + "50": [0, 0.44444, 0, 0, 0.575], + "51": [0.19444, 0.44444, 0, 0, 0.575], + "52": [0.19444, 0.44444, 0, 0, 0.575], + "53": [0.19444, 0.44444, 0, 0, 0.575], + "54": [0, 0.64444, 0, 0, 0.575], + "55": [0.19444, 0.44444, 0, 0, 0.575], + "56": [0, 0.64444, 0, 0, 0.575], + "57": [0.19444, 0.44444, 0, 0, 0.575], + "65": [0, 0.68611, 0, 0, 0.86944], + "66": [0, 0.68611, 0.04835, 0, 0.8664], + "67": [0, 0.68611, 0.06979, 0, 0.81694], + "68": [0, 0.68611, 0.03194, 0, 0.93812], + "69": [0, 0.68611, 0.05451, 0, 0.81007], + "70": [0, 0.68611, 0.15972, 0, 0.68889], + "71": [0, 0.68611, 0, 0, 0.88673], + "72": [0, 0.68611, 0.08229, 0, 0.98229], + "73": [0, 0.68611, 0.07778, 0, 0.51111], + "74": [0, 0.68611, 0.10069, 0, 0.63125], + "75": [0, 0.68611, 0.06979, 0, 0.97118], + "76": [0, 0.68611, 0, 0, 0.75555], + "77": [0, 0.68611, 0.11424, 0, 1.14201], + "78": [0, 0.68611, 0.11424, 0, 0.95034], + "79": [0, 0.68611, 0.03194, 0, 0.83666], + "80": [0, 0.68611, 0.15972, 0, 0.72309], + "81": [0.19444, 0.68611, 0, 0, 0.86861], + "82": [0, 0.68611, 0.00421, 0, 0.87235], + "83": [0, 0.68611, 0.05382, 0, 0.69271], + "84": [0, 0.68611, 0.15972, 0, 0.63663], + "85": [0, 0.68611, 0.11424, 0, 0.80027], + "86": [0, 0.68611, 0.25555, 0, 0.67778], + "87": [0, 0.68611, 0.15972, 0, 1.09305], + "88": [0, 0.68611, 0.07778, 0, 0.94722], + "89": [0, 0.68611, 0.25555, 0, 0.67458], + "90": [0, 0.68611, 0.06979, 0, 0.77257], + "97": [0, 0.44444, 0, 0, 0.63287], + "98": [0, 0.69444, 0, 0, 0.52083], + "99": [0, 0.44444, 0, 0, 0.51342], + "100": [0, 0.69444, 0, 0, 0.60972], + "101": [0, 0.44444, 0, 0, 0.55361], + "102": [0.19444, 0.69444, 0.11042, 0, 0.56806], + "103": [0.19444, 0.44444, 0.03704, 0, 0.5449], + "104": [0, 0.69444, 0, 0, 0.66759], + "105": [0, 0.69326, 0, 0, 0.4048], + "106": [0.19444, 0.69326, 0.0622, 0, 0.47083], + "107": [0, 0.69444, 0.01852, 0, 0.6037], + "108": [0, 0.69444, 0.0088, 0, 0.34815], + "109": [0, 0.44444, 0, 0, 1.0324], + "110": [0, 0.44444, 0, 0, 0.71296], + "111": [0, 0.44444, 0, 0, 0.58472], + "112": [0.19444, 0.44444, 0, 0, 0.60092], + "113": [0.19444, 0.44444, 0.03704, 0, 0.54213], + "114": [0, 0.44444, 0.03194, 0, 0.5287], + "115": [0, 0.44444, 0, 0, 0.53125], + "116": [0, 0.63492, 0, 0, 0.41528], + "117": [0, 0.44444, 0, 0, 0.68102], + "118": [0, 0.44444, 0.03704, 0, 0.56666], + "119": [0, 0.44444, 0.02778, 0, 0.83148], + "120": [0, 0.44444, 0, 0, 0.65903], + "121": [0.19444, 0.44444, 0.03704, 0, 0.59028], + "122": [0, 0.44444, 0.04213, 0, 0.55509], + "160": [0, 0, 0, 0, 0.25], + "915": [0, 0.68611, 0.15972, 0, 0.65694], + "916": [0, 0.68611, 0, 0, 0.95833], + "920": [0, 0.68611, 0.03194, 0, 0.86722], + "923": [0, 0.68611, 0, 0, 0.80555], + "926": [0, 0.68611, 0.07458, 0, 0.84125], + "928": [0, 0.68611, 0.08229, 0, 0.98229], + "931": [0, 0.68611, 0.05451, 0, 0.88507], + "933": [0, 0.68611, 0.15972, 0, 0.67083], + "934": [0, 0.68611, 0, 0, 0.76666], + "936": [0, 0.68611, 0.11653, 0, 0.71402], + "937": [0, 0.68611, 0.04835, 0, 0.8789], + "945": [0, 0.44444, 0, 0, 0.76064], + "946": [0.19444, 0.69444, 0.03403, 0, 0.65972], + "947": [0.19444, 0.44444, 0.06389, 0, 0.59003], + "948": [0, 0.69444, 0.03819, 0, 0.52222], + "949": [0, 0.44444, 0, 0, 0.52882], + "950": [0.19444, 0.69444, 0.06215, 0, 0.50833], + "951": [0.19444, 0.44444, 0.03704, 0, 0.6], + "952": [0, 0.69444, 0.03194, 0, 0.5618], + "953": [0, 0.44444, 0, 0, 0.41204], + "954": [0, 0.44444, 0, 0, 0.66759], + "955": [0, 0.69444, 0, 0, 0.67083], + "956": [0.19444, 0.44444, 0, 0, 0.70787], + "957": [0, 0.44444, 0.06898, 0, 0.57685], + "958": [0.19444, 0.69444, 0.03021, 0, 0.50833], + "959": [0, 0.44444, 0, 0, 0.58472], + "960": [0, 0.44444, 0.03704, 0, 0.68241], + "961": [0.19444, 0.44444, 0, 0, 0.6118], + "962": [0.09722, 0.44444, 0.07917, 0, 0.42361], + "963": [0, 0.44444, 0.03704, 0, 0.68588], + "964": [0, 0.44444, 0.13472, 0, 0.52083], + "965": [0, 0.44444, 0.03704, 0, 0.63055], + "966": [0.19444, 0.44444, 0, 0, 0.74722], + "967": [0.19444, 0.44444, 0, 0, 0.71805], + "968": [0.19444, 0.69444, 0.03704, 0, 0.75833], + "969": [0, 0.44444, 0.03704, 0, 0.71782], + "977": [0, 0.69444, 0, 0, 0.69155], + "981": [0.19444, 0.69444, 0, 0, 0.7125], + "982": [0, 0.44444, 0.03194, 0, 0.975], + "1009": [0.19444, 0.44444, 0, 0, 0.6118], + "1013": [0, 0.44444, 0, 0, 0.48333], + "57649": [0, 0.44444, 0, 0, 0.39352], + "57911": [0.19444, 0.44444, 0, 0, 0.43889] + }, + "Math-Italic": { + "32": [0, 0, 0, 0, 0.25], + "48": [0, 0.43056, 0, 0, 0.5], + "49": [0, 0.43056, 0, 0, 0.5], + "50": [0, 0.43056, 0, 0, 0.5], + "51": [0.19444, 0.43056, 0, 0, 0.5], + "52": [0.19444, 0.43056, 0, 0, 0.5], + "53": [0.19444, 0.43056, 0, 0, 0.5], + "54": [0, 0.64444, 0, 0, 0.5], + "55": [0.19444, 0.43056, 0, 0, 0.5], + "56": [0, 0.64444, 0, 0, 0.5], + "57": [0.19444, 0.43056, 0, 0, 0.5], + "65": [0, 0.68333, 0, 0.13889, 0.75], + "66": [0, 0.68333, 0.05017, 0.08334, 0.75851], + "67": [0, 0.68333, 0.07153, 0.08334, 0.71472], + "68": [0, 0.68333, 0.02778, 0.05556, 0.82792], + "69": [0, 0.68333, 0.05764, 0.08334, 0.7382], + "70": [0, 0.68333, 0.13889, 0.08334, 0.64306], + "71": [0, 0.68333, 0, 0.08334, 0.78625], + "72": [0, 0.68333, 0.08125, 0.05556, 0.83125], + "73": [0, 0.68333, 0.07847, 0.11111, 0.43958], + "74": [0, 0.68333, 0.09618, 0.16667, 0.55451], + "75": [0, 0.68333, 0.07153, 0.05556, 0.84931], + "76": [0, 0.68333, 0, 0.02778, 0.68056], + "77": [0, 0.68333, 0.10903, 0.08334, 0.97014], + "78": [0, 0.68333, 0.10903, 0.08334, 0.80347], + "79": [0, 0.68333, 0.02778, 0.08334, 0.76278], + "80": [0, 0.68333, 0.13889, 0.08334, 0.64201], + "81": [0.19444, 0.68333, 0, 0.08334, 0.79056], + "82": [0, 0.68333, 0.00773, 0.08334, 0.75929], + "83": [0, 0.68333, 0.05764, 0.08334, 0.6132], + "84": [0, 0.68333, 0.13889, 0.08334, 0.58438], + "85": [0, 0.68333, 0.10903, 0.02778, 0.68278], + "86": [0, 0.68333, 0.22222, 0, 0.58333], + "87": [0, 0.68333, 0.13889, 0, 0.94445], + "88": [0, 0.68333, 0.07847, 0.08334, 0.82847], + "89": [0, 0.68333, 0.22222, 0, 0.58056], + "90": [0, 0.68333, 0.07153, 0.08334, 0.68264], + "97": [0, 0.43056, 0, 0, 0.52859], + "98": [0, 0.69444, 0, 0, 0.42917], + "99": [0, 0.43056, 0, 0.05556, 0.43276], + "100": [0, 0.69444, 0, 0.16667, 0.52049], + "101": [0, 0.43056, 0, 0.05556, 0.46563], + "102": [0.19444, 0.69444, 0.10764, 0.16667, 0.48959], + "103": [0.19444, 0.43056, 0.03588, 0.02778, 0.47697], + "104": [0, 0.69444, 0, 0, 0.57616], + "105": [0, 0.65952, 0, 0, 0.34451], + "106": [0.19444, 0.65952, 0.05724, 0, 0.41181], + "107": [0, 0.69444, 0.03148, 0, 0.5206], + "108": [0, 0.69444, 0.01968, 0.08334, 0.29838], + "109": [0, 0.43056, 0, 0, 0.87801], + "110": [0, 0.43056, 0, 0, 0.60023], + "111": [0, 0.43056, 0, 0.05556, 0.48472], + "112": [0.19444, 0.43056, 0, 0.08334, 0.50313], + "113": [0.19444, 0.43056, 0.03588, 0.08334, 0.44641], + "114": [0, 0.43056, 0.02778, 0.05556, 0.45116], + "115": [0, 0.43056, 0, 0.05556, 0.46875], + "116": [0, 0.61508, 0, 0.08334, 0.36111], + "117": [0, 0.43056, 0, 0.02778, 0.57246], + "118": [0, 0.43056, 0.03588, 0.02778, 0.48472], + "119": [0, 0.43056, 0.02691, 0.08334, 0.71592], + "120": [0, 0.43056, 0, 0.02778, 0.57153], + "121": [0.19444, 0.43056, 0.03588, 0.05556, 0.49028], + "122": [0, 0.43056, 0.04398, 0.05556, 0.46505], + "160": [0, 0, 0, 0, 0.25], + "915": [0, 0.68333, 0.13889, 0.08334, 0.61528], + "916": [0, 0.68333, 0, 0.16667, 0.83334], + "920": [0, 0.68333, 0.02778, 0.08334, 0.76278], + "923": [0, 0.68333, 0, 0.16667, 0.69445], + "926": [0, 0.68333, 0.07569, 0.08334, 0.74236], + "928": [0, 0.68333, 0.08125, 0.05556, 0.83125], + "931": [0, 0.68333, 0.05764, 0.08334, 0.77986], + "933": [0, 0.68333, 0.13889, 0.05556, 0.58333], + "934": [0, 0.68333, 0, 0.08334, 0.66667], + "936": [0, 0.68333, 0.11, 0.05556, 0.61222], + "937": [0, 0.68333, 0.05017, 0.08334, 0.7724], + "945": [0, 0.43056, 0.0037, 0.02778, 0.6397], + "946": [0.19444, 0.69444, 0.05278, 0.08334, 0.56563], + "947": [0.19444, 0.43056, 0.05556, 0, 0.51773], + "948": [0, 0.69444, 0.03785, 0.05556, 0.44444], + "949": [0, 0.43056, 0, 0.08334, 0.46632], + "950": [0.19444, 0.69444, 0.07378, 0.08334, 0.4375], + "951": [0.19444, 0.43056, 0.03588, 0.05556, 0.49653], + "952": [0, 0.69444, 0.02778, 0.08334, 0.46944], + "953": [0, 0.43056, 0, 0.05556, 0.35394], + "954": [0, 0.43056, 0, 0, 0.57616], + "955": [0, 0.69444, 0, 0, 0.58334], + "956": [0.19444, 0.43056, 0, 0.02778, 0.60255], + "957": [0, 0.43056, 0.06366, 0.02778, 0.49398], + "958": [0.19444, 0.69444, 0.04601, 0.11111, 0.4375], + "959": [0, 0.43056, 0, 0.05556, 0.48472], + "960": [0, 0.43056, 0.03588, 0, 0.57003], + "961": [0.19444, 0.43056, 0, 0.08334, 0.51702], + "962": [0.09722, 0.43056, 0.07986, 0.08334, 0.36285], + "963": [0, 0.43056, 0.03588, 0, 0.57141], + "964": [0, 0.43056, 0.1132, 0.02778, 0.43715], + "965": [0, 0.43056, 0.03588, 0.02778, 0.54028], + "966": [0.19444, 0.43056, 0, 0.08334, 0.65417], + "967": [0.19444, 0.43056, 0, 0.05556, 0.62569], + "968": [0.19444, 0.69444, 0.03588, 0.11111, 0.65139], + "969": [0, 0.43056, 0.03588, 0, 0.62245], + "977": [0, 0.69444, 0, 0.08334, 0.59144], + "981": [0.19444, 0.69444, 0, 0.08334, 0.59583], + "982": [0, 0.43056, 0.02778, 0, 0.82813], + "1009": [0.19444, 0.43056, 0, 0.08334, 0.51702], + "1013": [0, 0.43056, 0, 0.05556, 0.4059], + "57649": [0, 0.43056, 0, 0.02778, 0.32246], + "57911": [0.19444, 0.43056, 0, 0.08334, 0.38403] + }, + "SansSerif-Bold": { + "32": [0, 0, 0, 0, 0.25], + "33": [0, 0.69444, 0, 0, 0.36667], + "34": [0, 0.69444, 0, 0, 0.55834], + "35": [0.19444, 0.69444, 0, 0, 0.91667], + "36": [0.05556, 0.75, 0, 0, 0.55], + "37": [0.05556, 0.75, 0, 0, 1.02912], + "38": [0, 0.69444, 0, 0, 0.83056], + "39": [0, 0.69444, 0, 0, 0.30556], + "40": [0.25, 0.75, 0, 0, 0.42778], + "41": [0.25, 0.75, 0, 0, 0.42778], + "42": [0, 0.75, 0, 0, 0.55], + "43": [0.11667, 0.61667, 0, 0, 0.85556], + "44": [0.10556, 0.13056, 0, 0, 0.30556], + "45": [0, 0.45833, 0, 0, 0.36667], + "46": [0, 0.13056, 0, 0, 0.30556], + "47": [0.25, 0.75, 0, 0, 0.55], + "48": [0, 0.69444, 0, 0, 0.55], + "49": [0, 0.69444, 0, 0, 0.55], + "50": [0, 0.69444, 0, 0, 0.55], + "51": [0, 0.69444, 0, 0, 0.55], + "52": [0, 0.69444, 0, 0, 0.55], + "53": [0, 0.69444, 0, 0, 0.55], + "54": [0, 0.69444, 0, 0, 0.55], + "55": [0, 0.69444, 0, 0, 0.55], + "56": [0, 0.69444, 0, 0, 0.55], + "57": [0, 0.69444, 0, 0, 0.55], + "58": [0, 0.45833, 0, 0, 0.30556], + "59": [0.10556, 0.45833, 0, 0, 0.30556], + "61": [-0.09375, 0.40625, 0, 0, 0.85556], + "63": [0, 0.69444, 0, 0, 0.51945], + "64": [0, 0.69444, 0, 0, 0.73334], + "65": [0, 0.69444, 0, 0, 0.73334], + "66": [0, 0.69444, 0, 0, 0.73334], + "67": [0, 0.69444, 0, 0, 0.70278], + "68": [0, 0.69444, 0, 0, 0.79445], + "69": [0, 0.69444, 0, 0, 0.64167], + "70": [0, 0.69444, 0, 0, 0.61111], + "71": [0, 0.69444, 0, 0, 0.73334], + "72": [0, 0.69444, 0, 0, 0.79445], + "73": [0, 0.69444, 0, 0, 0.33056], + "74": [0, 0.69444, 0, 0, 0.51945], + "75": [0, 0.69444, 0, 0, 0.76389], + "76": [0, 0.69444, 0, 0, 0.58056], + "77": [0, 0.69444, 0, 0, 0.97778], + "78": [0, 0.69444, 0, 0, 0.79445], + "79": [0, 0.69444, 0, 0, 0.79445], + "80": [0, 0.69444, 0, 0, 0.70278], + "81": [0.10556, 0.69444, 0, 0, 0.79445], + "82": [0, 0.69444, 0, 0, 0.70278], + "83": [0, 0.69444, 0, 0, 0.61111], + "84": [0, 0.69444, 0, 0, 0.73334], + "85": [0, 0.69444, 0, 0, 0.76389], + "86": [0, 0.69444, 0.01528, 0, 0.73334], + "87": [0, 0.69444, 0.01528, 0, 1.03889], + "88": [0, 0.69444, 0, 0, 0.73334], + "89": [0, 0.69444, 0.0275, 0, 0.73334], + "90": [0, 0.69444, 0, 0, 0.67223], + "91": [0.25, 0.75, 0, 0, 0.34306], + "93": [0.25, 0.75, 0, 0, 0.34306], + "94": [0, 0.69444, 0, 0, 0.55], + "95": [0.35, 0.10833, 0.03056, 0, 0.55], + "97": [0, 0.45833, 0, 0, 0.525], + "98": [0, 0.69444, 0, 0, 0.56111], + "99": [0, 0.45833, 0, 0, 0.48889], + "100": [0, 0.69444, 0, 0, 0.56111], + "101": [0, 0.45833, 0, 0, 0.51111], + "102": [0, 0.69444, 0.07639, 0, 0.33611], + "103": [0.19444, 0.45833, 0.01528, 0, 0.55], + "104": [0, 0.69444, 0, 0, 0.56111], + "105": [0, 0.69444, 0, 0, 0.25556], + "106": [0.19444, 0.69444, 0, 0, 0.28611], + "107": [0, 0.69444, 0, 0, 0.53056], + "108": [0, 0.69444, 0, 0, 0.25556], + "109": [0, 0.45833, 0, 0, 0.86667], + "110": [0, 0.45833, 0, 0, 0.56111], + "111": [0, 0.45833, 0, 0, 0.55], + "112": [0.19444, 0.45833, 0, 0, 0.56111], + "113": [0.19444, 0.45833, 0, 0, 0.56111], + "114": [0, 0.45833, 0.01528, 0, 0.37222], + "115": [0, 0.45833, 0, 0, 0.42167], + "116": [0, 0.58929, 0, 0, 0.40417], + "117": [0, 0.45833, 0, 0, 0.56111], + "118": [0, 0.45833, 0.01528, 0, 0.5], + "119": [0, 0.45833, 0.01528, 0, 0.74445], + "120": [0, 0.45833, 0, 0, 0.5], + "121": [0.19444, 0.45833, 0.01528, 0, 0.5], + "122": [0, 0.45833, 0, 0, 0.47639], + "126": [0.35, 0.34444, 0, 0, 0.55], + "160": [0, 0, 0, 0, 0.25], + "168": [0, 0.69444, 0, 0, 0.55], + "176": [0, 0.69444, 0, 0, 0.73334], + "180": [0, 0.69444, 0, 0, 0.55], + "184": [0.17014, 0, 0, 0, 0.48889], + "305": [0, 0.45833, 0, 0, 0.25556], + "567": [0.19444, 0.45833, 0, 0, 0.28611], + "710": [0, 0.69444, 0, 0, 0.55], + "711": [0, 0.63542, 0, 0, 0.55], + "713": [0, 0.63778, 0, 0, 0.55], + "728": [0, 0.69444, 0, 0, 0.55], + "729": [0, 0.69444, 0, 0, 0.30556], + "730": [0, 0.69444, 0, 0, 0.73334], + "732": [0, 0.69444, 0, 0, 0.55], + "733": [0, 0.69444, 0, 0, 0.55], + "915": [0, 0.69444, 0, 0, 0.58056], + "916": [0, 0.69444, 0, 0, 0.91667], + "920": [0, 0.69444, 0, 0, 0.85556], + "923": [0, 0.69444, 0, 0, 0.67223], + "926": [0, 0.69444, 0, 0, 0.73334], + "928": [0, 0.69444, 0, 0, 0.79445], + "931": [0, 0.69444, 0, 0, 0.79445], + "933": [0, 0.69444, 0, 0, 0.85556], + "934": [0, 0.69444, 0, 0, 0.79445], + "936": [0, 0.69444, 0, 0, 0.85556], + "937": [0, 0.69444, 0, 0, 0.79445], + "8211": [0, 0.45833, 0.03056, 0, 0.55], + "8212": [0, 0.45833, 0.03056, 0, 1.10001], + "8216": [0, 0.69444, 0, 0, 0.30556], + "8217": [0, 0.69444, 0, 0, 0.30556], + "8220": [0, 0.69444, 0, 0, 0.55834], + "8221": [0, 0.69444, 0, 0, 0.55834] + }, + "SansSerif-Italic": { + "32": [0, 0, 0, 0, 0.25], + "33": [0, 0.69444, 0.05733, 0, 0.31945], + "34": [0, 0.69444, 0.00316, 0, 0.5], + "35": [0.19444, 0.69444, 0.05087, 0, 0.83334], + "36": [0.05556, 0.75, 0.11156, 0, 0.5], + "37": [0.05556, 0.75, 0.03126, 0, 0.83334], + "38": [0, 0.69444, 0.03058, 0, 0.75834], + "39": [0, 0.69444, 0.07816, 0, 0.27778], + "40": [0.25, 0.75, 0.13164, 0, 0.38889], + "41": [0.25, 0.75, 0.02536, 0, 0.38889], + "42": [0, 0.75, 0.11775, 0, 0.5], + "43": [0.08333, 0.58333, 0.02536, 0, 0.77778], + "44": [0.125, 0.08333, 0, 0, 0.27778], + "45": [0, 0.44444, 0.01946, 0, 0.33333], + "46": [0, 0.08333, 0, 0, 0.27778], + "47": [0.25, 0.75, 0.13164, 0, 0.5], + "48": [0, 0.65556, 0.11156, 0, 0.5], + "49": [0, 0.65556, 0.11156, 0, 0.5], + "50": [0, 0.65556, 0.11156, 0, 0.5], + "51": [0, 0.65556, 0.11156, 0, 0.5], + "52": [0, 0.65556, 0.11156, 0, 0.5], + "53": [0, 0.65556, 0.11156, 0, 0.5], + "54": [0, 0.65556, 0.11156, 0, 0.5], + "55": [0, 0.65556, 0.11156, 0, 0.5], + "56": [0, 0.65556, 0.11156, 0, 0.5], + "57": [0, 0.65556, 0.11156, 0, 0.5], + "58": [0, 0.44444, 0.02502, 0, 0.27778], + "59": [0.125, 0.44444, 0.02502, 0, 0.27778], + "61": [-0.13, 0.37, 0.05087, 0, 0.77778], + "63": [0, 0.69444, 0.11809, 0, 0.47222], + "64": [0, 0.69444, 0.07555, 0, 0.66667], + "65": [0, 0.69444, 0, 0, 0.66667], + "66": [0, 0.69444, 0.08293, 0, 0.66667], + "67": [0, 0.69444, 0.11983, 0, 0.63889], + "68": [0, 0.69444, 0.07555, 0, 0.72223], + "69": [0, 0.69444, 0.11983, 0, 0.59722], + "70": [0, 0.69444, 0.13372, 0, 0.56945], + "71": [0, 0.69444, 0.11983, 0, 0.66667], + "72": [0, 0.69444, 0.08094, 0, 0.70834], + "73": [0, 0.69444, 0.13372, 0, 0.27778], + "74": [0, 0.69444, 0.08094, 0, 0.47222], + "75": [0, 0.69444, 0.11983, 0, 0.69445], + "76": [0, 0.69444, 0, 0, 0.54167], + "77": [0, 0.69444, 0.08094, 0, 0.875], + "78": [0, 0.69444, 0.08094, 0, 0.70834], + "79": [0, 0.69444, 0.07555, 0, 0.73611], + "80": [0, 0.69444, 0.08293, 0, 0.63889], + "81": [0.125, 0.69444, 0.07555, 0, 0.73611], + "82": [0, 0.69444, 0.08293, 0, 0.64584], + "83": [0, 0.69444, 0.09205, 0, 0.55556], + "84": [0, 0.69444, 0.13372, 0, 0.68056], + "85": [0, 0.69444, 0.08094, 0, 0.6875], + "86": [0, 0.69444, 0.1615, 0, 0.66667], + "87": [0, 0.69444, 0.1615, 0, 0.94445], + "88": [0, 0.69444, 0.13372, 0, 0.66667], + "89": [0, 0.69444, 0.17261, 0, 0.66667], + "90": [0, 0.69444, 0.11983, 0, 0.61111], + "91": [0.25, 0.75, 0.15942, 0, 0.28889], + "93": [0.25, 0.75, 0.08719, 0, 0.28889], + "94": [0, 0.69444, 0.0799, 0, 0.5], + "95": [0.35, 0.09444, 0.08616, 0, 0.5], + "97": [0, 0.44444, 0.00981, 0, 0.48056], + "98": [0, 0.69444, 0.03057, 0, 0.51667], + "99": [0, 0.44444, 0.08336, 0, 0.44445], + "100": [0, 0.69444, 0.09483, 0, 0.51667], + "101": [0, 0.44444, 0.06778, 0, 0.44445], + "102": [0, 0.69444, 0.21705, 0, 0.30556], + "103": [0.19444, 0.44444, 0.10836, 0, 0.5], + "104": [0, 0.69444, 0.01778, 0, 0.51667], + "105": [0, 0.67937, 0.09718, 0, 0.23889], + "106": [0.19444, 0.67937, 0.09162, 0, 0.26667], + "107": [0, 0.69444, 0.08336, 0, 0.48889], + "108": [0, 0.69444, 0.09483, 0, 0.23889], + "109": [0, 0.44444, 0.01778, 0, 0.79445], + "110": [0, 0.44444, 0.01778, 0, 0.51667], + "111": [0, 0.44444, 0.06613, 0, 0.5], + "112": [0.19444, 0.44444, 0.0389, 0, 0.51667], + "113": [0.19444, 0.44444, 0.04169, 0, 0.51667], + "114": [0, 0.44444, 0.10836, 0, 0.34167], + "115": [0, 0.44444, 0.0778, 0, 0.38333], + "116": [0, 0.57143, 0.07225, 0, 0.36111], + "117": [0, 0.44444, 0.04169, 0, 0.51667], + "118": [0, 0.44444, 0.10836, 0, 0.46111], + "119": [0, 0.44444, 0.10836, 0, 0.68334], + "120": [0, 0.44444, 0.09169, 0, 0.46111], + "121": [0.19444, 0.44444, 0.10836, 0, 0.46111], + "122": [0, 0.44444, 0.08752, 0, 0.43472], + "126": [0.35, 0.32659, 0.08826, 0, 0.5], + "160": [0, 0, 0, 0, 0.25], + "168": [0, 0.67937, 0.06385, 0, 0.5], + "176": [0, 0.69444, 0, 0, 0.73752], + "184": [0.17014, 0, 0, 0, 0.44445], + "305": [0, 0.44444, 0.04169, 0, 0.23889], + "567": [0.19444, 0.44444, 0.04169, 0, 0.26667], + "710": [0, 0.69444, 0.0799, 0, 0.5], + "711": [0, 0.63194, 0.08432, 0, 0.5], + "713": [0, 0.60889, 0.08776, 0, 0.5], + "714": [0, 0.69444, 0.09205, 0, 0.5], + "715": [0, 0.69444, 0, 0, 0.5], + "728": [0, 0.69444, 0.09483, 0, 0.5], + "729": [0, 0.67937, 0.07774, 0, 0.27778], + "730": [0, 0.69444, 0, 0, 0.73752], + "732": [0, 0.67659, 0.08826, 0, 0.5], + "733": [0, 0.69444, 0.09205, 0, 0.5], + "915": [0, 0.69444, 0.13372, 0, 0.54167], + "916": [0, 0.69444, 0, 0, 0.83334], + "920": [0, 0.69444, 0.07555, 0, 0.77778], + "923": [0, 0.69444, 0, 0, 0.61111], + "926": [0, 0.69444, 0.12816, 0, 0.66667], + "928": [0, 0.69444, 0.08094, 0, 0.70834], + "931": [0, 0.69444, 0.11983, 0, 0.72222], + "933": [0, 0.69444, 0.09031, 0, 0.77778], + "934": [0, 0.69444, 0.04603, 0, 0.72222], + "936": [0, 0.69444, 0.09031, 0, 0.77778], + "937": [0, 0.69444, 0.08293, 0, 0.72222], + "8211": [0, 0.44444, 0.08616, 0, 0.5], + "8212": [0, 0.44444, 0.08616, 0, 1.0], + "8216": [0, 0.69444, 0.07816, 0, 0.27778], + "8217": [0, 0.69444, 0.07816, 0, 0.27778], + "8220": [0, 0.69444, 0.14205, 0, 0.5], + "8221": [0, 0.69444, 0.00316, 0, 0.5] + }, + "SansSerif-Regular": { + "32": [0, 0, 0, 0, 0.25], + "33": [0, 0.69444, 0, 0, 0.31945], + "34": [0, 0.69444, 0, 0, 0.5], + "35": [0.19444, 0.69444, 0, 0, 0.83334], + "36": [0.05556, 0.75, 0, 0, 0.5], + "37": [0.05556, 0.75, 0, 0, 0.83334], + "38": [0, 0.69444, 0, 0, 0.75834], + "39": [0, 0.69444, 0, 0, 0.27778], + "40": [0.25, 0.75, 0, 0, 0.38889], + "41": [0.25, 0.75, 0, 0, 0.38889], + "42": [0, 0.75, 0, 0, 0.5], + "43": [0.08333, 0.58333, 0, 0, 0.77778], + "44": [0.125, 0.08333, 0, 0, 0.27778], + "45": [0, 0.44444, 0, 0, 0.33333], + "46": [0, 0.08333, 0, 0, 0.27778], + "47": [0.25, 0.75, 0, 0, 0.5], + "48": [0, 0.65556, 0, 0, 0.5], + "49": [0, 0.65556, 0, 0, 0.5], + "50": [0, 0.65556, 0, 0, 0.5], + "51": [0, 0.65556, 0, 0, 0.5], + "52": [0, 0.65556, 0, 0, 0.5], + "53": [0, 0.65556, 0, 0, 0.5], + "54": [0, 0.65556, 0, 0, 0.5], + "55": [0, 0.65556, 0, 0, 0.5], + "56": [0, 0.65556, 0, 0, 0.5], + "57": [0, 0.65556, 0, 0, 0.5], + "58": [0, 0.44444, 0, 0, 0.27778], + "59": [0.125, 0.44444, 0, 0, 0.27778], + "61": [-0.13, 0.37, 0, 0, 0.77778], + "63": [0, 0.69444, 0, 0, 0.47222], + "64": [0, 0.69444, 0, 0, 0.66667], + "65": [0, 0.69444, 0, 0, 0.66667], + "66": [0, 0.69444, 0, 0, 0.66667], + "67": [0, 0.69444, 0, 0, 0.63889], + "68": [0, 0.69444, 0, 0, 0.72223], + "69": [0, 0.69444, 0, 0, 0.59722], + "70": [0, 0.69444, 0, 0, 0.56945], + "71": [0, 0.69444, 0, 0, 0.66667], + "72": [0, 0.69444, 0, 0, 0.70834], + "73": [0, 0.69444, 0, 0, 0.27778], + "74": [0, 0.69444, 0, 0, 0.47222], + "75": [0, 0.69444, 0, 0, 0.69445], + "76": [0, 0.69444, 0, 0, 0.54167], + "77": [0, 0.69444, 0, 0, 0.875], + "78": [0, 0.69444, 0, 0, 0.70834], + "79": [0, 0.69444, 0, 0, 0.73611], + "80": [0, 0.69444, 0, 0, 0.63889], + "81": [0.125, 0.69444, 0, 0, 0.73611], + "82": [0, 0.69444, 0, 0, 0.64584], + "83": [0, 0.69444, 0, 0, 0.55556], + "84": [0, 0.69444, 0, 0, 0.68056], + "85": [0, 0.69444, 0, 0, 0.6875], + "86": [0, 0.69444, 0.01389, 0, 0.66667], + "87": [0, 0.69444, 0.01389, 0, 0.94445], + "88": [0, 0.69444, 0, 0, 0.66667], + "89": [0, 0.69444, 0.025, 0, 0.66667], + "90": [0, 0.69444, 0, 0, 0.61111], + "91": [0.25, 0.75, 0, 0, 0.28889], + "93": [0.25, 0.75, 0, 0, 0.28889], + "94": [0, 0.69444, 0, 0, 0.5], + "95": [0.35, 0.09444, 0.02778, 0, 0.5], + "97": [0, 0.44444, 0, 0, 0.48056], + "98": [0, 0.69444, 0, 0, 0.51667], + "99": [0, 0.44444, 0, 0, 0.44445], + "100": [0, 0.69444, 0, 0, 0.51667], + "101": [0, 0.44444, 0, 0, 0.44445], + "102": [0, 0.69444, 0.06944, 0, 0.30556], + "103": [0.19444, 0.44444, 0.01389, 0, 0.5], + "104": [0, 0.69444, 0, 0, 0.51667], + "105": [0, 0.67937, 0, 0, 0.23889], + "106": [0.19444, 0.67937, 0, 0, 0.26667], + "107": [0, 0.69444, 0, 0, 0.48889], + "108": [0, 0.69444, 0, 0, 0.23889], + "109": [0, 0.44444, 0, 0, 0.79445], + "110": [0, 0.44444, 0, 0, 0.51667], + "111": [0, 0.44444, 0, 0, 0.5], + "112": [0.19444, 0.44444, 0, 0, 0.51667], + "113": [0.19444, 0.44444, 0, 0, 0.51667], + "114": [0, 0.44444, 0.01389, 0, 0.34167], + "115": [0, 0.44444, 0, 0, 0.38333], + "116": [0, 0.57143, 0, 0, 0.36111], + "117": [0, 0.44444, 0, 0, 0.51667], + "118": [0, 0.44444, 0.01389, 0, 0.46111], + "119": [0, 0.44444, 0.01389, 0, 0.68334], + "120": [0, 0.44444, 0, 0, 0.46111], + "121": [0.19444, 0.44444, 0.01389, 0, 0.46111], + "122": [0, 0.44444, 0, 0, 0.43472], + "126": [0.35, 0.32659, 0, 0, 0.5], + "160": [0, 0, 0, 0, 0.25], + "168": [0, 0.67937, 0, 0, 0.5], + "176": [0, 0.69444, 0, 0, 0.66667], + "184": [0.17014, 0, 0, 0, 0.44445], + "305": [0, 0.44444, 0, 0, 0.23889], + "567": [0.19444, 0.44444, 0, 0, 0.26667], + "710": [0, 0.69444, 0, 0, 0.5], + "711": [0, 0.63194, 0, 0, 0.5], + "713": [0, 0.60889, 0, 0, 0.5], + "714": [0, 0.69444, 0, 0, 0.5], + "715": [0, 0.69444, 0, 0, 0.5], + "728": [0, 0.69444, 0, 0, 0.5], + "729": [0, 0.67937, 0, 0, 0.27778], + "730": [0, 0.69444, 0, 0, 0.66667], + "732": [0, 0.67659, 0, 0, 0.5], + "733": [0, 0.69444, 0, 0, 0.5], + "915": [0, 0.69444, 0, 0, 0.54167], + "916": [0, 0.69444, 0, 0, 0.83334], + "920": [0, 0.69444, 0, 0, 0.77778], + "923": [0, 0.69444, 0, 0, 0.61111], + "926": [0, 0.69444, 0, 0, 0.66667], + "928": [0, 0.69444, 0, 0, 0.70834], + "931": [0, 0.69444, 0, 0, 0.72222], + "933": [0, 0.69444, 0, 0, 0.77778], + "934": [0, 0.69444, 0, 0, 0.72222], + "936": [0, 0.69444, 0, 0, 0.77778], + "937": [0, 0.69444, 0, 0, 0.72222], + "8211": [0, 0.44444, 0.02778, 0, 0.5], + "8212": [0, 0.44444, 0.02778, 0, 1.0], + "8216": [0, 0.69444, 0, 0, 0.27778], + "8217": [0, 0.69444, 0, 0, 0.27778], + "8220": [0, 0.69444, 0, 0, 0.5], + "8221": [0, 0.69444, 0, 0, 0.5] + }, + "Script-Regular": { + "32": [0, 0, 0, 0, 0.25], + "65": [0, 0.7, 0.22925, 0, 0.80253], + "66": [0, 0.7, 0.04087, 0, 0.90757], + "67": [0, 0.7, 0.1689, 0, 0.66619], + "68": [0, 0.7, 0.09371, 0, 0.77443], + "69": [0, 0.7, 0.18583, 0, 0.56162], + "70": [0, 0.7, 0.13634, 0, 0.89544], + "71": [0, 0.7, 0.17322, 0, 0.60961], + "72": [0, 0.7, 0.29694, 0, 0.96919], + "73": [0, 0.7, 0.19189, 0, 0.80907], + "74": [0.27778, 0.7, 0.19189, 0, 1.05159], + "75": [0, 0.7, 0.31259, 0, 0.91364], + "76": [0, 0.7, 0.19189, 0, 0.87373], + "77": [0, 0.7, 0.15981, 0, 1.08031], + "78": [0, 0.7, 0.3525, 0, 0.9015], + "79": [0, 0.7, 0.08078, 0, 0.73787], + "80": [0, 0.7, 0.08078, 0, 1.01262], + "81": [0, 0.7, 0.03305, 0, 0.88282], + "82": [0, 0.7, 0.06259, 0, 0.85], + "83": [0, 0.7, 0.19189, 0, 0.86767], + "84": [0, 0.7, 0.29087, 0, 0.74697], + "85": [0, 0.7, 0.25815, 0, 0.79996], + "86": [0, 0.7, 0.27523, 0, 0.62204], + "87": [0, 0.7, 0.27523, 0, 0.80532], + "88": [0, 0.7, 0.26006, 0, 0.94445], + "89": [0, 0.7, 0.2939, 0, 0.70961], + "90": [0, 0.7, 0.24037, 0, 0.8212], + "160": [0, 0, 0, 0, 0.25] + }, + "Size1-Regular": { + "32": [0, 0, 0, 0, 0.25], + "40": [0.35001, 0.85, 0, 0, 0.45834], + "41": [0.35001, 0.85, 0, 0, 0.45834], + "47": [0.35001, 0.85, 0, 0, 0.57778], + "91": [0.35001, 0.85, 0, 0, 0.41667], + "92": [0.35001, 0.85, 0, 0, 0.57778], + "93": [0.35001, 0.85, 0, 0, 0.41667], + "123": [0.35001, 0.85, 0, 0, 0.58334], + "125": [0.35001, 0.85, 0, 0, 0.58334], + "160": [0, 0, 0, 0, 0.25], + "710": [0, 0.72222, 0, 0, 0.55556], + "732": [0, 0.72222, 0, 0, 0.55556], + "770": [0, 0.72222, 0, 0, 0.55556], + "771": [0, 0.72222, 0, 0, 0.55556], + "8214": [-0.00099, 0.601, 0, 0, 0.77778], + "8593": [1e-05, 0.6, 0, 0, 0.66667], + "8595": [1e-05, 0.6, 0, 0, 0.66667], + "8657": [1e-05, 0.6, 0, 0, 0.77778], + "8659": [1e-05, 0.6, 0, 0, 0.77778], + "8719": [0.25001, 0.75, 0, 0, 0.94445], + "8720": [0.25001, 0.75, 0, 0, 0.94445], + "8721": [0.25001, 0.75, 0, 0, 1.05556], + "8730": [0.35001, 0.85, 0, 0, 1.0], + "8739": [-0.00599, 0.606, 0, 0, 0.33333], + "8741": [-0.00599, 0.606, 0, 0, 0.55556], + "8747": [0.30612, 0.805, 0.19445, 0, 0.47222], + "8748": [0.306, 0.805, 0.19445, 0, 0.47222], + "8749": [0.306, 0.805, 0.19445, 0, 0.47222], + "8750": [0.30612, 0.805, 0.19445, 0, 0.47222], + "8896": [0.25001, 0.75, 0, 0, 0.83334], + "8897": [0.25001, 0.75, 0, 0, 0.83334], + "8898": [0.25001, 0.75, 0, 0, 0.83334], + "8899": [0.25001, 0.75, 0, 0, 0.83334], + "8968": [0.35001, 0.85, 0, 0, 0.47222], + "8969": [0.35001, 0.85, 0, 0, 0.47222], + "8970": [0.35001, 0.85, 0, 0, 0.47222], + "8971": [0.35001, 0.85, 0, 0, 0.47222], + "9168": [-0.00099, 0.601, 0, 0, 0.66667], + "10216": [0.35001, 0.85, 0, 0, 0.47222], + "10217": [0.35001, 0.85, 0, 0, 0.47222], + "10752": [0.25001, 0.75, 0, 0, 1.11111], + "10753": [0.25001, 0.75, 0, 0, 1.11111], + "10754": [0.25001, 0.75, 0, 0, 1.11111], + "10756": [0.25001, 0.75, 0, 0, 0.83334], + "10758": [0.25001, 0.75, 0, 0, 0.83334] + }, + "Size2-Regular": { + "32": [0, 0, 0, 0, 0.25], + "40": [0.65002, 1.15, 0, 0, 0.59722], + "41": [0.65002, 1.15, 0, 0, 0.59722], + "47": [0.65002, 1.15, 0, 0, 0.81111], + "91": [0.65002, 1.15, 0, 0, 0.47222], + "92": [0.65002, 1.15, 0, 0, 0.81111], + "93": [0.65002, 1.15, 0, 0, 0.47222], + "123": [0.65002, 1.15, 0, 0, 0.66667], + "125": [0.65002, 1.15, 0, 0, 0.66667], + "160": [0, 0, 0, 0, 0.25], + "710": [0, 0.75, 0, 0, 1.0], + "732": [0, 0.75, 0, 0, 1.0], + "770": [0, 0.75, 0, 0, 1.0], + "771": [0, 0.75, 0, 0, 1.0], + "8719": [0.55001, 1.05, 0, 0, 1.27778], + "8720": [0.55001, 1.05, 0, 0, 1.27778], + "8721": [0.55001, 1.05, 0, 0, 1.44445], + "8730": [0.65002, 1.15, 0, 0, 1.0], + "8747": [0.86225, 1.36, 0.44445, 0, 0.55556], + "8748": [0.862, 1.36, 0.44445, 0, 0.55556], + "8749": [0.862, 1.36, 0.44445, 0, 0.55556], + "8750": [0.86225, 1.36, 0.44445, 0, 0.55556], + "8896": [0.55001, 1.05, 0, 0, 1.11111], + "8897": [0.55001, 1.05, 0, 0, 1.11111], + "8898": [0.55001, 1.05, 0, 0, 1.11111], + "8899": [0.55001, 1.05, 0, 0, 1.11111], + "8968": [0.65002, 1.15, 0, 0, 0.52778], + "8969": [0.65002, 1.15, 0, 0, 0.52778], + "8970": [0.65002, 1.15, 0, 0, 0.52778], + "8971": [0.65002, 1.15, 0, 0, 0.52778], + "10216": [0.65002, 1.15, 0, 0, 0.61111], + "10217": [0.65002, 1.15, 0, 0, 0.61111], + "10752": [0.55001, 1.05, 0, 0, 1.51112], + "10753": [0.55001, 1.05, 0, 0, 1.51112], + "10754": [0.55001, 1.05, 0, 0, 1.51112], + "10756": [0.55001, 1.05, 0, 0, 1.11111], + "10758": [0.55001, 1.05, 0, 0, 1.11111] + }, + "Size3-Regular": { + "32": [0, 0, 0, 0, 0.25], + "40": [0.95003, 1.45, 0, 0, 0.73611], + "41": [0.95003, 1.45, 0, 0, 0.73611], + "47": [0.95003, 1.45, 0, 0, 1.04445], + "91": [0.95003, 1.45, 0, 0, 0.52778], + "92": [0.95003, 1.45, 0, 0, 1.04445], + "93": [0.95003, 1.45, 0, 0, 0.52778], + "123": [0.95003, 1.45, 0, 0, 0.75], + "125": [0.95003, 1.45, 0, 0, 0.75], + "160": [0, 0, 0, 0, 0.25], + "710": [0, 0.75, 0, 0, 1.44445], + "732": [0, 0.75, 0, 0, 1.44445], + "770": [0, 0.75, 0, 0, 1.44445], + "771": [0, 0.75, 0, 0, 1.44445], + "8730": [0.95003, 1.45, 0, 0, 1.0], + "8968": [0.95003, 1.45, 0, 0, 0.58334], + "8969": [0.95003, 1.45, 0, 0, 0.58334], + "8970": [0.95003, 1.45, 0, 0, 0.58334], + "8971": [0.95003, 1.45, 0, 0, 0.58334], + "10216": [0.95003, 1.45, 0, 0, 0.75], + "10217": [0.95003, 1.45, 0, 0, 0.75] + }, + "Size4-Regular": { + "32": [0, 0, 0, 0, 0.25], + "40": [1.25003, 1.75, 0, 0, 0.79167], + "41": [1.25003, 1.75, 0, 0, 0.79167], + "47": [1.25003, 1.75, 0, 0, 1.27778], + "91": [1.25003, 1.75, 0, 0, 0.58334], + "92": [1.25003, 1.75, 0, 0, 1.27778], + "93": [1.25003, 1.75, 0, 0, 0.58334], + "123": [1.25003, 1.75, 0, 0, 0.80556], + "125": [1.25003, 1.75, 0, 0, 0.80556], + "160": [0, 0, 0, 0, 0.25], + "710": [0, 0.825, 0, 0, 1.8889], + "732": [0, 0.825, 0, 0, 1.8889], + "770": [0, 0.825, 0, 0, 1.8889], + "771": [0, 0.825, 0, 0, 1.8889], + "8730": [1.25003, 1.75, 0, 0, 1.0], + "8968": [1.25003, 1.75, 0, 0, 0.63889], + "8969": [1.25003, 1.75, 0, 0, 0.63889], + "8970": [1.25003, 1.75, 0, 0, 0.63889], + "8971": [1.25003, 1.75, 0, 0, 0.63889], + "9115": [0.64502, 1.155, 0, 0, 0.875], + "9116": [1e-05, 0.6, 0, 0, 0.875], + "9117": [0.64502, 1.155, 0, 0, 0.875], + "9118": [0.64502, 1.155, 0, 0, 0.875], + "9119": [1e-05, 0.6, 0, 0, 0.875], + "9120": [0.64502, 1.155, 0, 0, 0.875], + "9121": [0.64502, 1.155, 0, 0, 0.66667], + "9122": [-0.00099, 0.601, 0, 0, 0.66667], + "9123": [0.64502, 1.155, 0, 0, 0.66667], + "9124": [0.64502, 1.155, 0, 0, 0.66667], + "9125": [-0.00099, 0.601, 0, 0, 0.66667], + "9126": [0.64502, 1.155, 0, 0, 0.66667], + "9127": [1e-05, 0.9, 0, 0, 0.88889], + "9128": [0.65002, 1.15, 0, 0, 0.88889], + "9129": [0.90001, 0, 0, 0, 0.88889], + "9130": [0, 0.3, 0, 0, 0.88889], + "9131": [1e-05, 0.9, 0, 0, 0.88889], + "9132": [0.65002, 1.15, 0, 0, 0.88889], + "9133": [0.90001, 0, 0, 0, 0.88889], + "9143": [0.88502, 0.915, 0, 0, 1.05556], + "10216": [1.25003, 1.75, 0, 0, 0.80556], + "10217": [1.25003, 1.75, 0, 0, 0.80556], + "57344": [-0.00499, 0.605, 0, 0, 1.05556], + "57345": [-0.00499, 0.605, 0, 0, 1.05556], + "57680": [0, 0.12, 0, 0, 0.45], + "57681": [0, 0.12, 0, 0, 0.45], + "57682": [0, 0.12, 0, 0, 0.45], + "57683": [0, 0.12, 0, 0, 0.45] + }, + "Typewriter-Regular": { + "32": [0, 0, 0, 0, 0.525], + "33": [0, 0.61111, 0, 0, 0.525], + "34": [0, 0.61111, 0, 0, 0.525], + "35": [0, 0.61111, 0, 0, 0.525], + "36": [0.08333, 0.69444, 0, 0, 0.525], + "37": [0.08333, 0.69444, 0, 0, 0.525], + "38": [0, 0.61111, 0, 0, 0.525], + "39": [0, 0.61111, 0, 0, 0.525], + "40": [0.08333, 0.69444, 0, 0, 0.525], + "41": [0.08333, 0.69444, 0, 0, 0.525], + "42": [0, 0.52083, 0, 0, 0.525], + "43": [-0.08056, 0.53055, 0, 0, 0.525], + "44": [0.13889, 0.125, 0, 0, 0.525], + "45": [-0.08056, 0.53055, 0, 0, 0.525], + "46": [0, 0.125, 0, 0, 0.525], + "47": [0.08333, 0.69444, 0, 0, 0.525], + "48": [0, 0.61111, 0, 0, 0.525], + "49": [0, 0.61111, 0, 0, 0.525], + "50": [0, 0.61111, 0, 0, 0.525], + "51": [0, 0.61111, 0, 0, 0.525], + "52": [0, 0.61111, 0, 0, 0.525], + "53": [0, 0.61111, 0, 0, 0.525], + "54": [0, 0.61111, 0, 0, 0.525], + "55": [0, 0.61111, 0, 0, 0.525], + "56": [0, 0.61111, 0, 0, 0.525], + "57": [0, 0.61111, 0, 0, 0.525], + "58": [0, 0.43056, 0, 0, 0.525], + "59": [0.13889, 0.43056, 0, 0, 0.525], + "60": [-0.05556, 0.55556, 0, 0, 0.525], + "61": [-0.19549, 0.41562, 0, 0, 0.525], + "62": [-0.05556, 0.55556, 0, 0, 0.525], + "63": [0, 0.61111, 0, 0, 0.525], + "64": [0, 0.61111, 0, 0, 0.525], + "65": [0, 0.61111, 0, 0, 0.525], + "66": [0, 0.61111, 0, 0, 0.525], + "67": [0, 0.61111, 0, 0, 0.525], + "68": [0, 0.61111, 0, 0, 0.525], + "69": [0, 0.61111, 0, 0, 0.525], + "70": [0, 0.61111, 0, 0, 0.525], + "71": [0, 0.61111, 0, 0, 0.525], + "72": [0, 0.61111, 0, 0, 0.525], + "73": [0, 0.61111, 0, 0, 0.525], + "74": [0, 0.61111, 0, 0, 0.525], + "75": [0, 0.61111, 0, 0, 0.525], + "76": [0, 0.61111, 0, 0, 0.525], + "77": [0, 0.61111, 0, 0, 0.525], + "78": [0, 0.61111, 0, 0, 0.525], + "79": [0, 0.61111, 0, 0, 0.525], + "80": [0, 0.61111, 0, 0, 0.525], + "81": [0.13889, 0.61111, 0, 0, 0.525], + "82": [0, 0.61111, 0, 0, 0.525], + "83": [0, 0.61111, 0, 0, 0.525], + "84": [0, 0.61111, 0, 0, 0.525], + "85": [0, 0.61111, 0, 0, 0.525], + "86": [0, 0.61111, 0, 0, 0.525], + "87": [0, 0.61111, 0, 0, 0.525], + "88": [0, 0.61111, 0, 0, 0.525], + "89": [0, 0.61111, 0, 0, 0.525], + "90": [0, 0.61111, 0, 0, 0.525], + "91": [0.08333, 0.69444, 0, 0, 0.525], + "92": [0.08333, 0.69444, 0, 0, 0.525], + "93": [0.08333, 0.69444, 0, 0, 0.525], + "94": [0, 0.61111, 0, 0, 0.525], + "95": [0.09514, 0, 0, 0, 0.525], + "96": [0, 0.61111, 0, 0, 0.525], + "97": [0, 0.43056, 0, 0, 0.525], + "98": [0, 0.61111, 0, 0, 0.525], + "99": [0, 0.43056, 0, 0, 0.525], + "100": [0, 0.61111, 0, 0, 0.525], + "101": [0, 0.43056, 0, 0, 0.525], + "102": [0, 0.61111, 0, 0, 0.525], + "103": [0.22222, 0.43056, 0, 0, 0.525], + "104": [0, 0.61111, 0, 0, 0.525], + "105": [0, 0.61111, 0, 0, 0.525], + "106": [0.22222, 0.61111, 0, 0, 0.525], + "107": [0, 0.61111, 0, 0, 0.525], + "108": [0, 0.61111, 0, 0, 0.525], + "109": [0, 0.43056, 0, 0, 0.525], + "110": [0, 0.43056, 0, 0, 0.525], + "111": [0, 0.43056, 0, 0, 0.525], + "112": [0.22222, 0.43056, 0, 0, 0.525], + "113": [0.22222, 0.43056, 0, 0, 0.525], + "114": [0, 0.43056, 0, 0, 0.525], + "115": [0, 0.43056, 0, 0, 0.525], + "116": [0, 0.55358, 0, 0, 0.525], + "117": [0, 0.43056, 0, 0, 0.525], + "118": [0, 0.43056, 0, 0, 0.525], + "119": [0, 0.43056, 0, 0, 0.525], + "120": [0, 0.43056, 0, 0, 0.525], + "121": [0.22222, 0.43056, 0, 0, 0.525], + "122": [0, 0.43056, 0, 0, 0.525], + "123": [0.08333, 0.69444, 0, 0, 0.525], + "124": [0.08333, 0.69444, 0, 0, 0.525], + "125": [0.08333, 0.69444, 0, 0, 0.525], + "126": [0, 0.61111, 0, 0, 0.525], + "127": [0, 0.61111, 0, 0, 0.525], + "160": [0, 0, 0, 0, 0.525], + "176": [0, 0.61111, 0, 0, 0.525], + "184": [0.19445, 0, 0, 0, 0.525], + "305": [0, 0.43056, 0, 0, 0.525], + "567": [0.22222, 0.43056, 0, 0, 0.525], + "711": [0, 0.56597, 0, 0, 0.525], + "713": [0, 0.56555, 0, 0, 0.525], + "714": [0, 0.61111, 0, 0, 0.525], + "715": [0, 0.61111, 0, 0, 0.525], + "728": [0, 0.61111, 0, 0, 0.525], + "730": [0, 0.61111, 0, 0, 0.525], + "770": [0, 0.61111, 0, 0, 0.525], + "771": [0, 0.61111, 0, 0, 0.525], + "776": [0, 0.61111, 0, 0, 0.525], + "915": [0, 0.61111, 0, 0, 0.525], + "916": [0, 0.61111, 0, 0, 0.525], + "920": [0, 0.61111, 0, 0, 0.525], + "923": [0, 0.61111, 0, 0, 0.525], + "926": [0, 0.61111, 0, 0, 0.525], + "928": [0, 0.61111, 0, 0, 0.525], + "931": [0, 0.61111, 0, 0, 0.525], + "933": [0, 0.61111, 0, 0, 0.525], + "934": [0, 0.61111, 0, 0, 0.525], + "936": [0, 0.61111, 0, 0, 0.525], + "937": [0, 0.61111, 0, 0, 0.525], + "8216": [0, 0.61111, 0, 0, 0.525], + "8217": [0, 0.61111, 0, 0, 0.525], + "8242": [0, 0.61111, 0, 0, 0.525], + "9251": [0.11111, 0.21944, 0, 0, 0.525] + } +}); + + +/** + * This file contains metrics regarding fonts and individual symbols. The sigma + * and xi variables, as well as the metricMap 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. + */ +// In TeX, there are actually three sets of dimensions, one for each of +// textstyle (size index 5 and higher: >=9pt), scriptstyle (size index 3 and 4: +// 7-8pt), and scriptscriptstyle (size index 1 and 2: 5-6pt). These are +// provided in the the arrays below, in that order. +// +// The font metrics are stored in fonts cmsy10, cmsy7, and cmsy5 respsectively. +// This was determined by running the following script: +// +// 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 retreived using the following commands: +// +// tftopl cmsy10 +// tftopl cmsy7 +// tftopl cmsy5 +// +// 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. +var sigmasAndXis = { + slant: [0.250, 0.250, 0.250], + // sigma1 + space: [0.000, 0.000, 0.000], + // sigma2 + stretch: [0.000, 0.000, 0.000], + // sigma3 + shrink: [0.000, 0.000, 0.000], + // sigma4 + xHeight: [0.431, 0.431, 0.431], + // sigma5 + quad: [1.000, 1.171, 1.472], + // sigma6 + extraSpace: [0.000, 0.000, 0.000], + // sigma7 + num1: [0.677, 0.732, 0.925], + // sigma8 + num2: [0.394, 0.384, 0.387], + // sigma9 + num3: [0.444, 0.471, 0.504], + // sigma10 + denom1: [0.686, 0.752, 1.025], + // sigma11 + denom2: [0.345, 0.344, 0.532], + // sigma12 + sup1: [0.413, 0.503, 0.504], + // sigma13 + sup2: [0.363, 0.431, 0.404], + // sigma14 + sup3: [0.289, 0.286, 0.294], + // sigma15 + sub1: [0.150, 0.143, 0.200], + // sigma16 + sub2: [0.247, 0.286, 0.400], + // sigma17 + supDrop: [0.386, 0.353, 0.494], + // sigma18 + subDrop: [0.050, 0.071, 0.100], + // sigma19 + delim1: [2.390, 1.700, 1.980], + // sigma20 + delim2: [1.010, 1.157, 1.420], + // sigma21 + axisHeight: [0.250, 0.250, 0.250], + // sigma22 + // These font metrics are extracted from TeX by using tftopl on cmex10.tfm; + // they correspond to the font parameters of the extension fonts (family 3). + // See the TeXbook, page 441. In AMSTeX, the extension fonts scale; to + // match cmex7, we'd use cmex7.tfm values for script and scriptscript + // values. + defaultRuleThickness: [0.04, 0.049, 0.049], + // xi8; cmex7: 0.049 + bigOpSpacing1: [0.111, 0.111, 0.111], + // xi9 + bigOpSpacing2: [0.166, 0.166, 0.166], + // xi10 + bigOpSpacing3: [0.2, 0.2, 0.2], + // xi11 + bigOpSpacing4: [0.6, 0.611, 0.611], + // xi12; cmex7: 0.611 + bigOpSpacing5: [0.1, 0.143, 0.143], + // xi13; cmex7: 0.143 + // The \sqrt rule width is taken from the height of the surd character. + // Since we use the same font at all sizes, this thickness doesn't scale. + sqrtRuleThickness: [0.04, 0.04, 0.04], + // This value determines how large a pt is, for metrics which are defined + // in terms of pts. + // This value is also used in katex.less; if you change it make sure the + // values match. + ptPerEm: [10.0, 10.0, 10.0], + // The space between adjacent `|` columns in an array definition. From + // `\showthe\doublerulesep` in LaTeX. Equals 2.0 / ptPerEm. + doubleRuleSep: [0.2, 0.2, 0.2], + // The width of separator lines in {array} environments. From + // `\showthe\arrayrulewidth` in LaTeX. Equals 0.4 / ptPerEm. + arrayRuleWidth: [0.04, 0.04, 0.04], + // Two values from LaTeX source2e: + fboxsep: [0.3, 0.3, 0.3], + // 3 pt / ptPerEm + fboxrule: [0.04, 0.04, 0.04] // 0.4 pt / ptPerEm + +}; // This map contains a mapping from font name and character code to character +// metrics, including height, depth, italic correction, and skew (kern from the +// character to the corresponding \skewchar) +// This map is generated via `make metrics`. It should not be changed manually. + + // 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. + +var extraCharacterMap = { + // Latin-1 + 'Å': 'A', + 'Ð': 'D', + 'Þ': 'o', + 'å': 'a', + 'ð': '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 adds new font metrics to default metricMap + * It can also override existing metrics + */ +function setFontMetrics(fontName, metrics) { + fontMetricsData[fontName] = metrics; +} +/** + * This function is a convenience function for looking up information in the + * metricMap table. It takes a character as a string, and a font. + * + * Note: the `width` property may be undefined if fontMetricsData.js wasn't + * built using `Make extended_metrics`. + */ + +function getCharacterMetrics(character, font, mode) { + if (!fontMetricsData[font]) { + throw new Error("Font metrics not found for font: " + font + "."); + } + + var ch = character.charCodeAt(0); + var metrics = fontMetricsData[font][ch]; + + if (!metrics && character[0] in extraCharacterMap) { + ch = extraCharacterMap[character[0]].charCodeAt(0); + metrics = fontMetricsData[font][ch]; + } + + if (!metrics && mode === 'text') { + // We don't typically have font metrics for Asian scripts. + // But since we support them in text mode, we need to return + // some sort of metrics. + // So if the character is in a script we support but we + // don't have metrics for it, just use the metrics for + // the Latin capital letter M. This is close enough because + // we (currently) only care about the height of the glpyh + // not its width. + if (supportedCodepoint(ch)) { + metrics = fontMetricsData[font][77]; // 77 is the charcode for 'M' + } + } + + if (metrics) { + return { + depth: metrics[0], + height: metrics[1], + italic: metrics[2], + skew: metrics[3], + width: metrics[4] + }; + } +} +var fontMetricsBySizeIndex = {}; +/** + * Get the font metrics for a given size. + */ + +function getGlobalMetrics(size) { + var sizeIndex; + + if (size >= 5) { + sizeIndex = 0; + } else if (size >= 3) { + sizeIndex = 1; + } else { + sizeIndex = 2; + } + + if (!fontMetricsBySizeIndex[sizeIndex]) { + var metrics = fontMetricsBySizeIndex[sizeIndex] = { + cssEmPerMu: sigmasAndXis.quad[sizeIndex] / 18 + }; + + for (var key in sigmasAndXis) { + if (sigmasAndXis.hasOwnProperty(key)) { + metrics[key] = sigmasAndXis[key][sizeIndex]; + } + } + } + + return fontMetricsBySizeIndex[sizeIndex]; +} +/** + * This file contains information about the options that the Parser carries + * around with it while parsing. Data is held in an `Options` object, and when + * recursing, a new `Options` object can be created with the `.with*` and + * `.reset` functions. + */ + +var sizeStyleMap = [// Each element contains [textsize, scriptsize, scriptscriptsize]. +// The size mappings are taken from TeX with \normalsize=10pt. +[1, 1, 1], // size1: [5, 5, 5] \tiny +[2, 1, 1], // size2: [6, 5, 5] +[3, 1, 1], // size3: [7, 5, 5] \scriptsize +[4, 2, 1], // size4: [8, 6, 5] \footnotesize +[5, 2, 1], // size5: [9, 6, 5] \small +[6, 3, 1], // size6: [10, 7, 5] \normalsize +[7, 4, 2], // size7: [12, 8, 6] \large +[8, 6, 3], // size8: [14.4, 10, 7] \Large +[9, 7, 6], // size9: [17.28, 12, 10] \LARGE +[10, 8, 7], // size10: [20.74, 14.4, 12] \huge +[11, 10, 9] // size11: [24.88, 20.74, 17.28] \HUGE +]; +var sizeMultipliers = [// fontMetrics.js:getGlobalMetrics also uses size indexes, so if +// you change size indexes, change that function. +0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.2, 1.44, 1.728, 2.074, 2.488]; + +var sizeAtStyle = function sizeAtStyle(size, style) { + return style.size < 2 ? size : sizeStyleMap[size - 1][style.size - 1]; +}; // In these types, "" (empty string) means "no change". + + +/** + * This is the main options class. It contains the current style, size, color, + * and font. + * + * Options objects should not be modified. To create a new Options with + * different properties, call a `.having*` method. + */ +var Options = /*#__PURE__*/function () { + // A font family applies to a group of fonts (i.e. SansSerif), while a font + // represents a specific font (i.e. SansSerif Bold). + // See: https://tex.stackexchange.com/questions/22350/difference-between-textrm-and-mathrm + + /** + * The base size index. + */ + function Options(data) { + this.style = void 0; + this.color = void 0; + this.size = void 0; + this.textSize = void 0; + this.phantom = void 0; + this.font = void 0; + this.fontFamily = void 0; + this.fontWeight = void 0; + this.fontShape = void 0; + this.sizeMultiplier = void 0; + this.maxSize = void 0; + this.minRuleThickness = void 0; + this._fontMetrics = void 0; + this.style = data.style; + this.color = data.color; + this.size = data.size || Options.BASESIZE; + this.textSize = data.textSize || this.size; + this.phantom = !!data.phantom; + this.font = data.font || ""; + this.fontFamily = data.fontFamily || ""; + this.fontWeight = data.fontWeight || ''; + this.fontShape = data.fontShape || ''; + this.sizeMultiplier = sizeMultipliers[this.size - 1]; + this.maxSize = data.maxSize; + this.minRuleThickness = data.minRuleThickness; + this._fontMetrics = undefined; + } + /** + * Returns a new options object with the same properties as "this". Properties + * from "extension" will be copied to the new options object. + */ + + + var _proto = Options.prototype; + + _proto.extend = function extend(extension) { + var data = { + style: this.style, + size: this.size, + textSize: this.textSize, + color: this.color, + phantom: this.phantom, + font: this.font, + fontFamily: this.fontFamily, + fontWeight: this.fontWeight, + fontShape: this.fontShape, + maxSize: this.maxSize, + minRuleThickness: this.minRuleThickness + }; + + for (var key in extension) { + if (extension.hasOwnProperty(key)) { + data[key] = extension[key]; + } + } + + return new Options(data); + } + /** + * Return an options object with the given style. If `this.style === style`, + * returns `this`. + */ + ; + + _proto.havingStyle = function havingStyle(style) { + if (this.style === style) { + return this; + } else { + return this.extend({ + style: style, + size: sizeAtStyle(this.textSize, style) + }); + } + } + /** + * Return an options object with a cramped version of the current style. If + * the current style is cramped, returns `this`. + */ + ; + + _proto.havingCrampedStyle = function havingCrampedStyle() { + return this.havingStyle(this.style.cramp()); + } + /** + * Return an options object with the given size and in at least `\textstyle`. + * Returns `this` if appropriate. + */ + ; + + _proto.havingSize = function havingSize(size) { + if (this.size === size && this.textSize === size) { + return this; + } else { + return this.extend({ + style: this.style.text(), + size: size, + textSize: size, + sizeMultiplier: sizeMultipliers[size - 1] + }); + } + } + /** + * Like `this.havingSize(BASESIZE).havingStyle(style)`. If `style` is omitted, + * changes to at least `\textstyle`. + */ + ; + + _proto.havingBaseStyle = function havingBaseStyle(style) { + style = style || this.style.text(); + var wantSize = sizeAtStyle(Options.BASESIZE, style); + + if (this.size === wantSize && this.textSize === Options.BASESIZE && this.style === style) { + return this; + } else { + return this.extend({ + style: style, + size: wantSize + }); + } + } + /** + * Remove the effect of sizing changes such as \Huge. + * Keep the effect of the current style, such as \scriptstyle. + */ + ; + + _proto.havingBaseSizing = function havingBaseSizing() { + var size; + + switch (this.style.id) { + case 4: + case 5: + size = 3; // normalsize in scriptstyle + + break; + + case 6: + case 7: + size = 1; // normalsize in scriptscriptstyle + + break; + + default: + size = 6; + // normalsize in textstyle or displaystyle + } + + return this.extend({ + style: this.style.text(), + size: size + }); + } + /** + * Create a new options object with the given color. + */ + ; + + _proto.withColor = function withColor(color) { + return this.extend({ + color: color + }); + } + /** + * Create a new options object with "phantom" set to true. + */ + ; + + _proto.withPhantom = function withPhantom() { + return this.extend({ + phantom: true + }); + } + /** + * Creates a new options object with the given math font or old text font. + * @type {[type]} + */ + ; + + _proto.withFont = function withFont(font) { + return this.extend({ + font: font + }); + } + /** + * Create a new options objects with the given fontFamily. + */ + ; + + _proto.withTextFontFamily = function withTextFontFamily(fontFamily) { + return this.extend({ + fontFamily: fontFamily, + font: "" + }); + } + /** + * Creates a new options object with the given font weight + */ + ; + + _proto.withTextFontWeight = function withTextFontWeight(fontWeight) { + return this.extend({ + fontWeight: fontWeight, + font: "" + }); + } + /** + * Creates a new options object with the given font weight + */ + ; + + _proto.withTextFontShape = function withTextFontShape(fontShape) { + return this.extend({ + fontShape: fontShape, + font: "" + }); + } + /** + * Return the CSS sizing classes required to switch from enclosing options + * `oldOptions` to `this`. Returns an array of classes. + */ + ; + + _proto.sizingClasses = function sizingClasses(oldOptions) { + if (oldOptions.size !== this.size) { + return ["sizing", "reset-size" + oldOptions.size, "size" + this.size]; + } else { + return []; + } + } + /** + * Return the CSS sizing classes required to switch to the base size. Like + * `this.havingSize(BASESIZE).sizingClasses(this)`. + */ + ; + + _proto.baseSizingClasses = function baseSizingClasses() { + if (this.size !== Options.BASESIZE) { + return ["sizing", "reset-size" + this.size, "size" + Options.BASESIZE]; + } else { + return []; + } + } + /** + * Return the font metrics for this size. + */ + ; + + _proto.fontMetrics = function fontMetrics() { + if (!this._fontMetrics) { + this._fontMetrics = getGlobalMetrics(this.size); + } + + return this._fontMetrics; + } + /** + * Gets the CSS color of the current options object + */ + ; + + _proto.getColor = function getColor() { + if (this.phantom) { + return "transparent"; + } else { + return this.color; + } + }; + + return Options; +}(); + +Options.BASESIZE = 6; +/* harmony default export */ var src_Options = (Options); +/** + * This file does conversion between units. In particular, it provides + * calculateSize to convert other units into ems. + */ + + // This table gives the number of TeX pts in one of each *absolute* TeX unit. +// Thus, multiplying a length by this number converts the length from units +// into pts. Dividing the result by ptPerEm gives the number of ems +// *assuming* a font size of ptPerEm (normal size, normal style). + +var ptPerUnit = { + // https://en.wikibooks.org/wiki/LaTeX/Lengths and + // https://tex.stackexchange.com/a/8263 + "pt": 1, + // TeX point + "mm": 7227 / 2540, + // millimeter + "cm": 7227 / 254, + // centimeter + "in": 72.27, + // inch + "bp": 803 / 800, + // big (PostScript) points + "pc": 12, + // pica + "dd": 1238 / 1157, + // didot + "cc": 14856 / 1157, + // cicero (12 didot) + "nd": 685 / 642, + // new didot + "nc": 1370 / 107, + // new cicero (12 new didot) + "sp": 1 / 65536, + // scaled point (TeX's internal smallest unit) + // https://tex.stackexchange.com/a/41371 + "px": 803 / 800 // \pdfpxdimen defaults to 1 bp in pdfTeX and LuaTeX + +}; // Dictionary of relative units, for fast validity testing. + +var relativeUnit = { + "ex": true, + "em": true, + "mu": true +}; + +/** + * Determine whether the specified unit (either a string defining the unit + * or a "size" parse node containing a unit field) is valid. + */ +var validUnit = function validUnit(unit) { + if (typeof unit !== "string") { + unit = unit.unit; + } + + return unit in ptPerUnit || unit in relativeUnit || unit === "ex"; +}; +/* + * Convert a "size" parse node (with numeric "number" and string "unit" fields, + * as parsed by functions.js argType "size") into a CSS em value for the + * current style/scale. `options` gives the current options. + */ + +var calculateSize = function calculateSize(sizeValue, options) { + var scale; + + if (sizeValue.unit in ptPerUnit) { + // Absolute units + scale = ptPerUnit[sizeValue.unit] // Convert unit to pt + / options.fontMetrics().ptPerEm // Convert pt to CSS em + / options.sizeMultiplier; // Unscale to make absolute units + } else if (sizeValue.unit === "mu") { + // `mu` units scale with scriptstyle/scriptscriptstyle. + scale = options.fontMetrics().cssEmPerMu; + } else { + // Other relative units always refer to the *textstyle* font + // in the current size. + var unitOptions; + + if (options.style.isTight()) { + // isTight() means current style is script/scriptscript. + unitOptions = options.havingStyle(options.style.text()); + } else { + unitOptions = options; + } // TODO: In TeX these units are relative to the quad of the current + // *text* font, e.g. cmr10. KaTeX instead uses values from the + // comparably-sized *Computer Modern symbol* font. At 10pt, these + // match. At 7pt and 5pt, they differ: cmr7=1.138894, cmsy7=1.170641; + // cmr5=1.361133, cmsy5=1.472241. Consider $\scriptsize a\kern1emb$. + // TeX \showlists shows a kern of 1.13889 * fontsize; + // KaTeX shows a kern of 1.171 * fontsize. + + + if (sizeValue.unit === "ex") { + scale = unitOptions.fontMetrics().xHeight; + } else if (sizeValue.unit === "em") { + scale = unitOptions.fontMetrics().quad; + } else { + throw new src_ParseError("Invalid unit: '" + sizeValue.unit + "'"); + } + + if (unitOptions !== options) { + scale *= unitOptions.sizeMultiplier / options.sizeMultiplier; + } + } + + return Math.min(sizeValue.number * scale, options.maxSize); +}; +/** + * Round `n` to 4 decimal places, or to the nearest 1/10,000th em. See + * https://github.com/KaTeX/KaTeX/pull/2460. + */ + +var makeEm = function makeEm(n) { + return +n.toFixed(4) + "em"; +}; +/** + * These objects store the data about the DOM nodes we create, as well as some + * extra data. They can then be transformed into real DOM nodes with the + * `toNode` function or HTML markup using `toMarkup`. They are useful for both + * storing extra properties on the nodes, as well as providing a way to easily + * work with the DOM. + * + * Similar functions for working with MathML nodes exist in mathMLTree.js. + * + * TODO: refactor `span` and `anchor` into common superclass when + * target environments support class inheritance + */ + + + + + + +/** + * Create an HTML className based on a list of classes. In addition to joining + * with spaces, we also remove empty classes. + */ +var createClass = function createClass(classes) { + return classes.filter(function (cls) { + return cls; + }).join(" "); +}; + +var initNode = function initNode(classes, options, style) { + this.classes = classes || []; + this.attributes = {}; + this.height = 0; + this.depth = 0; + this.maxFontSize = 0; + this.style = style || {}; + + if (options) { + if (options.style.isTight()) { + this.classes.push("mtight"); + } + + var color = options.getColor(); + + if (color) { + this.style.color = color; + } + } +}; +/** + * Convert into an HTML node + */ + + +var _toNode = function toNode(tagName) { + var node = document.createElement(tagName); // Apply the class + + node.className = createClass(this.classes); // Apply inline styles + + for (var style in this.style) { + if (this.style.hasOwnProperty(style)) { + // $FlowFixMe Flow doesn't seem to understand span.style's type. + node.style[style] = this.style[style]; + } + } // Apply attributes + + + for (var attr in this.attributes) { + if (this.attributes.hasOwnProperty(attr)) { + node.setAttribute(attr, this.attributes[attr]); + } + } // Append the children, also as HTML nodes + + + for (var i = 0; i < this.children.length; i++) { + node.appendChild(this.children[i].toNode()); + } + + return node; +}; +/** + * Convert into an HTML markup string + */ + + +var _toMarkup = function toMarkup(tagName) { + var markup = "<" + tagName; // Add the class + + if (this.classes.length) { + markup += " class=\"" + utils.escape(createClass(this.classes)) + "\""; + } + + var styles = ""; // Add the styles, after hyphenation + + for (var style in this.style) { + if (this.style.hasOwnProperty(style)) { + styles += utils.hyphenate(style) + ":" + this.style[style] + ";"; + } + } + + if (styles) { + markup += " style=\"" + utils.escape(styles) + "\""; + } // Add the attributes + + + for (var attr in this.attributes) { + if (this.attributes.hasOwnProperty(attr)) { + markup += " " + attr + "=\"" + utils.escape(this.attributes[attr]) + "\""; + } + } + + markup += ">"; // Add the markup of the children, also as markup + + for (var i = 0; i < this.children.length; i++) { + markup += this.children[i].toMarkup(); + } + + markup += ""; + return markup; +}; // Making the type below exact with all optional fields doesn't work due to +// - https://github.com/facebook/flow/issues/4582 +// - https://github.com/facebook/flow/issues/5688 +// However, since *all* fields are optional, $Shape<> works as suggested in 5688 +// above. +// This type does not include all CSS properties. Additional properties should +// be added as needed. + + +/** + * This node represents a span node, with a className, a list of children, and + * an inline style. It also contains information about its height, depth, and + * maxFontSize. + * + * Represents two types with different uses: SvgSpan to wrap an SVG and DomSpan + * otherwise. This typesafety is important when HTML builders access a span's + * children. + */ +var Span = /*#__PURE__*/function () { + function Span(classes, children, options, style) { + this.children = void 0; + this.attributes = void 0; + this.classes = void 0; + this.height = void 0; + this.depth = void 0; + this.width = void 0; + this.maxFontSize = void 0; + this.style = void 0; + initNode.call(this, classes, options, style); + this.children = children || []; + } + /** + * Sets an arbitrary attribute on the span. Warning: use this wisely. Not + * all browsers support attributes the same, and having too many custom + * attributes is probably bad. + */ + + + var _proto = Span.prototype; + + _proto.setAttribute = function setAttribute(attribute, value) { + this.attributes[attribute] = value; + }; + + _proto.hasClass = function hasClass(className) { + return utils.contains(this.classes, className); + }; + + _proto.toNode = function toNode() { + return _toNode.call(this, "span"); + }; + + _proto.toMarkup = function toMarkup() { + return _toMarkup.call(this, "span"); + }; + + return Span; +}(); +/** + * This node represents an anchor () element with a hyperlink. See `span` + * for further details. + */ + +var Anchor = /*#__PURE__*/function () { + function Anchor(href, classes, children, options) { + this.children = void 0; + this.attributes = void 0; + this.classes = void 0; + this.height = void 0; + this.depth = void 0; + this.maxFontSize = void 0; + this.style = void 0; + initNode.call(this, classes, options); + this.children = children || []; + this.setAttribute('href', href); + } + + var _proto2 = Anchor.prototype; + + _proto2.setAttribute = function setAttribute(attribute, value) { + this.attributes[attribute] = value; + }; + + _proto2.hasClass = function hasClass(className) { + return utils.contains(this.classes, className); + }; + + _proto2.toNode = function toNode() { + return _toNode.call(this, "a"); + }; + + _proto2.toMarkup = function toMarkup() { + return _toMarkup.call(this, "a"); + }; + + return Anchor; +}(); +/** + * This node represents an image embed () element. + */ + +var Img = /*#__PURE__*/function () { + function Img(src, alt, style) { + this.src = void 0; + this.alt = void 0; + this.classes = void 0; + this.height = void 0; + this.depth = void 0; + this.maxFontSize = void 0; + this.style = void 0; + this.alt = alt; + this.src = src; + this.classes = ["mord"]; + this.style = style; + } + + var _proto3 = Img.prototype; + + _proto3.hasClass = function hasClass(className) { + return utils.contains(this.classes, className); + }; + + _proto3.toNode = function toNode() { + var node = document.createElement("img"); + node.src = this.src; + node.alt = this.alt; + node.className = "mord"; // Apply inline styles + + for (var style in this.style) { + if (this.style.hasOwnProperty(style)) { + // $FlowFixMe + node.style[style] = this.style[style]; + } + } + + return node; + }; + + _proto3.toMarkup = function toMarkup() { + var markup = "" + this.alt + " 0) { + span = document.createElement("span"); + span.style.marginRight = makeEm(this.italic); + } + + if (this.classes.length > 0) { + span = span || document.createElement("span"); + span.className = createClass(this.classes); + } + + for (var style in this.style) { + if (this.style.hasOwnProperty(style)) { + span = span || document.createElement("span"); // $FlowFixMe Flow doesn't seem to understand span.style's type. + + span.style[style] = this.style[style]; + } + } + + if (span) { + span.appendChild(node); + return span; + } else { + return node; + } + } + /** + * Creates markup for a symbol node. + */ + ; + + _proto4.toMarkup = function toMarkup() { + // TODO(alpert): More duplication than I'd like from + // span.prototype.toMarkup and symbolNode.prototype.toNode... + var needsSpan = false; + var markup = " 0) { + styles += "margin-right:" + this.italic + "em;"; + } + + for (var style in this.style) { + if (this.style.hasOwnProperty(style)) { + styles += utils.hyphenate(style) + ":" + this.style[style] + ";"; + } + } + + if (styles) { + needsSpan = true; + markup += " style=\"" + utils.escape(styles) + "\""; + } + + var escaped = utils.escape(this.text); + + if (needsSpan) { + markup += ">"; + markup += escaped; + markup += ""; + return markup; + } else { + return escaped; + } + }; + + return SymbolNode; +}(); +/** + * SVG nodes are used to render stretchy wide elements. + */ + +var SvgNode = /*#__PURE__*/function () { + function SvgNode(children, attributes) { + this.children = void 0; + this.attributes = void 0; + this.children = children || []; + this.attributes = attributes || {}; + } + + var _proto5 = SvgNode.prototype; + + _proto5.toNode = function toNode() { + var svgNS = "http://www.w3.org/2000/svg"; + var node = document.createElementNS(svgNS, "svg"); // Apply attributes + + for (var attr in this.attributes) { + if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) { + node.setAttribute(attr, this.attributes[attr]); + } + } + + for (var i = 0; i < this.children.length; i++) { + node.appendChild(this.children[i].toNode()); + } + + return node; + }; + + _proto5.toMarkup = function toMarkup() { + var markup = ""; + } else { + return ""; + } + }; + + return PathNode; +}(); +var LineNode = /*#__PURE__*/function () { + function LineNode(attributes) { + this.attributes = void 0; + this.attributes = attributes || {}; + } + + var _proto7 = LineNode.prototype; + + _proto7.toNode = function toNode() { + var svgNS = "http://www.w3.org/2000/svg"; + var node = document.createElementNS(svgNS, "line"); // Apply attributes + + for (var attr in this.attributes) { + if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) { + node.setAttribute(attr, this.attributes[attr]); + } + } + + return node; + }; + + _proto7.toMarkup = function toMarkup() { + var markup = " but got " + String(group) + "."); + } +} +/** + * This file holds a list of all no-argument functions and single-character + * symbols (like 'a' or ';'). + * + * For each of the symbols, there are three properties they can have: + * - font (required): the font to be used for this symbol. Either "main" (the + normal font), or "ams" (the ams fonts). + * - group (required): the ParseNode group type the symbol should have (i.e. + "textord", "mathord", etc). + See https://github.com/KaTeX/KaTeX/wiki/Examining-TeX#group-types + * - replace: the character that this symbol or function should be + * replaced with (i.e. "\phi" has a replace value of "\u03d5", the phi + * character in the main font). + * + * The outermost map in the table indicates what mode the symbols should be + * accepted in (e.g. "math" or "text"). + */ +// Some of these have a "-token" suffix since these are also used as `ParseNode` +// types for raw text tokens, and we want to avoid conflicts with higher-level +// `ParseNode` types. These `ParseNode`s are constructed within `Parser` by +// looking up the `symbols` map. +var ATOMS = { + "bin": 1, + "close": 1, + "inner": 1, + "open": 1, + "punct": 1, + "rel": 1 +}; +var NON_ATOMS = { + "accent-token": 1, + "mathord": 1, + "op-token": 1, + "spacing": 1, + "textord": 1 +}; +var symbols = { + "math": {}, + "text": {} +}; +/* harmony default export */ var src_symbols = (symbols); +/** `acceptUnicodeChar = true` is only applicable if `replace` is set. */ + +function defineSymbol(mode, font, group, replace, name, acceptUnicodeChar) { + symbols[mode][name] = { + font: font, + group: group, + replace: replace + }; + + if (acceptUnicodeChar && replace) { + symbols[mode][replace] = symbols[mode][name]; + } +} // Some abbreviations for commonly used strings. +// This helps minify the code, and also spotting typos using jshint. +// modes: + +var math = "math"; +var symbols_text = "text"; // fonts: + +var main = "main"; +var ams = "ams"; // groups: + +var accent = "accent-token"; +var bin = "bin"; +var symbols_close = "close"; +var inner = "inner"; +var mathord = "mathord"; +var op = "op-token"; +var symbols_open = "open"; +var punct = "punct"; +var rel = "rel"; +var spacing = "spacing"; +var textord = "textord"; // Now comes the symbol table +// Relation Symbols + +defineSymbol(math, main, rel, "\u2261", "\\equiv", true); +defineSymbol(math, main, rel, "\u227A", "\\prec", true); +defineSymbol(math, main, rel, "\u227B", "\\succ", true); +defineSymbol(math, main, rel, "\u223C", "\\sim", true); +defineSymbol(math, main, rel, "\u22A5", "\\perp"); +defineSymbol(math, main, rel, "\u2AAF", "\\preceq", true); +defineSymbol(math, main, rel, "\u2AB0", "\\succeq", true); +defineSymbol(math, main, rel, "\u2243", "\\simeq", true); +defineSymbol(math, main, rel, "\u2223", "\\mid", true); +defineSymbol(math, main, rel, "\u226A", "\\ll", true); +defineSymbol(math, main, rel, "\u226B", "\\gg", true); +defineSymbol(math, main, rel, "\u224D", "\\asymp", true); +defineSymbol(math, main, rel, "\u2225", "\\parallel"); +defineSymbol(math, main, rel, "\u22C8", "\\bowtie", true); +defineSymbol(math, main, rel, "\u2323", "\\smile", true); +defineSymbol(math, main, rel, "\u2291", "\\sqsubseteq", true); +defineSymbol(math, main, rel, "\u2292", "\\sqsupseteq", true); +defineSymbol(math, main, rel, "\u2250", "\\doteq", true); +defineSymbol(math, main, rel, "\u2322", "\\frown", true); +defineSymbol(math, main, rel, "\u220B", "\\ni", true); +defineSymbol(math, main, rel, "\u221D", "\\propto", true); +defineSymbol(math, main, rel, "\u22A2", "\\vdash", true); +defineSymbol(math, main, rel, "\u22A3", "\\dashv", true); +defineSymbol(math, main, rel, "\u220B", "\\owns"); // Punctuation + +defineSymbol(math, main, punct, ".", "\\ldotp"); +defineSymbol(math, main, punct, "\u22C5", "\\cdotp"); // Misc Symbols + +defineSymbol(math, main, textord, "#", "\\#"); +defineSymbol(symbols_text, main, textord, "#", "\\#"); +defineSymbol(math, main, textord, "&", "\\&"); +defineSymbol(symbols_text, main, textord, "&", "\\&"); +defineSymbol(math, main, textord, "\u2135", "\\aleph", true); +defineSymbol(math, main, textord, "\u2200", "\\forall", true); +defineSymbol(math, main, textord, "\u210F", "\\hbar", true); +defineSymbol(math, main, textord, "\u2203", "\\exists", true); +defineSymbol(math, main, textord, "\u2207", "\\nabla", true); +defineSymbol(math, main, textord, "\u266D", "\\flat", true); +defineSymbol(math, main, textord, "\u2113", "\\ell", true); +defineSymbol(math, main, textord, "\u266E", "\\natural", true); +defineSymbol(math, main, textord, "\u2663", "\\clubsuit", true); +defineSymbol(math, main, textord, "\u2118", "\\wp", true); +defineSymbol(math, main, textord, "\u266F", "\\sharp", true); +defineSymbol(math, main, textord, "\u2662", "\\diamondsuit", true); +defineSymbol(math, main, textord, "\u211C", "\\Re", true); +defineSymbol(math, main, textord, "\u2661", "\\heartsuit", true); +defineSymbol(math, main, textord, "\u2111", "\\Im", true); +defineSymbol(math, main, textord, "\u2660", "\\spadesuit", true); +defineSymbol(math, main, textord, "\xA7", "\\S", true); +defineSymbol(symbols_text, main, textord, "\xA7", "\\S"); +defineSymbol(math, main, textord, "\xB6", "\\P", true); +defineSymbol(symbols_text, main, textord, "\xB6", "\\P"); // Math and Text + +defineSymbol(math, main, textord, "\u2020", "\\dag"); +defineSymbol(symbols_text, main, textord, "\u2020", "\\dag"); +defineSymbol(symbols_text, main, textord, "\u2020", "\\textdagger"); +defineSymbol(math, main, textord, "\u2021", "\\ddag"); +defineSymbol(symbols_text, main, textord, "\u2021", "\\ddag"); +defineSymbol(symbols_text, main, textord, "\u2021", "\\textdaggerdbl"); // Large Delimiters + +defineSymbol(math, main, symbols_close, "\u23B1", "\\rmoustache", true); +defineSymbol(math, main, symbols_open, "\u23B0", "\\lmoustache", true); +defineSymbol(math, main, symbols_close, "\u27EF", "\\rgroup", true); +defineSymbol(math, main, symbols_open, "\u27EE", "\\lgroup", true); // Binary Operators + +defineSymbol(math, main, bin, "\u2213", "\\mp", true); +defineSymbol(math, main, bin, "\u2296", "\\ominus", true); +defineSymbol(math, main, bin, "\u228E", "\\uplus", true); +defineSymbol(math, main, bin, "\u2293", "\\sqcap", true); +defineSymbol(math, main, bin, "\u2217", "\\ast"); +defineSymbol(math, main, bin, "\u2294", "\\sqcup", true); +defineSymbol(math, main, bin, "\u25EF", "\\bigcirc", true); +defineSymbol(math, main, bin, "\u2219", "\\bullet"); +defineSymbol(math, main, bin, "\u2021", "\\ddagger"); +defineSymbol(math, main, bin, "\u2240", "\\wr", true); +defineSymbol(math, main, bin, "\u2A3F", "\\amalg"); +defineSymbol(math, main, bin, "&", "\\And"); // from amsmath +// Arrow Symbols + +defineSymbol(math, main, rel, "\u27F5", "\\longleftarrow", true); +defineSymbol(math, main, rel, "\u21D0", "\\Leftarrow", true); +defineSymbol(math, main, rel, "\u27F8", "\\Longleftarrow", true); +defineSymbol(math, main, rel, "\u27F6", "\\longrightarrow", true); +defineSymbol(math, main, rel, "\u21D2", "\\Rightarrow", true); +defineSymbol(math, main, rel, "\u27F9", "\\Longrightarrow", true); +defineSymbol(math, main, rel, "\u2194", "\\leftrightarrow", true); +defineSymbol(math, main, rel, "\u27F7", "\\longleftrightarrow", true); +defineSymbol(math, main, rel, "\u21D4", "\\Leftrightarrow", true); +defineSymbol(math, main, rel, "\u27FA", "\\Longleftrightarrow", true); +defineSymbol(math, main, rel, "\u21A6", "\\mapsto", true); +defineSymbol(math, main, rel, "\u27FC", "\\longmapsto", true); +defineSymbol(math, main, rel, "\u2197", "\\nearrow", true); +defineSymbol(math, main, rel, "\u21A9", "\\hookleftarrow", true); +defineSymbol(math, main, rel, "\u21AA", "\\hookrightarrow", true); +defineSymbol(math, main, rel, "\u2198", "\\searrow", true); +defineSymbol(math, main, rel, "\u21BC", "\\leftharpoonup", true); +defineSymbol(math, main, rel, "\u21C0", "\\rightharpoonup", true); +defineSymbol(math, main, rel, "\u2199", "\\swarrow", true); +defineSymbol(math, main, rel, "\u21BD", "\\leftharpoondown", true); +defineSymbol(math, main, rel, "\u21C1", "\\rightharpoondown", true); +defineSymbol(math, main, rel, "\u2196", "\\nwarrow", true); +defineSymbol(math, main, rel, "\u21CC", "\\rightleftharpoons", true); // AMS Negated Binary Relations + +defineSymbol(math, ams, rel, "\u226E", "\\nless", true); // Symbol names preceeded by "@" each have a corresponding macro. + +defineSymbol(math, ams, rel, "\uE010", "\\@nleqslant"); +defineSymbol(math, ams, rel, "\uE011", "\\@nleqq"); +defineSymbol(math, ams, rel, "\u2A87", "\\lneq", true); +defineSymbol(math, ams, rel, "\u2268", "\\lneqq", true); +defineSymbol(math, ams, rel, "\uE00C", "\\@lvertneqq"); +defineSymbol(math, ams, rel, "\u22E6", "\\lnsim", true); +defineSymbol(math, ams, rel, "\u2A89", "\\lnapprox", true); +defineSymbol(math, ams, rel, "\u2280", "\\nprec", true); // unicode-math maps \u22e0 to \npreccurlyeq. We'll use the AMS synonym. + +defineSymbol(math, ams, rel, "\u22E0", "\\npreceq", true); +defineSymbol(math, ams, rel, "\u22E8", "\\precnsim", true); +defineSymbol(math, ams, rel, "\u2AB9", "\\precnapprox", true); +defineSymbol(math, ams, rel, "\u2241", "\\nsim", true); +defineSymbol(math, ams, rel, "\uE006", "\\@nshortmid"); +defineSymbol(math, ams, rel, "\u2224", "\\nmid", true); +defineSymbol(math, ams, rel, "\u22AC", "\\nvdash", true); +defineSymbol(math, ams, rel, "\u22AD", "\\nvDash", true); +defineSymbol(math, ams, rel, "\u22EA", "\\ntriangleleft"); +defineSymbol(math, ams, rel, "\u22EC", "\\ntrianglelefteq", true); +defineSymbol(math, ams, rel, "\u228A", "\\subsetneq", true); +defineSymbol(math, ams, rel, "\uE01A", "\\@varsubsetneq"); +defineSymbol(math, ams, rel, "\u2ACB", "\\subsetneqq", true); +defineSymbol(math, ams, rel, "\uE017", "\\@varsubsetneqq"); +defineSymbol(math, ams, rel, "\u226F", "\\ngtr", true); +defineSymbol(math, ams, rel, "\uE00F", "\\@ngeqslant"); +defineSymbol(math, ams, rel, "\uE00E", "\\@ngeqq"); +defineSymbol(math, ams, rel, "\u2A88", "\\gneq", true); +defineSymbol(math, ams, rel, "\u2269", "\\gneqq", true); +defineSymbol(math, ams, rel, "\uE00D", "\\@gvertneqq"); +defineSymbol(math, ams, rel, "\u22E7", "\\gnsim", true); +defineSymbol(math, ams, rel, "\u2A8A", "\\gnapprox", true); +defineSymbol(math, ams, rel, "\u2281", "\\nsucc", true); // unicode-math maps \u22e1 to \nsucccurlyeq. We'll use the AMS synonym. + +defineSymbol(math, ams, rel, "\u22E1", "\\nsucceq", true); +defineSymbol(math, ams, rel, "\u22E9", "\\succnsim", true); +defineSymbol(math, ams, rel, "\u2ABA", "\\succnapprox", true); // unicode-math maps \u2246 to \simneqq. We'll use the AMS synonym. + +defineSymbol(math, ams, rel, "\u2246", "\\ncong", true); +defineSymbol(math, ams, rel, "\uE007", "\\@nshortparallel"); +defineSymbol(math, ams, rel, "\u2226", "\\nparallel", true); +defineSymbol(math, ams, rel, "\u22AF", "\\nVDash", true); +defineSymbol(math, ams, rel, "\u22EB", "\\ntriangleright"); +defineSymbol(math, ams, rel, "\u22ED", "\\ntrianglerighteq", true); +defineSymbol(math, ams, rel, "\uE018", "\\@nsupseteqq"); +defineSymbol(math, ams, rel, "\u228B", "\\supsetneq", true); +defineSymbol(math, ams, rel, "\uE01B", "\\@varsupsetneq"); +defineSymbol(math, ams, rel, "\u2ACC", "\\supsetneqq", true); +defineSymbol(math, ams, rel, "\uE019", "\\@varsupsetneqq"); +defineSymbol(math, ams, rel, "\u22AE", "\\nVdash", true); +defineSymbol(math, ams, rel, "\u2AB5", "\\precneqq", true); +defineSymbol(math, ams, rel, "\u2AB6", "\\succneqq", true); +defineSymbol(math, ams, rel, "\uE016", "\\@nsubseteqq"); +defineSymbol(math, ams, bin, "\u22B4", "\\unlhd"); +defineSymbol(math, ams, bin, "\u22B5", "\\unrhd"); // AMS Negated Arrows + +defineSymbol(math, ams, rel, "\u219A", "\\nleftarrow", true); +defineSymbol(math, ams, rel, "\u219B", "\\nrightarrow", true); +defineSymbol(math, ams, rel, "\u21CD", "\\nLeftarrow", true); +defineSymbol(math, ams, rel, "\u21CF", "\\nRightarrow", true); +defineSymbol(math, ams, rel, "\u21AE", "\\nleftrightarrow", true); +defineSymbol(math, ams, rel, "\u21CE", "\\nLeftrightarrow", true); // AMS Misc + +defineSymbol(math, ams, rel, "\u25B3", "\\vartriangle"); +defineSymbol(math, ams, textord, "\u210F", "\\hslash"); +defineSymbol(math, ams, textord, "\u25BD", "\\triangledown"); +defineSymbol(math, ams, textord, "\u25CA", "\\lozenge"); +defineSymbol(math, ams, textord, "\u24C8", "\\circledS"); +defineSymbol(math, ams, textord, "\xAE", "\\circledR"); +defineSymbol(symbols_text, ams, textord, "\xAE", "\\circledR"); +defineSymbol(math, ams, textord, "\u2221", "\\measuredangle", true); +defineSymbol(math, ams, textord, "\u2204", "\\nexists"); +defineSymbol(math, ams, textord, "\u2127", "\\mho"); +defineSymbol(math, ams, textord, "\u2132", "\\Finv", true); +defineSymbol(math, ams, textord, "\u2141", "\\Game", true); +defineSymbol(math, ams, textord, "\u2035", "\\backprime"); +defineSymbol(math, ams, textord, "\u25B2", "\\blacktriangle"); +defineSymbol(math, ams, textord, "\u25BC", "\\blacktriangledown"); +defineSymbol(math, ams, textord, "\u25A0", "\\blacksquare"); +defineSymbol(math, ams, textord, "\u29EB", "\\blacklozenge"); +defineSymbol(math, ams, textord, "\u2605", "\\bigstar"); +defineSymbol(math, ams, textord, "\u2222", "\\sphericalangle", true); +defineSymbol(math, ams, textord, "\u2201", "\\complement", true); // unicode-math maps U+F0 to \matheth. We map to AMS function \eth + +defineSymbol(math, ams, textord, "\xF0", "\\eth", true); +defineSymbol(symbols_text, main, textord, "\xF0", "\xF0"); +defineSymbol(math, ams, textord, "\u2571", "\\diagup"); +defineSymbol(math, ams, textord, "\u2572", "\\diagdown"); +defineSymbol(math, ams, textord, "\u25A1", "\\square"); +defineSymbol(math, ams, textord, "\u25A1", "\\Box"); +defineSymbol(math, ams, textord, "\u25CA", "\\Diamond"); // unicode-math maps U+A5 to \mathyen. We map to AMS function \yen + +defineSymbol(math, ams, textord, "\xA5", "\\yen", true); +defineSymbol(symbols_text, ams, textord, "\xA5", "\\yen", true); +defineSymbol(math, ams, textord, "\u2713", "\\checkmark", true); +defineSymbol(symbols_text, ams, textord, "\u2713", "\\checkmark"); // AMS Hebrew + +defineSymbol(math, ams, textord, "\u2136", "\\beth", true); +defineSymbol(math, ams, textord, "\u2138", "\\daleth", true); +defineSymbol(math, ams, textord, "\u2137", "\\gimel", true); // AMS Greek + +defineSymbol(math, ams, textord, "\u03DD", "\\digamma", true); +defineSymbol(math, ams, textord, "\u03F0", "\\varkappa"); // AMS Delimiters + +defineSymbol(math, ams, symbols_open, "\u250C", "\\@ulcorner", true); +defineSymbol(math, ams, symbols_close, "\u2510", "\\@urcorner", true); +defineSymbol(math, ams, symbols_open, "\u2514", "\\@llcorner", true); +defineSymbol(math, ams, symbols_close, "\u2518", "\\@lrcorner", true); // AMS Binary Relations + +defineSymbol(math, ams, rel, "\u2266", "\\leqq", true); +defineSymbol(math, ams, rel, "\u2A7D", "\\leqslant", true); +defineSymbol(math, ams, rel, "\u2A95", "\\eqslantless", true); +defineSymbol(math, ams, rel, "\u2272", "\\lesssim", true); +defineSymbol(math, ams, rel, "\u2A85", "\\lessapprox", true); +defineSymbol(math, ams, rel, "\u224A", "\\approxeq", true); +defineSymbol(math, ams, bin, "\u22D6", "\\lessdot"); +defineSymbol(math, ams, rel, "\u22D8", "\\lll", true); +defineSymbol(math, ams, rel, "\u2276", "\\lessgtr", true); +defineSymbol(math, ams, rel, "\u22DA", "\\lesseqgtr", true); +defineSymbol(math, ams, rel, "\u2A8B", "\\lesseqqgtr", true); +defineSymbol(math, ams, rel, "\u2251", "\\doteqdot"); +defineSymbol(math, ams, rel, "\u2253", "\\risingdotseq", true); +defineSymbol(math, ams, rel, "\u2252", "\\fallingdotseq", true); +defineSymbol(math, ams, rel, "\u223D", "\\backsim", true); +defineSymbol(math, ams, rel, "\u22CD", "\\backsimeq", true); +defineSymbol(math, ams, rel, "\u2AC5", "\\subseteqq", true); +defineSymbol(math, ams, rel, "\u22D0", "\\Subset", true); +defineSymbol(math, ams, rel, "\u228F", "\\sqsubset", true); +defineSymbol(math, ams, rel, "\u227C", "\\preccurlyeq", true); +defineSymbol(math, ams, rel, "\u22DE", "\\curlyeqprec", true); +defineSymbol(math, ams, rel, "\u227E", "\\precsim", true); +defineSymbol(math, ams, rel, "\u2AB7", "\\precapprox", true); +defineSymbol(math, ams, rel, "\u22B2", "\\vartriangleleft"); +defineSymbol(math, ams, rel, "\u22B4", "\\trianglelefteq"); +defineSymbol(math, ams, rel, "\u22A8", "\\vDash", true); +defineSymbol(math, ams, rel, "\u22AA", "\\Vvdash", true); +defineSymbol(math, ams, rel, "\u2323", "\\smallsmile"); +defineSymbol(math, ams, rel, "\u2322", "\\smallfrown"); +defineSymbol(math, ams, rel, "\u224F", "\\bumpeq", true); +defineSymbol(math, ams, rel, "\u224E", "\\Bumpeq", true); +defineSymbol(math, ams, rel, "\u2267", "\\geqq", true); +defineSymbol(math, ams, rel, "\u2A7E", "\\geqslant", true); +defineSymbol(math, ams, rel, "\u2A96", "\\eqslantgtr", true); +defineSymbol(math, ams, rel, "\u2273", "\\gtrsim", true); +defineSymbol(math, ams, rel, "\u2A86", "\\gtrapprox", true); +defineSymbol(math, ams, bin, "\u22D7", "\\gtrdot"); +defineSymbol(math, ams, rel, "\u22D9", "\\ggg", true); +defineSymbol(math, ams, rel, "\u2277", "\\gtrless", true); +defineSymbol(math, ams, rel, "\u22DB", "\\gtreqless", true); +defineSymbol(math, ams, rel, "\u2A8C", "\\gtreqqless", true); +defineSymbol(math, ams, rel, "\u2256", "\\eqcirc", true); +defineSymbol(math, ams, rel, "\u2257", "\\circeq", true); +defineSymbol(math, ams, rel, "\u225C", "\\triangleq", true); +defineSymbol(math, ams, rel, "\u223C", "\\thicksim"); +defineSymbol(math, ams, rel, "\u2248", "\\thickapprox"); +defineSymbol(math, ams, rel, "\u2AC6", "\\supseteqq", true); +defineSymbol(math, ams, rel, "\u22D1", "\\Supset", true); +defineSymbol(math, ams, rel, "\u2290", "\\sqsupset", true); +defineSymbol(math, ams, rel, "\u227D", "\\succcurlyeq", true); +defineSymbol(math, ams, rel, "\u22DF", "\\curlyeqsucc", true); +defineSymbol(math, ams, rel, "\u227F", "\\succsim", true); +defineSymbol(math, ams, rel, "\u2AB8", "\\succapprox", true); +defineSymbol(math, ams, rel, "\u22B3", "\\vartriangleright"); +defineSymbol(math, ams, rel, "\u22B5", "\\trianglerighteq"); +defineSymbol(math, ams, rel, "\u22A9", "\\Vdash", true); +defineSymbol(math, ams, rel, "\u2223", "\\shortmid"); +defineSymbol(math, ams, rel, "\u2225", "\\shortparallel"); +defineSymbol(math, ams, rel, "\u226C", "\\between", true); +defineSymbol(math, ams, rel, "\u22D4", "\\pitchfork", true); +defineSymbol(math, ams, rel, "\u221D", "\\varpropto"); +defineSymbol(math, ams, rel, "\u25C0", "\\blacktriangleleft"); // unicode-math says that \therefore is a mathord atom. +// We kept the amssymb atom type, which is rel. + +defineSymbol(math, ams, rel, "\u2234", "\\therefore", true); +defineSymbol(math, ams, rel, "\u220D", "\\backepsilon"); +defineSymbol(math, ams, rel, "\u25B6", "\\blacktriangleright"); // unicode-math says that \because is a mathord atom. +// We kept the amssymb atom type, which is rel. + +defineSymbol(math, ams, rel, "\u2235", "\\because", true); +defineSymbol(math, ams, rel, "\u22D8", "\\llless"); +defineSymbol(math, ams, rel, "\u22D9", "\\gggtr"); +defineSymbol(math, ams, bin, "\u22B2", "\\lhd"); +defineSymbol(math, ams, bin, "\u22B3", "\\rhd"); +defineSymbol(math, ams, rel, "\u2242", "\\eqsim", true); +defineSymbol(math, main, rel, "\u22C8", "\\Join"); +defineSymbol(math, ams, rel, "\u2251", "\\Doteq", true); // AMS Binary Operators + +defineSymbol(math, ams, bin, "\u2214", "\\dotplus", true); +defineSymbol(math, ams, bin, "\u2216", "\\smallsetminus"); +defineSymbol(math, ams, bin, "\u22D2", "\\Cap", true); +defineSymbol(math, ams, bin, "\u22D3", "\\Cup", true); +defineSymbol(math, ams, bin, "\u2A5E", "\\doublebarwedge", true); +defineSymbol(math, ams, bin, "\u229F", "\\boxminus", true); +defineSymbol(math, ams, bin, "\u229E", "\\boxplus", true); +defineSymbol(math, ams, bin, "\u22C7", "\\divideontimes", true); +defineSymbol(math, ams, bin, "\u22C9", "\\ltimes", true); +defineSymbol(math, ams, bin, "\u22CA", "\\rtimes", true); +defineSymbol(math, ams, bin, "\u22CB", "\\leftthreetimes", true); +defineSymbol(math, ams, bin, "\u22CC", "\\rightthreetimes", true); +defineSymbol(math, ams, bin, "\u22CF", "\\curlywedge", true); +defineSymbol(math, ams, bin, "\u22CE", "\\curlyvee", true); +defineSymbol(math, ams, bin, "\u229D", "\\circleddash", true); +defineSymbol(math, ams, bin, "\u229B", "\\circledast", true); +defineSymbol(math, ams, bin, "\u22C5", "\\centerdot"); +defineSymbol(math, ams, bin, "\u22BA", "\\intercal", true); +defineSymbol(math, ams, bin, "\u22D2", "\\doublecap"); +defineSymbol(math, ams, bin, "\u22D3", "\\doublecup"); +defineSymbol(math, ams, bin, "\u22A0", "\\boxtimes", true); // AMS Arrows +// Note: unicode-math maps \u21e2 to their own function \rightdasharrow. +// We'll map it to AMS function \dashrightarrow. It produces the same atom. + +defineSymbol(math, ams, rel, "\u21E2", "\\dashrightarrow", true); // unicode-math maps \u21e0 to \leftdasharrow. We'll use the AMS synonym. + +defineSymbol(math, ams, rel, "\u21E0", "\\dashleftarrow", true); +defineSymbol(math, ams, rel, "\u21C7", "\\leftleftarrows", true); +defineSymbol(math, ams, rel, "\u21C6", "\\leftrightarrows", true); +defineSymbol(math, ams, rel, "\u21DA", "\\Lleftarrow", true); +defineSymbol(math, ams, rel, "\u219E", "\\twoheadleftarrow", true); +defineSymbol(math, ams, rel, "\u21A2", "\\leftarrowtail", true); +defineSymbol(math, ams, rel, "\u21AB", "\\looparrowleft", true); +defineSymbol(math, ams, rel, "\u21CB", "\\leftrightharpoons", true); +defineSymbol(math, ams, rel, "\u21B6", "\\curvearrowleft", true); // unicode-math maps \u21ba to \acwopencirclearrow. We'll use the AMS synonym. + +defineSymbol(math, ams, rel, "\u21BA", "\\circlearrowleft", true); +defineSymbol(math, ams, rel, "\u21B0", "\\Lsh", true); +defineSymbol(math, ams, rel, "\u21C8", "\\upuparrows", true); +defineSymbol(math, ams, rel, "\u21BF", "\\upharpoonleft", true); +defineSymbol(math, ams, rel, "\u21C3", "\\downharpoonleft", true); +defineSymbol(math, main, rel, "\u22B6", "\\origof", true); // not in font + +defineSymbol(math, main, rel, "\u22B7", "\\imageof", true); // not in font + +defineSymbol(math, ams, rel, "\u22B8", "\\multimap", true); +defineSymbol(math, ams, rel, "\u21AD", "\\leftrightsquigarrow", true); +defineSymbol(math, ams, rel, "\u21C9", "\\rightrightarrows", true); +defineSymbol(math, ams, rel, "\u21C4", "\\rightleftarrows", true); +defineSymbol(math, ams, rel, "\u21A0", "\\twoheadrightarrow", true); +defineSymbol(math, ams, rel, "\u21A3", "\\rightarrowtail", true); +defineSymbol(math, ams, rel, "\u21AC", "\\looparrowright", true); +defineSymbol(math, ams, rel, "\u21B7", "\\curvearrowright", true); // unicode-math maps \u21bb to \cwopencirclearrow. We'll use the AMS synonym. + +defineSymbol(math, ams, rel, "\u21BB", "\\circlearrowright", true); +defineSymbol(math, ams, rel, "\u21B1", "\\Rsh", true); +defineSymbol(math, ams, rel, "\u21CA", "\\downdownarrows", true); +defineSymbol(math, ams, rel, "\u21BE", "\\upharpoonright", true); +defineSymbol(math, ams, rel, "\u21C2", "\\downharpoonright", true); +defineSymbol(math, ams, rel, "\u21DD", "\\rightsquigarrow", true); +defineSymbol(math, ams, rel, "\u21DD", "\\leadsto"); +defineSymbol(math, ams, rel, "\u21DB", "\\Rrightarrow", true); +defineSymbol(math, ams, rel, "\u21BE", "\\restriction"); +defineSymbol(math, main, textord, "\u2018", "`"); +defineSymbol(math, main, textord, "$", "\\$"); +defineSymbol(symbols_text, main, textord, "$", "\\$"); +defineSymbol(symbols_text, main, textord, "$", "\\textdollar"); +defineSymbol(math, main, textord, "%", "\\%"); +defineSymbol(symbols_text, main, textord, "%", "\\%"); +defineSymbol(math, main, textord, "_", "\\_"); +defineSymbol(symbols_text, main, textord, "_", "\\_"); +defineSymbol(symbols_text, main, textord, "_", "\\textunderscore"); +defineSymbol(math, main, textord, "\u2220", "\\angle", true); +defineSymbol(math, main, textord, "\u221E", "\\infty", true); +defineSymbol(math, main, textord, "\u2032", "\\prime"); +defineSymbol(math, main, textord, "\u25B3", "\\triangle"); +defineSymbol(math, main, textord, "\u0393", "\\Gamma", true); +defineSymbol(math, main, textord, "\u0394", "\\Delta", true); +defineSymbol(math, main, textord, "\u0398", "\\Theta", true); +defineSymbol(math, main, textord, "\u039B", "\\Lambda", true); +defineSymbol(math, main, textord, "\u039E", "\\Xi", true); +defineSymbol(math, main, textord, "\u03A0", "\\Pi", true); +defineSymbol(math, main, textord, "\u03A3", "\\Sigma", true); +defineSymbol(math, main, textord, "\u03A5", "\\Upsilon", true); +defineSymbol(math, main, textord, "\u03A6", "\\Phi", true); +defineSymbol(math, main, textord, "\u03A8", "\\Psi", true); +defineSymbol(math, main, textord, "\u03A9", "\\Omega", true); +defineSymbol(math, main, textord, "A", "\u0391"); +defineSymbol(math, main, textord, "B", "\u0392"); +defineSymbol(math, main, textord, "E", "\u0395"); +defineSymbol(math, main, textord, "Z", "\u0396"); +defineSymbol(math, main, textord, "H", "\u0397"); +defineSymbol(math, main, textord, "I", "\u0399"); +defineSymbol(math, main, textord, "K", "\u039A"); +defineSymbol(math, main, textord, "M", "\u039C"); +defineSymbol(math, main, textord, "N", "\u039D"); +defineSymbol(math, main, textord, "O", "\u039F"); +defineSymbol(math, main, textord, "P", "\u03A1"); +defineSymbol(math, main, textord, "T", "\u03A4"); +defineSymbol(math, main, textord, "X", "\u03A7"); +defineSymbol(math, main, textord, "\xAC", "\\neg", true); +defineSymbol(math, main, textord, "\xAC", "\\lnot"); +defineSymbol(math, main, textord, "\u22A4", "\\top"); +defineSymbol(math, main, textord, "\u22A5", "\\bot"); +defineSymbol(math, main, textord, "\u2205", "\\emptyset"); +defineSymbol(math, ams, textord, "\u2205", "\\varnothing"); +defineSymbol(math, main, mathord, "\u03B1", "\\alpha", true); +defineSymbol(math, main, mathord, "\u03B2", "\\beta", true); +defineSymbol(math, main, mathord, "\u03B3", "\\gamma", true); +defineSymbol(math, main, mathord, "\u03B4", "\\delta", true); +defineSymbol(math, main, mathord, "\u03F5", "\\epsilon", true); +defineSymbol(math, main, mathord, "\u03B6", "\\zeta", true); +defineSymbol(math, main, mathord, "\u03B7", "\\eta", true); +defineSymbol(math, main, mathord, "\u03B8", "\\theta", true); +defineSymbol(math, main, mathord, "\u03B9", "\\iota", true); +defineSymbol(math, main, mathord, "\u03BA", "\\kappa", true); +defineSymbol(math, main, mathord, "\u03BB", "\\lambda", true); +defineSymbol(math, main, mathord, "\u03BC", "\\mu", true); +defineSymbol(math, main, mathord, "\u03BD", "\\nu", true); +defineSymbol(math, main, mathord, "\u03BE", "\\xi", true); +defineSymbol(math, main, mathord, "\u03BF", "\\omicron", true); +defineSymbol(math, main, mathord, "\u03C0", "\\pi", true); +defineSymbol(math, main, mathord, "\u03C1", "\\rho", true); +defineSymbol(math, main, mathord, "\u03C3", "\\sigma", true); +defineSymbol(math, main, mathord, "\u03C4", "\\tau", true); +defineSymbol(math, main, mathord, "\u03C5", "\\upsilon", true); +defineSymbol(math, main, mathord, "\u03D5", "\\phi", true); +defineSymbol(math, main, mathord, "\u03C7", "\\chi", true); +defineSymbol(math, main, mathord, "\u03C8", "\\psi", true); +defineSymbol(math, main, mathord, "\u03C9", "\\omega", true); +defineSymbol(math, main, mathord, "\u03B5", "\\varepsilon", true); +defineSymbol(math, main, mathord, "\u03D1", "\\vartheta", true); +defineSymbol(math, main, mathord, "\u03D6", "\\varpi", true); +defineSymbol(math, main, mathord, "\u03F1", "\\varrho", true); +defineSymbol(math, main, mathord, "\u03C2", "\\varsigma", true); +defineSymbol(math, main, mathord, "\u03C6", "\\varphi", true); +defineSymbol(math, main, bin, "\u2217", "*", true); +defineSymbol(math, main, bin, "+", "+"); +defineSymbol(math, main, bin, "\u2212", "-", true); +defineSymbol(math, main, bin, "\u22C5", "\\cdot", true); +defineSymbol(math, main, bin, "\u2218", "\\circ"); +defineSymbol(math, main, bin, "\xF7", "\\div", true); +defineSymbol(math, main, bin, "\xB1", "\\pm", true); +defineSymbol(math, main, bin, "\xD7", "\\times", true); +defineSymbol(math, main, bin, "\u2229", "\\cap", true); +defineSymbol(math, main, bin, "\u222A", "\\cup", true); +defineSymbol(math, main, bin, "\u2216", "\\setminus"); +defineSymbol(math, main, bin, "\u2227", "\\land"); +defineSymbol(math, main, bin, "\u2228", "\\lor"); +defineSymbol(math, main, bin, "\u2227", "\\wedge", true); +defineSymbol(math, main, bin, "\u2228", "\\vee", true); +defineSymbol(math, main, textord, "\u221A", "\\surd"); +defineSymbol(math, main, symbols_open, "\u27E8", "\\langle", true); +defineSymbol(math, main, symbols_open, "\u2223", "\\lvert"); +defineSymbol(math, main, symbols_open, "\u2225", "\\lVert"); +defineSymbol(math, main, symbols_close, "?", "?"); +defineSymbol(math, main, symbols_close, "!", "!"); +defineSymbol(math, main, symbols_close, "\u27E9", "\\rangle", true); +defineSymbol(math, main, symbols_close, "\u2223", "\\rvert"); +defineSymbol(math, main, symbols_close, "\u2225", "\\rVert"); +defineSymbol(math, main, rel, "=", "="); +defineSymbol(math, main, rel, ":", ":"); +defineSymbol(math, main, rel, "\u2248", "\\approx", true); +defineSymbol(math, main, rel, "\u2245", "\\cong", true); +defineSymbol(math, main, rel, "\u2265", "\\ge"); +defineSymbol(math, main, rel, "\u2265", "\\geq", true); +defineSymbol(math, main, rel, "\u2190", "\\gets"); +defineSymbol(math, main, rel, ">", "\\gt", true); +defineSymbol(math, main, rel, "\u2208", "\\in", true); +defineSymbol(math, main, rel, "\uE020", "\\@not"); +defineSymbol(math, main, rel, "\u2282", "\\subset", true); +defineSymbol(math, main, rel, "\u2283", "\\supset", true); +defineSymbol(math, main, rel, "\u2286", "\\subseteq", true); +defineSymbol(math, main, rel, "\u2287", "\\supseteq", true); +defineSymbol(math, ams, rel, "\u2288", "\\nsubseteq", true); +defineSymbol(math, ams, rel, "\u2289", "\\nsupseteq", true); +defineSymbol(math, main, rel, "\u22A8", "\\models"); +defineSymbol(math, main, rel, "\u2190", "\\leftarrow", true); +defineSymbol(math, main, rel, "\u2264", "\\le"); +defineSymbol(math, main, rel, "\u2264", "\\leq", true); +defineSymbol(math, main, rel, "<", "\\lt", true); +defineSymbol(math, main, rel, "\u2192", "\\rightarrow", true); +defineSymbol(math, main, rel, "\u2192", "\\to"); +defineSymbol(math, ams, rel, "\u2271", "\\ngeq", true); +defineSymbol(math, ams, rel, "\u2270", "\\nleq", true); +defineSymbol(math, main, spacing, "\xA0", "\\ "); +defineSymbol(math, main, spacing, "\xA0", "\\space"); // Ref: LaTeX Source 2e: \DeclareRobustCommand{\nobreakspace}{% + +defineSymbol(math, main, spacing, "\xA0", "\\nobreakspace"); +defineSymbol(symbols_text, main, spacing, "\xA0", "\\ "); +defineSymbol(symbols_text, main, spacing, "\xA0", " "); +defineSymbol(symbols_text, main, spacing, "\xA0", "\\space"); +defineSymbol(symbols_text, main, spacing, "\xA0", "\\nobreakspace"); +defineSymbol(math, main, spacing, null, "\\nobreak"); +defineSymbol(math, main, spacing, null, "\\allowbreak"); +defineSymbol(math, main, punct, ",", ","); +defineSymbol(math, main, punct, ";", ";"); +defineSymbol(math, ams, bin, "\u22BC", "\\barwedge", true); +defineSymbol(math, ams, bin, "\u22BB", "\\veebar", true); +defineSymbol(math, main, bin, "\u2299", "\\odot", true); +defineSymbol(math, main, bin, "\u2295", "\\oplus", true); +defineSymbol(math, main, bin, "\u2297", "\\otimes", true); +defineSymbol(math, main, textord, "\u2202", "\\partial", true); +defineSymbol(math, main, bin, "\u2298", "\\oslash", true); +defineSymbol(math, ams, bin, "\u229A", "\\circledcirc", true); +defineSymbol(math, ams, bin, "\u22A1", "\\boxdot", true); +defineSymbol(math, main, bin, "\u25B3", "\\bigtriangleup"); +defineSymbol(math, main, bin, "\u25BD", "\\bigtriangledown"); +defineSymbol(math, main, bin, "\u2020", "\\dagger"); +defineSymbol(math, main, bin, "\u22C4", "\\diamond"); +defineSymbol(math, main, bin, "\u22C6", "\\star"); +defineSymbol(math, main, bin, "\u25C3", "\\triangleleft"); +defineSymbol(math, main, bin, "\u25B9", "\\triangleright"); +defineSymbol(math, main, symbols_open, "{", "\\{"); +defineSymbol(symbols_text, main, textord, "{", "\\{"); +defineSymbol(symbols_text, main, textord, "{", "\\textbraceleft"); +defineSymbol(math, main, symbols_close, "}", "\\}"); +defineSymbol(symbols_text, main, textord, "}", "\\}"); +defineSymbol(symbols_text, main, textord, "}", "\\textbraceright"); +defineSymbol(math, main, symbols_open, "{", "\\lbrace"); +defineSymbol(math, main, symbols_close, "}", "\\rbrace"); +defineSymbol(math, main, symbols_open, "[", "\\lbrack", true); +defineSymbol(symbols_text, main, textord, "[", "\\lbrack", true); +defineSymbol(math, main, symbols_close, "]", "\\rbrack", true); +defineSymbol(symbols_text, main, textord, "]", "\\rbrack", true); +defineSymbol(math, main, symbols_open, "(", "\\lparen", true); +defineSymbol(math, main, symbols_close, ")", "\\rparen", true); +defineSymbol(symbols_text, main, textord, "<", "\\textless", true); // in T1 fontenc + +defineSymbol(symbols_text, main, textord, ">", "\\textgreater", true); // in T1 fontenc + +defineSymbol(math, main, symbols_open, "\u230A", "\\lfloor", true); +defineSymbol(math, main, symbols_close, "\u230B", "\\rfloor", true); +defineSymbol(math, main, symbols_open, "\u2308", "\\lceil", true); +defineSymbol(math, main, symbols_close, "\u2309", "\\rceil", true); +defineSymbol(math, main, textord, "\\", "\\backslash"); +defineSymbol(math, main, textord, "\u2223", "|"); +defineSymbol(math, main, textord, "\u2223", "\\vert"); +defineSymbol(symbols_text, main, textord, "|", "\\textbar", true); // in T1 fontenc + +defineSymbol(math, main, textord, "\u2225", "\\|"); +defineSymbol(math, main, textord, "\u2225", "\\Vert"); +defineSymbol(symbols_text, main, textord, "\u2225", "\\textbardbl"); +defineSymbol(symbols_text, main, textord, "~", "\\textasciitilde"); +defineSymbol(symbols_text, main, textord, "\\", "\\textbackslash"); +defineSymbol(symbols_text, main, textord, "^", "\\textasciicircum"); +defineSymbol(math, main, rel, "\u2191", "\\uparrow", true); +defineSymbol(math, main, rel, "\u21D1", "\\Uparrow", true); +defineSymbol(math, main, rel, "\u2193", "\\downarrow", true); +defineSymbol(math, main, rel, "\u21D3", "\\Downarrow", true); +defineSymbol(math, main, rel, "\u2195", "\\updownarrow", true); +defineSymbol(math, main, rel, "\u21D5", "\\Updownarrow", true); +defineSymbol(math, main, op, "\u2210", "\\coprod"); +defineSymbol(math, main, op, "\u22C1", "\\bigvee"); +defineSymbol(math, main, op, "\u22C0", "\\bigwedge"); +defineSymbol(math, main, op, "\u2A04", "\\biguplus"); +defineSymbol(math, main, op, "\u22C2", "\\bigcap"); +defineSymbol(math, main, op, "\u22C3", "\\bigcup"); +defineSymbol(math, main, op, "\u222B", "\\int"); +defineSymbol(math, main, op, "\u222B", "\\intop"); +defineSymbol(math, main, op, "\u222C", "\\iint"); +defineSymbol(math, main, op, "\u222D", "\\iiint"); +defineSymbol(math, main, op, "\u220F", "\\prod"); +defineSymbol(math, main, op, "\u2211", "\\sum"); +defineSymbol(math, main, op, "\u2A02", "\\bigotimes"); +defineSymbol(math, main, op, "\u2A01", "\\bigoplus"); +defineSymbol(math, main, op, "\u2A00", "\\bigodot"); +defineSymbol(math, main, op, "\u222E", "\\oint"); +defineSymbol(math, main, op, "\u222F", "\\oiint"); +defineSymbol(math, main, op, "\u2230", "\\oiiint"); +defineSymbol(math, main, op, "\u2A06", "\\bigsqcup"); +defineSymbol(math, main, op, "\u222B", "\\smallint"); +defineSymbol(symbols_text, main, inner, "\u2026", "\\textellipsis"); +defineSymbol(math, main, inner, "\u2026", "\\mathellipsis"); +defineSymbol(symbols_text, main, inner, "\u2026", "\\ldots", true); +defineSymbol(math, main, inner, "\u2026", "\\ldots", true); +defineSymbol(math, main, inner, "\u22EF", "\\@cdots", true); +defineSymbol(math, main, inner, "\u22F1", "\\ddots", true); +defineSymbol(math, main, textord, "\u22EE", "\\varvdots"); // \vdots is a macro + +defineSymbol(math, main, accent, "\u02CA", "\\acute"); +defineSymbol(math, main, accent, "\u02CB", "\\grave"); +defineSymbol(math, main, accent, "\xA8", "\\ddot"); +defineSymbol(math, main, accent, "~", "\\tilde"); +defineSymbol(math, main, accent, "\u02C9", "\\bar"); +defineSymbol(math, main, accent, "\u02D8", "\\breve"); +defineSymbol(math, main, accent, "\u02C7", "\\check"); +defineSymbol(math, main, accent, "^", "\\hat"); +defineSymbol(math, main, accent, "\u20D7", "\\vec"); +defineSymbol(math, main, accent, "\u02D9", "\\dot"); +defineSymbol(math, main, accent, "\u02DA", "\\mathring"); // \imath and \jmath should be invariant to \mathrm, \mathbf, etc., so use PUA + +defineSymbol(math, main, mathord, "\uE131", "\\@imath"); +defineSymbol(math, main, mathord, "\uE237", "\\@jmath"); +defineSymbol(math, main, textord, "\u0131", "\u0131"); +defineSymbol(math, main, textord, "\u0237", "\u0237"); +defineSymbol(symbols_text, main, textord, "\u0131", "\\i", true); +defineSymbol(symbols_text, main, textord, "\u0237", "\\j", true); +defineSymbol(symbols_text, main, textord, "\xDF", "\\ss", true); +defineSymbol(symbols_text, main, textord, "\xE6", "\\ae", true); +defineSymbol(symbols_text, main, textord, "\u0153", "\\oe", true); +defineSymbol(symbols_text, main, textord, "\xF8", "\\o", true); +defineSymbol(symbols_text, main, textord, "\xC6", "\\AE", true); +defineSymbol(symbols_text, main, textord, "\u0152", "\\OE", true); +defineSymbol(symbols_text, main, textord, "\xD8", "\\O", true); +defineSymbol(symbols_text, main, accent, "\u02CA", "\\'"); // acute + +defineSymbol(symbols_text, main, accent, "\u02CB", "\\`"); // grave + +defineSymbol(symbols_text, main, accent, "\u02C6", "\\^"); // circumflex + +defineSymbol(symbols_text, main, accent, "\u02DC", "\\~"); // tilde + +defineSymbol(symbols_text, main, accent, "\u02C9", "\\="); // macron + +defineSymbol(symbols_text, main, accent, "\u02D8", "\\u"); // breve + +defineSymbol(symbols_text, main, accent, "\u02D9", "\\."); // dot above + +defineSymbol(symbols_text, main, accent, "\xB8", "\\c"); // cedilla + +defineSymbol(symbols_text, main, accent, "\u02DA", "\\r"); // ring above + +defineSymbol(symbols_text, main, accent, "\u02C7", "\\v"); // caron + +defineSymbol(symbols_text, main, accent, "\xA8", '\\"'); // diaresis + +defineSymbol(symbols_text, main, accent, "\u02DD", "\\H"); // double acute + +defineSymbol(symbols_text, main, accent, "\u25EF", "\\textcircled"); // \bigcirc glyph +// These ligatures are detected and created in Parser.js's `formLigatures`. + +var ligatures = { + "--": true, + "---": true, + "``": true, + "''": true +}; +defineSymbol(symbols_text, main, textord, "\u2013", "--", true); +defineSymbol(symbols_text, main, textord, "\u2013", "\\textendash"); +defineSymbol(symbols_text, main, textord, "\u2014", "---", true); +defineSymbol(symbols_text, main, textord, "\u2014", "\\textemdash"); +defineSymbol(symbols_text, main, textord, "\u2018", "`", true); +defineSymbol(symbols_text, main, textord, "\u2018", "\\textquoteleft"); +defineSymbol(symbols_text, main, textord, "\u2019", "'", true); +defineSymbol(symbols_text, main, textord, "\u2019", "\\textquoteright"); +defineSymbol(symbols_text, main, textord, "\u201C", "``", true); +defineSymbol(symbols_text, main, textord, "\u201C", "\\textquotedblleft"); +defineSymbol(symbols_text, main, textord, "\u201D", "''", true); +defineSymbol(symbols_text, main, textord, "\u201D", "\\textquotedblright"); // \degree from gensymb package + +defineSymbol(math, main, textord, "\xB0", "\\degree", true); +defineSymbol(symbols_text, main, textord, "\xB0", "\\degree"); // \textdegree from inputenc package + +defineSymbol(symbols_text, main, textord, "\xB0", "\\textdegree", true); // TODO: In LaTeX, \pounds can generate a different character in text and math +// mode, but among our fonts, only Main-Regular defines this character "163". + +defineSymbol(math, main, textord, "\xA3", "\\pounds"); +defineSymbol(math, main, textord, "\xA3", "\\mathsterling", true); +defineSymbol(symbols_text, main, textord, "\xA3", "\\pounds"); +defineSymbol(symbols_text, main, textord, "\xA3", "\\textsterling", true); +defineSymbol(math, ams, textord, "\u2720", "\\maltese"); +defineSymbol(symbols_text, ams, textord, "\u2720", "\\maltese"); // There are lots of symbols which are the same, so we add them in afterwards. +// All of these are textords in math mode + +var mathTextSymbols = "0123456789/@.\""; + +for (var i = 0; i < mathTextSymbols.length; i++) { + var ch = mathTextSymbols.charAt(i); + defineSymbol(math, main, textord, ch, ch); +} // All of these are textords in text mode + + +var textSymbols = "0123456789!@*()-=+\";:?/.,"; + +for (var _i = 0; _i < textSymbols.length; _i++) { + var _ch = textSymbols.charAt(_i); + + defineSymbol(symbols_text, main, textord, _ch, _ch); +} // All of these are textords in text mode, and mathords in math mode + + +var letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +for (var _i2 = 0; _i2 < letters.length; _i2++) { + var _ch2 = letters.charAt(_i2); + + defineSymbol(math, main, mathord, _ch2, _ch2); + defineSymbol(symbols_text, main, textord, _ch2, _ch2); +} // Blackboard bold and script letters in Unicode range + + +defineSymbol(math, ams, textord, "C", "\u2102"); // blackboard bold + +defineSymbol(symbols_text, ams, textord, "C", "\u2102"); +defineSymbol(math, ams, textord, "H", "\u210D"); +defineSymbol(symbols_text, ams, textord, "H", "\u210D"); +defineSymbol(math, ams, textord, "N", "\u2115"); +defineSymbol(symbols_text, ams, textord, "N", "\u2115"); +defineSymbol(math, ams, textord, "P", "\u2119"); +defineSymbol(symbols_text, ams, textord, "P", "\u2119"); +defineSymbol(math, ams, textord, "Q", "\u211A"); +defineSymbol(symbols_text, ams, textord, "Q", "\u211A"); +defineSymbol(math, ams, textord, "R", "\u211D"); +defineSymbol(symbols_text, ams, textord, "R", "\u211D"); +defineSymbol(math, ams, textord, "Z", "\u2124"); +defineSymbol(symbols_text, ams, textord, "Z", "\u2124"); +defineSymbol(math, main, mathord, "h", "\u210E"); // italic h, Planck constant + +defineSymbol(symbols_text, main, mathord, "h", "\u210E"); // The next loop loads wide (surrogate pair) characters. +// We support some letters in the Unicode range U+1D400 to U+1D7FF, +// Mathematical Alphanumeric Symbols. +// Some editors do not deal well with wide characters. So don't write the +// string into this file. Instead, create the string from the surrogate pair. + +var wideChar = ""; + +for (var _i3 = 0; _i3 < letters.length; _i3++) { + var _ch3 = letters.charAt(_i3); // The hex numbers in the next line are a surrogate pair. + // 0xD835 is the high surrogate for all letters in the range we support. + // 0xDC00 is the low surrogate for bold A. + + + wideChar = String.fromCharCode(0xD835, 0xDC00 + _i3); // A-Z a-z bold + + defineSymbol(math, main, mathord, _ch3, wideChar); + defineSymbol(symbols_text, main, textord, _ch3, wideChar); + wideChar = String.fromCharCode(0xD835, 0xDC34 + _i3); // A-Z a-z italic + + defineSymbol(math, main, mathord, _ch3, wideChar); + defineSymbol(symbols_text, main, textord, _ch3, wideChar); + wideChar = String.fromCharCode(0xD835, 0xDC68 + _i3); // A-Z a-z bold italic + + defineSymbol(math, main, mathord, _ch3, wideChar); + defineSymbol(symbols_text, main, textord, _ch3, wideChar); + wideChar = String.fromCharCode(0xD835, 0xDD04 + _i3); // A-Z a-z Fractur + + defineSymbol(math, main, mathord, _ch3, wideChar); + defineSymbol(symbols_text, main, textord, _ch3, wideChar); + wideChar = String.fromCharCode(0xD835, 0xDDA0 + _i3); // A-Z a-z sans-serif + + defineSymbol(math, main, mathord, _ch3, wideChar); + defineSymbol(symbols_text, main, textord, _ch3, wideChar); + wideChar = String.fromCharCode(0xD835, 0xDDD4 + _i3); // A-Z a-z sans bold + + defineSymbol(math, main, mathord, _ch3, wideChar); + defineSymbol(symbols_text, main, textord, _ch3, wideChar); + wideChar = String.fromCharCode(0xD835, 0xDE08 + _i3); // A-Z a-z sans italic + + defineSymbol(math, main, mathord, _ch3, wideChar); + defineSymbol(symbols_text, main, textord, _ch3, wideChar); + wideChar = String.fromCharCode(0xD835, 0xDE70 + _i3); // A-Z a-z monospace + + defineSymbol(math, main, mathord, _ch3, wideChar); + defineSymbol(symbols_text, main, textord, _ch3, wideChar); + + if (_i3 < 26) { + // KaTeX fonts have only capital letters for blackboard bold and script. + // See exception for k below. + wideChar = String.fromCharCode(0xD835, 0xDD38 + _i3); // A-Z double struck + + defineSymbol(math, main, mathord, _ch3, wideChar); + defineSymbol(symbols_text, main, textord, _ch3, wideChar); + wideChar = String.fromCharCode(0xD835, 0xDC9C + _i3); // A-Z script + + defineSymbol(math, main, mathord, _ch3, wideChar); + defineSymbol(symbols_text, main, textord, _ch3, wideChar); + } // TODO: Add bold script when it is supported by a KaTeX font. + +} // "k" is the only double struck lower case letter in the KaTeX fonts. + + +wideChar = String.fromCharCode(0xD835, 0xDD5C); // k double struck + +defineSymbol(math, main, mathord, "k", wideChar); +defineSymbol(symbols_text, main, textord, "k", wideChar); // Next, some wide character numerals + +for (var _i4 = 0; _i4 < 10; _i4++) { + var _ch4 = _i4.toString(); + + wideChar = String.fromCharCode(0xD835, 0xDFCE + _i4); // 0-9 bold + + defineSymbol(math, main, mathord, _ch4, wideChar); + defineSymbol(symbols_text, main, textord, _ch4, wideChar); + wideChar = String.fromCharCode(0xD835, 0xDFE2 + _i4); // 0-9 sans serif + + defineSymbol(math, main, mathord, _ch4, wideChar); + defineSymbol(symbols_text, main, textord, _ch4, wideChar); + wideChar = String.fromCharCode(0xD835, 0xDFEC + _i4); // 0-9 bold sans + + defineSymbol(math, main, mathord, _ch4, wideChar); + defineSymbol(symbols_text, main, textord, _ch4, wideChar); + wideChar = String.fromCharCode(0xD835, 0xDFF6 + _i4); // 0-9 monospace + + defineSymbol(math, main, mathord, _ch4, wideChar); + defineSymbol(symbols_text, main, textord, _ch4, wideChar); +} // We add these Latin-1 letters as symbols for backwards-compatibility, +// but they are not actually in the font, nor are they supported by the +// Unicode accent mechanism, so they fall back to Times font and look ugly. +// TODO(edemaine): Fix this. + + +var extraLatin = "\xD0\xDE\xFE"; + +for (var _i5 = 0; _i5 < extraLatin.length; _i5++) { + var _ch5 = extraLatin.charAt(_i5); + + defineSymbol(math, main, mathord, _ch5, _ch5); + defineSymbol(symbols_text, main, textord, _ch5, _ch5); +} +/** + * This file provides support for Unicode range U+1D400 to U+1D7FF, + * Mathematical Alphanumeric Symbols. + * + * Function wideCharacterFont takes a wide character as input and returns + * the font information necessary to render it properly. + */ + +/** + * Data below is from https://www.unicode.org/charts/PDF/U1D400.pdf + * That document sorts characters into groups by font type, say bold or italic. + * + * In the arrays below, each subarray consists three elements: + * * The CSS class of that group when in math mode. + * * The CSS class of that group when in text mode. + * * The font name, so that KaTeX can get font metrics. + */ + +var wideLatinLetterData = [["mathbf", "textbf", "Main-Bold"], // A-Z bold upright +["mathbf", "textbf", "Main-Bold"], // a-z bold upright +["mathnormal", "textit", "Math-Italic"], // A-Z italic +["mathnormal", "textit", "Math-Italic"], // a-z italic +["boldsymbol", "boldsymbol", "Main-BoldItalic"], // A-Z bold italic +["boldsymbol", "boldsymbol", "Main-BoldItalic"], // a-z bold italic +// Map fancy A-Z letters to script, not calligraphic. +// This aligns with unicode-math and math fonts (except Cambria Math). +["mathscr", "textscr", "Script-Regular"], // A-Z script +["", "", ""], // a-z script. No font +["", "", ""], // A-Z bold script. No font +["", "", ""], // a-z bold script. No font +["mathfrak", "textfrak", "Fraktur-Regular"], // A-Z Fraktur +["mathfrak", "textfrak", "Fraktur-Regular"], // a-z Fraktur +["mathbb", "textbb", "AMS-Regular"], // A-Z double-struck +["mathbb", "textbb", "AMS-Regular"], // k double-struck +["", "", ""], // A-Z bold Fraktur No font metrics +["", "", ""], // a-z bold Fraktur. No font. +["mathsf", "textsf", "SansSerif-Regular"], // A-Z sans-serif +["mathsf", "textsf", "SansSerif-Regular"], // a-z sans-serif +["mathboldsf", "textboldsf", "SansSerif-Bold"], // A-Z bold sans-serif +["mathboldsf", "textboldsf", "SansSerif-Bold"], // a-z bold sans-serif +["mathitsf", "textitsf", "SansSerif-Italic"], // A-Z italic sans-serif +["mathitsf", "textitsf", "SansSerif-Italic"], // a-z italic sans-serif +["", "", ""], // A-Z bold italic sans. No font +["", "", ""], // a-z bold italic sans. No font +["mathtt", "texttt", "Typewriter-Regular"], // A-Z monospace +["mathtt", "texttt", "Typewriter-Regular"] // a-z monospace +]; +var wideNumeralData = [["mathbf", "textbf", "Main-Bold"], // 0-9 bold +["", "", ""], // 0-9 double-struck. No KaTeX font. +["mathsf", "textsf", "SansSerif-Regular"], // 0-9 sans-serif +["mathboldsf", "textboldsf", "SansSerif-Bold"], // 0-9 bold sans-serif +["mathtt", "texttt", "Typewriter-Regular"] // 0-9 monospace +]; +var wideCharacterFont = function wideCharacterFont(wideChar, mode) { + // IE doesn't support codePointAt(). So work with the surrogate pair. + var H = wideChar.charCodeAt(0); // high surrogate + + var L = wideChar.charCodeAt(1); // low surrogate + + var codePoint = (H - 0xD800) * 0x400 + (L - 0xDC00) + 0x10000; + var j = mode === "math" ? 0 : 1; // column index for CSS class. + + if (0x1D400 <= codePoint && codePoint < 0x1D6A4) { + // wideLatinLetterData contains exactly 26 chars on each row. + // So we can calculate the relevant row. No traverse necessary. + var i = Math.floor((codePoint - 0x1D400) / 26); + return [wideLatinLetterData[i][2], wideLatinLetterData[i][j]]; + } else if (0x1D7CE <= codePoint && codePoint <= 0x1D7FF) { + // Numerals, ten per row. + var _i = Math.floor((codePoint - 0x1D7CE) / 10); + + return [wideNumeralData[_i][2], wideNumeralData[_i][j]]; + } else if (codePoint === 0x1D6A5 || codePoint === 0x1D6A6) { + // dotless i or j + return [wideLatinLetterData[0][2], wideLatinLetterData[0][j]]; + } else if (0x1D6A6 < codePoint && codePoint < 0x1D7CE) { + // Greek letters. Not supported, yet. + return ["", ""]; + } else { + // We don't support any wide characters outside 1D400–1D7FF. + throw new src_ParseError("Unsupported character: " + wideChar); + } +}; +/* eslint no-console:0 */ + +/** + * This module contains general functions that can be used for building + * different kinds of domTree nodes in a consistent manner. + */ + + + + + + + +/** + * Looks up the given symbol in fontMetrics, after applying any symbol + * replacements defined in symbol.js + */ +var lookupSymbol = function lookupSymbol(value, // TODO(#963): Use a union type for this. +fontName, mode) { + // Replace the value with its replaced value from symbol.js + if (src_symbols[mode][value] && src_symbols[mode][value].replace) { + value = src_symbols[mode][value].replace; + } + + return { + value: value, + metrics: getCharacterMetrics(value, fontName, mode) + }; +}; +/** + * Makes a symbolNode after translation via the list of symbols in symbols.js. + * Correctly pulls out metrics for the character, and optionally takes a list of + * classes to be attached to the node. + * + * TODO: make argument order closer to makeSpan + * TODO: add a separate argument for math class (e.g. `mop`, `mbin`), which + * should if present come first in `classes`. + * TODO(#953): Make `options` mandatory and always pass it in. + */ + + +var makeSymbol = function makeSymbol(value, fontName, mode, options, classes) { + var lookup = lookupSymbol(value, fontName, mode); + var metrics = lookup.metrics; + value = lookup.value; + var symbolNode; + + if (metrics) { + var italic = metrics.italic; + + if (mode === "text" || options && options.font === "mathit") { + italic = 0; + } + + symbolNode = new SymbolNode(value, metrics.height, metrics.depth, italic, metrics.skew, metrics.width, classes); + } else { + // TODO(emily): Figure out a good way to only print this in development + typeof console !== "undefined" && console.warn("No character metrics " + ("for '" + value + "' in style '" + fontName + "' and mode '" + mode + "'")); + symbolNode = new SymbolNode(value, 0, 0, 0, 0, 0, classes); + } + + if (options) { + symbolNode.maxFontSize = options.sizeMultiplier; + + if (options.style.isTight()) { + symbolNode.classes.push("mtight"); + } + + var color = options.getColor(); + + if (color) { + symbolNode.style.color = color; + } + } + + return symbolNode; +}; +/** + * Makes a symbol in Main-Regular or AMS-Regular. + * Used for rel, bin, open, close, inner, and punct. + */ + + +var mathsym = function mathsym(value, mode, options, classes) { + if (classes === void 0) { + classes = []; + } + + // Decide what font to render the symbol in by its entry in the symbols + // table. + // Have a special case for when the value = \ because the \ is used as a + // textord in unsupported command errors but cannot be parsed as a regular + // text ordinal and is therefore not present as a symbol in the symbols + // table for text, as well as a special case for boldsymbol because it + // can be used for bold + and - + if (options.font === "boldsymbol" && lookupSymbol(value, "Main-Bold", mode).metrics) { + return makeSymbol(value, "Main-Bold", mode, options, classes.concat(["mathbf"])); + } else if (value === "\\" || src_symbols[mode][value].font === "main") { + return makeSymbol(value, "Main-Regular", mode, options, classes); + } else { + return makeSymbol(value, "AMS-Regular", mode, options, classes.concat(["amsrm"])); + } +}; +/** + * Determines which of the two font names (Main-Bold and Math-BoldItalic) and + * corresponding style tags (mathbf or boldsymbol) to use for font "boldsymbol", + * depending on the symbol. Use this function instead of fontMap for font + * "boldsymbol". + */ + + +var boldsymbol = function boldsymbol(value, mode, options, classes, type) { + if (type !== "textord" && lookupSymbol(value, "Math-BoldItalic", mode).metrics) { + return { + fontName: "Math-BoldItalic", + fontClass: "boldsymbol" + }; + } else { + // Some glyphs do not exist in Math-BoldItalic so we need to use + // Main-Bold instead. + return { + fontName: "Main-Bold", + fontClass: "mathbf" + }; + } +}; +/** + * Makes either a mathord or textord in the correct font and color. + */ + + +var makeOrd = function makeOrd(group, options, type) { + var mode = group.mode; + var text = group.text; + var classes = ["mord"]; // Math mode or Old font (i.e. \rm) + + var isFont = mode === "math" || mode === "text" && options.font; + var fontOrFamily = isFont ? options.font : options.fontFamily; + + if (text.charCodeAt(0) === 0xD835) { + // surrogate pairs get special treatment + var _wideCharacterFont = wideCharacterFont(text, mode), + wideFontName = _wideCharacterFont[0], + wideFontClass = _wideCharacterFont[1]; + + return makeSymbol(text, wideFontName, mode, options, classes.concat(wideFontClass)); + } else if (fontOrFamily) { + var fontName; + var fontClasses; + + if (fontOrFamily === "boldsymbol") { + var fontData = boldsymbol(text, mode, options, classes, type); + fontName = fontData.fontName; + fontClasses = [fontData.fontClass]; + } else if (isFont) { + fontName = fontMap[fontOrFamily].fontName; + fontClasses = [fontOrFamily]; + } else { + fontName = retrieveTextFontName(fontOrFamily, options.fontWeight, options.fontShape); + fontClasses = [fontOrFamily, options.fontWeight, options.fontShape]; + } + + if (lookupSymbol(text, fontName, mode).metrics) { + return makeSymbol(text, fontName, mode, options, classes.concat(fontClasses)); + } else if (ligatures.hasOwnProperty(text) && fontName.substr(0, 10) === "Typewriter") { + // Deconstruct ligatures in monospace fonts (\texttt, \tt). + var parts = []; + + for (var i = 0; i < text.length; i++) { + parts.push(makeSymbol(text[i], fontName, mode, options, classes.concat(fontClasses))); + } + + return makeFragment(parts); + } + } // Makes a symbol in the default font for mathords and textords. + + + if (type === "mathord") { + return makeSymbol(text, "Math-Italic", mode, options, classes.concat(["mathnormal"])); + } else if (type === "textord") { + var font = src_symbols[mode][text] && src_symbols[mode][text].font; + + if (font === "ams") { + var _fontName = retrieveTextFontName("amsrm", options.fontWeight, options.fontShape); + + return makeSymbol(text, _fontName, mode, options, classes.concat("amsrm", options.fontWeight, options.fontShape)); + } else if (font === "main" || !font) { + var _fontName2 = retrieveTextFontName("textrm", options.fontWeight, options.fontShape); + + return makeSymbol(text, _fontName2, mode, options, classes.concat(options.fontWeight, options.fontShape)); + } else { + // fonts added by plugins + var _fontName3 = retrieveTextFontName(font, options.fontWeight, options.fontShape); // We add font name as a css class + + + return makeSymbol(text, _fontName3, mode, options, classes.concat(_fontName3, options.fontWeight, options.fontShape)); + } + } else { + throw new Error("unexpected type: " + type + " in makeOrd"); + } +}; +/** + * Returns true if subsequent symbolNodes have the same classes, skew, maxFont, + * and styles. + */ + + +var canCombine = function canCombine(prev, next) { + if (createClass(prev.classes) !== createClass(next.classes) || prev.skew !== next.skew || prev.maxFontSize !== next.maxFontSize) { + return false; + } // If prev and next both are just "mbin"s or "mord"s we don't combine them + // so that the proper spacing can be preserved. + + + if (prev.classes.length === 1) { + var cls = prev.classes[0]; + + if (cls === "mbin" || cls === "mord") { + return false; + } + } + + for (var style in prev.style) { + if (prev.style.hasOwnProperty(style) && prev.style[style] !== next.style[style]) { + return false; + } + } + + for (var _style in next.style) { + if (next.style.hasOwnProperty(_style) && prev.style[_style] !== next.style[_style]) { + return false; + } + } + + return true; +}; +/** + * Combine consecutive domTree.symbolNodes into a single symbolNode. + * Note: this function mutates the argument. + */ + + +var tryCombineChars = function tryCombineChars(chars) { + for (var i = 0; i < chars.length - 1; i++) { + var prev = chars[i]; + var next = chars[i + 1]; + + if (prev instanceof SymbolNode && next instanceof SymbolNode && canCombine(prev, next)) { + prev.text += next.text; + prev.height = Math.max(prev.height, next.height); + prev.depth = Math.max(prev.depth, next.depth); // Use the last character's italic correction since we use + // it to add padding to the right of the span created from + // the combined characters. + + prev.italic = next.italic; + chars.splice(i + 1, 1); + i--; + } + } + + return chars; +}; +/** + * Calculate the height, depth, and maxFontSize of an element based on its + * children. + */ + + +var sizeElementFromChildren = function sizeElementFromChildren(elem) { + var height = 0; + var depth = 0; + var maxFontSize = 0; + + for (var i = 0; i < elem.children.length; i++) { + var child = elem.children[i]; + + if (child.height > height) { + height = child.height; + } + + if (child.depth > depth) { + depth = child.depth; + } + + if (child.maxFontSize > maxFontSize) { + maxFontSize = child.maxFontSize; + } + } + + elem.height = height; + elem.depth = depth; + elem.maxFontSize = maxFontSize; +}; +/** + * Makes a span with the given list of classes, list of children, and options. + * + * TODO(#953): Ensure that `options` is always provided (currently some call + * sites don't pass it) and make the type below mandatory. + * TODO: add a separate argument for math class (e.g. `mop`, `mbin`), which + * should if present come first in `classes`. + */ + + +var makeSpan = function makeSpan(classes, children, options, style) { + var span = new Span(classes, children, options, style); + sizeElementFromChildren(span); + return span; +}; // SVG one is simpler -- doesn't require height, depth, max-font setting. +// This is also a separate method for typesafety. + + +var makeSvgSpan = function makeSvgSpan(classes, children, options, style) { + return new Span(classes, children, options, style); +}; + +var makeLineSpan = function makeLineSpan(className, options, thickness) { + var line = makeSpan([className], [], options); + line.height = Math.max(thickness || options.fontMetrics().defaultRuleThickness, options.minRuleThickness); + line.style.borderBottomWidth = makeEm(line.height); + line.maxFontSize = 1.0; + return line; +}; +/** + * Makes an anchor with the given href, list of classes, list of children, + * and options. + */ + + +var makeAnchor = function makeAnchor(href, classes, children, options) { + var anchor = new Anchor(href, classes, children, options); + sizeElementFromChildren(anchor); + return anchor; +}; +/** + * Makes a document fragment with the given list of children. + */ + + +var makeFragment = function makeFragment(children) { + var fragment = new DocumentFragment(children); + sizeElementFromChildren(fragment); + return fragment; +}; +/** + * Wraps group in a span if it's a document fragment, allowing to apply classes + * and styles + */ + + +var wrapFragment = function wrapFragment(group, options) { + if (group instanceof DocumentFragment) { + return makeSpan([], [group], options); + } + + return group; +}; // These are exact object types to catch typos in the names of the optional fields. + + +// Computes the updated `children` list and the overall depth. +// +// This helper function for makeVList makes it easier to enforce type safety by +// allowing early exits (returns) in the logic. +var getVListChildrenAndDepth = function getVListChildrenAndDepth(params) { + if (params.positionType === "individualShift") { + var oldChildren = params.children; + var children = [oldChildren[0]]; // Add in kerns to the list of params.children to get each element to be + // shifted to the correct specified shift + + var _depth = -oldChildren[0].shift - oldChildren[0].elem.depth; + + var currPos = _depth; + + for (var i = 1; i < oldChildren.length; i++) { + var diff = -oldChildren[i].shift - currPos - oldChildren[i].elem.depth; + var size = diff - (oldChildren[i - 1].elem.height + oldChildren[i - 1].elem.depth); + currPos = currPos + diff; + children.push({ + type: "kern", + size: size + }); + children.push(oldChildren[i]); + } + + return { + children: children, + depth: _depth + }; + } + + var depth; + + if (params.positionType === "top") { + // We always start at the bottom, so calculate the bottom by adding up + // all the sizes + var bottom = params.positionData; + + for (var _i = 0; _i < params.children.length; _i++) { + var child = params.children[_i]; + bottom -= child.type === "kern" ? child.size : child.elem.height + child.elem.depth; + } + + depth = bottom; + } else if (params.positionType === "bottom") { + depth = -params.positionData; + } else { + var firstChild = params.children[0]; + + if (firstChild.type !== "elem") { + throw new Error('First child must have type "elem".'); + } + + if (params.positionType === "shift") { + depth = -firstChild.elem.depth - params.positionData; + } else if (params.positionType === "firstBaseline") { + depth = -firstChild.elem.depth; + } else { + throw new Error("Invalid positionType " + params.positionType + "."); + } + } + + return { + children: params.children, + depth: depth + }; +}; +/** + * 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. + */ + + +var makeVList = function makeVList(params, options) { + var _getVListChildrenAndD = getVListChildrenAndDepth(params), + children = _getVListChildrenAndD.children, + depth = _getVListChildrenAndD.depth; // 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. + + + var pstrutSize = 0; + + for (var i = 0; i < children.length; i++) { + var child = children[i]; + + if (child.type === "elem") { + var elem = child.elem; + pstrutSize = Math.max(pstrutSize, elem.maxFontSize, elem.height); + } + } + + pstrutSize += 2; + var pstrut = makeSpan(["pstrut"], []); + pstrut.style.height = makeEm(pstrutSize); // Create a new list of actual children at the correct offsets + + var realChildren = []; + var minPos = depth; + var maxPos = depth; + var currPos = depth; + + for (var _i2 = 0; _i2 < children.length; _i2++) { + var _child = children[_i2]; + + if (_child.type === "kern") { + currPos += _child.size; + } else { + var _elem = _child.elem; + var classes = _child.wrapperClasses || []; + var style = _child.wrapperStyle || {}; + var childWrap = makeSpan(classes, [pstrut, _elem], undefined, style); + childWrap.style.top = makeEm(-pstrutSize - currPos - _elem.depth); + + if (_child.marginLeft) { + childWrap.style.marginLeft = _child.marginLeft; + } + + if (_child.marginRight) { + childWrap.style.marginRight = _child.marginRight; + } + + realChildren.push(childWrap); + currPos += _elem.height + _elem.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. + + + var vlist = makeSpan(["vlist"], realChildren); + vlist.style.height = makeEm(maxPos); // A second row is used if necessary to represent the vlist's depth. + + var rows; + + if (minPos < 0) { + // We will define depth in an empty span with display: table-cell. + // It should render with the height that we define. But Chrome, in + // contenteditable mode only, treats that span as if it contains some + // text content. And that min-height over-rides our desired height. + // So we put another empty span inside the depth strut span. + var emptySpan = makeSpan([], []); + var depthStrut = makeSpan(["vlist"], [emptySpan]); + depthStrut.style.height = makeEm(-minPos); // Safari wants the first row to have inline content; otherwise it + // puts the bottom of the *second* row on the baseline. + + var topStrut = makeSpan(["vlist-s"], [new SymbolNode("\u200B")]); + rows = [makeSpan(["vlist-r"], [vlist, topStrut]), makeSpan(["vlist-r"], [depthStrut])]; + } else { + rows = [makeSpan(["vlist-r"], [vlist])]; + } + + var vtable = makeSpan(["vlist-t"], rows); + + if (rows.length === 2) { + vtable.classes.push("vlist-t2"); + } + + vtable.height = maxPos; + vtable.depth = -minPos; + return vtable; +}; // Glue is a concept from TeX which is a flexible space between elements in +// either a vertical or horizontal list. In KaTeX, at least for now, it's +// static space between elements in a horizontal layout. + + +var makeGlue = function makeGlue(measurement, options) { + // Make an empty span for the space + var rule = makeSpan(["mspace"], [], options); + var size = calculateSize(measurement, options); + rule.style.marginRight = makeEm(size); + return rule; +}; // Takes font options, and returns the appropriate fontLookup name + + +var retrieveTextFontName = function retrieveTextFontName(fontFamily, fontWeight, fontShape) { + var baseFontName = ""; + + switch (fontFamily) { + case "amsrm": + baseFontName = "AMS"; + break; + + case "textrm": + baseFontName = "Main"; + break; + + case "textsf": + baseFontName = "SansSerif"; + break; + + case "texttt": + baseFontName = "Typewriter"; + break; + + default: + baseFontName = fontFamily; + // use fonts added by a plugin + } + + var fontStylesName; + + if (fontWeight === "textbf" && fontShape === "textit") { + fontStylesName = "BoldItalic"; + } else if (fontWeight === "textbf") { + fontStylesName = "Bold"; + } else if (fontWeight === "textit") { + fontStylesName = "Italic"; + } else { + fontStylesName = "Regular"; + } + + return baseFontName + "-" + fontStylesName; +}; +/** + * Maps TeX font commands to objects containing: + * - variant: string used for "mathvariant" attribute in buildMathML.js + * - fontName: the "style" parameter to fontMetrics.getCharacterMetrics + */ +// A map between tex font commands an MathML mathvariant attribute values + + +var fontMap = { + // styles + "mathbf": { + variant: "bold", + fontName: "Main-Bold" + }, + "mathrm": { + variant: "normal", + fontName: "Main-Regular" + }, + "textit": { + variant: "italic", + fontName: "Main-Italic" + }, + "mathit": { + variant: "italic", + fontName: "Main-Italic" + }, + "mathnormal": { + variant: "italic", + fontName: "Math-Italic" + }, + // "boldsymbol" is missing because they require the use of multiple fonts: + // Math-BoldItalic and Main-Bold. This is handled by a special case in + // makeOrd which ends up calling boldsymbol. + // families + "mathbb": { + variant: "double-struck", + fontName: "AMS-Regular" + }, + "mathcal": { + variant: "script", + fontName: "Caligraphic-Regular" + }, + "mathfrak": { + variant: "fraktur", + fontName: "Fraktur-Regular" + }, + "mathscr": { + variant: "script", + fontName: "Script-Regular" + }, + "mathsf": { + variant: "sans-serif", + fontName: "SansSerif-Regular" + }, + "mathtt": { + variant: "monospace", + fontName: "Typewriter-Regular" + } +}; +var svgData = { + // path, width, height + vec: ["vec", 0.471, 0.714], + // values from the font glyph + oiintSize1: ["oiintSize1", 0.957, 0.499], + // oval to overlay the integrand + oiintSize2: ["oiintSize2", 1.472, 0.659], + oiiintSize1: ["oiiintSize1", 1.304, 0.499], + oiiintSize2: ["oiiintSize2", 1.98, 0.659] +}; + +var staticSvg = function staticSvg(value, options) { + // Create a span with inline SVG for the element. + var _svgData$value = svgData[value], + pathName = _svgData$value[0], + width = _svgData$value[1], + height = _svgData$value[2]; + var path = new PathNode(pathName); + var svgNode = new SvgNode([path], { + "width": makeEm(width), + "height": makeEm(height), + // Override CSS rule `.katex svg { width: 100% }` + "style": "width:" + makeEm(width), + "viewBox": "0 0 " + 1000 * width + " " + 1000 * height, + "preserveAspectRatio": "xMinYMin" + }); + var span = makeSvgSpan(["overlay"], [svgNode], options); + span.height = height; + span.style.height = makeEm(height); + span.style.width = makeEm(width); + return span; +}; + +/* harmony default export */ var buildCommon = ({ + fontMap: fontMap, + makeSymbol: makeSymbol, + mathsym: mathsym, + makeSpan: makeSpan, + makeSvgSpan: makeSvgSpan, + makeLineSpan: makeLineSpan, + makeAnchor: makeAnchor, + makeFragment: makeFragment, + wrapFragment: wrapFragment, + makeVList: makeVList, + makeOrd: makeOrd, + makeGlue: makeGlue, + staticSvg: staticSvg, + svgData: svgData, + tryCombineChars: tryCombineChars +}); +/** + * Describes spaces between different classes of atoms. + */ +var thinspace = { + number: 3, + unit: "mu" +}; +var mediumspace = { + number: 4, + unit: "mu" +}; +var thickspace = { + number: 5, + unit: "mu" +}; // Making the type below exact with all optional fields doesn't work due to +// - https://github.com/facebook/flow/issues/4582 +// - https://github.com/facebook/flow/issues/5688 +// However, since *all* fields are optional, $Shape<> works as suggested in 5688 +// above. + +// Spacing relationships for display and text styles +var spacings = { + mord: { + mop: thinspace, + mbin: mediumspace, + mrel: thickspace, + minner: thinspace + }, + mop: { + mord: thinspace, + mop: thinspace, + mrel: thickspace, + minner: thinspace + }, + mbin: { + mord: mediumspace, + mop: mediumspace, + mopen: mediumspace, + minner: mediumspace + }, + mrel: { + mord: thickspace, + mop: thickspace, + mopen: thickspace, + minner: thickspace + }, + mopen: {}, + mclose: { + mop: thinspace, + mbin: mediumspace, + mrel: thickspace, + minner: thinspace + }, + mpunct: { + mord: thinspace, + mop: thinspace, + mrel: thickspace, + mopen: thinspace, + mclose: thinspace, + mpunct: thinspace, + minner: thinspace + }, + minner: { + mord: thinspace, + mop: thinspace, + mbin: mediumspace, + mrel: thickspace, + mopen: thinspace, + mpunct: thinspace, + minner: thinspace + } +}; // Spacing relationships for script and scriptscript styles + +var tightSpacings = { + mord: { + mop: thinspace + }, + mop: { + mord: thinspace, + mop: thinspace + }, + mbin: {}, + mrel: {}, + mopen: {}, + mclose: { + mop: thinspace + }, + mpunct: {}, + minner: { + mop: thinspace + } +}; +/** Context provided to function handlers for error messages. */ +// Note: reverse the order of the return type union will cause a flow error. +// See https://github.com/facebook/flow/issues/3663. +// More general version of `HtmlBuilder` for nodes (e.g. \sum, accent types) +// whose presence impacts super/subscripting. In this case, ParseNode<"supsub"> +// delegates its HTML building to the HtmlBuilder corresponding to these nodes. + +/** + * Final function spec for use at parse time. + * This is almost identical to `FunctionPropSpec`, except it + * 1. includes the function handler, and + * 2. requires all arguments except argTypes. + * It is generated by `defineFunction()` below. + */ + +/** + * All registered functions. + * `functions.js` just exports this same dictionary again and makes it public. + * `Parser.js` requires this dictionary. + */ +var _functions = {}; +/** + * All HTML builders. Should be only used in the `define*` and the `build*ML` + * functions. + */ + +var _htmlGroupBuilders = {}; +/** + * All MathML builders. Should be only used in the `define*` and the `build*ML` + * functions. + */ + +var _mathmlGroupBuilders = {}; +function defineFunction(_ref) { + var type = _ref.type, + names = _ref.names, + props = _ref.props, + handler = _ref.handler, + htmlBuilder = _ref.htmlBuilder, + mathmlBuilder = _ref.mathmlBuilder; + // Set default values of functions + var data = { + type: type, + numArgs: props.numArgs, + argTypes: props.argTypes, + allowedInArgument: !!props.allowedInArgument, + allowedInText: !!props.allowedInText, + allowedInMath: props.allowedInMath === undefined ? true : props.allowedInMath, + numOptionalArgs: props.numOptionalArgs || 0, + infix: !!props.infix, + primitive: !!props.primitive, + handler: handler + }; + + for (var i = 0; i < names.length; ++i) { + _functions[names[i]] = data; + } + + if (type) { + if (htmlBuilder) { + _htmlGroupBuilders[type] = htmlBuilder; + } + + if (mathmlBuilder) { + _mathmlGroupBuilders[type] = mathmlBuilder; + } + } +} +/** + * Use this to register only the HTML and MathML builders for a function (e.g. + * if the function's ParseNode is generated in Parser.js rather than via a + * stand-alone handler provided to `defineFunction`). + */ + +function defineFunctionBuilders(_ref2) { + var type = _ref2.type, + htmlBuilder = _ref2.htmlBuilder, + mathmlBuilder = _ref2.mathmlBuilder; + defineFunction({ + type: type, + names: [], + props: { + numArgs: 0 + }, + handler: function handler() { + throw new Error('Should never be called.'); + }, + htmlBuilder: htmlBuilder, + mathmlBuilder: mathmlBuilder + }); +} +var normalizeArgument = function normalizeArgument(arg) { + return arg.type === "ordgroup" && arg.body.length === 1 ? arg.body[0] : arg; +}; // Since the corresponding buildHTML/buildMathML function expects a +// list of elements, we normalize for different kinds of arguments + +var ordargument = function ordargument(arg) { + return arg.type === "ordgroup" ? arg.body : [arg]; +}; +/** + * This file does the main work of building a domTree structure from a parse + * tree. The entry point is the `buildHTML` function, which takes a parse tree. + * Then, the buildExpression, buildGroup, and various groupBuilders functions + * are called, to produce a final HTML tree. + */ + + + + + + + + + +var buildHTML_makeSpan = buildCommon.makeSpan; // Binary atoms (first class `mbin`) change into ordinary atoms (`mord`) +// depending on their surroundings. See TeXbook pg. 442-446, Rules 5 and 6, +// and the text before Rule 19. + +var binLeftCanceller = ["leftmost", "mbin", "mopen", "mrel", "mop", "mpunct"]; +var binRightCanceller = ["rightmost", "mrel", "mclose", "mpunct"]; +var styleMap = { + "display": src_Style.DISPLAY, + "text": src_Style.TEXT, + "script": src_Style.SCRIPT, + "scriptscript": src_Style.SCRIPTSCRIPT +}; +var DomEnum = { + mord: "mord", + mop: "mop", + mbin: "mbin", + mrel: "mrel", + mopen: "mopen", + mclose: "mclose", + mpunct: "mpunct", + minner: "minner" +}; + +/** + * Take a list of nodes, build them in order, and return a list of the built + * nodes. documentFragments are flattened into their contents, so the + * returned list contains no fragments. `isRealGroup` is true if `expression` + * is a real group (no atoms will be added on either side), as opposed to + * a partial group (e.g. one created by \color). `surrounding` is an array + * consisting type of nodes that will be added to the left and right. + */ +var buildExpression = function buildExpression(expression, options, isRealGroup, surrounding) { + if (surrounding === void 0) { + surrounding = [null, null]; + } + + // Parse expressions into `groups`. + var groups = []; + + for (var i = 0; i < expression.length; i++) { + var output = buildGroup(expression[i], options); + + if (output instanceof DocumentFragment) { + var children = output.children; + groups.push.apply(groups, children); + } else { + groups.push(output); + } + } // Combine consecutive domTree.symbolNodes into a single symbolNode. + + + buildCommon.tryCombineChars(groups); // If `expression` is a partial group, let the parent handle spacings + // to avoid processing groups multiple times. + + if (!isRealGroup) { + return groups; + } + + var glueOptions = options; + + if (expression.length === 1) { + var node = expression[0]; + + if (node.type === "sizing") { + glueOptions = options.havingSize(node.size); + } else if (node.type === "styling") { + glueOptions = options.havingStyle(styleMap[node.style]); + } + } // Dummy spans for determining spacings between surrounding atoms. + // If `expression` has no atoms on the left or right, class "leftmost" + // or "rightmost", respectively, is used to indicate it. + + + var dummyPrev = buildHTML_makeSpan([surrounding[0] || "leftmost"], [], options); + var dummyNext = buildHTML_makeSpan([surrounding[1] || "rightmost"], [], options); // TODO: These code assumes that a node's math class is the first element + // of its `classes` array. A later cleanup should ensure this, for + // instance by changing the signature of `makeSpan`. + // Before determining what spaces to insert, perform bin cancellation. + // Binary operators change to ordinary symbols in some contexts. + + var isRoot = isRealGroup === "root"; + traverseNonSpaceNodes(groups, function (node, prev) { + var prevType = prev.classes[0]; + var type = node.classes[0]; + + if (prevType === "mbin" && utils.contains(binRightCanceller, type)) { + prev.classes[0] = "mord"; + } else if (type === "mbin" && utils.contains(binLeftCanceller, prevType)) { + node.classes[0] = "mord"; + } + }, { + node: dummyPrev + }, dummyNext, isRoot); + traverseNonSpaceNodes(groups, function (node, prev) { + var prevType = getTypeOfDomTree(prev); + var type = getTypeOfDomTree(node); // 'mtight' indicates that the node is script or scriptscript style. + + var space = prevType && type ? node.hasClass("mtight") ? tightSpacings[prevType][type] : spacings[prevType][type] : null; + + if (space) { + // Insert glue (spacing) after the `prev`. + return buildCommon.makeGlue(space, glueOptions); + } + }, { + node: dummyPrev + }, dummyNext, isRoot); + return groups; +}; // Depth-first traverse non-space `nodes`, calling `callback` with the current and +// previous node as arguments, optionally returning a node to insert after the +// previous node. `prev` is an object with the previous node and `insertAfter` +// function to insert after it. `next` is a node that will be added to the right. +// Used for bin cancellation and inserting spacings. + +var traverseNonSpaceNodes = function traverseNonSpaceNodes(nodes, callback, prev, next, isRoot) { + if (next) { + // temporarily append the right node, if exists + nodes.push(next); + } + + var i = 0; + + for (; i < nodes.length; i++) { + var node = nodes[i]; + var partialGroup = checkPartialGroup(node); + + if (partialGroup) { + // Recursive DFS + // $FlowFixMe: make nodes a $ReadOnlyArray by returning a new array + traverseNonSpaceNodes(partialGroup.children, callback, prev, null, isRoot); + continue; + } // Ignore explicit spaces (e.g., \;, \,) when determining what implicit + // spacing should go between atoms of different classes + + + var nonspace = !node.hasClass("mspace"); + + if (nonspace) { + var result = callback(node, prev.node); + + if (result) { + if (prev.insertAfter) { + prev.insertAfter(result); + } else { + // insert at front + nodes.unshift(result); + i++; + } + } + } + + if (nonspace) { + prev.node = node; + } else if (isRoot && node.hasClass("newline")) { + prev.node = buildHTML_makeSpan(["leftmost"]); // treat like beginning of line + } + + prev.insertAfter = function (index) { + return function (n) { + nodes.splice(index + 1, 0, n); + i++; + }; + }(i); + } + + if (next) { + nodes.pop(); + } +}; // Check if given node is a partial group, i.e., does not affect spacing around. + + +var checkPartialGroup = function checkPartialGroup(node) { + if (node instanceof DocumentFragment || node instanceof Anchor || node instanceof Span && node.hasClass("enclosing")) { + return node; + } + + return null; +}; // Return the outermost node of a domTree. + + +var getOutermostNode = function getOutermostNode(node, side) { + var partialGroup = checkPartialGroup(node); + + if (partialGroup) { + var children = partialGroup.children; + + if (children.length) { + if (side === "right") { + return getOutermostNode(children[children.length - 1], "right"); + } else if (side === "left") { + return getOutermostNode(children[0], "left"); + } + } + } + + return node; +}; // Return math atom class (mclass) of a domTree. +// If `side` is given, it will get the type of the outermost node at given side. + + +var getTypeOfDomTree = function getTypeOfDomTree(node, side) { + if (!node) { + return null; + } + + if (side) { + node = getOutermostNode(node, side); + } // This makes a lot of assumptions as to where the type of atom + // appears. We should do a better job of enforcing this. + + + return DomEnum[node.classes[0]] || null; +}; +var makeNullDelimiter = function makeNullDelimiter(options, classes) { + var moreClasses = ["nulldelimiter"].concat(options.baseSizingClasses()); + return buildHTML_makeSpan(classes.concat(moreClasses)); +}; +/** + * buildGroup is the function that takes a group and calls the correct groupType + * function for it. It also handles the interaction of size and style changes + * between parents and children. + */ + +var buildGroup = function buildGroup(group, options, baseOptions) { + if (!group) { + return buildHTML_makeSpan(); + } + + if (_htmlGroupBuilders[group.type]) { + // Call the groupBuilders function + // $FlowFixMe + var groupNode = _htmlGroupBuilders[group.type](group, options); // If the size changed between the parent and the current group, account + // for that size difference. + + if (baseOptions && options.size !== baseOptions.size) { + groupNode = buildHTML_makeSpan(options.sizingClasses(baseOptions), [groupNode], options); + var multiplier = options.sizeMultiplier / baseOptions.sizeMultiplier; + groupNode.height *= multiplier; + groupNode.depth *= multiplier; + } + + return groupNode; + } else { + throw new src_ParseError("Got group of unknown type: '" + group.type + "'"); + } +}; +/** + * Combine an array of HTML DOM nodes (e.g., the output of `buildExpression`) + * into an unbreakable HTML node of class .base, with proper struts to + * guarantee correct vertical extent. `buildHTML` calls this repeatedly to + * make up the entire expression as a sequence of unbreakable units. + */ + +function buildHTMLUnbreakable(children, options) { + // Compute height and depth of this chunk. + var body = buildHTML_makeSpan(["base"], children, options); // Add strut, which ensures that the top of the HTML element falls at + // the height of the expression, and the bottom of the HTML element + // falls at the depth of the expression. + + var strut = buildHTML_makeSpan(["strut"]); + strut.style.height = makeEm(body.height + body.depth); + + if (body.depth) { + strut.style.verticalAlign = makeEm(-body.depth); + } + + body.children.unshift(strut); + return body; +} +/** + * Take an entire parse tree, and build it into an appropriate set of HTML + * nodes. + */ + + +function buildHTML(tree, options) { + // Strip off outer tag wrapper for processing below. + var tag = null; + + if (tree.length === 1 && tree[0].type === "tag") { + tag = tree[0].tag; + tree = tree[0].body; + } // Build the expression contained in the tree + + + var expression = buildExpression(tree, options, "root"); + var eqnNum; + + if (expression.length === 2 && expression[1].hasClass("tag")) { + // An environment with automatic equation numbers, e.g. {gather}. + eqnNum = expression.pop(); + } + + var children = []; // Create one base node for each chunk between potential line breaks. + // The TeXBook [p.173] says "A formula will be broken only after a + // relation symbol like $=$ or $<$ or $\rightarrow$, or after a binary + // operation symbol like $+$ or $-$ or $\times$, where the relation or + // binary operation is on the ``outer level'' of the formula (i.e., not + // enclosed in {...} and not part of an \over construction)." + + var parts = []; + + for (var i = 0; i < expression.length; i++) { + parts.push(expression[i]); + + if (expression[i].hasClass("mbin") || expression[i].hasClass("mrel") || expression[i].hasClass("allowbreak")) { + // Put any post-operator glue on same line as operator. + // Watch for \nobreak along the way, and stop at \newline. + var nobreak = false; + + while (i < expression.length - 1 && expression[i + 1].hasClass("mspace") && !expression[i + 1].hasClass("newline")) { + i++; + parts.push(expression[i]); + + if (expression[i].hasClass("nobreak")) { + nobreak = true; + } + } // Don't allow break if \nobreak among the post-operator glue. + + + if (!nobreak) { + children.push(buildHTMLUnbreakable(parts, options)); + parts = []; + } + } else if (expression[i].hasClass("newline")) { + // Write the line except the newline + parts.pop(); + + if (parts.length > 0) { + children.push(buildHTMLUnbreakable(parts, options)); + parts = []; + } // Put the newline at the top level + + + children.push(expression[i]); + } + } + + if (parts.length > 0) { + children.push(buildHTMLUnbreakable(parts, options)); + } // Now, if there was a tag, build it too and append it as a final child. + + + var tagChild; + + if (tag) { + tagChild = buildHTMLUnbreakable(buildExpression(tag, options, true)); + tagChild.classes = ["tag"]; + children.push(tagChild); + } else if (eqnNum) { + children.push(eqnNum); + } + + var htmlNode = buildHTML_makeSpan(["katex-html"], children); + htmlNode.setAttribute("aria-hidden", "true"); // Adjust the strut of the tag to be the maximum height of all children + // (the height of the enclosing htmlNode) for proper vertical alignment. + + if (tagChild) { + var strut = tagChild.children[0]; + strut.style.height = makeEm(htmlNode.height + htmlNode.depth); + + if (htmlNode.depth) { + strut.style.verticalAlign = makeEm(-htmlNode.depth); + } + } + + return htmlNode; +} +/** + * These objects store data about MathML nodes. This is the MathML equivalent + * of the types in domTree.js. Since MathML handles its own rendering, and + * since we're mainly using MathML to improve accessibility, we don't manage + * any of the styling state that the plain DOM nodes do. + * + * The `toNode` and `toMarkup` functions work simlarly to how they do in + * domTree.js, creating namespaced DOM nodes and HTML text markup respectively. + */ + + + + +function newDocumentFragment(children) { + return new DocumentFragment(children); +} +/** + * This node represents a general purpose MathML node of any type. The + * constructor requires the type of node to create (for example, `"mo"` or + * `"mspace"`, corresponding to `` and `` tags). + */ + +var MathNode = /*#__PURE__*/function () { + function MathNode(type, children, classes) { + this.type = void 0; + this.attributes = void 0; + this.children = void 0; + this.classes = void 0; + this.type = type; + this.attributes = {}; + this.children = children || []; + this.classes = classes || []; + } + /** + * Sets an attribute on a MathML node. MathML depends on attributes to convey a + * semantic content, so this is used heavily. + */ + + + var _proto = MathNode.prototype; + + _proto.setAttribute = function setAttribute(name, value) { + this.attributes[name] = value; + } + /** + * Gets an attribute on a MathML node. + */ + ; + + _proto.getAttribute = function getAttribute(name) { + return this.attributes[name]; + } + /** + * Converts the math node into a MathML-namespaced DOM element. + */ + ; + + _proto.toNode = function toNode() { + var node = document.createElementNS("http://www.w3.org/1998/Math/MathML", this.type); + + for (var attr in this.attributes) { + if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) { + node.setAttribute(attr, this.attributes[attr]); + } + } + + if (this.classes.length > 0) { + node.className = createClass(this.classes); + } + + for (var i = 0; i < this.children.length; i++) { + node.appendChild(this.children[i].toNode()); + } + + return node; + } + /** + * Converts the math node into an HTML markup string. + */ + ; + + _proto.toMarkup = function toMarkup() { + var markup = "<" + this.type; // Add the attributes + + for (var attr in this.attributes) { + if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) { + markup += " " + attr + "=\""; + markup += utils.escape(this.attributes[attr]); + markup += "\""; + } + } + + if (this.classes.length > 0) { + markup += " class =\"" + utils.escape(createClass(this.classes)) + "\""; + } + + markup += ">"; + + for (var i = 0; i < this.children.length; i++) { + markup += this.children[i].toMarkup(); + } + + markup += ""; + return markup; + } + /** + * Converts the math node into a string, similar to innerText, but escaped. + */ + ; + + _proto.toText = function toText() { + return this.children.map(function (child) { + return child.toText(); + }).join(""); + }; + + return MathNode; +}(); +/** + * This node represents a piece of text. + */ + +var TextNode = /*#__PURE__*/function () { + function TextNode(text) { + this.text = void 0; + this.text = text; + } + /** + * Converts the text node into a DOM text node. + */ + + + var _proto2 = TextNode.prototype; + + _proto2.toNode = function toNode() { + return document.createTextNode(this.text); + } + /** + * Converts the text node into escaped HTML markup + * (representing the text itself). + */ + ; + + _proto2.toMarkup = function toMarkup() { + return utils.escape(this.toText()); + } + /** + * Converts the text node into a string + * (representing the text iteself). + */ + ; + + _proto2.toText = function toText() { + return this.text; + }; + + return TextNode; +}(); +/** + * This node represents a space, but may render as or as text, + * depending on the width. + */ + +var SpaceNode = /*#__PURE__*/function () { + /** + * Create a Space node with width given in CSS ems. + */ + function SpaceNode(width) { + this.width = void 0; + this.character = void 0; + this.width = width; // See https://www.w3.org/TR/2000/WD-MathML2-20000328/chapter6.html + // for a table of space-like characters. We use Unicode + // representations instead of &LongNames; as it's not clear how to + // make the latter via document.createTextNode. + + if (width >= 0.05555 && width <= 0.05556) { + this.character = "\u200A"; //   + } else if (width >= 0.1666 && width <= 0.1667) { + this.character = "\u2009"; //   + } else if (width >= 0.2222 && width <= 0.2223) { + this.character = "\u2005"; //   + } else if (width >= 0.2777 && width <= 0.2778) { + this.character = "\u2005\u200A"; //    + } else if (width >= -0.05556 && width <= -0.05555) { + this.character = "\u200A\u2063"; // ​ + } else if (width >= -0.1667 && width <= -0.1666) { + this.character = "\u2009\u2063"; // ​ + } else if (width >= -0.2223 && width <= -0.2222) { + this.character = "\u205F\u2063"; // ​ + } else if (width >= -0.2778 && width <= -0.2777) { + this.character = "\u2005\u2063"; // ​ + } else { + this.character = null; + } + } + /** + * Converts the math node into a MathML-namespaced DOM element. + */ + + + var _proto3 = SpaceNode.prototype; + + _proto3.toNode = function toNode() { + if (this.character) { + return document.createTextNode(this.character); + } else { + var node = document.createElementNS("http://www.w3.org/1998/Math/MathML", "mspace"); + node.setAttribute("width", makeEm(this.width)); + return node; + } + } + /** + * Converts the math node into an HTML markup string. + */ + ; + + _proto3.toMarkup = function toMarkup() { + if (this.character) { + return "" + this.character + ""; + } else { + return ""; + } + } + /** + * Converts the math node into a string, similar to innerText. + */ + ; + + _proto3.toText = function toText() { + if (this.character) { + return this.character; + } else { + return " "; + } + }; + + return SpaceNode; +}(); + +/* harmony default export */ var mathMLTree = ({ + MathNode: MathNode, + TextNode: TextNode, + SpaceNode: SpaceNode, + newDocumentFragment: newDocumentFragment +}); +/** + * This file converts a parse tree into a cooresponding MathML tree. The main + * entry point is the `buildMathML` function, which takes a parse tree from the + * parser. + */ + + + + + + + + + +/** + * Takes a symbol and converts it into a MathML text node after performing + * optional replacement from symbols.js. + */ +var makeText = function makeText(text, mode, options) { + if (src_symbols[mode][text] && src_symbols[mode][text].replace && text.charCodeAt(0) !== 0xD835 && !(ligatures.hasOwnProperty(text) && options && (options.fontFamily && options.fontFamily.substr(4, 2) === "tt" || options.font && options.font.substr(4, 2) === "tt"))) { + text = src_symbols[mode][text].replace; + } + + return new mathMLTree.TextNode(text); +}; +/** + * Wrap the given array of nodes in an node if needed, i.e., + * unless the array has length 1. Always returns a single node. + */ + +var makeRow = function makeRow(body) { + if (body.length === 1) { + return body[0]; + } else { + return new mathMLTree.MathNode("mrow", body); + } +}; +/** + * Returns the math variant as a string or null if none is required. + */ + +var getVariant = function getVariant(group, options) { + // Handle \text... font specifiers as best we can. + // MathML has a limited list of allowable mathvariant specifiers; see + // https://www.w3.org/TR/MathML3/chapter3.html#presm.commatt + if (options.fontFamily === "texttt") { + return "monospace"; + } else if (options.fontFamily === "textsf") { + if (options.fontShape === "textit" && options.fontWeight === "textbf") { + return "sans-serif-bold-italic"; + } else if (options.fontShape === "textit") { + return "sans-serif-italic"; + } else if (options.fontWeight === "textbf") { + return "bold-sans-serif"; + } else { + return "sans-serif"; + } + } else if (options.fontShape === "textit" && options.fontWeight === "textbf") { + return "bold-italic"; + } else if (options.fontShape === "textit") { + return "italic"; + } else if (options.fontWeight === "textbf") { + return "bold"; + } + + var font = options.font; + + if (!font || font === "mathnormal") { + return null; + } + + var mode = group.mode; + + if (font === "mathit") { + return "italic"; + } else if (font === "boldsymbol") { + return group.type === "textord" ? "bold" : "bold-italic"; + } else if (font === "mathbf") { + return "bold"; + } else if (font === "mathbb") { + return "double-struck"; + } else if (font === "mathfrak") { + return "fraktur"; + } else if (font === "mathscr" || font === "mathcal") { + // MathML makes no distinction between script and caligrahpic + return "script"; + } else if (font === "mathsf") { + return "sans-serif"; + } else if (font === "mathtt") { + return "monospace"; + } + + var text = group.text; + + if (utils.contains(["\\imath", "\\jmath"], text)) { + return null; + } + + if (src_symbols[mode][text] && src_symbols[mode][text].replace) { + text = src_symbols[mode][text].replace; + } + + var fontName = buildCommon.fontMap[font].fontName; + + if (getCharacterMetrics(text, fontName, mode)) { + return buildCommon.fontMap[font].variant; + } + + return null; +}; +/** + * Takes a list of nodes, builds them, and returns a list of the generated + * MathML nodes. Also combine consecutive outputs into a single + * tag. + */ + +var buildMathML_buildExpression = function buildExpression(expression, options, isOrdgroup) { + if (expression.length === 1) { + var group = buildMathML_buildGroup(expression[0], options); + + if (isOrdgroup && group instanceof MathNode && group.type === "mo") { + // When TeX writers want to suppress spacing on an operator, + // they often put the operator by itself inside braces. + group.setAttribute("lspace", "0em"); + group.setAttribute("rspace", "0em"); + } + + return [group]; + } + + var groups = []; + var lastGroup; + + for (var i = 0; i < expression.length; i++) { + var _group = buildMathML_buildGroup(expression[i], options); + + if (_group instanceof MathNode && lastGroup instanceof MathNode) { + // Concatenate adjacent s + if (_group.type === 'mtext' && lastGroup.type === 'mtext' && _group.getAttribute('mathvariant') === lastGroup.getAttribute('mathvariant')) { + var _lastGroup$children; + + (_lastGroup$children = lastGroup.children).push.apply(_lastGroup$children, _group.children); + + continue; // Concatenate adjacent s + } else if (_group.type === 'mn' && lastGroup.type === 'mn') { + var _lastGroup$children2; + + (_lastGroup$children2 = lastGroup.children).push.apply(_lastGroup$children2, _group.children); + + continue; // Concatenate ... followed by . + } else if (_group.type === 'mi' && _group.children.length === 1 && lastGroup.type === 'mn') { + var child = _group.children[0]; + + if (child instanceof TextNode && child.text === '.') { + var _lastGroup$children3; + + (_lastGroup$children3 = lastGroup.children).push.apply(_lastGroup$children3, _group.children); + + continue; + } + } else if (lastGroup.type === 'mi' && lastGroup.children.length === 1) { + var lastChild = lastGroup.children[0]; + + if (lastChild instanceof TextNode && lastChild.text === "\u0338" && (_group.type === 'mo' || _group.type === 'mi' || _group.type === 'mn')) { + var _child = _group.children[0]; + + if (_child instanceof TextNode && _child.text.length > 0) { + // Overlay with combining character long solidus + _child.text = _child.text.slice(0, 1) + "\u0338" + _child.text.slice(1); + groups.pop(); + } + } + } + } + + groups.push(_group); + lastGroup = _group; + } + + return groups; +}; +/** + * Equivalent to buildExpression, but wraps the elements in an + * if there's more than one. Returns a single node instead of an array. + */ + +var buildExpressionRow = function buildExpressionRow(expression, options, isOrdgroup) { + return makeRow(buildMathML_buildExpression(expression, options, isOrdgroup)); +}; +/** + * Takes a group from the parser and calls the appropriate groupBuilders function + * on it to produce a MathML node. + */ + +var buildMathML_buildGroup = function buildGroup(group, options) { + if (!group) { + return new mathMLTree.MathNode("mrow"); + } + + if (_mathmlGroupBuilders[group.type]) { + // Call the groupBuilders function + // $FlowFixMe + var result = _mathmlGroupBuilders[group.type](group, options); // $FlowFixMe + + return result; + } else { + throw new src_ParseError("Got group of unknown type: '" + group.type + "'"); + } +}; +/** + * Takes a full parse tree and settings and builds a MathML representation of + * it. In particular, we put the elements from building the parse tree into a + * tag so we can also include that TeX source as an annotation. + * + * Note that we actually return a domTree element with a `` inside it so + * we can do appropriate styling. + */ + +function buildMathML(tree, texExpression, options, isDisplayMode, forMathmlOnly) { + var expression = buildMathML_buildExpression(tree, options); // TODO: Make a pass thru the MathML similar to buildHTML.traverseNonSpaceNodes + // and add spacing nodes. This is necessary only adjacent to math operators + // like \sin or \lim or to subsup elements that contain math operators. + // MathML takes care of the other spacing issues. + // Wrap up the expression in an mrow so it is presented in the semantics + // tag correctly, unless it's a single or . + + var wrapper; + + if (expression.length === 1 && expression[0] instanceof MathNode && utils.contains(["mrow", "mtable"], expression[0].type)) { + wrapper = expression[0]; + } else { + wrapper = new mathMLTree.MathNode("mrow", expression); + } // Build a TeX annotation of the source + + + var annotation = new mathMLTree.MathNode("annotation", [new mathMLTree.TextNode(texExpression)]); + annotation.setAttribute("encoding", "application/x-tex"); + var semantics = new mathMLTree.MathNode("semantics", [wrapper, annotation]); + var math = new mathMLTree.MathNode("math", [semantics]); + math.setAttribute("xmlns", "http://www.w3.org/1998/Math/MathML"); + + if (isDisplayMode) { + math.setAttribute("display", "block"); + } // You can't style nodes, so we wrap the node in a span. + // NOTE: The span class is not typed to have nodes as children, and + // we don't want to make the children type more generic since the children + // of span are expected to have more fields in `buildHtml` contexts. + + + var wrapperClass = forMathmlOnly ? "katex" : "katex-mathml"; // $FlowFixMe + + return buildCommon.makeSpan([wrapperClass], [math]); +} + + + + + + + +var optionsFromSettings = function optionsFromSettings(settings) { + return new src_Options({ + style: settings.displayMode ? src_Style.DISPLAY : src_Style.TEXT, + maxSize: settings.maxSize, + minRuleThickness: settings.minRuleThickness + }); +}; + +var displayWrap = function displayWrap(node, settings) { + if (settings.displayMode) { + var classes = ["katex-display"]; + + if (settings.leqno) { + classes.push("leqno"); + } + + if (settings.fleqn) { + classes.push("fleqn"); + } + + node = buildCommon.makeSpan(classes, [node]); + } + + return node; +}; + +var buildTree = function buildTree(tree, expression, settings) { + var options = optionsFromSettings(settings); + var katexNode; + + if (settings.output === "mathml") { + return buildMathML(tree, expression, options, settings.displayMode, true); + } else if (settings.output === "html") { + var htmlNode = buildHTML(tree, options); + katexNode = buildCommon.makeSpan(["katex"], [htmlNode]); + } else { + var mathMLNode = buildMathML(tree, expression, options, settings.displayMode, false); + + var _htmlNode = buildHTML(tree, options); + + katexNode = buildCommon.makeSpan(["katex"], [mathMLNode, _htmlNode]); + } + + return displayWrap(katexNode, settings); +}; +var buildHTMLTree = function buildHTMLTree(tree, expression, settings) { + var options = optionsFromSettings(settings); + var htmlNode = buildHTML(tree, options); + var katexNode = buildCommon.makeSpan(["katex"], [htmlNode]); + return displayWrap(katexNode, settings); +}; +/** + * This file provides support to buildMathML.js and buildHTML.js + * for stretchy wide elements rendered from SVG files + * and other CSS trickery. + */ + + + + + +var stretchyCodePoint = { + 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", + // Not a perfect match. + xleftequilibrium: "\u21CB", + // None better available. + "\\cdrightarrow": "\u2192", + "\\cdleftarrow": "\u2190", + "\\cdlongequal": "=" +}; + +var mathMLnode = function mathMLnode(label) { + var node = new mathMLTree.MathNode("mo", [new mathMLTree.TextNode(stretchyCodePoint[label.replace(/^\\/, '')])]); + node.setAttribute("stretchy", "true"); + return node; +}; // Many of the KaTeX SVG images have been adapted from glyphs in KaTeX fonts. +// Copyright (c) 2009-2010, Design Science, Inc. () +// Copyright (c) 2014-2017 Khan Academy () +// Licensed under the SIL Open Font License, Version 1.1. +// See \nhttp://scripts.sil.org/OFL +// Very Long SVGs +// Many of the KaTeX stretchy wide elements use a long SVG image and an +// overflow: hidden tactic to achieve a stretchy image while avoiding +// distortion of arrowheads or brace corners. +// The SVG typically contains a very long (400 em) arrow. +// The SVG is in a container span that has overflow: hidden, so the span +// acts like a window that exposes only part of the SVG. +// The SVG always has a longer, thinner aspect ratio than the container span. +// After the SVG fills 100% of the height of the container span, +// there is a long arrow shaft left over. That left-over shaft is not shown. +// Instead, it is sliced off because the span's CSS has overflow: hidden. +// Thus, the reader sees an arrow that matches the subject matter width +// without distortion. +// Some functions, such as \cancel, need to vary their aspect ratio. These +// functions do not get the overflow SVG treatment. +// Second Brush Stroke +// Low resolution monitors struggle to display images in fine detail. +// So browsers apply anti-aliasing. A long straight arrow shaft therefore +// will sometimes appear as if it has a blurred edge. +// To mitigate this, these SVG files contain a second "brush-stroke" on the +// arrow shafts. That is, a second long thin rectangular SVG path has been +// written directly on top of each arrow shaft. This reinforcement causes +// some of the screen pixels to display as black instead of the anti-aliased +// gray pixel that a single path would generate. So we get arrow shafts +// whose edges appear to be sharper. +// In the katexImagesData object just below, the dimensions all +// correspond to path geometry inside the relevant SVG. +// For example, \overrightarrow uses the same arrowhead as glyph U+2192 +// from the KaTeX Main font. The scaling factor is 1000. +// That is, inside the font, that arrowhead is 522 units tall, which +// corresponds to 0.522 em inside the document. + + +var katexImagesData = { + // path(s), minWidth, height, align + 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"], + "\\cdrightarrow": [["rightarrow"], 3.0, 522, "xMaxYMin"], + // CD minwwidth2.5pc + xleftarrow: [["leftarrow"], 1.469, 522, "xMinYMin"], + "\\cdleftarrow": [["leftarrow"], 3.0, 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"], + "\\cdlongequal": [["longequal"], 3.0, 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] +}; + +var groupLength = function groupLength(arg) { + if (arg.type === "ordgroup") { + return arg.body.length; + } else { + return 1; + } +}; + +var svgSpan = function svgSpan(group, options) { + // Create a span with inline SVG for the element. + function buildSvgSpan_() { + var viewBoxWidth = 400000; // default + + var label = group.label.substr(1); + + if (utils.contains(["widehat", "widecheck", "widetilde", "utilde"], label)) { + // Each type in the `if` statement corresponds to one of the ParseNode + // types below. This narrowing is required to access `grp.base`. + // $FlowFixMe + var grp = group; // There are four SVG images available for each function. + // Choose a taller image when there are more characters. + + var numChars = groupLength(grp.base); + var viewBoxHeight; + var pathName; + + var _height; + + if (numChars > 5) { + if (label === "widehat" || label === "widecheck") { + viewBoxHeight = 420; + viewBoxWidth = 2364; + _height = 0.42; + pathName = label + "4"; + } else { + viewBoxHeight = 312; + viewBoxWidth = 2340; + _height = 0.34; + pathName = "tilde4"; + } + } else { + var imgIndex = [1, 1, 2, 2, 3, 3][numChars]; + + if (label === "widehat" || label === "widecheck") { + viewBoxWidth = [0, 1062, 2364, 2364, 2364][imgIndex]; + viewBoxHeight = [0, 239, 300, 360, 420][imgIndex]; + _height = [0, 0.24, 0.3, 0.3, 0.36, 0.42][imgIndex]; + pathName = label + imgIndex; + } else { + viewBoxWidth = [0, 600, 1033, 2339, 2340][imgIndex]; + viewBoxHeight = [0, 260, 286, 306, 312][imgIndex]; + _height = [0, 0.26, 0.286, 0.3, 0.306, 0.34][imgIndex]; + pathName = "tilde" + imgIndex; + } + } + + var path = new PathNode(pathName); + var svgNode = new SvgNode([path], { + "width": "100%", + "height": makeEm(_height), + "viewBox": "0 0 " + viewBoxWidth + " " + viewBoxHeight, + "preserveAspectRatio": "none" + }); + return { + span: buildCommon.makeSvgSpan([], [svgNode], options), + minWidth: 0, + height: _height + }; + } else { + var spans = []; + var data = katexImagesData[label]; + var paths = data[0], + _minWidth = data[1], + _viewBoxHeight = data[2]; + + var _height2 = _viewBoxHeight / 1000; + + var numSvgChildren = paths.length; + var widthClasses; + var aligns; + + if (numSvgChildren === 1) { + // $FlowFixMe: All these cases must be of the 4-tuple type. + var align1 = data[3]; + widthClasses = ["hide-tail"]; + aligns = [align1]; + } else if (numSvgChildren === 2) { + widthClasses = ["halfarrow-left", "halfarrow-right"]; + aligns = ["xMinYMin", "xMaxYMin"]; + } else if (numSvgChildren === 3) { + widthClasses = ["brace-left", "brace-center", "brace-right"]; + aligns = ["xMinYMin", "xMidYMin", "xMaxYMin"]; + } else { + throw new Error("Correct katexImagesData or update code here to support\n " + numSvgChildren + " children."); + } + + for (var i = 0; i < numSvgChildren; i++) { + var _path = new PathNode(paths[i]); + + var _svgNode = new SvgNode([_path], { + "width": "400em", + "height": makeEm(_height2), + "viewBox": "0 0 " + viewBoxWidth + " " + _viewBoxHeight, + "preserveAspectRatio": aligns[i] + " slice" + }); + + var _span = buildCommon.makeSvgSpan([widthClasses[i]], [_svgNode], options); + + if (numSvgChildren === 1) { + return { + span: _span, + minWidth: _minWidth, + height: _height2 + }; + } else { + _span.style.height = makeEm(_height2); + spans.push(_span); + } + } + + return { + span: buildCommon.makeSpan(["stretchy"], spans, options), + minWidth: _minWidth, + height: _height2 + }; + } + } // buildSvgSpan_() + + + var _buildSvgSpan_ = buildSvgSpan_(), + span = _buildSvgSpan_.span, + minWidth = _buildSvgSpan_.minWidth, + height = _buildSvgSpan_.height; // Note that we are returning span.depth = 0. + // Any adjustments relative to the baseline must be done in buildHTML. + + + span.height = height; + span.style.height = makeEm(height); + + if (minWidth > 0) { + span.style.minWidth = makeEm(minWidth); + } + + return span; +}; + +var encloseSpan = function encloseSpan(inner, label, topPad, bottomPad, options) { + // Return an image span for \cancel, \bcancel, \xcancel, \fbox, or \angl + var img; + var totalHeight = inner.height + inner.depth + topPad + bottomPad; + + if (/fbox|color|angl/.test(label)) { + img = buildCommon.makeSpan(["stretchy", label], [], options); + + if (label === "fbox") { + var color = options.color && options.getColor(); + + if (color) { + img.style.borderColor = color; + } + } + } else { + // \cancel, \bcancel, or \xcancel + // Since \cancel's SVG is inline and it omits the viewBox attribute, + // its stroke-width will not vary with span area. + var lines = []; + + if (/^[bx]cancel$/.test(label)) { + lines.push(new LineNode({ + "x1": "0", + "y1": "0", + "x2": "100%", + "y2": "100%", + "stroke-width": "0.046em" + })); + } + + if (/^x?cancel$/.test(label)) { + lines.push(new LineNode({ + "x1": "0", + "y1": "100%", + "x2": "100%", + "y2": "0", + "stroke-width": "0.046em" + })); + } + + var svgNode = new SvgNode(lines, { + "width": "100%", + "height": makeEm(totalHeight) + }); + img = buildCommon.makeSvgSpan([], [svgNode], options); + } + + img.height = totalHeight; + img.style.height = makeEm(totalHeight); + return img; +}; + +/* harmony default export */ var stretchy = ({ + encloseSpan: encloseSpan, + mathMLnode: mathMLnode, + svgSpan: svgSpan +}); + + +/** + * Asserts that the node is of the given type and returns it with stricter + * typing. Throws if the node's type does not match. + */ +function assertNodeType(node, type) { + if (!node || node.type !== type) { + throw new Error("Expected node of type " + type + ", but got " + (node ? "node of type " + node.type : String(node))); + } // $FlowFixMe, >=0.125 + + + return node; +} +/** + * Returns the node more strictly typed iff it is of the given type. Otherwise, + * returns null. + */ + +function assertSymbolNodeType(node) { + var typedNode = checkSymbolNodeType(node); + + if (!typedNode) { + throw new Error("Expected node of symbol group type, but got " + (node ? "node of type " + node.type : String(node))); + } + + return typedNode; +} +/** + * Returns the node more strictly typed iff it is of the given type. Otherwise, + * returns null. + */ + +function checkSymbolNodeType(node) { + if (node && (node.type === "atom" || NON_ATOMS.hasOwnProperty(node.type))) { + // $FlowFixMe + return node; + } + + return null; +} + + + + + + + + + + +// NOTE: Unlike most `htmlBuilder`s, this one handles not only "accent", but +// also "supsub" since an accent can affect super/subscripting. +var htmlBuilder = function htmlBuilder(grp, options) { + // Accents are handled in the TeXbook pg. 443, rule 12. + var base; + var group; + var supSubGroup; + + if (grp && grp.type === "supsub") { + // If our base is a character box, and we have superscripts and + // subscripts, the supsub will defer to us. In particular, we want + // to attach the superscripts and subscripts to the inner body (so + // that the position of the superscripts and subscripts won't be + // affected by the height of the accent). We accomplish this by + // sticking the base of the accent into the base of the supsub, and + // rendering that, while keeping track of where the accent is. + // The real accent group is the base of the supsub group + group = assertNodeType(grp.base, "accent"); // The character box is the base of the accent group + + base = group.base; // Stick the character box into the base of the supsub group + + grp.base = base; // Rerender the supsub group with its new base, and store that + // result. + + supSubGroup = assertSpan(buildGroup(grp, options)); // reset original base + + grp.base = group; + } else { + group = assertNodeType(grp, "accent"); + base = group.base; + } // Build the base group + + + var body = buildGroup(base, options.havingCrampedStyle()); // Does the accent need to shift for the skew of a character? + + var mustShift = group.isShifty && utils.isCharacterBox(base); // Calculate the skew of the accent. This is based on the line "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. + + var skew = 0; + + if (mustShift) { + // If the base is a character box, then we want the skew of the + // innermost character. To do that, we find the innermost character: + var baseChar = utils.getBaseElem(base); // Then, we render its group to get the symbol inside it + + var baseGroup = buildGroup(baseChar, options.havingCrampedStyle()); // Finally, we pull the skew off of the symbol. + + skew = assertSymbolDomNode(baseGroup).skew; // Note that we now throw away baseGroup, because the layers we + // removed with getBaseElem might contain things like \color which + // we can't get rid of. + // TODO(emily): Find a better way to get the skew + } + + var accentBelow = group.label === "\\c"; // calculate the amount of space between the body and the accent + + var clearance = accentBelow ? body.height + body.depth : Math.min(body.height, options.fontMetrics().xHeight); // Build the accent + + var accentBody; + + if (!group.isStretchy) { + var accent; + var width; + + if (group.label === "\\vec") { + // Before version 0.9, \vec used the combining font glyph U+20D7. + // But browsers, especially Safari, are not consistent in how they + // render combining characters when not preceded by a character. + // So now we use an SVG. + // If Safari reforms, we should consider reverting to the glyph. + accent = buildCommon.staticSvg("vec", options); + width = buildCommon.svgData.vec[1]; + } else { + accent = buildCommon.makeOrd({ + mode: group.mode, + text: group.label + }, options, "textord"); + accent = assertSymbolDomNode(accent); // 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; + width = accent.width; + + if (accentBelow) { + clearance += accent.depth; + } + } + + accentBody = buildCommon.makeSpan(["accent-body"], [accent]); // "Full" accents expand the width of the resulting symbol to be + // at least the width of the accent, and overlap directly onto the + // character without any vertical offset. + + var accentFull = group.label === "\\textcircled"; + + if (accentFull) { + accentBody.classes.push('accent-full'); + clearance = body.height; + } // Shift the accent over by the skew. + + + var left = skew; // CSS defines `.katex .accent .accent-body:not(.accent-full) { width: 0 }` + // so that the accent doesn't contribute to the bounding box. + // We need to shift the character by its width (effectively half + // its width) to compensate. + + if (!accentFull) { + left -= width / 2; + } + + accentBody.style.left = makeEm(left); // \textcircled uses the \bigcirc glyph, so it needs some + // vertical adjustment to match LaTeX. + + if (group.label === "\\textcircled") { + accentBody.style.top = ".2em"; + } + + accentBody = buildCommon.makeVList({ + positionType: "firstBaseline", + children: [{ + type: "elem", + elem: body + }, { + type: "kern", + size: -clearance + }, { + type: "elem", + elem: accentBody + }] + }, options); + } else { + accentBody = stretchy.svgSpan(group, options); + accentBody = buildCommon.makeVList({ + positionType: "firstBaseline", + children: [{ + type: "elem", + elem: body + }, { + type: "elem", + elem: accentBody, + wrapperClasses: ["svg-align"], + wrapperStyle: skew > 0 ? { + width: "calc(100% - " + makeEm(2 * skew) + ")", + marginLeft: makeEm(2 * skew) + } : undefined + }] + }, options); + } + + var accentWrap = buildCommon.makeSpan(["mord", "accent"], [accentBody], options); + + if (supSubGroup) { + // Here, we replace the "base" child of the supsub with our newly + // generated accent. + supSubGroup.children[0] = accentWrap; // Since we don't rerun the height calculation after replacing the + // accent, we manually recalculate height. + + supSubGroup.height = Math.max(accentWrap.height, supSubGroup.height); // Accents should always be ords, even when their innards are not. + + supSubGroup.classes[0] = "mord"; + return supSubGroup; + } else { + return accentWrap; + } +}; + +var mathmlBuilder = function mathmlBuilder(group, options) { + var accentNode = group.isStretchy ? stretchy.mathMLnode(group.label) : new mathMLTree.MathNode("mo", [makeText(group.label, group.mode)]); + var node = new mathMLTree.MathNode("mover", [buildMathML_buildGroup(group.base, options), accentNode]); + node.setAttribute("accent", "true"); + return node; +}; + +var NON_STRETCHY_ACCENT_REGEX = new RegExp(["\\acute", "\\grave", "\\ddot", "\\tilde", "\\bar", "\\breve", "\\check", "\\hat", "\\vec", "\\dot", "\\mathring"].map(function (accent) { + return "\\" + accent; +}).join("|")); // Accents + +defineFunction({ + type: "accent", + names: ["\\acute", "\\grave", "\\ddot", "\\tilde", "\\bar", "\\breve", "\\check", "\\hat", "\\vec", "\\dot", "\\mathring", "\\widecheck", "\\widehat", "\\widetilde", "\\overrightarrow", "\\overleftarrow", "\\Overrightarrow", "\\overleftrightarrow", "\\overgroup", "\\overlinesegment", "\\overleftharpoon", "\\overrightharpoon"], + props: { + numArgs: 1 + }, + handler: function handler(context, args) { + var base = normalizeArgument(args[0]); + var isStretchy = !NON_STRETCHY_ACCENT_REGEX.test(context.funcName); + var isShifty = !isStretchy || context.funcName === "\\widehat" || context.funcName === "\\widetilde" || context.funcName === "\\widecheck"; + return { + type: "accent", + mode: context.parser.mode, + label: context.funcName, + isStretchy: isStretchy, + isShifty: isShifty, + base: base + }; + }, + htmlBuilder: htmlBuilder, + mathmlBuilder: mathmlBuilder +}); // Text-mode accents + +defineFunction({ + type: "accent", + names: ["\\'", "\\`", "\\^", "\\~", "\\=", "\\u", "\\.", '\\"', "\\c", "\\r", "\\H", "\\v", "\\textcircled"], + props: { + numArgs: 1, + allowedInText: true, + allowedInMath: true, + // unless in strict mode + argTypes: ["primitive"] + }, + handler: function handler(context, args) { + var base = args[0]; + var mode = context.parser.mode; + + if (mode === "math") { + context.parser.settings.reportNonstrict("mathVsTextAccents", "LaTeX's accent " + context.funcName + " works only in text mode"); + mode = "text"; + } + + return { + type: "accent", + mode: mode, + label: context.funcName, + isStretchy: false, + isShifty: true, + base: base + }; + }, + htmlBuilder: htmlBuilder, + mathmlBuilder: mathmlBuilder +}); +// Horizontal overlap functions + + + + + + +defineFunction({ + type: "accentUnder", + names: ["\\underleftarrow", "\\underrightarrow", "\\underleftrightarrow", "\\undergroup", "\\underlinesegment", "\\utilde"], + props: { + numArgs: 1 + }, + handler: function handler(_ref, args) { + var parser = _ref.parser, + funcName = _ref.funcName; + var base = args[0]; + return { + type: "accentUnder", + mode: parser.mode, + label: funcName, + base: base + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + // Treat under accents much like underlines. + var innerGroup = buildGroup(group.base, options); + var accentBody = stretchy.svgSpan(group, options); + var kern = group.label === "\\utilde" ? 0.12 : 0; // Generate the vlist, with the appropriate kerns + + var vlist = buildCommon.makeVList({ + positionType: "top", + positionData: innerGroup.height, + children: [{ + type: "elem", + elem: accentBody, + wrapperClasses: ["svg-align"] + }, { + type: "kern", + size: kern + }, { + type: "elem", + elem: innerGroup + }] + }, options); + return buildCommon.makeSpan(["mord", "accentunder"], [vlist], options); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var accentNode = stretchy.mathMLnode(group.label); + var node = new mathMLTree.MathNode("munder", [buildMathML_buildGroup(group.base, options), accentNode]); + node.setAttribute("accentunder", "true"); + return node; + } +}); + + + + + + + +// Helper function +var paddedNode = function paddedNode(group) { + var node = new mathMLTree.MathNode("mpadded", group ? [group] : []); + node.setAttribute("width", "+0.6em"); + node.setAttribute("lspace", "0.3em"); + return node; +}; // Stretchy arrows with an optional argument + + +defineFunction({ + type: "xArrow", + names: ["\\xleftarrow", "\\xrightarrow", "\\xLeftarrow", "\\xRightarrow", "\\xleftrightarrow", "\\xLeftrightarrow", "\\xhookleftarrow", "\\xhookrightarrow", "\\xmapsto", "\\xrightharpoondown", "\\xrightharpoonup", "\\xleftharpoondown", "\\xleftharpoonup", "\\xrightleftharpoons", "\\xleftrightharpoons", "\\xlongequal", "\\xtwoheadrightarrow", "\\xtwoheadleftarrow", "\\xtofrom", // The next 3 functions are here to support the mhchem extension. + // Direct use of these functions is discouraged and may break someday. + "\\xrightleftarrows", "\\xrightequilibrium", "\\xleftequilibrium", // The next 3 functions are here only to support the {CD} environment. + "\\\\cdrightarrow", "\\\\cdleftarrow", "\\\\cdlongequal"], + props: { + numArgs: 1, + numOptionalArgs: 1 + }, + handler: function handler(_ref, args, optArgs) { + var parser = _ref.parser, + funcName = _ref.funcName; + return { + type: "xArrow", + mode: parser.mode, + label: funcName, + body: args[0], + below: optArgs[0] + }; + }, + // Flow is unable to correctly infer the type of `group`, even though it's + // unamibiguously determined from the passed-in `type` above. + htmlBuilder: function htmlBuilder(group, options) { + var style = options.style; // Build the argument groups in the appropriate style. + // Ref: amsmath.dtx: \hbox{$\scriptstyle\mkern#3mu{#6}\mkern#4mu$}% + // Some groups can return document fragments. Handle those by wrapping + // them in a span. + + var newOptions = options.havingStyle(style.sup()); + var upperGroup = buildCommon.wrapFragment(buildGroup(group.body, newOptions, options), options); + var arrowPrefix = group.label.slice(0, 2) === "\\x" ? "x" : "cd"; + upperGroup.classes.push(arrowPrefix + "-arrow-pad"); + var lowerGroup; + + if (group.below) { + // Build the lower group + newOptions = options.havingStyle(style.sub()); + lowerGroup = buildCommon.wrapFragment(buildGroup(group.below, newOptions, options), options); + lowerGroup.classes.push(arrowPrefix + "-arrow-pad"); + } + + var arrowBody = stretchy.svgSpan(group, options); // Re shift: Note that stretchy.svgSpan returned arrowBody.depth = 0. + // The point we want on the math axis is at 0.5 * arrowBody.height. + + var arrowShift = -options.fontMetrics().axisHeight + 0.5 * arrowBody.height; // 2 mu kern. Ref: amsmath.dtx: #7\if0#2\else\mkern#2mu\fi + + var upperShift = -options.fontMetrics().axisHeight - 0.5 * arrowBody.height - 0.111; // 0.111 em = 2 mu + + if (upperGroup.depth > 0.25 || group.label === "\\xleftequilibrium") { + upperShift -= upperGroup.depth; // shift up if depth encroaches + } // Generate the vlist + + + var vlist; + + if (lowerGroup) { + var lowerShift = -options.fontMetrics().axisHeight + lowerGroup.height + 0.5 * arrowBody.height + 0.111; + vlist = buildCommon.makeVList({ + positionType: "individualShift", + children: [{ + type: "elem", + elem: upperGroup, + shift: upperShift + }, { + type: "elem", + elem: arrowBody, + shift: arrowShift + }, { + type: "elem", + elem: lowerGroup, + shift: lowerShift + }] + }, options); + } else { + vlist = buildCommon.makeVList({ + positionType: "individualShift", + children: [{ + type: "elem", + elem: upperGroup, + shift: upperShift + }, { + type: "elem", + elem: arrowBody, + shift: arrowShift + }] + }, options); + } // $FlowFixMe: Replace this with passing "svg-align" into makeVList. + + + vlist.children[0].children[0].children[1].classes.push("svg-align"); + return buildCommon.makeSpan(["mrel", "x-arrow"], [vlist], options); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var arrowNode = stretchy.mathMLnode(group.label); + arrowNode.setAttribute("minsize", group.label.charAt(0) === "x" ? "1.75em" : "3.0em"); + var node; + + if (group.body) { + var upperNode = paddedNode(buildMathML_buildGroup(group.body, options)); + + if (group.below) { + var lowerNode = paddedNode(buildMathML_buildGroup(group.below, options)); + node = new mathMLTree.MathNode("munderover", [arrowNode, lowerNode, upperNode]); + } else { + node = new mathMLTree.MathNode("mover", [arrowNode, upperNode]); + } + } else if (group.below) { + var _lowerNode = paddedNode(buildMathML_buildGroup(group.below, options)); + + node = new mathMLTree.MathNode("munder", [arrowNode, _lowerNode]); + } else { + // This should never happen. + // Parser.js throws an error if there is no argument. + node = paddedNode(); + node = new mathMLTree.MathNode("mover", [arrowNode, node]); + } + + return node; + } +}); + + + + + + + + +var cdArrowFunctionName = { + ">": "\\\\cdrightarrow", + "<": "\\\\cdleftarrow", + "=": "\\\\cdlongequal", + "A": "\\uparrow", + "V": "\\downarrow", + "|": "\\Vert", + ".": "no arrow" +}; + +var newCell = function newCell() { + // Create an empty cell, to be filled below with parse nodes. + // The parseTree from this module must be constructed like the + // one created by parseArray(), so an empty CD cell must + // be a ParseNode<"styling">. And CD is always displaystyle. + // So these values are fixed and flow can do implicit typing. + return { + type: "styling", + body: [], + mode: "math", + style: "display" + }; +}; + +var isStartOfArrow = function isStartOfArrow(node) { + return node.type === "textord" && node.text === "@"; +}; + +var isLabelEnd = function isLabelEnd(node, endChar) { + return (node.type === "mathord" || node.type === "atom") && node.text === endChar; +}; + +function cdArrow(arrowChar, labels, parser) { + // Return a parse tree of an arrow and its labels. + // This acts in a way similar to a macro expansion. + var funcName = cdArrowFunctionName[arrowChar]; + + switch (funcName) { + case "\\\\cdrightarrow": + case "\\\\cdleftarrow": + return parser.callFunction(funcName, [labels[0]], [labels[1]]); + + case "\\uparrow": + case "\\downarrow": + { + var leftLabel = parser.callFunction("\\\\cdleft", [labels[0]], []); + var bareArrow = { + type: "atom", + text: funcName, + mode: "math", + family: "rel" + }; + var sizedArrow = parser.callFunction("\\Big", [bareArrow], []); + var rightLabel = parser.callFunction("\\\\cdright", [labels[1]], []); + var arrowGroup = { + type: "ordgroup", + mode: "math", + body: [leftLabel, sizedArrow, rightLabel] + }; + return parser.callFunction("\\\\cdparent", [arrowGroup], []); + } + + case "\\\\cdlongequal": + return parser.callFunction("\\\\cdlongequal", [], []); + + case "\\Vert": + { + var arrow = { + type: "textord", + text: "\\Vert", + mode: "math" + }; + return parser.callFunction("\\Big", [arrow], []); + } + + default: + return { + type: "textord", + text: " ", + mode: "math" + }; + } +} + +function parseCD(parser) { + // Get the array's parse nodes with \\ temporarily mapped to \cr. + var parsedRows = []; + parser.gullet.beginGroup(); + parser.gullet.macros.set("\\cr", "\\\\\\relax"); + parser.gullet.beginGroup(); + + while (true) { + // eslint-disable-line no-constant-condition + // Get the parse nodes for the next row. + parsedRows.push(parser.parseExpression(false, "\\\\")); + parser.gullet.endGroup(); + parser.gullet.beginGroup(); + var next = parser.fetch().text; + + if (next === "&" || next === "\\\\") { + parser.consume(); + } else if (next === "\\end") { + if (parsedRows[parsedRows.length - 1].length === 0) { + parsedRows.pop(); // final row ended in \\ + } + + break; + } else { + throw new src_ParseError("Expected \\\\ or \\cr or \\end", parser.nextToken); + } + } + + var row = []; + var body = [row]; // Loop thru the parse nodes. Collect them into cells and arrows. + + for (var i = 0; i < parsedRows.length; i++) { + // Start a new row. + var rowNodes = parsedRows[i]; // Create the first cell. + + var cell = newCell(); + + for (var j = 0; j < rowNodes.length; j++) { + if (!isStartOfArrow(rowNodes[j])) { + // If a parseNode is not an arrow, it goes into a cell. + cell.body.push(rowNodes[j]); + } else { + // Parse node j is an "@", the start of an arrow. + // Before starting on the arrow, push the cell into `row`. + row.push(cell); // Now collect parseNodes into an arrow. + // The character after "@" defines the arrow type. + + j += 1; + var arrowChar = assertSymbolNodeType(rowNodes[j]).text; // Create two empty label nodes. We may or may not use them. + + var labels = new Array(2); + labels[0] = { + type: "ordgroup", + mode: "math", + body: [] + }; + labels[1] = { + type: "ordgroup", + mode: "math", + body: [] + }; // Process the arrow. + + if ("=|.".indexOf(arrowChar) > -1) ; else if ("<>AV".indexOf(arrowChar) > -1) { + // Four arrows, `@>>>`, `@<<<`, `@AAA`, and `@VVV`, each take + // two optional labels. E.g. the right-point arrow syntax is + // really: @>{optional label}>{optional label}> + // Collect parseNodes into labels. + for (var labelNum = 0; labelNum < 2; labelNum++) { + var inLabel = true; + + for (var k = j + 1; k < rowNodes.length; k++) { + if (isLabelEnd(rowNodes[k], arrowChar)) { + inLabel = false; + j = k; + break; + } + + if (isStartOfArrow(rowNodes[k])) { + throw new src_ParseError("Missing a " + arrowChar + " character to complete a CD arrow.", rowNodes[k]); + } + + labels[labelNum].body.push(rowNodes[k]); + } + + if (inLabel) { + // isLabelEnd never returned a true. + throw new src_ParseError("Missing a " + arrowChar + " character to complete a CD arrow.", rowNodes[j]); + } + } + } else { + throw new src_ParseError("Expected one of \"<>AV=|.\" after @", rowNodes[j]); + } // Now join the arrow to its labels. + + + var arrow = cdArrow(arrowChar, labels, parser); // Wrap the arrow in ParseNode<"styling">. + // This is done to match parseArray() behavior. + + var wrappedArrow = { + type: "styling", + body: [arrow], + mode: "math", + style: "display" // CD is always displaystyle. + + }; + row.push(wrappedArrow); // In CD's syntax, cells are implicit. That is, everything that + // is not an arrow gets collected into a cell. So create an empty + // cell now. It will collect upcoming parseNodes. + + cell = newCell(); + } + } + + if (i % 2 === 0) { + // Even-numbered rows consist of: cell, arrow, cell, arrow, ... cell + // The last cell is not yet pushed into `row`, so: + row.push(cell); + } else { + // Odd-numbered rows consist of: vert arrow, empty cell, ... vert arrow + // Remove the empty cell that was placed at the beginning of `row`. + row.shift(); + } + + row = []; + body.push(row); + } // End row group + + + parser.gullet.endGroup(); // End array group defining \\ + + parser.gullet.endGroup(); // define column separation. + + var cols = new Array(body[0].length).fill({ + type: "align", + align: "c", + pregap: 0.25, + // CD package sets \enskip between columns. + postgap: 0.25 // So pre and post each get half an \enskip, i.e. 0.25em. + + }); + return { + type: "array", + mode: "math", + body: body, + arraystretch: 1, + addJot: true, + rowGaps: [null], + cols: cols, + colSeparationType: "CD", + hLinesBeforeRow: new Array(body.length + 1).fill([]) + }; +} // The functions below are not available for general use. +// They are here only for internal use by the {CD} environment in placing labels +// next to vertical arrows. +// We don't need any such functions for horizontal arrows because we can reuse +// the functionality that already exists for extensible arrows. + +defineFunction({ + type: "cdlabel", + names: ["\\\\cdleft", "\\\\cdright"], + props: { + numArgs: 1 + }, + handler: function handler(_ref, args) { + var parser = _ref.parser, + funcName = _ref.funcName; + return { + type: "cdlabel", + mode: parser.mode, + side: funcName.slice(4), + label: args[0] + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + var newOptions = options.havingStyle(options.style.sup()); + var label = buildCommon.wrapFragment(buildGroup(group.label, newOptions, options), options); + label.classes.push("cd-label-" + group.side); + label.style.bottom = makeEm(0.8 - label.depth); // Zero out label height & depth, so vertical align of arrow is set + // by the arrow height, not by the label. + + label.height = 0; + label.depth = 0; + return label; + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var label = new mathMLTree.MathNode("mrow", [buildMathML_buildGroup(group.label, options)]); + label = new mathMLTree.MathNode("mpadded", [label]); + label.setAttribute("width", "0"); + + if (group.side === "left") { + label.setAttribute("lspace", "-1width"); + } // We have to guess at vertical alignment. We know the arrow is 1.8em tall, + // But we don't know the height or depth of the label. + + + label.setAttribute("voffset", "0.7em"); + label = new mathMLTree.MathNode("mstyle", [label]); + label.setAttribute("displaystyle", "false"); + label.setAttribute("scriptlevel", "1"); + return label; + } +}); +defineFunction({ + type: "cdlabelparent", + names: ["\\\\cdparent"], + props: { + numArgs: 1 + }, + handler: function handler(_ref2, args) { + var parser = _ref2.parser; + return { + type: "cdlabelparent", + mode: parser.mode, + fragment: args[0] + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + // Wrap the vertical arrow and its labels. + // The parent gets position: relative. The child gets position: absolute. + // So CSS can locate the label correctly. + var parent = buildCommon.wrapFragment(buildGroup(group.fragment, options), options); + parent.classes.push("cd-vert-arrow"); + return parent; + }, + mathmlBuilder: function mathmlBuilder(group, options) { + return new mathMLTree.MathNode("mrow", [buildMathML_buildGroup(group.fragment, options)]); + } +}); + + + // \@char is an internal function that takes a grouped decimal argument like +// {123} and converts into symbol with code 123. It is used by the *macro* +// \char defined in macros.js. + +defineFunction({ + type: "textord", + names: ["\\@char"], + props: { + numArgs: 1, + allowedInText: true + }, + handler: function handler(_ref, args) { + var parser = _ref.parser; + var arg = assertNodeType(args[0], "ordgroup"); + var group = arg.body; + var number = ""; + + for (var i = 0; i < group.length; i++) { + var node = assertNodeType(group[i], "textord"); + number += node.text; + } + + var code = parseInt(number); + var text; + + if (isNaN(code)) { + throw new src_ParseError("\\@char has non-numeric argument " + number); // If we drop IE support, the following code could be replaced with + // text = String.fromCodePoint(code) + } else if (code < 0 || code >= 0x10ffff) { + throw new src_ParseError("\\@char with invalid code point " + number); + } else if (code <= 0xffff) { + text = String.fromCharCode(code); + } else { + // Astral code point; split into surrogate halves + code -= 0x10000; + text = String.fromCharCode((code >> 10) + 0xd800, (code & 0x3ff) + 0xdc00); + } + + return { + type: "textord", + mode: parser.mode, + text: text + }; + } +}); + + + + + + + +var color_htmlBuilder = function htmlBuilder(group, options) { + var elements = buildExpression(group.body, options.withColor(group.color), false); // \color isn't supposed to affect the type of the elements it contains. + // To accomplish this, we wrap the results in a fragment, so the inner + // elements will be able to directly interact with their neighbors. For + // example, `\color{red}{2 +} 3` has the same spacing as `2 + 3` + + return buildCommon.makeFragment(elements); +}; + +var color_mathmlBuilder = function mathmlBuilder(group, options) { + var inner = buildMathML_buildExpression(group.body, options.withColor(group.color)); + var node = new mathMLTree.MathNode("mstyle", inner); + node.setAttribute("mathcolor", group.color); + return node; +}; + +defineFunction({ + type: "color", + names: ["\\textcolor"], + props: { + numArgs: 2, + allowedInText: true, + argTypes: ["color", "original"] + }, + handler: function handler(_ref, args) { + var parser = _ref.parser; + var color = assertNodeType(args[0], "color-token").color; + var body = args[1]; + return { + type: "color", + mode: parser.mode, + color: color, + body: ordargument(body) + }; + }, + htmlBuilder: color_htmlBuilder, + mathmlBuilder: color_mathmlBuilder +}); +defineFunction({ + type: "color", + names: ["\\color"], + props: { + numArgs: 1, + allowedInText: true, + argTypes: ["color"] + }, + handler: function handler(_ref2, args) { + var parser = _ref2.parser, + breakOnTokenText = _ref2.breakOnTokenText; + var color = assertNodeType(args[0], "color-token").color; // Set macro \current@color in current namespace to store the current + // color, mimicking the behavior of color.sty. + // This is currently used just to correctly color a \right + // that follows a \color command. + + parser.gullet.macros.set("\\current@color", color); // Parse out the implicit body that should be colored. + + var body = parser.parseExpression(true, breakOnTokenText); + return { + type: "color", + mode: parser.mode, + color: color, + body: body + }; + }, + htmlBuilder: color_htmlBuilder, + mathmlBuilder: color_mathmlBuilder +}); +// Row breaks within tabular environments, and line breaks at top level + + + + + // \DeclareRobustCommand\\{...\@xnewline} + +defineFunction({ + type: "cr", + names: ["\\\\"], + props: { + numArgs: 0, + numOptionalArgs: 1, + argTypes: ["size"], + allowedInText: true + }, + handler: function handler(_ref, args, optArgs) { + var parser = _ref.parser; + var size = optArgs[0]; + var newLine = !parser.settings.displayMode || !parser.settings.useStrictBehavior("newLineInDisplayMode", "In LaTeX, \\\\ or \\newline " + "does nothing in display mode"); + return { + type: "cr", + mode: parser.mode, + newLine: newLine, + size: size && assertNodeType(size, "size").value + }; + }, + // The following builders are called only at the top level, + // not within tabular/array environments. + htmlBuilder: function htmlBuilder(group, options) { + var span = buildCommon.makeSpan(["mspace"], [], options); + + if (group.newLine) { + span.classes.push("newline"); + + if (group.size) { + span.style.marginTop = makeEm(calculateSize(group.size, options)); + } + } + + return span; + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var node = new mathMLTree.MathNode("mspace"); + + if (group.newLine) { + node.setAttribute("linebreak", "newline"); + + if (group.size) { + node.setAttribute("height", makeEm(calculateSize(group.size, options))); + } + } + + return node; + } +}); + + + +var globalMap = { + "\\global": "\\global", + "\\long": "\\\\globallong", + "\\\\globallong": "\\\\globallong", + "\\def": "\\gdef", + "\\gdef": "\\gdef", + "\\edef": "\\xdef", + "\\xdef": "\\xdef", + "\\let": "\\\\globallet", + "\\futurelet": "\\\\globalfuture" +}; + +var checkControlSequence = function checkControlSequence(tok) { + var name = tok.text; + + if (/^(?:[\\{}$&#^_]|EOF)$/.test(name)) { + throw new src_ParseError("Expected a control sequence", tok); + } + + return name; +}; + +var getRHS = function getRHS(parser) { + var tok = parser.gullet.popToken(); + + if (tok.text === "=") { + // consume optional equals + tok = parser.gullet.popToken(); + + if (tok.text === " ") { + // consume one optional space + tok = parser.gullet.popToken(); + } + } + + return tok; +}; + +var letCommand = function letCommand(parser, name, tok, global) { + var macro = parser.gullet.macros.get(tok.text); + + if (macro == null) { + // don't expand it later even if a macro with the same name is defined + // e.g., \let\foo=\frac \def\frac{\relax} \frac12 + tok.noexpand = true; + macro = { + tokens: [tok], + numArgs: 0, + // reproduce the same behavior in expansion + unexpandable: !parser.gullet.isExpandable(tok.text) + }; + } + + parser.gullet.macros.set(name, macro, global); +}; // -> | +// -> |\global +// -> | +// -> \global|\long|\outer + + +defineFunction({ + type: "internal", + names: ["\\global", "\\long", "\\\\globallong" // can’t be entered directly + ], + props: { + numArgs: 0, + allowedInText: true + }, + handler: function handler(_ref) { + var parser = _ref.parser, + funcName = _ref.funcName; + parser.consumeSpaces(); + var token = parser.fetch(); + + if (globalMap[token.text]) { + // KaTeX doesn't have \par, so ignore \long + if (funcName === "\\global" || funcName === "\\\\globallong") { + token.text = globalMap[token.text]; + } + + return assertNodeType(parser.parseFunction(), "internal"); + } + + throw new src_ParseError("Invalid token after macro prefix", token); + } +}); // Basic support for macro definitions: \def, \gdef, \edef, \xdef +// -> +// -> \def|\gdef|\edef|\xdef +// -> + +defineFunction({ + type: "internal", + names: ["\\def", "\\gdef", "\\edef", "\\xdef"], + props: { + numArgs: 0, + allowedInText: true, + primitive: true + }, + handler: function handler(_ref2) { + var parser = _ref2.parser, + funcName = _ref2.funcName; + var tok = parser.gullet.popToken(); + var name = tok.text; + + if (/^(?:[\\{}$&#^_]|EOF)$/.test(name)) { + throw new src_ParseError("Expected a control sequence", tok); + } + + var numArgs = 0; + var insert; + var delimiters = [[]]; // contains no braces + + while (parser.gullet.future().text !== "{") { + tok = parser.gullet.popToken(); + + if (tok.text === "#") { + // If the very last character of the is #, so that + // this # is immediately followed by {, TeX will behave as if the { + // had been inserted at the right end of both the parameter text + // and the replacement text. + if (parser.gullet.future().text === "{") { + insert = parser.gullet.future(); + delimiters[numArgs].push("{"); + break; + } // A parameter, the first appearance of # must be followed by 1, + // the next by 2, and so on; up to nine #’s are allowed + + + tok = parser.gullet.popToken(); + + if (!/^[1-9]$/.test(tok.text)) { + throw new src_ParseError("Invalid argument number \"" + tok.text + "\""); + } + + if (parseInt(tok.text) !== numArgs + 1) { + throw new src_ParseError("Argument number \"" + tok.text + "\" out of order"); + } + + numArgs++; + delimiters.push([]); + } else if (tok.text === "EOF") { + throw new src_ParseError("Expected a macro definition"); + } else { + delimiters[numArgs].push(tok.text); + } + } // replacement text, enclosed in '{' and '}' and properly nested + + + var _parser$gullet$consum = parser.gullet.consumeArg(), + tokens = _parser$gullet$consum.tokens; + + if (insert) { + tokens.unshift(insert); + } + + if (funcName === "\\edef" || funcName === "\\xdef") { + tokens = parser.gullet.expandTokens(tokens); + tokens.reverse(); // to fit in with stack order + } // Final arg is the expansion of the macro + + + parser.gullet.macros.set(name, { + tokens: tokens, + numArgs: numArgs, + delimiters: delimiters + }, funcName === globalMap[funcName]); + return { + type: "internal", + mode: parser.mode + }; + } +}); // -> +// -> \futurelet +// | \let +// -> |= + +defineFunction({ + type: "internal", + names: ["\\let", "\\\\globallet" // can’t be entered directly + ], + props: { + numArgs: 0, + allowedInText: true, + primitive: true + }, + handler: function handler(_ref3) { + var parser = _ref3.parser, + funcName = _ref3.funcName; + var name = checkControlSequence(parser.gullet.popToken()); + parser.gullet.consumeSpaces(); + var tok = getRHS(parser); + letCommand(parser, name, tok, funcName === "\\\\globallet"); + return { + type: "internal", + mode: parser.mode + }; + } +}); // ref: https://www.tug.org/TUGboat/tb09-3/tb22bechtolsheim.pdf + +defineFunction({ + type: "internal", + names: ["\\futurelet", "\\\\globalfuture" // can’t be entered directly + ], + props: { + numArgs: 0, + allowedInText: true, + primitive: true + }, + handler: function handler(_ref4) { + var parser = _ref4.parser, + funcName = _ref4.funcName; + var name = checkControlSequence(parser.gullet.popToken()); + var middle = parser.gullet.popToken(); + var tok = parser.gullet.popToken(); + letCommand(parser, name, tok, funcName === "\\\\globalfuture"); + parser.gullet.pushToken(tok); + parser.gullet.pushToken(middle); + return { + type: "internal", + mode: parser.mode + }; + } +}); +/** + * This file 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`. + */ + + + + + + + + + + + +/** + * Get the metrics for a given symbol and font, after transformation (i.e. + * after following replacement from symbols.js) + */ +var getMetrics = function getMetrics(symbol, font, mode) { + var replace = src_symbols.math[symbol] && src_symbols.math[symbol].replace; + var metrics = getCharacterMetrics(replace || symbol, font, mode); + + if (!metrics) { + throw new Error("Unsupported symbol " + symbol + " and font size " + font + "."); + } + + return metrics; +}; +/** + * Puts a delimiter span in a given style, and adds appropriate height, depth, + * and maxFontSizes. + */ + + +var styleWrap = function styleWrap(delim, toStyle, options, classes) { + var newOptions = options.havingBaseStyle(toStyle); + var span = buildCommon.makeSpan(classes.concat(newOptions.sizingClasses(options)), [delim], options); + var delimSizeMultiplier = newOptions.sizeMultiplier / options.sizeMultiplier; + span.height *= delimSizeMultiplier; + span.depth *= delimSizeMultiplier; + span.maxFontSize = newOptions.sizeMultiplier; + return span; +}; + +var centerSpan = function centerSpan(span, options, style) { + var newOptions = options.havingBaseStyle(style); + var shift = (1 - options.sizeMultiplier / newOptions.sizeMultiplier) * options.fontMetrics().axisHeight; + span.classes.push("delimcenter"); + span.style.top = makeEm(shift); + span.height -= shift; + span.depth += shift; +}; +/** + * 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. + */ + + +var makeSmallDelim = function makeSmallDelim(delim, style, center, options, mode, classes) { + var text = buildCommon.makeSymbol(delim, "Main-Regular", mode, options); + var span = styleWrap(text, style, options, classes); + + if (center) { + centerSpan(span, options, style); + } + + return span; +}; +/** + * Builds a symbol in the given font size (note size is an integer) + */ + + +var mathrmSize = function mathrmSize(value, size, mode, options) { + return buildCommon.makeSymbol(value, "Size" + size + "-Regular", mode, options); +}; +/** + * Makes a large delimiter. This is a delimiter that comes in the Size1, Size2, + * Size3, or Size4 fonts. It is always rendered in textstyle. + */ + + +var makeLargeDelim = function makeLargeDelim(delim, size, center, options, mode, classes) { + var inner = mathrmSize(delim, size, mode, options); + var span = styleWrap(buildCommon.makeSpan(["delimsizing", "size" + size], [inner], options), src_Style.TEXT, options, classes); + + if (center) { + centerSpan(span, options, src_Style.TEXT); + } + + return span; +}; +/** + * Make a span from a font glyph with the given offset and in the given font. + * This is used in makeStackedDelim to make the stacking pieces for the delimiter. + */ + + +var makeGlyphSpan = function makeGlyphSpan(symbol, font, mode) { + var sizeClass; // Apply the correct CSS class to choose the right font. + + if (font === "Size1-Regular") { + sizeClass = "delim-size1"; + } else + /* if (font === "Size4-Regular") */ + { + sizeClass = "delim-size4"; + } + + var corner = buildCommon.makeSpan(["delimsizinginner", sizeClass], [buildCommon.makeSpan([], [buildCommon.makeSymbol(symbol, font, mode)])]); // Since this will be passed into `makeVList` in the end, wrap the element + // in the appropriate tag that VList uses. + + return { + type: "elem", + elem: corner + }; +}; + +var makeInner = function makeInner(ch, height, options) { + // Create a span with inline SVG for the inner part of a tall stacked delimiter. + var width = fontMetricsData["Size4-Regular"][ch.charCodeAt(0)] ? fontMetricsData["Size4-Regular"][ch.charCodeAt(0)][4] : fontMetricsData["Size1-Regular"][ch.charCodeAt(0)][4]; + var path = new PathNode("inner", innerPath(ch, Math.round(1000 * height))); + var svgNode = new SvgNode([path], { + "width": makeEm(width), + "height": makeEm(height), + // Override CSS rule `.katex svg { width: 100% }` + "style": "width:" + makeEm(width), + "viewBox": "0 0 " + 1000 * width + " " + Math.round(1000 * height), + "preserveAspectRatio": "xMinYMin" + }); + var span = buildCommon.makeSvgSpan([], [svgNode], options); + span.height = height; + span.style.height = makeEm(height); + span.style.width = makeEm(width); + return { + type: "elem", + elem: span + }; +}; // Helpers for makeStackedDelim + + +var lapInEms = 0.008; +var lap = { + type: "kern", + size: -1 * lapInEms +}; +var verts = ["|", "\\lvert", "\\rvert", "\\vert"]; +var doubleVerts = ["\\|", "\\lVert", "\\rVert", "\\Vert"]; +/** + * 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. + */ + +var makeStackedDelim = function makeStackedDelim(delim, heightTotal, center, options, mode, classes) { + // There are four parts, the top, an optional middle, a repeated part, and a + // bottom. + var top; + var middle; + var repeat; + var bottom; + top = repeat = bottom = delim; + middle = null; // Also keep track of what font the delimiters are in + + var font = "Size1-Regular"; // We set the parts and font based on the symbol. Note that we use + // '\u23d0' instead of '|' and '\u2016' instead of '\\|' for the + // repeats of the arrows + + if (delim === "\\uparrow") { + repeat = bottom = "\u23D0"; + } else if (delim === "\\Uparrow") { + repeat = bottom = "\u2016"; + } else if (delim === "\\downarrow") { + top = repeat = "\u23D0"; + } else if (delim === "\\Downarrow") { + top = repeat = "\u2016"; + } else if (delim === "\\updownarrow") { + top = "\\uparrow"; + repeat = "\u23D0"; + bottom = "\\downarrow"; + } else if (delim === "\\Updownarrow") { + top = "\\Uparrow"; + repeat = "\u2016"; + bottom = "\\Downarrow"; + } else if (utils.contains(verts, delim)) { + repeat = "\u2223"; + } else if (utils.contains(doubleVerts, delim)) { + repeat = "\u2225"; + } else if (delim === "[" || delim === "\\lbrack") { + top = "\u23A1"; + repeat = "\u23A2"; + bottom = "\u23A3"; + font = "Size4-Regular"; + } else if (delim === "]" || delim === "\\rbrack") { + top = "\u23A4"; + repeat = "\u23A5"; + bottom = "\u23A6"; + font = "Size4-Regular"; + } else if (delim === "\\lfloor" || delim === "\u230A") { + repeat = top = "\u23A2"; + bottom = "\u23A3"; + font = "Size4-Regular"; + } else if (delim === "\\lceil" || delim === "\u2308") { + top = "\u23A1"; + repeat = bottom = "\u23A2"; + font = "Size4-Regular"; + } else if (delim === "\\rfloor" || delim === "\u230B") { + repeat = top = "\u23A5"; + bottom = "\u23A6"; + font = "Size4-Regular"; + } else if (delim === "\\rceil" || delim === "\u2309") { + top = "\u23A4"; + repeat = bottom = "\u23A5"; + font = "Size4-Regular"; + } else if (delim === "(" || delim === "\\lparen") { + top = "\u239B"; + repeat = "\u239C"; + bottom = "\u239D"; + font = "Size4-Regular"; + } else if (delim === ")" || delim === "\\rparen") { + top = "\u239E"; + repeat = "\u239F"; + bottom = "\u23A0"; + font = "Size4-Regular"; + } else if (delim === "\\{" || delim === "\\lbrace") { + top = "\u23A7"; + middle = "\u23A8"; + bottom = "\u23A9"; + repeat = "\u23AA"; + font = "Size4-Regular"; + } else if (delim === "\\}" || delim === "\\rbrace") { + top = "\u23AB"; + middle = "\u23AC"; + bottom = "\u23AD"; + repeat = "\u23AA"; + font = "Size4-Regular"; + } else if (delim === "\\lgroup" || delim === "\u27EE") { + top = "\u23A7"; + bottom = "\u23A9"; + repeat = "\u23AA"; + font = "Size4-Regular"; + } else if (delim === "\\rgroup" || delim === "\u27EF") { + top = "\u23AB"; + bottom = "\u23AD"; + repeat = "\u23AA"; + font = "Size4-Regular"; + } else if (delim === "\\lmoustache" || delim === "\u23B0") { + top = "\u23A7"; + bottom = "\u23AD"; + repeat = "\u23AA"; + font = "Size4-Regular"; + } else if (delim === "\\rmoustache" || delim === "\u23B1") { + top = "\u23AB"; + bottom = "\u23A9"; + repeat = "\u23AA"; + font = "Size4-Regular"; + } // Get the metrics of the four sections + + + var topMetrics = getMetrics(top, font, mode); + var topHeightTotal = topMetrics.height + topMetrics.depth; + var repeatMetrics = getMetrics(repeat, font, mode); + var repeatHeightTotal = repeatMetrics.height + repeatMetrics.depth; + var bottomMetrics = getMetrics(bottom, font, mode); + var bottomHeightTotal = bottomMetrics.height + bottomMetrics.depth; + var middleHeightTotal = 0; + var middleFactor = 1; + + if (middle !== null) { + var middleMetrics = getMetrics(middle, font, mode); + middleHeightTotal = middleMetrics.height + middleMetrics.depth; + middleFactor = 2; // repeat symmetrically above and below middle + } // Calcuate the minimal height that the delimiter can have. + // It is at least the size of the top, bottom, and optional middle combined. + + + var minHeight = topHeightTotal + bottomHeightTotal + middleHeightTotal; // Compute the number of copies of the repeat symbol we will need + + var repeatCount = Math.max(0, Math.ceil((heightTotal - minHeight) / (middleFactor * repeatHeightTotal))); // Compute the total height of the delimiter including all the symbols + + var 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. + + var axisHeight = options.fontMetrics().axisHeight; + + if (center) { + axisHeight *= options.sizeMultiplier; + } // Calculate the depth + + + var depth = realHeightTotal / 2 - axisHeight; // Now, we start building the pieces that will go into the vlist + // Keep a list of the pieces of the stacked delimiter + + var stack = []; // Add the bottom symbol + + stack.push(makeGlyphSpan(bottom, font, mode)); + stack.push(lap); // overlap + + if (middle === null) { + // The middle section will be an SVG. Make it an extra 0.016em tall. + // We'll overlap by 0.008em at top and bottom. + var innerHeight = realHeightTotal - topHeightTotal - bottomHeightTotal + 2 * lapInEms; + stack.push(makeInner(repeat, innerHeight, options)); + } else { + // When there is a middle bit, we need the middle part and two repeated + // sections + var _innerHeight = (realHeightTotal - topHeightTotal - bottomHeightTotal - middleHeightTotal) / 2 + 2 * lapInEms; + + stack.push(makeInner(repeat, _innerHeight, options)); // Now insert the middle of the brace. + + stack.push(lap); + stack.push(makeGlyphSpan(middle, font, mode)); + stack.push(lap); + stack.push(makeInner(repeat, _innerHeight, options)); + } // Add the top symbol + + + stack.push(lap); + stack.push(makeGlyphSpan(top, font, mode)); // Finally, build the vlist + + var newOptions = options.havingBaseStyle(src_Style.TEXT); + var inner = buildCommon.makeVList({ + positionType: "bottom", + positionData: depth, + children: stack + }, newOptions); + return styleWrap(buildCommon.makeSpan(["delimsizing", "mult"], [inner], newOptions), src_Style.TEXT, options, classes); +}; // All surds have 0.08em padding above the viniculum inside the SVG. +// That keeps browser span height rounding error from pinching the line. + + +var vbPad = 80; // padding above the surd, measured inside the viewBox. + +var emPad = 0.08; // padding, in ems, measured in the document. + +var sqrtSvg = function sqrtSvg(sqrtName, height, viewBoxHeight, extraViniculum, options) { + var path = sqrtPath(sqrtName, extraViniculum, viewBoxHeight); + var pathNode = new PathNode(sqrtName, path); + var svg = new SvgNode([pathNode], { + // Note: 1000:1 ratio of viewBox to document em width. + "width": "400em", + "height": makeEm(height), + "viewBox": "0 0 400000 " + viewBoxHeight, + "preserveAspectRatio": "xMinYMin slice" + }); + return buildCommon.makeSvgSpan(["hide-tail"], [svg], options); +}; +/** + * Make a sqrt image of the given height, + */ + + +var makeSqrtImage = function makeSqrtImage(height, options) { + // Define a newOptions that removes the effect of size changes such as \Huge. + // We don't pick different a height surd for \Huge. For it, we scale up. + var newOptions = options.havingBaseSizing(); // Pick the desired surd glyph from a sequence of surds. + + var delim = traverseSequence("\\surd", height * newOptions.sizeMultiplier, stackLargeDelimiterSequence, newOptions); + var sizeMultiplier = newOptions.sizeMultiplier; // default + // The standard sqrt SVGs each have a 0.04em thick viniculum. + // If Settings.minRuleThickness is larger than that, we add extraViniculum. + + var extraViniculum = Math.max(0, options.minRuleThickness - options.fontMetrics().sqrtRuleThickness); // Create a span containing an SVG image of a sqrt symbol. + + var span; + var spanHeight = 0; + var texHeight = 0; + var viewBoxHeight = 0; + var advanceWidth; // We create viewBoxes with 80 units of "padding" above each surd. + // Then browser rounding error on the parent span height will not + // encroach on the ink of the viniculum. But that padding is not + // included in the TeX-like `height` used for calculation of + // vertical alignment. So texHeight = span.height < span.style.height. + + if (delim.type === "small") { + // Get an SVG that is derived from glyph U+221A in font KaTeX-Main. + // 1000 unit normal glyph height. + viewBoxHeight = 1000 + 1000 * extraViniculum + vbPad; + + if (height < 1.0) { + sizeMultiplier = 1.0; // mimic a \textfont radical + } else if (height < 1.4) { + sizeMultiplier = 0.7; // mimic a \scriptfont radical + } + + spanHeight = (1.0 + extraViniculum + emPad) / sizeMultiplier; + texHeight = (1.00 + extraViniculum) / sizeMultiplier; + span = sqrtSvg("sqrtMain", spanHeight, viewBoxHeight, extraViniculum, options); + span.style.minWidth = "0.853em"; + advanceWidth = 0.833 / sizeMultiplier; // from the font. + } else if (delim.type === "large") { + // These SVGs come from fonts: KaTeX_Size1, _Size2, etc. + viewBoxHeight = (1000 + vbPad) * sizeToMaxHeight[delim.size]; + texHeight = (sizeToMaxHeight[delim.size] + extraViniculum) / sizeMultiplier; + spanHeight = (sizeToMaxHeight[delim.size] + extraViniculum + emPad) / sizeMultiplier; + span = sqrtSvg("sqrtSize" + delim.size, spanHeight, viewBoxHeight, extraViniculum, options); + span.style.minWidth = "1.02em"; + advanceWidth = 1.0 / sizeMultiplier; // 1.0 from the font. + } else { + // Tall sqrt. In TeX, this would be stacked using multiple glyphs. + // We'll use a single SVG to accomplish the same thing. + spanHeight = height + extraViniculum + emPad; + texHeight = height + extraViniculum; + viewBoxHeight = Math.floor(1000 * height + extraViniculum) + vbPad; + span = sqrtSvg("sqrtTall", spanHeight, viewBoxHeight, extraViniculum, options); + span.style.minWidth = "0.742em"; + advanceWidth = 1.056; + } + + span.height = texHeight; + span.style.height = makeEm(spanHeight); + return { + span: span, + advanceWidth: advanceWidth, + // Calculate the actual line width. + // This actually should depend on the chosen font -- e.g. \boldmath + // should use the thicker surd symbols from e.g. KaTeX_Main-Bold, and + // have thicker rules. + ruleWidth: (options.fontMetrics().sqrtRuleThickness + extraViniculum) * sizeMultiplier + }; +}; // There are three kinds of delimiters, delimiters that stack when they become +// too large + + +var stackLargeDelimiters = ["(", "\\lparen", ")", "\\rparen", "[", "\\lbrack", "]", "\\rbrack", "\\{", "\\lbrace", "\\}", "\\rbrace", "\\lfloor", "\\rfloor", "\u230A", "\u230B", "\\lceil", "\\rceil", "\u2308", "\u2309", "\\surd"]; // delimiters that always stack + +var stackAlwaysDelimiters = ["\\uparrow", "\\downarrow", "\\updownarrow", "\\Uparrow", "\\Downarrow", "\\Updownarrow", "|", "\\|", "\\vert", "\\Vert", "\\lvert", "\\rvert", "\\lVert", "\\rVert", "\\lgroup", "\\rgroup", "\u27EE", "\u27EF", "\\lmoustache", "\\rmoustache", "\u23B0", "\u23B1"]; // and delimiters that never stack + +var stackNeverDelimiters = ["<", ">", "\\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. + +var sizeToMaxHeight = [0, 1.2, 1.8, 2.4, 3.0]; +/** + * Used to create a delimiter of a specific size, where `size` is 1, 2, 3, or 4. + */ + +var makeSizedDelim = function makeSizedDelim(delim, size, options, mode, 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 (utils.contains(stackLargeDelimiters, delim) || utils.contains(stackNeverDelimiters, delim)) { + return makeLargeDelim(delim, size, false, options, mode, classes); + } else if (utils.contains(stackAlwaysDelimiters, delim)) { + return makeStackedDelim(delim, sizeToMaxHeight[size], false, options, mode, classes); + } else { + throw new src_ParseError("Illegal delimiter: '" + delim + "'"); + } +}; +/** + * There are three different sequences of delimiter sizes that the delimiters + * follow depending on the kind of delimiter. This is used when creating custom + * sized delimiters to decide whether to create a small, large, or stacked + * delimiter. + * + * In real TeX, these sequences aren't explicitly defined, but are instead + * defined inside the font metrics. Since there are only three sequences that + * are possible for the delimiters that TeX defines, it is easier to just encode + * them explicitly here. + */ + + +// Delimiters that never stack try small delimiters and large delimiters only +var stackNeverDelimiterSequence = [{ + type: "small", + style: src_Style.SCRIPTSCRIPT +}, { + type: "small", + style: src_Style.SCRIPT +}, { + type: "small", + style: src_Style.TEXT +}, { + 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 + +var stackAlwaysDelimiterSequence = [{ + type: "small", + style: src_Style.SCRIPTSCRIPT +}, { + type: "small", + style: src_Style.SCRIPT +}, { + type: "small", + style: src_Style.TEXT +}, { + type: "stack" +}]; // Delimiters that stack when large try the small and then large delimiters, and +// stack afterwards + +var stackLargeDelimiterSequence = [{ + type: "small", + style: src_Style.SCRIPTSCRIPT +}, { + type: "small", + style: src_Style.SCRIPT +}, { + type: "small", + style: src_Style.TEXT +}, { + 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. + * TODO(#963) Use more specific font family return type once that is introduced. + */ + +var delimTypeToFont = function delimTypeToFont(type) { + if (type.type === "small") { + return "Main-Regular"; + } else if (type.type === "large") { + return "Size" + type.size + "-Regular"; + } else if (type.type === "stack") { + return "Size4-Regular"; + } else { + throw new Error("Add support for delim type '" + type.type + "' here."); + } +}; +/** + * 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. + */ + + +var traverseSequence = function traverseSequence(delim, height, sequence, options) { + // 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 3-3=0, script starts + // at index 3-2=1, text starts at 3-1=2, and display starts at min(2,3-0)=2 + var start = Math.min(2, 3 - options.style.size); + + for (var 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; + } + + var metrics = getMetrics(delim, delimTypeToFont(sequence[i]), "math"); + var 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") { + var newOptions = options.havingBaseStyle(sequence[i].style); + heightDepth *= newOptions.sizeMultiplier; + } // 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. + */ + + +var makeCustomSizedDelim = function makeCustomSizedDelim(delim, height, center, options, mode, classes) { + if (delim === "<" || delim === "\\lt" || delim === "\u27E8") { + delim = "\\langle"; + } else if (delim === ">" || delim === "\\gt" || delim === "\u27E9") { + delim = "\\rangle"; + } // Decide what sequence to use + + + var sequence; + + if (utils.contains(stackNeverDelimiters, delim)) { + sequence = stackNeverDelimiterSequence; + } else if (utils.contains(stackLargeDelimiters, delim)) { + sequence = stackLargeDelimiterSequence; + } else { + sequence = stackAlwaysDelimiterSequence; + } // Look through the sequence + + + var delimType = traverseSequence(delim, height, sequence, options); // Get the delimiter from font glyphs. + // Depending on the sequence element we decided on, call the + // appropriate function. + + if (delimType.type === "small") { + return makeSmallDelim(delim, delimType.style, center, options, mode, classes); + } else if (delimType.type === "large") { + return makeLargeDelim(delim, delimType.size, center, options, mode, classes); + } else + /* if (delimType.type === "stack") */ + { + return makeStackedDelim(delim, height, center, options, mode, classes); + } +}; +/** + * Make a delimiter for use with `\left` and `\right`, given a height and depth + * of an expression that the delimiters surround. + */ + + +var makeLeftRightDelim = function makeLeftRightDelim(delim, height, depth, options, mode, classes) { + // We always center \left/\right delimiters, so the axis is always shifted + var axisHeight = options.fontMetrics().axisHeight * options.sizeMultiplier; // Taken from TeX source, tex.web, function make_left_right + + var delimiterFactor = 901; + var delimiterExtend = 5.0 / options.fontMetrics().ptPerEm; + var maxDistFromAxis = Math.max(height - axisHeight, depth + axisHeight); + var totalHeight = Math.max( // In real TeX, calculations are done using integral values which are + // 65536 per pt, or 655360 per em. So, the division here truncates in + // TeX but doesn't here, producing different results. If we wanted to + // exactly match TeX's calculation, we could do + // Math.floor(655360 * maxDistFromAxis / 500) * + // delimiterFactor / 655360 + // (To see the difference, compare + // x^{x^{\left(\rule{0.1em}{0.68em}\right)}} + // in TeX and KaTeX) + maxDistFromAxis / 500 * delimiterFactor, 2 * maxDistFromAxis - delimiterExtend); // Finally, we defer to `makeCustomSizedDelim` with our calculated total + // height + + return makeCustomSizedDelim(delim, totalHeight, true, options, mode, classes); +}; + +/* harmony default export */ var delimiter = ({ + sqrtImage: makeSqrtImage, + sizedDelim: makeSizedDelim, + sizeToMaxHeight: sizeToMaxHeight, + customSizedDelim: makeCustomSizedDelim, + leftRightDelim: makeLeftRightDelim +}); + + + + + + + + + + +// Extra data needed for the delimiter handler down below +var delimiterSizes = { + "\\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 + } +}; +var delimiters = ["(", "\\lparen", ")", "\\rparen", "[", "\\lbrack", "]", "\\rbrack", "\\{", "\\lbrace", "\\}", "\\rbrace", "\\lfloor", "\\rfloor", "\u230A", "\u230B", "\\lceil", "\\rceil", "\u2308", "\u2309", "<", ">", "\\langle", "\u27E8", "\\rangle", "\u27E9", "\\lt", "\\gt", "\\lvert", "\\rvert", "\\lVert", "\\rVert", "\\lgroup", "\\rgroup", "\u27EE", "\u27EF", "\\lmoustache", "\\rmoustache", "\u23B0", "\u23B1", "/", "\\backslash", "|", "\\vert", "\\|", "\\Vert", "\\uparrow", "\\Uparrow", "\\downarrow", "\\Downarrow", "\\updownarrow", "\\Updownarrow", "."]; + +// Delimiter functions +function checkDelimiter(delim, context) { + var symDelim = checkSymbolNodeType(delim); + + if (symDelim && utils.contains(delimiters, symDelim.text)) { + return symDelim; + } else if (symDelim) { + throw new src_ParseError("Invalid delimiter '" + symDelim.text + "' after '" + context.funcName + "'", delim); + } else { + throw new src_ParseError("Invalid delimiter type '" + delim.type + "'", delim); + } +} + +defineFunction({ + type: "delimsizing", + names: ["\\bigl", "\\Bigl", "\\biggl", "\\Biggl", "\\bigr", "\\Bigr", "\\biggr", "\\Biggr", "\\bigm", "\\Bigm", "\\biggm", "\\Biggm", "\\big", "\\Big", "\\bigg", "\\Bigg"], + props: { + numArgs: 1, + argTypes: ["primitive"] + }, + handler: function handler(context, args) { + var delim = checkDelimiter(args[0], context); + return { + type: "delimsizing", + mode: context.parser.mode, + size: delimiterSizes[context.funcName].size, + mclass: delimiterSizes[context.funcName].mclass, + delim: delim.text + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + if (group.delim === ".") { + // Empty delimiters still count as elements, even though they don't + // show anything. + return buildCommon.makeSpan([group.mclass]); + } // Use delimiter.sizedDelim to generate the delimiter. + + + return delimiter.sizedDelim(group.delim, group.size, options, group.mode, [group.mclass]); + }, + mathmlBuilder: function mathmlBuilder(group) { + var children = []; + + if (group.delim !== ".") { + children.push(makeText(group.delim, group.mode)); + } + + var node = new mathMLTree.MathNode("mo", children); + + if (group.mclass === "mopen" || group.mclass === "mclose") { + // Only some of the delimsizing functions act as fences, and they + // return "mopen" or "mclose" mclass. + node.setAttribute("fence", "true"); + } else { + // Explicitly disable fencing if it's not a fence, to override the + // defaults. + node.setAttribute("fence", "false"); + } + + node.setAttribute("stretchy", "true"); + var size = makeEm(delimiter.sizeToMaxHeight[group.size]); + node.setAttribute("minsize", size); + node.setAttribute("maxsize", size); + return node; + } +}); + +function assertParsed(group) { + if (!group.body) { + throw new Error("Bug: The leftright ParseNode wasn't fully parsed."); + } +} + +defineFunction({ + type: "leftright-right", + names: ["\\right"], + props: { + numArgs: 1, + primitive: true + }, + handler: function handler(context, args) { + // \left case below triggers parsing of \right in + // `const right = parser.parseFunction();` + // uses this return value. + var color = context.parser.gullet.macros.get("\\current@color"); + + if (color && typeof color !== "string") { + throw new src_ParseError("\\current@color set to non-string in \\right"); + } + + return { + type: "leftright-right", + mode: context.parser.mode, + delim: checkDelimiter(args[0], context).text, + color: color // undefined if not set via \color + + }; + } +}); +defineFunction({ + type: "leftright", + names: ["\\left"], + props: { + numArgs: 1, + primitive: true + }, + handler: function handler(context, args) { + var delim = checkDelimiter(args[0], context); + var parser = context.parser; // Parse out the implicit body + + ++parser.leftrightDepth; // parseExpression stops before '\\right' + + var body = parser.parseExpression(false); + --parser.leftrightDepth; // Check the next token + + parser.expect("\\right", false); + var right = assertNodeType(parser.parseFunction(), "leftright-right"); + return { + type: "leftright", + mode: parser.mode, + body: body, + left: delim.text, + right: right.delim, + rightColor: right.color + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + assertParsed(group); // Build the inner expression + + var inner = buildExpression(group.body, options, true, ["mopen", "mclose"]); + var innerHeight = 0; + var innerDepth = 0; + var hadMiddle = false; // Calculate its height and depth + + for (var i = 0; i < inner.length; i++) { + // Property `isMiddle` not defined on `span`. See comment in + // "middle"'s htmlBuilder. + // $FlowFixMe + if (inner[i].isMiddle) { + hadMiddle = true; + } else { + innerHeight = Math.max(inner[i].height, innerHeight); + innerDepth = Math.max(inner[i].depth, innerDepth); + } + } // The size of delimiters is the same, regardless of what style 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. + + + innerHeight *= options.sizeMultiplier; + innerDepth *= options.sizeMultiplier; + var leftDelim; + + if (group.left === ".") { + // Empty delimiters in \left and \right make null delimiter spaces. + leftDelim = makeNullDelimiter(options, ["mopen"]); + } else { + // Otherwise, use leftRightDelim to generate the correct sized + // delimiter. + leftDelim = delimiter.leftRightDelim(group.left, innerHeight, innerDepth, options, group.mode, ["mopen"]); + } // Add it to the beginning of the expression + + + inner.unshift(leftDelim); // Handle middle delimiters + + if (hadMiddle) { + for (var _i = 1; _i < inner.length; _i++) { + var middleDelim = inner[_i]; // Property `isMiddle` not defined on `span`. See comment in + // "middle"'s htmlBuilder. + // $FlowFixMe + + var isMiddle = middleDelim.isMiddle; + + if (isMiddle) { + // Apply the options that were active when \middle was called + inner[_i] = delimiter.leftRightDelim(isMiddle.delim, innerHeight, innerDepth, isMiddle.options, group.mode, []); + } + } + } + + var rightDelim; // Same for the right delimiter, but using color specified by \color + + if (group.right === ".") { + rightDelim = makeNullDelimiter(options, ["mclose"]); + } else { + var colorOptions = group.rightColor ? options.withColor(group.rightColor) : options; + rightDelim = delimiter.leftRightDelim(group.right, innerHeight, innerDepth, colorOptions, group.mode, ["mclose"]); + } // Add it to the end of the expression. + + + inner.push(rightDelim); + return buildCommon.makeSpan(["minner"], inner, options); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + assertParsed(group); + var inner = buildMathML_buildExpression(group.body, options); + + if (group.left !== ".") { + var leftNode = new mathMLTree.MathNode("mo", [makeText(group.left, group.mode)]); + leftNode.setAttribute("fence", "true"); + inner.unshift(leftNode); + } + + if (group.right !== ".") { + var rightNode = new mathMLTree.MathNode("mo", [makeText(group.right, group.mode)]); + rightNode.setAttribute("fence", "true"); + + if (group.rightColor) { + rightNode.setAttribute("mathcolor", group.rightColor); + } + + inner.push(rightNode); + } + + return makeRow(inner); + } +}); +defineFunction({ + type: "middle", + names: ["\\middle"], + props: { + numArgs: 1, + primitive: true + }, + handler: function handler(context, args) { + var delim = checkDelimiter(args[0], context); + + if (!context.parser.leftrightDepth) { + throw new src_ParseError("\\middle without preceding \\left", delim); + } + + return { + type: "middle", + mode: context.parser.mode, + delim: delim.text + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + var middleDelim; + + if (group.delim === ".") { + middleDelim = makeNullDelimiter(options, []); + } else { + middleDelim = delimiter.sizedDelim(group.delim, 1, options, group.mode, []); + var isMiddle = { + delim: group.delim, + options: options + }; // Property `isMiddle` not defined on `span`. It is only used in + // this file above. + // TODO: Fix this violation of the `span` type and possibly rename + // things since `isMiddle` sounds like a boolean, but is a struct. + // $FlowFixMe + + middleDelim.isMiddle = isMiddle; + } + + return middleDelim; + }, + mathmlBuilder: function mathmlBuilder(group, options) { + // A Firefox \middle will strech a character vertically only if it + // is in the fence part of the operator dictionary at: + // https://www.w3.org/TR/MathML3/appendixc.html. + // So we need to avoid U+2223 and use plain "|" instead. + var textNode = group.delim === "\\vert" || group.delim === "|" ? makeText("|", "text") : makeText(group.delim, group.mode); + var middleNode = new mathMLTree.MathNode("mo", [textNode]); + middleNode.setAttribute("fence", "true"); // MathML gives 5/18em spacing to each element. + // \middle should get delimiter spacing instead. + + middleNode.setAttribute("lspace", "0.05em"); + middleNode.setAttribute("rspace", "0.05em"); + return middleNode; + } +}); + + + + + + + + + + + + +var enclose_htmlBuilder = function htmlBuilder(group, options) { + // \cancel, \bcancel, \xcancel, \sout, \fbox, \colorbox, \fcolorbox, \phase + // Some groups can return document fragments. Handle those by wrapping + // them in a span. + var inner = buildCommon.wrapFragment(buildGroup(group.body, options), options); + var label = group.label.substr(1); + var scale = options.sizeMultiplier; + var img; + var imgShift = 0; // In the LaTeX cancel package, line geometry is slightly different + // depending on whether the subject is wider than it is tall, or vice versa. + // We don't know the width of a group, so as a proxy, we test if + // the subject is a single character. This captures most of the + // subjects that should get the "tall" treatment. + + var isSingleChar = utils.isCharacterBox(group.body); + + if (label === "sout") { + img = buildCommon.makeSpan(["stretchy", "sout"]); + img.height = options.fontMetrics().defaultRuleThickness / scale; + imgShift = -0.5 * options.fontMetrics().xHeight; + } else if (label === "phase") { + // Set a couple of dimensions from the steinmetz package. + var lineWeight = calculateSize({ + number: 0.6, + unit: "pt" + }, options); + var clearance = calculateSize({ + number: 0.35, + unit: "ex" + }, options); // Prevent size changes like \Huge from affecting line thickness + + var newOptions = options.havingBaseSizing(); + scale = scale / newOptions.sizeMultiplier; + var angleHeight = inner.height + inner.depth + lineWeight + clearance; // Reserve a left pad for the angle. + + inner.style.paddingLeft = makeEm(angleHeight / 2 + lineWeight); // Create an SVG + + var viewBoxHeight = Math.floor(1000 * angleHeight * scale); + var path = phasePath(viewBoxHeight); + var svgNode = new SvgNode([new PathNode("phase", path)], { + "width": "400em", + "height": makeEm(viewBoxHeight / 1000), + "viewBox": "0 0 400000 " + viewBoxHeight, + "preserveAspectRatio": "xMinYMin slice" + }); // Wrap it in a span with overflow: hidden. + + img = buildCommon.makeSvgSpan(["hide-tail"], [svgNode], options); + img.style.height = makeEm(angleHeight); + imgShift = inner.depth + lineWeight + clearance; + } else { + // Add horizontal padding + if (/cancel/.test(label)) { + if (!isSingleChar) { + inner.classes.push("cancel-pad"); + } + } else if (label === "angl") { + inner.classes.push("anglpad"); + } else { + inner.classes.push("boxpad"); + } // Add vertical padding + + + var topPad = 0; + var bottomPad = 0; + var ruleThickness = 0; // ref: cancel package: \advance\totalheight2\p@ % "+2" + + if (/box/.test(label)) { + ruleThickness = Math.max(options.fontMetrics().fboxrule, // default + options.minRuleThickness // User override. + ); + topPad = options.fontMetrics().fboxsep + (label === "colorbox" ? 0 : ruleThickness); + bottomPad = topPad; + } else if (label === "angl") { + ruleThickness = Math.max(options.fontMetrics().defaultRuleThickness, options.minRuleThickness); + topPad = 4 * ruleThickness; // gap = 3 × line, plus the line itself. + + bottomPad = Math.max(0, 0.25 - inner.depth); + } else { + topPad = isSingleChar ? 0.2 : 0; + bottomPad = topPad; + } + + img = stretchy.encloseSpan(inner, label, topPad, bottomPad, options); + + if (/fbox|boxed|fcolorbox/.test(label)) { + img.style.borderStyle = "solid"; + img.style.borderWidth = makeEm(ruleThickness); + } else if (label === "angl" && ruleThickness !== 0.049) { + img.style.borderTopWidth = makeEm(ruleThickness); + img.style.borderRightWidth = makeEm(ruleThickness); + } + + imgShift = inner.depth + bottomPad; + + if (group.backgroundColor) { + img.style.backgroundColor = group.backgroundColor; + + if (group.borderColor) { + img.style.borderColor = group.borderColor; + } + } + } + + var vlist; + + if (group.backgroundColor) { + vlist = buildCommon.makeVList({ + positionType: "individualShift", + children: [// Put the color background behind inner; + { + type: "elem", + elem: img, + shift: imgShift + }, { + type: "elem", + elem: inner, + shift: 0 + }] + }, options); + } else { + var classes = /cancel|phase/.test(label) ? ["svg-align"] : []; + vlist = buildCommon.makeVList({ + positionType: "individualShift", + children: [// Write the \cancel stroke on top of inner. + { + type: "elem", + elem: inner, + shift: 0 + }, { + type: "elem", + elem: img, + shift: imgShift, + wrapperClasses: classes + }] + }, options); + } + + if (/cancel/.test(label)) { + // The cancel package documentation says that cancel lines add their height + // to the expression, but tests show that isn't how it actually works. + vlist.height = inner.height; + vlist.depth = inner.depth; + } + + if (/cancel/.test(label) && !isSingleChar) { + // cancel does not create horiz space for its line extension. + return buildCommon.makeSpan(["mord", "cancel-lap"], [vlist], options); + } else { + return buildCommon.makeSpan(["mord"], [vlist], options); + } +}; + +var enclose_mathmlBuilder = function mathmlBuilder(group, options) { + var fboxsep = 0; + var node = new mathMLTree.MathNode(group.label.indexOf("colorbox") > -1 ? "mpadded" : "menclose", [buildMathML_buildGroup(group.body, options)]); + + switch (group.label) { + case "\\cancel": + node.setAttribute("notation", "updiagonalstrike"); + break; + + case "\\bcancel": + node.setAttribute("notation", "downdiagonalstrike"); + break; + + case "\\phase": + node.setAttribute("notation", "phasorangle"); + break; + + case "\\sout": + node.setAttribute("notation", "horizontalstrike"); + break; + + case "\\fbox": + node.setAttribute("notation", "box"); + break; + + case "\\angl": + node.setAttribute("notation", "actuarial"); + break; + + case "\\fcolorbox": + case "\\colorbox": + // doesn't have a good notation option. So use + // instead. Set some attributes that come included with . + fboxsep = options.fontMetrics().fboxsep * options.fontMetrics().ptPerEm; + node.setAttribute("width", "+" + 2 * fboxsep + "pt"); + node.setAttribute("height", "+" + 2 * fboxsep + "pt"); + node.setAttribute("lspace", fboxsep + "pt"); // + + node.setAttribute("voffset", fboxsep + "pt"); + + if (group.label === "\\fcolorbox") { + var thk = Math.max(options.fontMetrics().fboxrule, // default + options.minRuleThickness // user override + ); + node.setAttribute("style", "border: " + thk + "em solid " + String(group.borderColor)); + } + + break; + + case "\\xcancel": + node.setAttribute("notation", "updiagonalstrike downdiagonalstrike"); + break; + } + + if (group.backgroundColor) { + node.setAttribute("mathbackground", group.backgroundColor); + } + + return node; +}; + +defineFunction({ + type: "enclose", + names: ["\\colorbox"], + props: { + numArgs: 2, + allowedInText: true, + argTypes: ["color", "text"] + }, + handler: function handler(_ref, args, optArgs) { + var parser = _ref.parser, + funcName = _ref.funcName; + var color = assertNodeType(args[0], "color-token").color; + var body = args[1]; + return { + type: "enclose", + mode: parser.mode, + label: funcName, + backgroundColor: color, + body: body + }; + }, + htmlBuilder: enclose_htmlBuilder, + mathmlBuilder: enclose_mathmlBuilder +}); +defineFunction({ + type: "enclose", + names: ["\\fcolorbox"], + props: { + numArgs: 3, + allowedInText: true, + argTypes: ["color", "color", "text"] + }, + handler: function handler(_ref2, args, optArgs) { + var parser = _ref2.parser, + funcName = _ref2.funcName; + var borderColor = assertNodeType(args[0], "color-token").color; + var backgroundColor = assertNodeType(args[1], "color-token").color; + var body = args[2]; + return { + type: "enclose", + mode: parser.mode, + label: funcName, + backgroundColor: backgroundColor, + borderColor: borderColor, + body: body + }; + }, + htmlBuilder: enclose_htmlBuilder, + mathmlBuilder: enclose_mathmlBuilder +}); +defineFunction({ + type: "enclose", + names: ["\\fbox"], + props: { + numArgs: 1, + argTypes: ["hbox"], + allowedInText: true + }, + handler: function handler(_ref3, args) { + var parser = _ref3.parser; + return { + type: "enclose", + mode: parser.mode, + label: "\\fbox", + body: args[0] + }; + } +}); +defineFunction({ + type: "enclose", + names: ["\\cancel", "\\bcancel", "\\xcancel", "\\sout", "\\phase"], + props: { + numArgs: 1 + }, + handler: function handler(_ref4, args) { + var parser = _ref4.parser, + funcName = _ref4.funcName; + var body = args[0]; + return { + type: "enclose", + mode: parser.mode, + label: funcName, + body: body + }; + }, + htmlBuilder: enclose_htmlBuilder, + mathmlBuilder: enclose_mathmlBuilder +}); +defineFunction({ + type: "enclose", + names: ["\\angl"], + props: { + numArgs: 1, + argTypes: ["hbox"], + allowedInText: false + }, + handler: function handler(_ref5, args) { + var parser = _ref5.parser; + return { + type: "enclose", + mode: parser.mode, + label: "\\angl", + body: args[0] + }; + } +}); + + +/** + * All registered environments. + * `environments.js` exports this same dictionary again and makes it public. + * `Parser.js` requires this dictionary via `environments.js`. + */ +var _environments = {}; +function defineEnvironment(_ref) { + var type = _ref.type, + names = _ref.names, + props = _ref.props, + handler = _ref.handler, + htmlBuilder = _ref.htmlBuilder, + mathmlBuilder = _ref.mathmlBuilder; + // Set default values of environments. + var data = { + type: type, + numArgs: props.numArgs || 0, + allowedInText: false, + numOptionalArgs: 0, + handler: handler + }; + + for (var i = 0; i < names.length; ++i) { + // TODO: The value type of _environments should be a type union of all + // possible `EnvSpec<>` possibilities instead of `EnvSpec<*>`, which is + // an existential type. + _environments[names[i]] = data; + } + + if (htmlBuilder) { + _htmlGroupBuilders[type] = htmlBuilder; + } + + if (mathmlBuilder) { + _mathmlGroupBuilders[type] = mathmlBuilder; + } +} + + + + + + + + + + + + + + +// Helper functions +function getHLines(parser) { + // Return an array. The array length = number of hlines. + // Each element in the array tells if the line is dashed. + var hlineInfo = []; + parser.consumeSpaces(); + var nxt = parser.fetch().text; + + while (nxt === "\\hline" || nxt === "\\hdashline") { + parser.consume(); + hlineInfo.push(nxt === "\\hdashline"); + parser.consumeSpaces(); + nxt = parser.fetch().text; + } + + return hlineInfo; +} + +var validateAmsEnvironmentContext = function validateAmsEnvironmentContext(context) { + var settings = context.parser.settings; + + if (!settings.displayMode) { + throw new src_ParseError("{" + context.envName + "} can be used only in" + " display mode."); + } +}; +/** + * Parse the body of the environment, with rows delimited by \\ and + * columns delimited by &, and create a nested list in row-major order + * with one group per cell. If given an optional argument style + * ("text", "display", etc.), then each cell is cast into that style. + */ + + +function parseArray(parser, _ref, style) { + var hskipBeforeAndAfter = _ref.hskipBeforeAndAfter, + addJot = _ref.addJot, + cols = _ref.cols, + arraystretch = _ref.arraystretch, + colSeparationType = _ref.colSeparationType, + addEqnNum = _ref.addEqnNum, + singleRow = _ref.singleRow, + emptySingleRow = _ref.emptySingleRow, + maxNumCols = _ref.maxNumCols, + leqno = _ref.leqno; + parser.gullet.beginGroup(); + + if (!singleRow) { + // \cr is equivalent to \\ without the optional size argument (see below) + // TODO: provide helpful error when \cr is used outside array environment + parser.gullet.macros.set("\\cr", "\\\\\\relax"); + } // Get current arraystretch if it's not set by the environment + + + if (!arraystretch) { + var stretch = parser.gullet.expandMacroAsText("\\arraystretch"); + + if (stretch == null) { + // Default \arraystretch from lttab.dtx + arraystretch = 1; + } else { + arraystretch = parseFloat(stretch); + + if (!arraystretch || arraystretch < 0) { + throw new src_ParseError("Invalid \\arraystretch: " + stretch); + } + } + } // Start group for first cell + + + parser.gullet.beginGroup(); + var row = []; + var body = [row]; + var rowGaps = []; + var hLinesBeforeRow = []; // Test for \hline at the top of the array. + + hLinesBeforeRow.push(getHLines(parser)); + + while (true) { + // eslint-disable-line no-constant-condition + // Parse each cell in its own group (namespace) + var cell = parser.parseExpression(false, singleRow ? "\\end" : "\\\\"); + parser.gullet.endGroup(); + parser.gullet.beginGroup(); + cell = { + type: "ordgroup", + mode: parser.mode, + body: cell + }; + + if (style) { + cell = { + type: "styling", + mode: parser.mode, + style: style, + body: [cell] + }; + } + + row.push(cell); + var next = parser.fetch().text; + + if (next === "&") { + if (maxNumCols && row.length === maxNumCols) { + if (singleRow || colSeparationType) { + // {equation} or {split} + throw new src_ParseError("Too many tab characters: &", parser.nextToken); + } else { + // {array} environment + parser.settings.reportNonstrict("textEnv", "Too few columns " + "specified in the {array} column argument."); + } + } + + parser.consume(); + } else if (next === "\\end") { + // Arrays terminate newlines with `\crcr` which consumes a `\cr` if + // the last line is empty. However, AMS environments keep the + // empty row if it's the only one. + // NOTE: Currently, `cell` is the last item added into `row`. + if (row.length === 1 && cell.type === "styling" && cell.body[0].body.length === 0 && (body.length > 1 || !emptySingleRow)) { + body.pop(); + } + + if (hLinesBeforeRow.length < body.length + 1) { + hLinesBeforeRow.push([]); + } + + break; + } else if (next === "\\\\") { + parser.consume(); + var size = void 0; // \def\Let@{\let\\\math@cr} + // \def\math@cr{...\math@cr@} + // \def\math@cr@{\new@ifnextchar[\math@cr@@{\math@cr@@[\z@]}} + // \def\math@cr@@[#1]{...\math@cr@@@...} + // \def\math@cr@@@{\cr} + + if (parser.gullet.future().text !== " ") { + size = parser.parseSizeGroup(true); + } + + rowGaps.push(size ? size.value : null); // check for \hline(s) following the row separator + + hLinesBeforeRow.push(getHLines(parser)); + row = []; + body.push(row); + } else { + throw new src_ParseError("Expected & or \\\\ or \\cr or \\end", parser.nextToken); + } + } // End cell group + + + parser.gullet.endGroup(); // End array group defining \cr + + parser.gullet.endGroup(); + return { + type: "array", + mode: parser.mode, + addJot: addJot, + arraystretch: arraystretch, + body: body, + cols: cols, + rowGaps: rowGaps, + hskipBeforeAndAfter: hskipBeforeAndAfter, + hLinesBeforeRow: hLinesBeforeRow, + colSeparationType: colSeparationType, + addEqnNum: addEqnNum, + leqno: leqno + }; +} // Decides on a style for cells in an array according to whether the given +// environment name starts with the letter 'd'. + + +function dCellStyle(envName) { + if (envName.substr(0, 1) === "d") { + return "display"; + } else { + return "text"; + } +} + +var array_htmlBuilder = function htmlBuilder(group, options) { + var r; + var c; + var nr = group.body.length; + var hLinesBeforeRow = group.hLinesBeforeRow; + var nc = 0; + var body = new Array(nr); + var hlines = []; + var ruleThickness = Math.max( // From LaTeX \showthe\arrayrulewidth. Equals 0.04 em. + options.fontMetrics().arrayRuleWidth, options.minRuleThickness // User override. + ); // Horizontal spacing + + var pt = 1 / options.fontMetrics().ptPerEm; + var arraycolsep = 5 * pt; // default value, i.e. \arraycolsep in article.cls + + if (group.colSeparationType && group.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. + var localMultiplier = options.havingStyle(src_Style.SCRIPT).sizeMultiplier; + arraycolsep = 0.2778 * (localMultiplier / options.sizeMultiplier); + } // Vertical spacing + + + var baselineskip = group.colSeparationType === "CD" ? calculateSize({ + number: 3, + unit: "ex" + }, options) : 12 * pt; // see size10.clo + // Default \jot from ltmath.dtx + // TODO(edemaine): allow overriding \jot via \setlength (#687) + + var jot = 3 * pt; + var arrayskip = group.arraystretch * baselineskip; + var arstrutHeight = 0.7 * arrayskip; // \strutbox in ltfsstrc.dtx and + + var arstrutDepth = 0.3 * arrayskip; // \@arstrutbox in lttab.dtx + + var totalHeight = 0; // Set a position for \hline(s) at the top of the array, if any. + + function setHLinePos(hlinesInGap) { + for (var i = 0; i < hlinesInGap.length; ++i) { + if (i > 0) { + totalHeight += 0.25; + } + + hlines.push({ + pos: totalHeight, + isDashed: hlinesInGap[i] + }); + } + } + + setHLinePos(hLinesBeforeRow[0]); + + for (r = 0; r < group.body.length; ++r) { + var inrow = group.body[r]; + var height = arstrutHeight; // \@array adds an \@arstrut + + var depth = arstrutDepth; // to each tow (via the template) + + if (nc < inrow.length) { + nc = inrow.length; + } + + var outrow = new Array(inrow.length); + + for (c = 0; c < inrow.length; ++c) { + var elt = buildGroup(inrow[c], options); + + if (depth < elt.depth) { + depth = elt.depth; + } + + if (height < elt.height) { + height = elt.height; + } + + outrow[c] = elt; + } + + var rowGap = group.rowGaps[r]; + var gap = 0; + + if (rowGap) { + gap = calculateSize(rowGap, options); + + if (gap > 0) { + // \@argarraycr + gap += arstrutDepth; + + if (depth < gap) { + depth = gap; // \@xargarraycr + } + + gap = 0; + } + } // In AMS multiline environments such as aligned and gathered, rows + // correspond to lines that have additional \jot added to the + // \baselineskip via \openup. + + + if (group.addJot) { + depth += jot; + } + + outrow.height = height; + outrow.depth = depth; + totalHeight += height; + outrow.pos = totalHeight; + totalHeight += depth + gap; // \@yargarraycr + + body[r] = outrow; // Set a position for \hline(s), if any. + + setHLinePos(hLinesBeforeRow[r + 1]); + } + + var offset = totalHeight / 2 + options.fontMetrics().axisHeight; + var colDescriptions = group.cols || []; + var cols = []; + var colSep; + var colDescrNum; + var eqnNumSpans = []; + + if (group.addEqnNum) { + // An environment with automatic equation numbers. + // Create node(s) that will trigger CSS counter increment. + for (r = 0; r < nr; ++r) { + var rw = body[r]; + var shift = rw.pos - offset; + var eqnTag = buildCommon.makeSpan(["eqn-num"], [], options); + eqnTag.depth = rw.depth; + eqnTag.height = rw.height; + eqnNumSpans.push({ + type: "elem", + elem: eqnTag, + shift: shift + }); + } + } + + for (c = 0, colDescrNum = 0; // Continue while either there are more columns or more column + // descriptions, so trailing separators don't get lost. + c < nc || colDescrNum < colDescriptions.length; ++c, ++colDescrNum) { + var colDescr = colDescriptions[colDescrNum] || {}; + var firstSeparator = true; + + while (colDescr.type === "separator") { + // If there is more than one separator in a row, add a space + // between them. + if (!firstSeparator) { + colSep = buildCommon.makeSpan(["arraycolsep"], []); + colSep.style.width = makeEm(options.fontMetrics().doubleRuleSep); + cols.push(colSep); + } + + if (colDescr.separator === "|" || colDescr.separator === ":") { + var lineType = colDescr.separator === "|" ? "solid" : "dashed"; + var separator = buildCommon.makeSpan(["vertical-separator"], [], options); + separator.style.height = makeEm(totalHeight); + separator.style.borderRightWidth = makeEm(ruleThickness); + separator.style.borderRightStyle = lineType; + separator.style.margin = "0 " + makeEm(-ruleThickness / 2); + + var _shift = totalHeight - offset; + + if (_shift) { + separator.style.verticalAlign = makeEm(-_shift); + } + + cols.push(separator); + } else { + throw new src_ParseError("Invalid separator type: " + colDescr.separator); + } + + colDescrNum++; + colDescr = colDescriptions[colDescrNum] || {}; + firstSeparator = false; + } + + if (c >= nc) { + continue; + } + + var sepwidth = void 0; + + if (c > 0 || group.hskipBeforeAndAfter) { + sepwidth = utils.deflt(colDescr.pregap, arraycolsep); + + if (sepwidth !== 0) { + colSep = buildCommon.makeSpan(["arraycolsep"], []); + colSep.style.width = makeEm(sepwidth); + cols.push(colSep); + } + } + + var col = []; + + for (r = 0; r < nr; ++r) { + var row = body[r]; + var elem = row[c]; + + if (!elem) { + continue; + } + + var _shift2 = row.pos - offset; + + elem.depth = row.depth; + elem.height = row.height; + col.push({ + type: "elem", + elem: elem, + shift: _shift2 + }); + } + + col = buildCommon.makeVList({ + positionType: "individualShift", + children: col + }, options); + col = buildCommon.makeSpan(["col-align-" + (colDescr.align || "c")], [col]); + cols.push(col); + + if (c < nc - 1 || group.hskipBeforeAndAfter) { + sepwidth = utils.deflt(colDescr.postgap, arraycolsep); + + if (sepwidth !== 0) { + colSep = buildCommon.makeSpan(["arraycolsep"], []); + colSep.style.width = makeEm(sepwidth); + cols.push(colSep); + } + } + } + + body = buildCommon.makeSpan(["mtable"], cols); // Add \hline(s), if any. + + if (hlines.length > 0) { + var line = buildCommon.makeLineSpan("hline", options, ruleThickness); + var dashes = buildCommon.makeLineSpan("hdashline", options, ruleThickness); + var vListElems = [{ + type: "elem", + elem: body, + shift: 0 + }]; + + while (hlines.length > 0) { + var hline = hlines.pop(); + var lineShift = hline.pos - offset; + + if (hline.isDashed) { + vListElems.push({ + type: "elem", + elem: dashes, + shift: lineShift + }); + } else { + vListElems.push({ + type: "elem", + elem: line, + shift: lineShift + }); + } + } + + body = buildCommon.makeVList({ + positionType: "individualShift", + children: vListElems + }, options); + } + + if (!group.addEqnNum) { + return buildCommon.makeSpan(["mord"], [body], options); + } else { + var eqnNumCol = buildCommon.makeVList({ + positionType: "individualShift", + children: eqnNumSpans + }, options); + eqnNumCol = buildCommon.makeSpan(["tag"], [eqnNumCol], options); + return buildCommon.makeFragment([body, eqnNumCol]); + } +}; + +var alignMap = { + c: "center ", + l: "left ", + r: "right " +}; + +var array_mathmlBuilder = function mathmlBuilder(group, options) { + var tbl = []; + var glue = new mathMLTree.MathNode("mtd", [], ["mtr-glue"]); + var tag = new mathMLTree.MathNode("mtd", [], ["mml-eqn-num"]); + + for (var i = 0; i < group.body.length; i++) { + var rw = group.body[i]; + var row = []; + + for (var j = 0; j < rw.length; j++) { + row.push(new mathMLTree.MathNode("mtd", [buildMathML_buildGroup(rw[j], options)])); + } + + if (group.addEqnNum) { + row.unshift(glue); + row.push(glue); + + if (group.leqno) { + row.unshift(tag); + } else { + row.push(tag); + } + } + + tbl.push(new mathMLTree.MathNode("mtr", row)); + } + + var table = new mathMLTree.MathNode("mtable", tbl); // Set column alignment, row spacing, column spacing, and + // array lines by setting attributes on the table element. + // Set the row spacing. In MathML, we specify a gap distance. + // We do not use rowGap[] because MathML automatically increases + // cell height with the height/depth of the element content. + // LaTeX \arraystretch multiplies the row baseline-to-baseline distance. + // We simulate this by adding (arraystretch - 1)em to the gap. This + // does a reasonable job of adjusting arrays containing 1 em tall content. + // The 0.16 and 0.09 values are found emprically. They produce an array + // similar to LaTeX and in which content does not interfere with \hines. + + var gap = group.arraystretch === 0.5 ? 0.1 // {smallmatrix}, {subarray} + : 0.16 + group.arraystretch - 1 + (group.addJot ? 0.09 : 0); + table.setAttribute("rowspacing", makeEm(gap)); // MathML table lines go only between cells. + // To place a line on an edge we'll use , if necessary. + + var menclose = ""; + var align = ""; + + if (group.cols && group.cols.length > 0) { + // Find column alignment, column spacing, and vertical lines. + var cols = group.cols; + var columnLines = ""; + var prevTypeWasAlign = false; + var iStart = 0; + var iEnd = cols.length; + + if (cols[0].type === "separator") { + menclose += "top "; + iStart = 1; + } + + if (cols[cols.length - 1].type === "separator") { + menclose += "bottom "; + iEnd -= 1; + } + + for (var _i = iStart; _i < iEnd; _i++) { + if (cols[_i].type === "align") { + align += alignMap[cols[_i].align]; + + if (prevTypeWasAlign) { + columnLines += "none "; + } + + prevTypeWasAlign = true; + } else if (cols[_i].type === "separator") { + // MathML accepts only single lines between cells. + // So we read only the first of consecutive separators. + if (prevTypeWasAlign) { + columnLines += cols[_i].separator === "|" ? "solid " : "dashed "; + prevTypeWasAlign = false; + } + } + } + + table.setAttribute("columnalign", align.trim()); + + if (/[sd]/.test(columnLines)) { + table.setAttribute("columnlines", columnLines.trim()); + } + } // Set column spacing. + + + if (group.colSeparationType === "align") { + var _cols = group.cols || []; + + var spacing = ""; + + for (var _i2 = 1; _i2 < _cols.length; _i2++) { + spacing += _i2 % 2 ? "0em " : "1em "; + } + + table.setAttribute("columnspacing", spacing.trim()); + } else if (group.colSeparationType === "alignat" || group.colSeparationType === "gather") { + table.setAttribute("columnspacing", "0em"); + } else if (group.colSeparationType === "small") { + table.setAttribute("columnspacing", "0.2778em"); + } else if (group.colSeparationType === "CD") { + table.setAttribute("columnspacing", "0.5em"); + } else { + table.setAttribute("columnspacing", "1em"); + } // Address \hline and \hdashline + + + var rowLines = ""; + var hlines = group.hLinesBeforeRow; + menclose += hlines[0].length > 0 ? "left " : ""; + menclose += hlines[hlines.length - 1].length > 0 ? "right " : ""; + + for (var _i3 = 1; _i3 < hlines.length - 1; _i3++) { + rowLines += hlines[_i3].length === 0 ? "none " // MathML accepts only a single line between rows. Read one element. + : hlines[_i3][0] ? "dashed " : "solid "; + } + + if (/[sd]/.test(rowLines)) { + table.setAttribute("rowlines", rowLines.trim()); + } + + if (menclose !== "") { + table = new mathMLTree.MathNode("menclose", [table]); + table.setAttribute("notation", menclose.trim()); + } + + if (group.arraystretch && group.arraystretch < 1) { + // A small array. Wrap in scriptstyle so row gap is not too large. + table = new mathMLTree.MathNode("mstyle", [table]); + table.setAttribute("scriptlevel", "1"); + } + + return table; +}; // Convenience function for align, align*, aligned, alignat, alignat*, alignedat. + + +var alignedHandler = function alignedHandler(context, args) { + if (context.envName.indexOf("ed") === -1) { + validateAmsEnvironmentContext(context); + } + + var cols = []; + var separationType = context.envName.indexOf("at") > -1 ? "alignat" : "align"; + var res = parseArray(context.parser, { + cols: cols, + addJot: true, + addEqnNum: context.envName === "align" || context.envName === "alignat", + emptySingleRow: true, + colSeparationType: separationType, + maxNumCols: context.envName === "split" ? 2 : undefined, + leqno: context.parser.settings.leqno + }, "display"); // Determining number of columns. + // 1. If the first argument is given, we use it as a number of columns, + // and makes sure that each row doesn't exceed that number. + // 2. Otherwise, just count number of columns = maximum number + // of cells in each row ("aligned" mode -- isAligned will be true). + // + // At the same time, prepend empty group {} at beginning of every second + // cell in each row (starting with second cell) so that operators become + // binary. This behavior is implemented in amsmath's \start@aligned. + + var numMaths; + var numCols = 0; + var emptyGroup = { + type: "ordgroup", + mode: context.mode, + body: [] + }; + + if (args[0] && args[0].type === "ordgroup") { + var arg0 = ""; + + for (var i = 0; i < args[0].body.length; i++) { + var textord = assertNodeType(args[0].body[i], "textord"); + arg0 += textord.text; + } + + numMaths = Number(arg0); + numCols = numMaths * 2; + } + + var isAligned = !numCols; + res.body.forEach(function (row) { + for (var _i4 = 1; _i4 < row.length; _i4 += 2) { + // Modify ordgroup node within styling node + var styling = assertNodeType(row[_i4], "styling"); + var ordgroup = assertNodeType(styling.body[0], "ordgroup"); + ordgroup.body.unshift(emptyGroup); + } + + if (!isAligned) { + // Case 1 + var curMaths = row.length / 2; + + if (numMaths < curMaths) { + throw new src_ParseError("Too many math in a row: " + ("expected " + numMaths + ", but got " + curMaths), row[0]); + } + } else if (numCols < row.length) { + // Case 2 + numCols = row.length; + } + }); // Adjusting alignment. + // In aligned mode, we add one \qquad between columns; + // otherwise we add nothing. + + for (var _i5 = 0; _i5 < numCols; ++_i5) { + var align = "r"; + var pregap = 0; + + if (_i5 % 2 === 1) { + align = "l"; + } else if (_i5 > 0 && isAligned) { + // "aligned" mode. + pregap = 1; // add one \quad + } + + cols[_i5] = { + type: "align", + align: align, + pregap: pregap, + postgap: 0 + }; + } + + res.colSeparationType = isAligned ? "align" : "alignat"; + return res; +}; // Arrays are part of LaTeX, defined in lttab.dtx so its documentation +// is part of the source2e.pdf file of LaTeX2e source documentation. +// {darray} is an {array} environment where cells are set in \displaystyle, +// as defined in nccmath.sty. + + +defineEnvironment({ + type: "array", + names: ["array", "darray"], + props: { + numArgs: 1 + }, + handler: function handler(context, args) { + // Since no types are specified above, the two possibilities are + // - The argument is wrapped in {} or [], in which case Parser's + // parseGroup() returns an "ordgroup" wrapping some symbol node. + // - The argument is a bare symbol node. + var symNode = checkSymbolNodeType(args[0]); + var colalign = symNode ? [args[0]] : assertNodeType(args[0], "ordgroup").body; + var cols = colalign.map(function (nde) { + var node = assertSymbolNodeType(nde); + var ca = node.text; + + if ("lcr".indexOf(ca) !== -1) { + return { + type: "align", + align: ca + }; + } else if (ca === "|") { + return { + type: "separator", + separator: "|" + }; + } else if (ca === ":") { + return { + type: "separator", + separator: ":" + }; + } + + throw new src_ParseError("Unknown column alignment: " + ca, nde); + }); + var res = { + cols: cols, + hskipBeforeAndAfter: true, + // \@preamble in lttab.dtx + maxNumCols: cols.length + }; + return parseArray(context.parser, res, dCellStyle(context.envName)); + }, + htmlBuilder: array_htmlBuilder, + mathmlBuilder: array_mathmlBuilder +}); // The matrix environments of amsmath builds on the array environment +// of LaTeX, which is discussed above. +// The mathtools package adds starred versions of the same environments. +// These have an optional argument to choose left|center|right justification. + +defineEnvironment({ + type: "array", + names: ["matrix", "pmatrix", "bmatrix", "Bmatrix", "vmatrix", "Vmatrix", "matrix*", "pmatrix*", "bmatrix*", "Bmatrix*", "vmatrix*", "Vmatrix*"], + props: { + numArgs: 0 + }, + handler: function handler(context) { + var delimiters = { + "matrix": null, + "pmatrix": ["(", ")"], + "bmatrix": ["[", "]"], + "Bmatrix": ["\\{", "\\}"], + "vmatrix": ["|", "|"], + "Vmatrix": ["\\Vert", "\\Vert"] + }[context.envName.replace("*", "")]; // \hskip -\arraycolsep in amsmath + + var colAlign = "c"; + var payload = { + hskipBeforeAndAfter: false, + cols: [{ + type: "align", + align: colAlign + }] + }; + + if (context.envName.charAt(context.envName.length - 1) === "*") { + // It's one of the mathtools starred functions. + // Parse the optional alignment argument. + var parser = context.parser; + parser.consumeSpaces(); + + if (parser.fetch().text === "[") { + parser.consume(); + parser.consumeSpaces(); + colAlign = parser.fetch().text; + + if ("lcr".indexOf(colAlign) === -1) { + throw new src_ParseError("Expected l or c or r", parser.nextToken); + } + + parser.consume(); + parser.consumeSpaces(); + parser.expect("]"); + parser.consume(); + payload.cols = [{ + type: "align", + align: colAlign + }]; + } + } + + var res = parseArray(context.parser, payload, dCellStyle(context.envName)); // Populate cols with the correct number of column alignment specs. + + var numCols = Math.max.apply(Math, [0].concat(res.body.map(function (row) { + return row.length; + }))); + res.cols = new Array(numCols).fill({ + type: "align", + align: colAlign + }); + return delimiters ? { + type: "leftright", + mode: context.mode, + body: [res], + left: delimiters[0], + right: delimiters[1], + rightColor: undefined // \right uninfluenced by \color in array + + } : res; + }, + htmlBuilder: array_htmlBuilder, + mathmlBuilder: array_mathmlBuilder +}); +defineEnvironment({ + type: "array", + names: ["smallmatrix"], + props: { + numArgs: 0 + }, + handler: function handler(context) { + var payload = { + arraystretch: 0.5 + }; + var res = parseArray(context.parser, payload, "script"); + res.colSeparationType = "small"; + return res; + }, + htmlBuilder: array_htmlBuilder, + mathmlBuilder: array_mathmlBuilder +}); +defineEnvironment({ + type: "array", + names: ["subarray"], + props: { + numArgs: 1 + }, + handler: function handler(context, args) { + // Parsing of {subarray} is similar to {array} + var symNode = checkSymbolNodeType(args[0]); + var colalign = symNode ? [args[0]] : assertNodeType(args[0], "ordgroup").body; + var cols = colalign.map(function (nde) { + var node = assertSymbolNodeType(nde); + var ca = node.text; // {subarray} only recognizes "l" & "c" + + if ("lc".indexOf(ca) !== -1) { + return { + type: "align", + align: ca + }; + } + + throw new src_ParseError("Unknown column alignment: " + ca, nde); + }); + + if (cols.length > 1) { + throw new src_ParseError("{subarray} can contain only one column"); + } + + var res = { + cols: cols, + hskipBeforeAndAfter: false, + arraystretch: 0.5 + }; + res = parseArray(context.parser, res, "script"); + + if (res.body.length > 0 && res.body[0].length > 1) { + throw new src_ParseError("{subarray} can contain only one column"); + } + + return res; + }, + htmlBuilder: array_htmlBuilder, + mathmlBuilder: array_mathmlBuilder +}); // A cases environment (in amsmath.sty) is almost equivalent to +// \def\arraystretch{1.2}% +// \left\{\begin{array}{@{}l@{\quad}l@{}} … \end{array}\right. +// {dcases} is a {cases} environment where cells are set in \displaystyle, +// as defined in mathtools.sty. +// {rcases} is another mathtools environment. It's brace is on the right side. + +defineEnvironment({ + type: "array", + names: ["cases", "dcases", "rcases", "drcases"], + props: { + numArgs: 0 + }, + handler: function handler(context) { + var payload = { + arraystretch: 1.2, + cols: [{ + type: "align", + align: "l", + pregap: 0, + // TODO(kevinb) get the current style. + // For now we use the metrics for TEXT style which is what we were + // doing before. Before attempting to get the current style we + // should look at TeX's behavior especially for \over and matrices. + postgap: 1.0 + /* 1em quad */ + + }, { + type: "align", + align: "l", + pregap: 0, + postgap: 0 + }] + }; + var res = parseArray(context.parser, payload, dCellStyle(context.envName)); + return { + type: "leftright", + mode: context.mode, + body: [res], + left: context.envName.indexOf("r") > -1 ? "." : "\\{", + right: context.envName.indexOf("r") > -1 ? "\\}" : ".", + rightColor: undefined + }; + }, + htmlBuilder: array_htmlBuilder, + mathmlBuilder: array_mathmlBuilder +}); // In the align environment, one uses ampersands, &, to specify number of +// columns in each row, and to locate spacing between each column. +// align gets automatic numbering. align* and aligned do not. +// The alignedat environment can be used in math mode. +// Note that we assume \nomallineskiplimit to be zero, +// so that \strut@ is the same as \strut. + +defineEnvironment({ + type: "array", + names: ["align", "align*", "aligned", "split"], + props: { + numArgs: 0 + }, + handler: alignedHandler, + htmlBuilder: array_htmlBuilder, + mathmlBuilder: array_mathmlBuilder +}); // A gathered environment is like an array environment with one centered +// column, but where rows are considered lines so get \jot line spacing +// and contents are set in \displaystyle. + +defineEnvironment({ + type: "array", + names: ["gathered", "gather", "gather*"], + props: { + numArgs: 0 + }, + handler: function handler(context) { + if (utils.contains(["gather", "gather*"], context.envName)) { + validateAmsEnvironmentContext(context); + } + + var res = { + cols: [{ + type: "align", + align: "c" + }], + addJot: true, + colSeparationType: "gather", + addEqnNum: context.envName === "gather", + emptySingleRow: true, + leqno: context.parser.settings.leqno + }; + return parseArray(context.parser, res, "display"); + }, + htmlBuilder: array_htmlBuilder, + mathmlBuilder: array_mathmlBuilder +}); // alignat environment is like an align environment, but one must explicitly +// specify maximum number of columns in each row, and can adjust spacing between +// each columns. + +defineEnvironment({ + type: "array", + names: ["alignat", "alignat*", "alignedat"], + props: { + numArgs: 1 + }, + handler: alignedHandler, + htmlBuilder: array_htmlBuilder, + mathmlBuilder: array_mathmlBuilder +}); +defineEnvironment({ + type: "array", + names: ["equation", "equation*"], + props: { + numArgs: 0 + }, + handler: function handler(context) { + validateAmsEnvironmentContext(context); + var res = { + addEqnNum: context.envName === "equation", + emptySingleRow: true, + singleRow: true, + maxNumCols: 1, + leqno: context.parser.settings.leqno + }; + return parseArray(context.parser, res, "display"); + }, + htmlBuilder: array_htmlBuilder, + mathmlBuilder: array_mathmlBuilder +}); +defineEnvironment({ + type: "array", + names: ["CD"], + props: { + numArgs: 0 + }, + handler: function handler(context) { + validateAmsEnvironmentContext(context); + return parseCD(context.parser); + }, + htmlBuilder: array_htmlBuilder, + mathmlBuilder: array_mathmlBuilder +}); // Catch \hline outside array environment + +defineFunction({ + type: "text", + // Doesn't matter what this is. + names: ["\\hline", "\\hdashline"], + props: { + numArgs: 0, + allowedInText: true, + allowedInMath: true + }, + handler: function handler(context, args) { + throw new src_ParseError(context.funcName + " valid only within array environment"); + } +}); + +var environments = _environments; +/* harmony default export */ var src_environments = (environments); // All environment definitions should be imported below + + + + // Environment delimiters. HTML/MathML rendering is defined in the corresponding +// defineEnvironment definitions. + +defineFunction({ + type: "environment", + names: ["\\begin", "\\end"], + props: { + numArgs: 1, + argTypes: ["text"] + }, + handler: function handler(_ref, args) { + var parser = _ref.parser, + funcName = _ref.funcName; + var nameGroup = args[0]; + + if (nameGroup.type !== "ordgroup") { + throw new src_ParseError("Invalid environment name", nameGroup); + } + + var envName = ""; + + for (var i = 0; i < nameGroup.body.length; ++i) { + envName += assertNodeType(nameGroup.body[i], "textord").text; + } + + if (funcName === "\\begin") { + // begin...end is similar to left...right + if (!src_environments.hasOwnProperty(envName)) { + throw new src_ParseError("No such environment: " + envName, nameGroup); + } // Build the environment object. Arguments and other information will + // be made available to the begin and end methods using properties. + + + var env = src_environments[envName]; + + var _parser$parseArgument = parser.parseArguments("\\begin{" + envName + "}", env), + _args = _parser$parseArgument.args, + optArgs = _parser$parseArgument.optArgs; + + var context = { + mode: parser.mode, + envName: envName, + parser: parser + }; + var result = env.handler(context, _args, optArgs); + parser.expect("\\end", false); + var endNameToken = parser.nextToken; + var end = assertNodeType(parser.parseFunction(), "environment"); + + if (end.name !== envName) { + throw new src_ParseError("Mismatch: \\begin{" + envName + "} matched by \\end{" + end.name + "}", endNameToken); + } // $FlowFixMe, "environment" handler returns an environment ParseNode + + + return result; + } + + return { + type: "environment", + mode: parser.mode, + name: envName, + nameGroup: nameGroup + }; + } +}); + + + + + + +var mclass_makeSpan = buildCommon.makeSpan; + +function mclass_htmlBuilder(group, options) { + var elements = buildExpression(group.body, options, true); + return mclass_makeSpan([group.mclass], elements, options); +} + +function mclass_mathmlBuilder(group, options) { + var node; + var inner = buildMathML_buildExpression(group.body, options); + + if (group.mclass === "minner") { + return mathMLTree.newDocumentFragment(inner); + } else if (group.mclass === "mord") { + if (group.isCharacterBox) { + node = inner[0]; + node.type = "mi"; + } else { + node = new mathMLTree.MathNode("mi", inner); + } + } else { + if (group.isCharacterBox) { + node = inner[0]; + node.type = "mo"; + } else { + node = new mathMLTree.MathNode("mo", inner); + } // Set spacing based on what is the most likely adjacent atom type. + // See TeXbook p170. + + + if (group.mclass === "mbin") { + node.attributes.lspace = "0.22em"; // medium space + + node.attributes.rspace = "0.22em"; + } else if (group.mclass === "mpunct") { + node.attributes.lspace = "0em"; + node.attributes.rspace = "0.17em"; // thinspace + } else if (group.mclass === "mopen" || group.mclass === "mclose") { + node.attributes.lspace = "0em"; + node.attributes.rspace = "0em"; + } // MathML default space is 5/18 em, so needs no action. + // Ref: https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mo + + } + + return node; +} // Math class commands except \mathop + + +defineFunction({ + type: "mclass", + names: ["\\mathord", "\\mathbin", "\\mathrel", "\\mathopen", "\\mathclose", "\\mathpunct", "\\mathinner"], + props: { + numArgs: 1, + primitive: true + }, + handler: function handler(_ref, args) { + var parser = _ref.parser, + funcName = _ref.funcName; + var body = args[0]; + return { + type: "mclass", + mode: parser.mode, + mclass: "m" + funcName.substr(5), + // TODO(kevinb): don't prefix with 'm' + body: ordargument(body), + isCharacterBox: utils.isCharacterBox(body) + }; + }, + htmlBuilder: mclass_htmlBuilder, + mathmlBuilder: mclass_mathmlBuilder +}); +var binrelClass = function binrelClass(arg) { + // \binrel@ spacing varies with (bin|rel|ord) of the atom in the argument. + // (by rendering separately and with {}s before and after, and measuring + // the change in spacing). We'll do roughly the same by detecting the + // atom type directly. + var atom = arg.type === "ordgroup" && arg.body.length ? arg.body[0] : arg; + + if (atom.type === "atom" && (atom.family === "bin" || atom.family === "rel")) { + return "m" + atom.family; + } else { + return "mord"; + } +}; // \@binrel{x}{y} renders like y but as mbin/mrel/mord if x is mbin/mrel/mord. +// This is equivalent to \binrel@{x}\binrel@@{y} in AMSTeX. + +defineFunction({ + type: "mclass", + names: ["\\@binrel"], + props: { + numArgs: 2 + }, + handler: function handler(_ref2, args) { + var parser = _ref2.parser; + return { + type: "mclass", + mode: parser.mode, + mclass: binrelClass(args[0]), + body: ordargument(args[1]), + isCharacterBox: utils.isCharacterBox(args[1]) + }; + } +}); // Build a relation or stacked op by placing one symbol on top of another + +defineFunction({ + type: "mclass", + names: ["\\stackrel", "\\overset", "\\underset"], + props: { + numArgs: 2 + }, + handler: function handler(_ref3, args) { + var parser = _ref3.parser, + funcName = _ref3.funcName; + var baseArg = args[1]; + var shiftedArg = args[0]; + var mclass; + + if (funcName !== "\\stackrel") { + // LaTeX applies \binrel spacing to \overset and \underset. + mclass = binrelClass(baseArg); + } else { + mclass = "mrel"; // for \stackrel + } + + var baseOp = { + type: "op", + mode: baseArg.mode, + limits: true, + alwaysHandleSupSub: true, + parentIsSupSub: false, + symbol: false, + suppressBaseShift: funcName !== "\\stackrel", + body: ordargument(baseArg) + }; + var supsub = { + type: "supsub", + mode: shiftedArg.mode, + base: baseOp, + sup: funcName === "\\underset" ? null : shiftedArg, + sub: funcName === "\\underset" ? shiftedArg : null + }; + return { + type: "mclass", + mode: parser.mode, + mclass: mclass, + body: [supsub], + isCharacterBox: utils.isCharacterBox(supsub) + }; + }, + htmlBuilder: mclass_htmlBuilder, + mathmlBuilder: mclass_mathmlBuilder +}); +// TODO(kevinb): implement \\sl and \\sc + + + + + + +var font_htmlBuilder = function htmlBuilder(group, options) { + var font = group.font; + var newOptions = options.withFont(font); + return buildGroup(group.body, newOptions); +}; + +var font_mathmlBuilder = function mathmlBuilder(group, options) { + var font = group.font; + var newOptions = options.withFont(font); + return buildMathML_buildGroup(group.body, newOptions); +}; + +var fontAliases = { + "\\Bbb": "\\mathbb", + "\\bold": "\\mathbf", + "\\frak": "\\mathfrak", + "\\bm": "\\boldsymbol" +}; +defineFunction({ + type: "font", + names: [// styles, except \boldsymbol defined below + "\\mathrm", "\\mathit", "\\mathbf", "\\mathnormal", // families + "\\mathbb", "\\mathcal", "\\mathfrak", "\\mathscr", "\\mathsf", "\\mathtt", // aliases, except \bm defined below + "\\Bbb", "\\bold", "\\frak"], + props: { + numArgs: 1, + allowedInArgument: true + }, + handler: function handler(_ref, args) { + var parser = _ref.parser, + funcName = _ref.funcName; + var body = normalizeArgument(args[0]); + var func = funcName; + + if (func in fontAliases) { + func = fontAliases[func]; + } + + return { + type: "font", + mode: parser.mode, + font: func.slice(1), + body: body + }; + }, + htmlBuilder: font_htmlBuilder, + mathmlBuilder: font_mathmlBuilder +}); +defineFunction({ + type: "mclass", + names: ["\\boldsymbol", "\\bm"], + props: { + numArgs: 1 + }, + handler: function handler(_ref2, args) { + var parser = _ref2.parser; + var body = args[0]; + var isCharacterBox = utils.isCharacterBox(body); // amsbsy.sty's \boldsymbol uses \binrel spacing to inherit the + // argument's bin|rel|ord status + + return { + type: "mclass", + mode: parser.mode, + mclass: binrelClass(body), + body: [{ + type: "font", + mode: parser.mode, + font: "boldsymbol", + body: body + }], + isCharacterBox: isCharacterBox + }; + } +}); // Old font changing functions + +defineFunction({ + type: "font", + names: ["\\rm", "\\sf", "\\tt", "\\bf", "\\it", "\\cal"], + props: { + numArgs: 0, + allowedInText: true + }, + handler: function handler(_ref3, args) { + var parser = _ref3.parser, + funcName = _ref3.funcName, + breakOnTokenText = _ref3.breakOnTokenText; + var mode = parser.mode; + var body = parser.parseExpression(true, breakOnTokenText); + var style = "math" + funcName.slice(1); + return { + type: "font", + mode: mode, + font: style, + body: { + type: "ordgroup", + mode: parser.mode, + body: body + } + }; + }, + htmlBuilder: font_htmlBuilder, + mathmlBuilder: font_mathmlBuilder +}); + + + + + + + + + + + +var adjustStyle = function adjustStyle(size, originalStyle) { + // Figure out what style this fraction should be in based on the + // function used + var style = originalStyle; + + if (size === "display") { + // Get display style as a default. + // If incoming style is sub/sup, use style.text() to get correct size. + style = style.id >= src_Style.SCRIPT.id ? style.text() : src_Style.DISPLAY; + } else if (size === "text" && style.size === src_Style.DISPLAY.size) { + // We're in a \tfrac but incoming style is displaystyle, so: + style = src_Style.TEXT; + } else if (size === "script") { + style = src_Style.SCRIPT; + } else if (size === "scriptscript") { + style = src_Style.SCRIPTSCRIPT; + } + + return style; +}; + +var genfrac_htmlBuilder = function htmlBuilder(group, options) { + // Fractions are handled in the TeXbook on pages 444-445, rules 15(a-e). + var style = adjustStyle(group.size, options.style); + var nstyle = style.fracNum(); + var dstyle = style.fracDen(); + var newOptions; + newOptions = options.havingStyle(nstyle); + var numerm = buildGroup(group.numer, newOptions, options); + + if (group.continued) { + // \cfrac inserts a \strut into the numerator. + // Get \strut dimensions from TeXbook page 353. + var hStrut = 8.5 / options.fontMetrics().ptPerEm; + var dStrut = 3.5 / options.fontMetrics().ptPerEm; + numerm.height = numerm.height < hStrut ? hStrut : numerm.height; + numerm.depth = numerm.depth < dStrut ? dStrut : numerm.depth; + } + + newOptions = options.havingStyle(dstyle); + var denomm = buildGroup(group.denom, newOptions, options); + var rule; + var ruleWidth; + var ruleSpacing; + + if (group.hasBarLine) { + if (group.barSize) { + ruleWidth = calculateSize(group.barSize, options); + rule = buildCommon.makeLineSpan("frac-line", options, ruleWidth); + } else { + rule = buildCommon.makeLineSpan("frac-line", options); + } + + ruleWidth = rule.height; + ruleSpacing = rule.height; + } else { + rule = null; + ruleWidth = 0; + ruleSpacing = options.fontMetrics().defaultRuleThickness; + } // Rule 15b + + + var numShift; + var clearance; + var denomShift; + + if (style.size === src_Style.DISPLAY.size || group.size === "display") { + numShift = options.fontMetrics().num1; + + if (ruleWidth > 0) { + clearance = 3 * ruleSpacing; + } else { + clearance = 7 * ruleSpacing; + } + + denomShift = options.fontMetrics().denom1; + } else { + if (ruleWidth > 0) { + numShift = options.fontMetrics().num2; + clearance = ruleSpacing; + } else { + numShift = options.fontMetrics().num3; + clearance = 3 * ruleSpacing; + } + + denomShift = options.fontMetrics().denom2; + } + + var frac; + + if (!rule) { + // Rule 15c + var candidateClearance = numShift - numerm.depth - (denomm.height - denomShift); + + if (candidateClearance < clearance) { + numShift += 0.5 * (clearance - candidateClearance); + denomShift += 0.5 * (clearance - candidateClearance); + } + + frac = buildCommon.makeVList({ + positionType: "individualShift", + children: [{ + type: "elem", + elem: denomm, + shift: denomShift + }, { + type: "elem", + elem: numerm, + shift: -numShift + }] + }, options); + } else { + // Rule 15d + var axisHeight = options.fontMetrics().axisHeight; + + if (numShift - numerm.depth - (axisHeight + 0.5 * ruleWidth) < clearance) { + numShift += clearance - (numShift - numerm.depth - (axisHeight + 0.5 * ruleWidth)); + } + + if (axisHeight - 0.5 * ruleWidth - (denomm.height - denomShift) < clearance) { + denomShift += clearance - (axisHeight - 0.5 * ruleWidth - (denomm.height - denomShift)); + } + + var midShift = -(axisHeight - 0.5 * ruleWidth); + frac = buildCommon.makeVList({ + positionType: "individualShift", + children: [{ + type: "elem", + elem: denomm, + shift: denomShift + }, { + type: "elem", + elem: rule, + shift: midShift + }, { + type: "elem", + elem: numerm, + shift: -numShift + }] + }, options); + } // Since we manually change the style sometimes (with \dfrac or \tfrac), + // account for the possible size change here. + + + newOptions = options.havingStyle(style); + frac.height *= newOptions.sizeMultiplier / options.sizeMultiplier; + frac.depth *= newOptions.sizeMultiplier / options.sizeMultiplier; // Rule 15e + + var delimSize; + + if (style.size === src_Style.DISPLAY.size) { + delimSize = options.fontMetrics().delim1; + } else if (style.size === src_Style.SCRIPTSCRIPT.size) { + delimSize = options.havingStyle(src_Style.SCRIPT).fontMetrics().delim2; + } else { + delimSize = options.fontMetrics().delim2; + } + + var leftDelim; + var rightDelim; + + if (group.leftDelim == null) { + leftDelim = makeNullDelimiter(options, ["mopen"]); + } else { + leftDelim = delimiter.customSizedDelim(group.leftDelim, delimSize, true, options.havingStyle(style), group.mode, ["mopen"]); + } + + if (group.continued) { + rightDelim = buildCommon.makeSpan([]); // zero width for \cfrac + } else if (group.rightDelim == null) { + rightDelim = makeNullDelimiter(options, ["mclose"]); + } else { + rightDelim = delimiter.customSizedDelim(group.rightDelim, delimSize, true, options.havingStyle(style), group.mode, ["mclose"]); + } + + return buildCommon.makeSpan(["mord"].concat(newOptions.sizingClasses(options)), [leftDelim, buildCommon.makeSpan(["mfrac"], [frac]), rightDelim], options); +}; + +var genfrac_mathmlBuilder = function mathmlBuilder(group, options) { + var node = new mathMLTree.MathNode("mfrac", [buildMathML_buildGroup(group.numer, options), buildMathML_buildGroup(group.denom, options)]); + + if (!group.hasBarLine) { + node.setAttribute("linethickness", "0px"); + } else if (group.barSize) { + var ruleWidth = calculateSize(group.barSize, options); + node.setAttribute("linethickness", makeEm(ruleWidth)); + } + + var style = adjustStyle(group.size, options.style); + + if (style.size !== options.style.size) { + node = new mathMLTree.MathNode("mstyle", [node]); + var isDisplay = style.size === src_Style.DISPLAY.size ? "true" : "false"; + node.setAttribute("displaystyle", isDisplay); + node.setAttribute("scriptlevel", "0"); + } + + if (group.leftDelim != null || group.rightDelim != null) { + var withDelims = []; + + if (group.leftDelim != null) { + var leftOp = new mathMLTree.MathNode("mo", [new mathMLTree.TextNode(group.leftDelim.replace("\\", ""))]); + leftOp.setAttribute("fence", "true"); + withDelims.push(leftOp); + } + + withDelims.push(node); + + if (group.rightDelim != null) { + var rightOp = new mathMLTree.MathNode("mo", [new mathMLTree.TextNode(group.rightDelim.replace("\\", ""))]); + rightOp.setAttribute("fence", "true"); + withDelims.push(rightOp); + } + + return makeRow(withDelims); + } + + return node; +}; + +defineFunction({ + type: "genfrac", + names: ["\\dfrac", "\\frac", "\\tfrac", "\\dbinom", "\\binom", "\\tbinom", "\\\\atopfrac", // can’t be entered directly + "\\\\bracefrac", "\\\\brackfrac" // ditto + ], + props: { + numArgs: 2, + allowedInArgument: true + }, + handler: function handler(_ref, args) { + var parser = _ref.parser, + funcName = _ref.funcName; + var numer = args[0]; + var denom = args[1]; + var hasBarLine; + var leftDelim = null; + var rightDelim = null; + var size = "auto"; + + switch (funcName) { + case "\\dfrac": + case "\\frac": + case "\\tfrac": + hasBarLine = true; + break; + + case "\\\\atopfrac": + hasBarLine = false; + break; + + case "\\dbinom": + case "\\binom": + case "\\tbinom": + hasBarLine = false; + leftDelim = "("; + rightDelim = ")"; + break; + + case "\\\\bracefrac": + hasBarLine = false; + leftDelim = "\\{"; + rightDelim = "\\}"; + break; + + case "\\\\brackfrac": + hasBarLine = false; + leftDelim = "["; + rightDelim = "]"; + break; + + default: + throw new Error("Unrecognized genfrac command"); + } + + switch (funcName) { + case "\\dfrac": + case "\\dbinom": + size = "display"; + break; + + case "\\tfrac": + case "\\tbinom": + size = "text"; + break; + } + + return { + type: "genfrac", + mode: parser.mode, + continued: false, + numer: numer, + denom: denom, + hasBarLine: hasBarLine, + leftDelim: leftDelim, + rightDelim: rightDelim, + size: size, + barSize: null + }; + }, + htmlBuilder: genfrac_htmlBuilder, + mathmlBuilder: genfrac_mathmlBuilder +}); +defineFunction({ + type: "genfrac", + names: ["\\cfrac"], + props: { + numArgs: 2 + }, + handler: function handler(_ref2, args) { + var parser = _ref2.parser; + _ref2.funcName; + var numer = args[0]; + var denom = args[1]; + return { + type: "genfrac", + mode: parser.mode, + continued: true, + numer: numer, + denom: denom, + hasBarLine: true, + leftDelim: null, + rightDelim: null, + size: "display", + barSize: null + }; + } +}); // Infix generalized fractions -- these are not rendered directly, but replaced +// immediately by one of the variants above. + +defineFunction({ + type: "infix", + names: ["\\over", "\\choose", "\\atop", "\\brace", "\\brack"], + props: { + numArgs: 0, + infix: true + }, + handler: function handler(_ref3) { + var parser = _ref3.parser, + funcName = _ref3.funcName, + token = _ref3.token; + var replaceWith; + + switch (funcName) { + case "\\over": + replaceWith = "\\frac"; + break; + + case "\\choose": + replaceWith = "\\binom"; + break; + + case "\\atop": + replaceWith = "\\\\atopfrac"; + break; + + case "\\brace": + replaceWith = "\\\\bracefrac"; + break; + + case "\\brack": + replaceWith = "\\\\brackfrac"; + break; + + default: + throw new Error("Unrecognized infix genfrac command"); + } + + return { + type: "infix", + mode: parser.mode, + replaceWith: replaceWith, + token: token + }; + } +}); +var stylArray = ["display", "text", "script", "scriptscript"]; + +var delimFromValue = function delimFromValue(delimString) { + var delim = null; + + if (delimString.length > 0) { + delim = delimString; + delim = delim === "." ? null : delim; + } + + return delim; +}; + +defineFunction({ + type: "genfrac", + names: ["\\genfrac"], + props: { + numArgs: 6, + allowedInArgument: true, + argTypes: ["math", "math", "size", "text", "math", "math"] + }, + handler: function handler(_ref4, args) { + var parser = _ref4.parser; + var numer = args[4]; + var denom = args[5]; // Look into the parse nodes to get the desired delimiters. + + var leftNode = normalizeArgument(args[0]); + var leftDelim = leftNode.type === "atom" && leftNode.family === "open" ? delimFromValue(leftNode.text) : null; + var rightNode = normalizeArgument(args[1]); + var rightDelim = rightNode.type === "atom" && rightNode.family === "close" ? delimFromValue(rightNode.text) : null; + var barNode = assertNodeType(args[2], "size"); + var hasBarLine; + var barSize = null; + + if (barNode.isBlank) { + // \genfrac acts differently than \above. + // \genfrac treats an empty size group as a signal to use a + // standard bar size. \above would see size = 0 and omit the bar. + hasBarLine = true; + } else { + barSize = barNode.value; + hasBarLine = barSize.number > 0; + } // Find out if we want displaystyle, textstyle, etc. + + + var size = "auto"; + var styl = args[3]; + + if (styl.type === "ordgroup") { + if (styl.body.length > 0) { + var textOrd = assertNodeType(styl.body[0], "textord"); + size = stylArray[Number(textOrd.text)]; + } + } else { + styl = assertNodeType(styl, "textord"); + size = stylArray[Number(styl.text)]; + } + + return { + type: "genfrac", + mode: parser.mode, + numer: numer, + denom: denom, + continued: false, + hasBarLine: hasBarLine, + barSize: barSize, + leftDelim: leftDelim, + rightDelim: rightDelim, + size: size + }; + }, + htmlBuilder: genfrac_htmlBuilder, + mathmlBuilder: genfrac_mathmlBuilder +}); // \above is an infix fraction that also defines a fraction bar size. + +defineFunction({ + type: "infix", + names: ["\\above"], + props: { + numArgs: 1, + argTypes: ["size"], + infix: true + }, + handler: function handler(_ref5, args) { + var parser = _ref5.parser; + _ref5.funcName; + var token = _ref5.token; + return { + type: "infix", + mode: parser.mode, + replaceWith: "\\\\abovefrac", + size: assertNodeType(args[0], "size").value, + token: token + }; + } +}); +defineFunction({ + type: "genfrac", + names: ["\\\\abovefrac"], + props: { + numArgs: 3, + argTypes: ["math", "size", "math"] + }, + handler: function handler(_ref6, args) { + var parser = _ref6.parser; + _ref6.funcName; + var numer = args[0]; + var barSize = assert(assertNodeType(args[1], "infix").size); + var denom = args[2]; + var hasBarLine = barSize.number > 0; + return { + type: "genfrac", + mode: parser.mode, + numer: numer, + denom: denom, + continued: false, + hasBarLine: hasBarLine, + barSize: barSize, + leftDelim: null, + rightDelim: null, + size: "auto" + }; + }, + htmlBuilder: genfrac_htmlBuilder, + mathmlBuilder: genfrac_mathmlBuilder +}); + + + + + + + + +// NOTE: Unlike most `htmlBuilder`s, this one handles not only "horizBrace", but +// also "supsub" since an over/underbrace can affect super/subscripting. +var horizBrace_htmlBuilder = function htmlBuilder(grp, options) { + var style = options.style; // Pull out the `ParseNode<"horizBrace">` if `grp` is a "supsub" node. + + var supSubGroup; + var group; + + if (grp.type === "supsub") { + // Ref: LaTeX source2e: }}}}\limits} + // i.e. LaTeX treats the brace similar to an op and passes it + // with \limits, so we need to assign supsub style. + supSubGroup = grp.sup ? buildGroup(grp.sup, options.havingStyle(style.sup()), options) : buildGroup(grp.sub, options.havingStyle(style.sub()), options); + group = assertNodeType(grp.base, "horizBrace"); + } else { + group = assertNodeType(grp, "horizBrace"); + } // Build the base group + + + var body = buildGroup(group.base, options.havingBaseStyle(src_Style.DISPLAY)); // Create the stretchy element + + var braceBody = stretchy.svgSpan(group, options); // Generate the vlist, with the appropriate kerns ┏━━━━━━━━┓ + // This first vlist contains the content and the brace: equation + + var vlist; + + if (group.isOver) { + vlist = buildCommon.makeVList({ + positionType: "firstBaseline", + children: [{ + type: "elem", + elem: body + }, { + type: "kern", + size: 0.1 + }, { + type: "elem", + elem: braceBody + }] + }, options); // $FlowFixMe: Replace this with passing "svg-align" into makeVList. + + vlist.children[0].children[0].children[1].classes.push("svg-align"); + } else { + vlist = buildCommon.makeVList({ + positionType: "bottom", + positionData: body.depth + 0.1 + braceBody.height, + children: [{ + type: "elem", + elem: braceBody + }, { + type: "kern", + size: 0.1 + }, { + type: "elem", + elem: body + }] + }, options); // $FlowFixMe: Replace this with passing "svg-align" into makeVList. + + vlist.children[0].children[0].children[0].classes.push("svg-align"); + } + + if (supSubGroup) { + // To write the supsub, wrap the first vlist in another vlist: + // They can't all go in the same vlist, because the note might be + // wider than the equation. We want the equation to control the + // brace width. + // note long note long note + // ┏━━━━━━━━┓ or ┏━━━┓ not ┏━━━━━━━━━┓ + // equation eqn eqn + var vSpan = buildCommon.makeSpan(["mord", group.isOver ? "mover" : "munder"], [vlist], options); + + if (group.isOver) { + vlist = buildCommon.makeVList({ + positionType: "firstBaseline", + children: [{ + type: "elem", + elem: vSpan + }, { + type: "kern", + size: 0.2 + }, { + type: "elem", + elem: supSubGroup + }] + }, options); + } else { + vlist = buildCommon.makeVList({ + positionType: "bottom", + positionData: vSpan.depth + 0.2 + supSubGroup.height + supSubGroup.depth, + children: [{ + type: "elem", + elem: supSubGroup + }, { + type: "kern", + size: 0.2 + }, { + type: "elem", + elem: vSpan + }] + }, options); + } + } + + return buildCommon.makeSpan(["mord", group.isOver ? "mover" : "munder"], [vlist], options); +}; + +var horizBrace_mathmlBuilder = function mathmlBuilder(group, options) { + var accentNode = stretchy.mathMLnode(group.label); + return new mathMLTree.MathNode(group.isOver ? "mover" : "munder", [buildMathML_buildGroup(group.base, options), accentNode]); +}; // Horizontal stretchy braces + + +defineFunction({ + type: "horizBrace", + names: ["\\overbrace", "\\underbrace"], + props: { + numArgs: 1 + }, + handler: function handler(_ref, args) { + var parser = _ref.parser, + funcName = _ref.funcName; + return { + type: "horizBrace", + mode: parser.mode, + label: funcName, + isOver: /^\\over/.test(funcName), + base: args[0] + }; + }, + htmlBuilder: horizBrace_htmlBuilder, + mathmlBuilder: horizBrace_mathmlBuilder +}); + + + + + + +defineFunction({ + type: "href", + names: ["\\href"], + props: { + numArgs: 2, + argTypes: ["url", "original"], + allowedInText: true + }, + handler: function handler(_ref, args) { + var parser = _ref.parser; + var body = args[1]; + var href = assertNodeType(args[0], "url").url; + + if (!parser.settings.isTrusted({ + command: "\\href", + url: href + })) { + return parser.formatUnsupportedCmd("\\href"); + } + + return { + type: "href", + mode: parser.mode, + href: href, + body: ordargument(body) + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + var elements = buildExpression(group.body, options, false); + return buildCommon.makeAnchor(group.href, [], elements, options); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var math = buildExpressionRow(group.body, options); + + if (!(math instanceof MathNode)) { + math = new MathNode("mrow", [math]); + } + + math.setAttribute("href", group.href); + return math; + } +}); +defineFunction({ + type: "href", + names: ["\\url"], + props: { + numArgs: 1, + argTypes: ["url"], + allowedInText: true + }, + handler: function handler(_ref2, args) { + var parser = _ref2.parser; + var href = assertNodeType(args[0], "url").url; + + if (!parser.settings.isTrusted({ + command: "\\url", + url: href + })) { + return parser.formatUnsupportedCmd("\\url"); + } + + var chars = []; + + for (var i = 0; i < href.length; i++) { + var c = href[i]; + + if (c === "~") { + c = "\\textasciitilde"; + } + + chars.push({ + type: "textord", + mode: "text", + text: c + }); + } + + var body = { + type: "text", + mode: parser.mode, + font: "\\texttt", + body: chars + }; + return { + type: "href", + mode: parser.mode, + href: href, + body: ordargument(body) + }; + } +}); + + + + + // \hbox is provided for compatibility with LaTeX \vcenter. +// In LaTeX, \vcenter can act only on a box, as in +// \vcenter{\hbox{$\frac{a+b}{\dfrac{c}{d}}$}} +// This function by itself doesn't do anything but prevent a soft line break. + +defineFunction({ + type: "hbox", + names: ["\\hbox"], + props: { + numArgs: 1, + argTypes: ["text"], + allowedInText: true, + primitive: true + }, + handler: function handler(_ref, args) { + var parser = _ref.parser; + return { + type: "hbox", + mode: parser.mode, + body: ordargument(args[0]) + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + var elements = buildExpression(group.body, options, false); + return buildCommon.makeFragment(elements); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + return new mathMLTree.MathNode("mrow", buildMathML_buildExpression(group.body, options)); + } +}); + + + + + + +defineFunction({ + type: "html", + names: ["\\htmlClass", "\\htmlId", "\\htmlStyle", "\\htmlData"], + props: { + numArgs: 2, + argTypes: ["raw", "original"], + allowedInText: true + }, + handler: function handler(_ref, args) { + var parser = _ref.parser, + funcName = _ref.funcName; + _ref.token; + var value = assertNodeType(args[0], "raw").string; + var body = args[1]; + + if (parser.settings.strict) { + parser.settings.reportNonstrict("htmlExtension", "HTML extension is disabled on strict mode"); + } + + var trustContext; + var attributes = {}; + + switch (funcName) { + case "\\htmlClass": + attributes.class = value; + trustContext = { + command: "\\htmlClass", + class: value + }; + break; + + case "\\htmlId": + attributes.id = value; + trustContext = { + command: "\\htmlId", + id: value + }; + break; + + case "\\htmlStyle": + attributes.style = value; + trustContext = { + command: "\\htmlStyle", + style: value + }; + break; + + case "\\htmlData": + { + var data = value.split(","); + + for (var i = 0; i < data.length; i++) { + var keyVal = data[i].split("="); + + if (keyVal.length !== 2) { + throw new src_ParseError("Error parsing key-value for \\htmlData"); + } + + attributes["data-" + keyVal[0].trim()] = keyVal[1].trim(); + } + + trustContext = { + command: "\\htmlData", + attributes: attributes + }; + break; + } + + default: + throw new Error("Unrecognized html command"); + } + + if (!parser.settings.isTrusted(trustContext)) { + return parser.formatUnsupportedCmd(funcName); + } + + return { + type: "html", + mode: parser.mode, + attributes: attributes, + body: ordargument(body) + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + var elements = buildExpression(group.body, options, false); + var classes = ["enclosing"]; + + if (group.attributes.class) { + classes.push.apply(classes, group.attributes.class.trim().split(/\s+/)); + } + + var span = buildCommon.makeSpan(classes, elements, options); + + for (var attr in group.attributes) { + if (attr !== "class" && group.attributes.hasOwnProperty(attr)) { + span.setAttribute(attr, group.attributes[attr]); + } + } + + return span; + }, + mathmlBuilder: function mathmlBuilder(group, options) { + return buildExpressionRow(group.body, options); + } +}); + + + + +defineFunction({ + type: "htmlmathml", + names: ["\\html@mathml"], + props: { + numArgs: 2, + allowedInText: true + }, + handler: function handler(_ref, args) { + var parser = _ref.parser; + return { + type: "htmlmathml", + mode: parser.mode, + html: ordargument(args[0]), + mathml: ordargument(args[1]) + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + var elements = buildExpression(group.html, options, false); + return buildCommon.makeFragment(elements); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + return buildExpressionRow(group.mathml, options); + } +}); + + + + + + + +var sizeData = function sizeData(str) { + if (/^[-+]? *(\d+(\.\d*)?|\.\d+)$/.test(str)) { + // str is a number with no unit specified. + // default unit is bp, per graphix package. + return { + number: +str, + unit: "bp" + }; + } else { + var match = /([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/.exec(str); + + if (!match) { + throw new src_ParseError("Invalid size: '" + str + "' in \\includegraphics"); + } + + var data = { + number: +(match[1] + match[2]), + // sign + magnitude, cast to number + unit: match[3] + }; + + if (!validUnit(data)) { + throw new src_ParseError("Invalid unit: '" + data.unit + "' in \\includegraphics."); + } + + return data; + } +}; + +defineFunction({ + type: "includegraphics", + names: ["\\includegraphics"], + props: { + numArgs: 1, + numOptionalArgs: 1, + argTypes: ["raw", "url"], + allowedInText: false + }, + handler: function handler(_ref, args, optArgs) { + var parser = _ref.parser; + var width = { + number: 0, + unit: "em" + }; + var height = { + number: 0.9, + unit: "em" + }; // sorta character sized. + + var totalheight = { + number: 0, + unit: "em" + }; + var alt = ""; + + if (optArgs[0]) { + var attributeStr = assertNodeType(optArgs[0], "raw").string; // Parser.js does not parse key/value pairs. We get a string. + + var attributes = attributeStr.split(","); + + for (var i = 0; i < attributes.length; i++) { + var keyVal = attributes[i].split("="); + + if (keyVal.length === 2) { + var str = keyVal[1].trim(); + + switch (keyVal[0].trim()) { + case "alt": + alt = str; + break; + + case "width": + width = sizeData(str); + break; + + case "height": + height = sizeData(str); + break; + + case "totalheight": + totalheight = sizeData(str); + break; + + default: + throw new src_ParseError("Invalid key: '" + keyVal[0] + "' in \\includegraphics."); + } + } + } + } + + var src = assertNodeType(args[0], "url").url; + + if (alt === "") { + // No alt given. Use the file name. Strip away the path. + alt = src; + alt = alt.replace(/^.*[\\/]/, ''); + alt = alt.substring(0, alt.lastIndexOf('.')); + } + + if (!parser.settings.isTrusted({ + command: "\\includegraphics", + url: src + })) { + return parser.formatUnsupportedCmd("\\includegraphics"); + } + + return { + type: "includegraphics", + mode: parser.mode, + alt: alt, + width: width, + height: height, + totalheight: totalheight, + src: src + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + var height = calculateSize(group.height, options); + var depth = 0; + + if (group.totalheight.number > 0) { + depth = calculateSize(group.totalheight, options) - height; + } + + var width = 0; + + if (group.width.number > 0) { + width = calculateSize(group.width, options); + } + + var style = { + height: makeEm(height + depth) + }; + + if (width > 0) { + style.width = makeEm(width); + } + + if (depth > 0) { + style.verticalAlign = makeEm(-depth); + } + + var node = new Img(group.src, group.alt, style); + node.height = height; + node.depth = depth; + return node; + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var node = new mathMLTree.MathNode("mglyph", []); + node.setAttribute("alt", group.alt); + var height = calculateSize(group.height, options); + var depth = 0; + + if (group.totalheight.number > 0) { + depth = calculateSize(group.totalheight, options) - height; + node.setAttribute("valign", makeEm(-depth)); + } + + node.setAttribute("height", makeEm(height + depth)); + + if (group.width.number > 0) { + var width = calculateSize(group.width, options); + node.setAttribute("width", makeEm(width)); + } + + node.setAttribute("src", group.src); + return node; + } +}); +// Horizontal spacing commands + + + + + // TODO: \hskip and \mskip should support plus and minus in lengths + +defineFunction({ + type: "kern", + names: ["\\kern", "\\mkern", "\\hskip", "\\mskip"], + props: { + numArgs: 1, + argTypes: ["size"], + primitive: true, + allowedInText: true + }, + handler: function handler(_ref, args) { + var parser = _ref.parser, + funcName = _ref.funcName; + var size = assertNodeType(args[0], "size"); + + if (parser.settings.strict) { + var mathFunction = funcName[1] === 'm'; // \mkern, \mskip + + var muUnit = size.value.unit === 'mu'; + + if (mathFunction) { + if (!muUnit) { + parser.settings.reportNonstrict("mathVsTextUnits", "LaTeX's " + funcName + " supports only mu units, " + ("not " + size.value.unit + " units")); + } + + if (parser.mode !== "math") { + parser.settings.reportNonstrict("mathVsTextUnits", "LaTeX's " + funcName + " works only in math mode"); + } + } else { + // !mathFunction + if (muUnit) { + parser.settings.reportNonstrict("mathVsTextUnits", "LaTeX's " + funcName + " doesn't support mu units"); + } + } + } + + return { + type: "kern", + mode: parser.mode, + dimension: size.value + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + return buildCommon.makeGlue(group.dimension, options); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var dimension = calculateSize(group.dimension, options); + return new mathMLTree.SpaceNode(dimension); + } +}); +// Horizontal overlap functions + + + + + + +defineFunction({ + type: "lap", + names: ["\\mathllap", "\\mathrlap", "\\mathclap"], + props: { + numArgs: 1, + allowedInText: true + }, + handler: function handler(_ref, args) { + var parser = _ref.parser, + funcName = _ref.funcName; + var body = args[0]; + return { + type: "lap", + mode: parser.mode, + alignment: funcName.slice(5), + body: body + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + // mathllap, mathrlap, mathclap + var inner; + + if (group.alignment === "clap") { + // ref: https://www.math.lsu.edu/~aperlis/publications/mathclap/ + inner = buildCommon.makeSpan([], [buildGroup(group.body, options)]); // wrap, since CSS will center a .clap > .inner > span + + inner = buildCommon.makeSpan(["inner"], [inner], options); + } else { + inner = buildCommon.makeSpan(["inner"], [buildGroup(group.body, options)]); + } + + var fix = buildCommon.makeSpan(["fix"], []); + var node = buildCommon.makeSpan([group.alignment], [inner, fix], options); // At this point, we have correctly set horizontal alignment of the + // two items involved in the lap. + // Next, use a strut to set the height of the HTML bounding box. + // Otherwise, a tall argument may be misplaced. + // This code resolved issue #1153 + + var strut = buildCommon.makeSpan(["strut"]); + strut.style.height = makeEm(node.height + node.depth); + + if (node.depth) { + strut.style.verticalAlign = makeEm(-node.depth); + } + + node.children.unshift(strut); // Next, prevent vertical misplacement when next to something tall. + // This code resolves issue #1234 + + node = buildCommon.makeSpan(["thinbox"], [node], options); + return buildCommon.makeSpan(["mord", "vbox"], [node], options); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + // mathllap, mathrlap, mathclap + var node = new mathMLTree.MathNode("mpadded", [buildMathML_buildGroup(group.body, options)]); + + if (group.alignment !== "rlap") { + var offset = group.alignment === "llap" ? "-1" : "-0.5"; + node.setAttribute("lspace", offset + "width"); + } + + node.setAttribute("width", "0px"); + return node; + } +}); + + // Switching from text mode back to math mode + +defineFunction({ + type: "styling", + names: ["\\(", "$"], + props: { + numArgs: 0, + allowedInText: true, + allowedInMath: false + }, + handler: function handler(_ref, args) { + var funcName = _ref.funcName, + parser = _ref.parser; + var outerMode = parser.mode; + parser.switchMode("math"); + var close = funcName === "\\(" ? "\\)" : "$"; + var body = parser.parseExpression(false, close); + parser.expect(close); + parser.switchMode(outerMode); + return { + type: "styling", + mode: parser.mode, + style: "text", + body: body + }; + } +}); // Check for extra closing math delimiters + +defineFunction({ + type: "text", + // Doesn't matter what this is. + names: ["\\)", "\\]"], + props: { + numArgs: 0, + allowedInText: true, + allowedInMath: false + }, + handler: function handler(context, args) { + throw new src_ParseError("Mismatched " + context.funcName); + } +}); + + + + + + +var chooseMathStyle = function chooseMathStyle(group, options) { + switch (options.style.size) { + case src_Style.DISPLAY.size: + return group.display; + + case src_Style.TEXT.size: + return group.text; + + case src_Style.SCRIPT.size: + return group.script; + + case src_Style.SCRIPTSCRIPT.size: + return group.scriptscript; + + default: + return group.text; + } +}; + +defineFunction({ + type: "mathchoice", + names: ["\\mathchoice"], + props: { + numArgs: 4, + primitive: true + }, + handler: function handler(_ref, args) { + var parser = _ref.parser; + return { + type: "mathchoice", + mode: parser.mode, + display: ordargument(args[0]), + text: ordargument(args[1]), + script: ordargument(args[2]), + scriptscript: ordargument(args[3]) + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + var body = chooseMathStyle(group, options); + var elements = buildExpression(body, options, false); + return buildCommon.makeFragment(elements); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var body = chooseMathStyle(group, options); + return buildExpressionRow(body, options); + } +}); + + + + // For an operator with limits, assemble the base, sup, and sub into a span. + +var assembleSupSub = function assembleSupSub(base, supGroup, subGroup, options, style, slant, baseShift) { + base = buildCommon.makeSpan([], [base]); + var subIsSingleCharacter = subGroup && utils.isCharacterBox(subGroup); + var sub; + var sup; // We manually have to handle the superscripts and subscripts. This, + // aside from the kern calculations, is copied from supsub. + + if (supGroup) { + var elem = buildGroup(supGroup, options.havingStyle(style.sup()), options); + sup = { + elem: elem, + kern: Math.max(options.fontMetrics().bigOpSpacing1, options.fontMetrics().bigOpSpacing3 - elem.depth) + }; + } + + if (subGroup) { + var _elem = buildGroup(subGroup, options.havingStyle(style.sub()), options); + + sub = { + elem: _elem, + kern: Math.max(options.fontMetrics().bigOpSpacing2, options.fontMetrics().bigOpSpacing4 - _elem.height) + }; + } // Build the final group as a vlist of the possible subscript, base, + // and possible superscript. + + + var finalGroup; + + if (sup && sub) { + var bottom = options.fontMetrics().bigOpSpacing5 + sub.elem.height + sub.elem.depth + sub.kern + base.depth + baseShift; + finalGroup = buildCommon.makeVList({ + positionType: "bottom", + positionData: bottom, + children: [{ + type: "kern", + size: options.fontMetrics().bigOpSpacing5 + }, { + type: "elem", + elem: sub.elem, + marginLeft: makeEm(-slant) + }, { + type: "kern", + size: sub.kern + }, { + type: "elem", + elem: base + }, { + type: "kern", + size: sup.kern + }, { + type: "elem", + elem: sup.elem, + marginLeft: makeEm(slant) + }, { + type: "kern", + size: options.fontMetrics().bigOpSpacing5 + }] + }, options); + } else if (sub) { + var top = base.height - baseShift; // 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. + + finalGroup = buildCommon.makeVList({ + positionType: "top", + positionData: top, + children: [{ + type: "kern", + size: options.fontMetrics().bigOpSpacing5 + }, { + type: "elem", + elem: sub.elem, + marginLeft: makeEm(-slant) + }, { + type: "kern", + size: sub.kern + }, { + type: "elem", + elem: base + }] + }, options); + } else if (sup) { + var _bottom = base.depth + baseShift; + + finalGroup = buildCommon.makeVList({ + positionType: "bottom", + positionData: _bottom, + children: [{ + type: "elem", + elem: base + }, { + type: "kern", + size: sup.kern + }, { + type: "elem", + elem: sup.elem, + marginLeft: makeEm(slant) + }, { + type: "kern", + size: options.fontMetrics().bigOpSpacing5 + }] + }, options); + } else { + // This case probably shouldn't occur (this would mean the + // supsub was sending us a group with no superscript or + // subscript) but be safe. + return base; + } + + var parts = [finalGroup]; + + if (sub && slant !== 0 && !subIsSingleCharacter) { + // A negative margin-left was applied to the lower limit. + // Avoid an overlap by placing a spacer on the left on the group. + var spacer = buildCommon.makeSpan(["mspace"], [], options); + spacer.style.marginRight = makeEm(slant); + parts.unshift(spacer); + } + + return buildCommon.makeSpan(["mop", "op-limits"], parts, options); +}; +// Limits, symbols + + + + + + + + + + + +// Most operators have a large successor symbol, but these don't. +var noSuccessor = ["\\smallint"]; // NOTE: Unlike most `htmlBuilder`s, this one handles not only "op", but also +// "supsub" since some of them (like \int) can affect super/subscripting. + +var op_htmlBuilder = function htmlBuilder(grp, options) { + // Operators are handled in the TeXbook pg. 443-444, rule 13(a). + var supGroup; + var subGroup; + var hasLimits = false; + var group; + + if (grp.type === "supsub") { + // If we have limits, supsub will pass us its group to handle. Pull + // out the superscript and subscript and set the group to the op in + // its base. + supGroup = grp.sup; + subGroup = grp.sub; + group = assertNodeType(grp.base, "op"); + hasLimits = true; + } else { + group = assertNodeType(grp, "op"); + } + + var style = options.style; + var large = false; + + if (style.size === src_Style.DISPLAY.size && group.symbol && !utils.contains(noSuccessor, group.name)) { + // Most symbol operators get larger in displaystyle (rule 13) + large = true; + } + + var base; + + if (group.symbol) { + // If this is a symbol, create the symbol. + var fontName = large ? "Size2-Regular" : "Size1-Regular"; + var stash = ""; + + if (group.name === "\\oiint" || group.name === "\\oiiint") { + // No font glyphs yet, so use a glyph w/o the oval. + // TODO: When font glyphs are available, delete this code. + stash = group.name.substr(1); + group.name = stash === "oiint" ? "\\iint" : "\\iiint"; + } + + base = buildCommon.makeSymbol(group.name, fontName, "math", options, ["mop", "op-symbol", large ? "large-op" : "small-op"]); + + if (stash.length > 0) { + // We're in \oiint or \oiiint. Overlay the oval. + // TODO: When font glyphs are available, delete this code. + var italic = base.italic; + var oval = buildCommon.staticSvg(stash + "Size" + (large ? "2" : "1"), options); + base = buildCommon.makeVList({ + positionType: "individualShift", + children: [{ + type: "elem", + elem: base, + shift: 0 + }, { + type: "elem", + elem: oval, + shift: large ? 0.08 : 0 + }] + }, options); + group.name = "\\" + stash; + base.classes.unshift("mop"); // $FlowFixMe + + base.italic = italic; + } + } else if (group.body) { + // If this is a list, compose that list. + var inner = buildExpression(group.body, options, true); + + if (inner.length === 1 && inner[0] instanceof SymbolNode) { + base = inner[0]; + base.classes[0] = "mop"; // replace old mclass + } else { + base = buildCommon.makeSpan(["mop"], inner, options); + } + } else { + // Otherwise, this is a text operator. Build the text from the + // operator's name. + var output = []; + + for (var i = 1; i < group.name.length; i++) { + output.push(buildCommon.mathsym(group.name[i], group.mode, options)); + } + + base = buildCommon.makeSpan(["mop"], output, options); + } // If content of op is a single symbol, shift it vertically. + + + var baseShift = 0; + var slant = 0; + + if ((base instanceof SymbolNode || group.name === "\\oiint" || group.name === "\\oiiint") && !group.suppressBaseShift) { + // We suppress the shift of the base of \overset and \underset. Otherwise, + // 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 - options.fontMetrics().axisHeight; // The slant of the symbol is just its italic correction. + // $FlowFixMe + + slant = base.italic; + } + + if (hasLimits) { + return assembleSupSub(base, supGroup, subGroup, options, style, slant, baseShift); + } else { + if (baseShift) { + base.style.position = "relative"; + base.style.top = makeEm(baseShift); + } + + return base; + } +}; + +var op_mathmlBuilder = function mathmlBuilder(group, options) { + var node; + + if (group.symbol) { + // This is a symbol. Just add the symbol. + node = new MathNode("mo", [makeText(group.name, group.mode)]); + + if (utils.contains(noSuccessor, group.name)) { + node.setAttribute("largeop", "false"); + } + } else if (group.body) { + // This is an operator with children. Add them. + node = new MathNode("mo", buildMathML_buildExpression(group.body, options)); + } else { + // This is a text operator. Add all of the characters from the + // operator's name. + node = new MathNode("mi", [new TextNode(group.name.slice(1))]); // Append an ⁡. + // ref: https://www.w3.org/TR/REC-MathML/chap3_2.html#sec3.2.4 + + var operator = new MathNode("mo", [makeText("\u2061", "text")]); + + if (group.parentIsSupSub) { + node = new MathNode("mrow", [node, operator]); + } else { + node = newDocumentFragment([node, operator]); + } + } + + return node; +}; + +var singleCharBigOps = { + "\u220F": "\\prod", + "\u2210": "\\coprod", + "\u2211": "\\sum", + "\u22C0": "\\bigwedge", + "\u22C1": "\\bigvee", + "\u22C2": "\\bigcap", + "\u22C3": "\\bigcup", + "\u2A00": "\\bigodot", + "\u2A01": "\\bigoplus", + "\u2A02": "\\bigotimes", + "\u2A04": "\\biguplus", + "\u2A06": "\\bigsqcup" +}; +defineFunction({ + type: "op", + names: ["\\coprod", "\\bigvee", "\\bigwedge", "\\biguplus", "\\bigcap", "\\bigcup", "\\intop", "\\prod", "\\sum", "\\bigotimes", "\\bigoplus", "\\bigodot", "\\bigsqcup", "\\smallint", "\u220F", "\u2210", "\u2211", "\u22C0", "\u22C1", "\u22C2", "\u22C3", "\u2A00", "\u2A01", "\u2A02", "\u2A04", "\u2A06"], + props: { + numArgs: 0 + }, + handler: function handler(_ref, args) { + var parser = _ref.parser, + funcName = _ref.funcName; + var fName = funcName; + + if (fName.length === 1) { + fName = singleCharBigOps[fName]; + } + + return { + type: "op", + mode: parser.mode, + limits: true, + parentIsSupSub: false, + symbol: true, + name: fName + }; + }, + htmlBuilder: op_htmlBuilder, + mathmlBuilder: op_mathmlBuilder +}); // Note: calling defineFunction with a type that's already been defined only +// works because the same htmlBuilder and mathmlBuilder are being used. + +defineFunction({ + type: "op", + names: ["\\mathop"], + props: { + numArgs: 1, + primitive: true + }, + handler: function handler(_ref2, args) { + var parser = _ref2.parser; + var body = args[0]; + return { + type: "op", + mode: parser.mode, + limits: false, + parentIsSupSub: false, + symbol: false, + body: ordargument(body) + }; + }, + htmlBuilder: op_htmlBuilder, + mathmlBuilder: op_mathmlBuilder +}); // There are 2 flags for operators; whether they produce limits in +// displaystyle, and whether they are symbols and should grow in +// displaystyle. These four groups cover the four possible choices. + +var singleCharIntegrals = { + "\u222B": "\\int", + "\u222C": "\\iint", + "\u222D": "\\iiint", + "\u222E": "\\oint", + "\u222F": "\\oiint", + "\u2230": "\\oiiint" +}; // No limits, not symbols + +defineFunction({ + type: "op", + names: ["\\arcsin", "\\arccos", "\\arctan", "\\arctg", "\\arcctg", "\\arg", "\\ch", "\\cos", "\\cosec", "\\cosh", "\\cot", "\\cotg", "\\coth", "\\csc", "\\ctg", "\\cth", "\\deg", "\\dim", "\\exp", "\\hom", "\\ker", "\\lg", "\\ln", "\\log", "\\sec", "\\sin", "\\sinh", "\\sh", "\\tan", "\\tanh", "\\tg", "\\th"], + props: { + numArgs: 0 + }, + handler: function handler(_ref3) { + var parser = _ref3.parser, + funcName = _ref3.funcName; + return { + type: "op", + mode: parser.mode, + limits: false, + parentIsSupSub: false, + symbol: false, + name: funcName + }; + }, + htmlBuilder: op_htmlBuilder, + mathmlBuilder: op_mathmlBuilder +}); // Limits, not symbols + +defineFunction({ + type: "op", + names: ["\\det", "\\gcd", "\\inf", "\\lim", "\\max", "\\min", "\\Pr", "\\sup"], + props: { + numArgs: 0 + }, + handler: function handler(_ref4) { + var parser = _ref4.parser, + funcName = _ref4.funcName; + return { + type: "op", + mode: parser.mode, + limits: true, + parentIsSupSub: false, + symbol: false, + name: funcName + }; + }, + htmlBuilder: op_htmlBuilder, + mathmlBuilder: op_mathmlBuilder +}); // No limits, symbols + +defineFunction({ + type: "op", + names: ["\\int", "\\iint", "\\iiint", "\\oint", "\\oiint", "\\oiiint", "\u222B", "\u222C", "\u222D", "\u222E", "\u222F", "\u2230"], + props: { + numArgs: 0 + }, + handler: function handler(_ref5) { + var parser = _ref5.parser, + funcName = _ref5.funcName; + var fName = funcName; + + if (fName.length === 1) { + fName = singleCharIntegrals[fName]; + } + + return { + type: "op", + mode: parser.mode, + limits: false, + parentIsSupSub: false, + symbol: true, + name: fName + }; + }, + htmlBuilder: op_htmlBuilder, + mathmlBuilder: op_mathmlBuilder +}); + + +/** + * All registered global/built-in macros. + * `macros.js` exports this same dictionary again and makes it public. + * `Parser.js` requires this dictionary via `macros.js`. + */ +var _macros = {}; // This function might one day accept an additional argument and do more things. + +function defineMacro(name, body) { + _macros[name] = body; +} + + + + + + + + + +// NOTE: Unlike most `htmlBuilder`s, this one handles not only +// "operatorname", but also "supsub" since \operatorname* can +// affect super/subscripting. +var operatorname_htmlBuilder = function htmlBuilder(grp, options) { + // Operators are handled in the TeXbook pg. 443-444, rule 13(a). + var supGroup; + var subGroup; + var hasLimits = false; + var group; + + if (grp.type === "supsub") { + // If we have limits, supsub will pass us its group to handle. Pull + // out the superscript and subscript and set the group to the op in + // its base. + supGroup = grp.sup; + subGroup = grp.sub; + group = assertNodeType(grp.base, "operatorname"); + hasLimits = true; + } else { + group = assertNodeType(grp, "operatorname"); + } + + var base; + + if (group.body.length > 0) { + var body = group.body.map(function (child) { + // $FlowFixMe: Check if the node has a string `text` property. + var childText = child.text; + + if (typeof childText === "string") { + return { + type: "textord", + mode: child.mode, + text: childText + }; + } else { + return child; + } + }); // Consolidate function names into symbol characters. + + var expression = buildExpression(body, options.withFont("mathrm"), true); + + for (var i = 0; i < expression.length; i++) { + var child = expression[i]; + + if (child instanceof SymbolNode) { + // Per amsopn package, + // change minus to hyphen and \ast to asterisk + child.text = child.text.replace(/\u2212/, "-").replace(/\u2217/, "*"); + } + } + + base = buildCommon.makeSpan(["mop"], expression, options); + } else { + base = buildCommon.makeSpan(["mop"], [], options); + } + + if (hasLimits) { + return assembleSupSub(base, supGroup, subGroup, options, options.style, 0, 0); + } else { + return base; + } +}; + +var operatorname_mathmlBuilder = function mathmlBuilder(group, options) { + // The steps taken here are similar to the html version. + var expression = buildMathML_buildExpression(group.body, options.withFont("mathrm")); // Is expression a string or has it something like a fraction? + + var isAllString = true; // default + + for (var i = 0; i < expression.length; i++) { + var node = expression[i]; + + if (node instanceof mathMLTree.SpaceNode) ; else if (node instanceof mathMLTree.MathNode) { + switch (node.type) { + case "mi": + case "mn": + case "ms": + case "mspace": + case "mtext": + break; + // Do nothing yet. + + case "mo": + { + var child = node.children[0]; + + if (node.children.length === 1 && child instanceof mathMLTree.TextNode) { + child.text = child.text.replace(/\u2212/, "-").replace(/\u2217/, "*"); + } else { + isAllString = false; + } + + break; + } + + default: + isAllString = false; + } + } else { + isAllString = false; + } + } + + if (isAllString) { + // Write a single TextNode instead of multiple nested tags. + var word = expression.map(function (node) { + return node.toText(); + }).join(""); + expression = [new mathMLTree.TextNode(word)]; + } + + var identifier = new mathMLTree.MathNode("mi", expression); + identifier.setAttribute("mathvariant", "normal"); // \u2061 is the same as ⁡ + // ref: https://www.w3schools.com/charsets/ref_html_entities_a.asp + + var operator = new mathMLTree.MathNode("mo", [makeText("\u2061", "text")]); + + if (group.parentIsSupSub) { + return new mathMLTree.MathNode("mrow", [identifier, operator]); + } else { + return mathMLTree.newDocumentFragment([identifier, operator]); + } +}; // \operatorname +// amsopn.dtx: \mathop{#1\kern\z@\operator@font#3}\newmcodes@ + + +defineFunction({ + type: "operatorname", + names: ["\\operatorname@", "\\operatornamewithlimits"], + props: { + numArgs: 1 + }, + handler: function handler(_ref, args) { + var parser = _ref.parser, + funcName = _ref.funcName; + var body = args[0]; + return { + type: "operatorname", + mode: parser.mode, + body: ordargument(body), + alwaysHandleSupSub: funcName === "\\operatornamewithlimits", + limits: false, + parentIsSupSub: false + }; + }, + htmlBuilder: operatorname_htmlBuilder, + mathmlBuilder: operatorname_mathmlBuilder +}); +defineMacro("\\operatorname", "\\@ifstar\\operatornamewithlimits\\operatorname@"); + + + + +defineFunctionBuilders({ + type: "ordgroup", + htmlBuilder: function htmlBuilder(group, options) { + if (group.semisimple) { + return buildCommon.makeFragment(buildExpression(group.body, options, false)); + } + + return buildCommon.makeSpan(["mord"], buildExpression(group.body, options, true), options); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + return buildExpressionRow(group.body, options, true); + } +}); + + + + + +defineFunction({ + type: "overline", + names: ["\\overline"], + props: { + numArgs: 1 + }, + handler: function handler(_ref, args) { + var parser = _ref.parser; + var body = args[0]; + return { + type: "overline", + mode: parser.mode, + body: body + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + // Overlines are handled in the TeXbook pg 443, Rule 9. + // Build the inner group in the cramped style. + var innerGroup = buildGroup(group.body, options.havingCrampedStyle()); // Create the line above the body + + var line = buildCommon.makeLineSpan("overline-line", options); // Generate the vlist, with the appropriate kerns + + var defaultRuleThickness = options.fontMetrics().defaultRuleThickness; + var vlist = buildCommon.makeVList({ + positionType: "firstBaseline", + children: [{ + type: "elem", + elem: innerGroup + }, { + type: "kern", + size: 3 * defaultRuleThickness + }, { + type: "elem", + elem: line + }, { + type: "kern", + size: defaultRuleThickness + }] + }, options); + return buildCommon.makeSpan(["mord", "overline"], [vlist], options); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var operator = new mathMLTree.MathNode("mo", [new mathMLTree.TextNode("\u203E")]); + operator.setAttribute("stretchy", "true"); + var node = new mathMLTree.MathNode("mover", [buildMathML_buildGroup(group.body, options), operator]); + node.setAttribute("accent", "true"); + return node; + } +}); + + + + + +defineFunction({ + type: "phantom", + names: ["\\phantom"], + props: { + numArgs: 1, + allowedInText: true + }, + handler: function handler(_ref, args) { + var parser = _ref.parser; + var body = args[0]; + return { + type: "phantom", + mode: parser.mode, + body: ordargument(body) + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + var elements = buildExpression(group.body, options.withPhantom(), false); // \phantom isn't supposed to affect the elements it contains. + // See "color" for more details. + + return buildCommon.makeFragment(elements); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var inner = buildMathML_buildExpression(group.body, options); + return new mathMLTree.MathNode("mphantom", inner); + } +}); +defineFunction({ + type: "hphantom", + names: ["\\hphantom"], + props: { + numArgs: 1, + allowedInText: true + }, + handler: function handler(_ref2, args) { + var parser = _ref2.parser; + var body = args[0]; + return { + type: "hphantom", + mode: parser.mode, + body: body + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + var node = buildCommon.makeSpan([], [buildGroup(group.body, options.withPhantom())]); + node.height = 0; + node.depth = 0; + + if (node.children) { + for (var i = 0; i < node.children.length; i++) { + node.children[i].height = 0; + node.children[i].depth = 0; + } + } // See smash for comment re: use of makeVList + + + node = buildCommon.makeVList({ + positionType: "firstBaseline", + children: [{ + type: "elem", + elem: node + }] + }, options); // For spacing, TeX treats \smash as a math group (same spacing as ord). + + return buildCommon.makeSpan(["mord"], [node], options); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var inner = buildMathML_buildExpression(ordargument(group.body), options); + var phantom = new mathMLTree.MathNode("mphantom", inner); + var node = new mathMLTree.MathNode("mpadded", [phantom]); + node.setAttribute("height", "0px"); + node.setAttribute("depth", "0px"); + return node; + } +}); +defineFunction({ + type: "vphantom", + names: ["\\vphantom"], + props: { + numArgs: 1, + allowedInText: true + }, + handler: function handler(_ref3, args) { + var parser = _ref3.parser; + var body = args[0]; + return { + type: "vphantom", + mode: parser.mode, + body: body + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + var inner = buildCommon.makeSpan(["inner"], [buildGroup(group.body, options.withPhantom())]); + var fix = buildCommon.makeSpan(["fix"], []); + return buildCommon.makeSpan(["mord", "rlap"], [inner, fix], options); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var inner = buildMathML_buildExpression(ordargument(group.body), options); + var phantom = new mathMLTree.MathNode("mphantom", inner); + var node = new mathMLTree.MathNode("mpadded", [phantom]); + node.setAttribute("width", "0px"); + return node; + } +}); + + + + + + + // Box manipulation + +defineFunction({ + type: "raisebox", + names: ["\\raisebox"], + props: { + numArgs: 2, + argTypes: ["size", "hbox"], + allowedInText: true + }, + handler: function handler(_ref, args) { + var parser = _ref.parser; + var amount = assertNodeType(args[0], "size").value; + var body = args[1]; + return { + type: "raisebox", + mode: parser.mode, + dy: amount, + body: body + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + var body = buildGroup(group.body, options); + var dy = calculateSize(group.dy, options); + return buildCommon.makeVList({ + positionType: "shift", + positionData: -dy, + children: [{ + type: "elem", + elem: body + }] + }, options); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var node = new mathMLTree.MathNode("mpadded", [buildMathML_buildGroup(group.body, options)]); + var dy = group.dy.number + group.dy.unit; + node.setAttribute("voffset", dy); + return node; + } +}); + + + + + +defineFunction({ + type: "rule", + names: ["\\rule"], + props: { + numArgs: 2, + numOptionalArgs: 1, + argTypes: ["size", "size", "size"] + }, + handler: function handler(_ref, args, optArgs) { + var parser = _ref.parser; + var shift = optArgs[0]; + var width = assertNodeType(args[0], "size"); + var height = assertNodeType(args[1], "size"); + return { + type: "rule", + mode: parser.mode, + shift: shift && assertNodeType(shift, "size").value, + width: width.value, + height: height.value + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + // Make an empty span for the rule + var rule = buildCommon.makeSpan(["mord", "rule"], [], options); // Calculate the shift, width, and height of the rule, and account for units + + var width = calculateSize(group.width, options); + var height = calculateSize(group.height, options); + var shift = group.shift ? calculateSize(group.shift, options) : 0; // Style the rule to the right size + + rule.style.borderRightWidth = makeEm(width); + rule.style.borderTopWidth = makeEm(height); + rule.style.bottom = makeEm(shift); // Record the height and width + + rule.width = width; + rule.height = height + shift; + rule.depth = -shift; // Font size is the number large enough that the browser will + // reserve at least `absHeight` space above the baseline. + // The 1.125 factor was empirically determined + + rule.maxFontSize = height * 1.125 * options.sizeMultiplier; + return rule; + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var width = calculateSize(group.width, options); + var height = calculateSize(group.height, options); + var shift = group.shift ? calculateSize(group.shift, options) : 0; + var color = options.color && options.getColor() || "black"; + var rule = new mathMLTree.MathNode("mspace"); + rule.setAttribute("mathbackground", color); + rule.setAttribute("width", makeEm(width)); + rule.setAttribute("height", makeEm(height)); + var wrapper = new mathMLTree.MathNode("mpadded", [rule]); + + if (shift >= 0) { + wrapper.setAttribute("height", makeEm(shift)); + } else { + wrapper.setAttribute("height", makeEm(shift)); + wrapper.setAttribute("depth", makeEm(-shift)); + } + + wrapper.setAttribute("voffset", makeEm(shift)); + return wrapper; + } +}); + + + + + + +function sizingGroup(value, options, baseOptions) { + var inner = buildExpression(value, options, false); + var multiplier = options.sizeMultiplier / baseOptions.sizeMultiplier; // Add size-resetting classes to the inner list and set maxFontSize + // manually. Handle nested size changes. + + for (var i = 0; i < inner.length; i++) { + var pos = inner[i].classes.indexOf("sizing"); + + if (pos < 0) { + Array.prototype.push.apply(inner[i].classes, options.sizingClasses(baseOptions)); + } else if (inner[i].classes[pos + 1] === "reset-size" + options.size) { + // This is a nested size change: e.g., inner[i] is the "b" in + // `\Huge a \small b`. Override the old size (the `reset-` class) + // but not the new size. + inner[i].classes[pos + 1] = "reset-size" + baseOptions.size; + } + + inner[i].height *= multiplier; + inner[i].depth *= multiplier; + } + + return buildCommon.makeFragment(inner); +} +var sizeFuncs = ["\\tiny", "\\sixptsize", "\\scriptsize", "\\footnotesize", "\\small", "\\normalsize", "\\large", "\\Large", "\\LARGE", "\\huge", "\\Huge"]; +var sizing_htmlBuilder = function htmlBuilder(group, options) { + // Handle sizing operators like \Huge. Real TeX doesn't actually allow + // these functions inside of math expressions, so we do some special + // handling. + var newOptions = options.havingSize(group.size); + return sizingGroup(group.body, newOptions, options); +}; +defineFunction({ + type: "sizing", + names: sizeFuncs, + props: { + numArgs: 0, + allowedInText: true + }, + handler: function handler(_ref, args) { + var breakOnTokenText = _ref.breakOnTokenText, + funcName = _ref.funcName, + parser = _ref.parser; + var body = parser.parseExpression(false, breakOnTokenText); + return { + type: "sizing", + mode: parser.mode, + // Figure out what size to use based on the list of functions above + size: sizeFuncs.indexOf(funcName) + 1, + body: body + }; + }, + htmlBuilder: sizing_htmlBuilder, + mathmlBuilder: function mathmlBuilder(group, options) { + var newOptions = options.havingSize(group.size); + var inner = buildMathML_buildExpression(group.body, newOptions); + var node = new mathMLTree.MathNode("mstyle", inner); // TODO(emily): This doesn't produce the correct size for nested size + // changes, because we don't keep state of what style we're currently + // in, so we can't reset the size to normal before changing it. Now + // that we're passing an options parameter we should be able to fix + // this. + + node.setAttribute("mathsize", makeEm(newOptions.sizeMultiplier)); + return node; + } +}); +// smash, with optional [tb], as in AMS + + + + + + +defineFunction({ + type: "smash", + names: ["\\smash"], + props: { + numArgs: 1, + numOptionalArgs: 1, + allowedInText: true + }, + handler: function handler(_ref, args, optArgs) { + var parser = _ref.parser; + var smashHeight = false; + var smashDepth = false; + var tbArg = optArgs[0] && assertNodeType(optArgs[0], "ordgroup"); + + if (tbArg) { + // Optional [tb] argument is engaged. + // ref: amsmath: \renewcommand{\smash}[1][tb]{% + // def\mb@t{\ht}\def\mb@b{\dp}\def\mb@tb{\ht\z@\z@\dp}% + var letter = ""; + + for (var i = 0; i < tbArg.body.length; ++i) { + var node = tbArg.body[i]; // $FlowFixMe: Not every node type has a `text` property. + + letter = node.text; + + if (letter === "t") { + smashHeight = true; + } else if (letter === "b") { + smashDepth = true; + } else { + smashHeight = false; + smashDepth = false; + break; + } + } + } else { + smashHeight = true; + smashDepth = true; + } + + var body = args[0]; + return { + type: "smash", + mode: parser.mode, + body: body, + smashHeight: smashHeight, + smashDepth: smashDepth + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + var node = buildCommon.makeSpan([], [buildGroup(group.body, options)]); + + if (!group.smashHeight && !group.smashDepth) { + return node; + } + + if (group.smashHeight) { + node.height = 0; // In order to influence makeVList, we have to reset the children. + + if (node.children) { + for (var i = 0; i < node.children.length; i++) { + node.children[i].height = 0; + } + } + } + + if (group.smashDepth) { + node.depth = 0; + + if (node.children) { + for (var _i = 0; _i < node.children.length; _i++) { + node.children[_i].depth = 0; + } + } + } // At this point, we've reset the TeX-like height and depth values. + // But the span still has an HTML line height. + // makeVList applies "display: table-cell", which prevents the browser + // from acting on that line height. So we'll call makeVList now. + + + var smashedNode = buildCommon.makeVList({ + positionType: "firstBaseline", + children: [{ + type: "elem", + elem: node + }] + }, options); // For spacing, TeX treats \hphantom as a math group (same spacing as ord). + + return buildCommon.makeSpan(["mord"], [smashedNode], options); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var node = new mathMLTree.MathNode("mpadded", [buildMathML_buildGroup(group.body, options)]); + + if (group.smashHeight) { + node.setAttribute("height", "0px"); + } + + if (group.smashDepth) { + node.setAttribute("depth", "0px"); + } + + return node; + } +}); + + + + + + + + +defineFunction({ + type: "sqrt", + names: ["\\sqrt"], + props: { + numArgs: 1, + numOptionalArgs: 1 + }, + handler: function handler(_ref, args, optArgs) { + var parser = _ref.parser; + var index = optArgs[0]; + var body = args[0]; + return { + type: "sqrt", + mode: parser.mode, + body: body, + index: index + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + // Square roots are handled in the TeXbook pg. 443, Rule 11. + // First, we do the same steps as in overline to build the inner group + // and line + var inner = buildGroup(group.body, options.havingCrampedStyle()); + + if (inner.height === 0) { + // Render a small surd. + inner.height = options.fontMetrics().xHeight; + } // Some groups can return document fragments. Handle those by wrapping + // them in a span. + + + inner = buildCommon.wrapFragment(inner, options); // Calculate the minimum size for the \surd delimiter + + var metrics = options.fontMetrics(); + var theta = metrics.defaultRuleThickness; + var phi = theta; + + if (options.style.id < src_Style.TEXT.id) { + phi = options.fontMetrics().xHeight; + } // Calculate the clearance between the body and line + + + var lineClearance = theta + phi / 4; + var minDelimiterHeight = inner.height + inner.depth + lineClearance + theta; // Create a sqrt SVG of the required minimum size + + var _delimiter$sqrtImage = delimiter.sqrtImage(minDelimiterHeight, options), + img = _delimiter$sqrtImage.span, + ruleWidth = _delimiter$sqrtImage.ruleWidth, + advanceWidth = _delimiter$sqrtImage.advanceWidth; + + var delimDepth = img.height - ruleWidth; // Adjust the clearance based on the delimiter size + + if (delimDepth > inner.height + inner.depth + lineClearance) { + lineClearance = (lineClearance + delimDepth - inner.height - inner.depth) / 2; + } // Shift the sqrt image + + + var imgShift = img.height - inner.height - lineClearance - ruleWidth; + inner.style.paddingLeft = makeEm(advanceWidth); // Overlay the image and the argument. + + var body = buildCommon.makeVList({ + positionType: "firstBaseline", + children: [{ + type: "elem", + elem: inner, + wrapperClasses: ["svg-align"] + }, { + type: "kern", + size: -(inner.height + imgShift) + }, { + type: "elem", + elem: img + }, { + type: "kern", + size: ruleWidth + }] + }, options); + + if (!group.index) { + return buildCommon.makeSpan(["mord", "sqrt"], [body], options); + } else { + // Handle the optional root index + // The index is always in scriptscript style + var newOptions = options.havingStyle(src_Style.SCRIPTSCRIPT); + var rootm = buildGroup(group.index, newOptions, options); // The amount the index is shifted by. This is taken from the TeX + // source, in the definition of `\r@@t`. + + var toShift = 0.6 * (body.height - body.depth); // Build a VList with the superscript shifted up correctly + + var rootVList = buildCommon.makeVList({ + positionType: "shift", + positionData: -toShift, + children: [{ + type: "elem", + elem: rootm + }] + }, options); // Add a class surrounding it so we can add on the appropriate + // kerning + + var rootVListWrap = buildCommon.makeSpan(["root"], [rootVList]); + return buildCommon.makeSpan(["mord", "sqrt"], [rootVListWrap, body], options); + } + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var body = group.body, + index = group.index; + return index ? new mathMLTree.MathNode("mroot", [buildMathML_buildGroup(body, options), buildMathML_buildGroup(index, options)]) : new mathMLTree.MathNode("msqrt", [buildMathML_buildGroup(body, options)]); + } +}); + + + + + +var styling_styleMap = { + "display": src_Style.DISPLAY, + "text": src_Style.TEXT, + "script": src_Style.SCRIPT, + "scriptscript": src_Style.SCRIPTSCRIPT +}; +defineFunction({ + type: "styling", + names: ["\\displaystyle", "\\textstyle", "\\scriptstyle", "\\scriptscriptstyle"], + props: { + numArgs: 0, + allowedInText: true, + primitive: true + }, + handler: function handler(_ref, args) { + var breakOnTokenText = _ref.breakOnTokenText, + funcName = _ref.funcName, + parser = _ref.parser; + // parse out the implicit body + var body = parser.parseExpression(true, breakOnTokenText); // TODO: Refactor to avoid duplicating styleMap in multiple places (e.g. + // here and in buildHTML and de-dupe the enumeration of all the styles). + // $FlowFixMe: The names above exactly match the styles. + + var style = funcName.slice(1, funcName.length - 5); + return { + type: "styling", + mode: parser.mode, + // Figure out what style to use by pulling out the style from + // the function name + style: style, + body: body + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + // Style changes are handled in the TeXbook on pg. 442, Rule 3. + var newStyle = styling_styleMap[group.style]; + var newOptions = options.havingStyle(newStyle).withFont(''); + return sizingGroup(group.body, newOptions, options); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + // Figure out what style we're changing to. + var newStyle = styling_styleMap[group.style]; + var newOptions = options.havingStyle(newStyle); + var inner = buildMathML_buildExpression(group.body, newOptions); + var node = new mathMLTree.MathNode("mstyle", inner); + var styleAttributes = { + "display": ["0", "true"], + "text": ["0", "false"], + "script": ["1", "false"], + "scriptscript": ["2", "false"] + }; + var attr = styleAttributes[group.style]; + node.setAttribute("scriptlevel", attr[0]); + node.setAttribute("displaystyle", attr[1]); + return node; + } +}); + + + + + + + + + + + + + + +/** + * Sometimes, groups perform special rules when they have superscripts or + * subscripts attached to them. This function lets the `supsub` group know that + * Sometimes, groups perform special rules when they have superscripts or + * its inner element should handle the superscripts and subscripts instead of + * handling them itself. + */ +var htmlBuilderDelegate = function htmlBuilderDelegate(group, options) { + var base = group.base; + + if (!base) { + return null; + } else if (base.type === "op") { + // Operators handle supsubs differently when they have limits + // (e.g. `\displaystyle\sum_2^3`) + var delegate = base.limits && (options.style.size === src_Style.DISPLAY.size || base.alwaysHandleSupSub); + return delegate ? op_htmlBuilder : null; + } else if (base.type === "operatorname") { + var _delegate = base.alwaysHandleSupSub && (options.style.size === src_Style.DISPLAY.size || base.limits); + + return _delegate ? operatorname_htmlBuilder : null; + } else if (base.type === "accent") { + return utils.isCharacterBox(base.base) ? htmlBuilder : null; + } else if (base.type === "horizBrace") { + var isSup = !group.sub; + return isSup === base.isOver ? horizBrace_htmlBuilder : null; + } else { + return null; + } +}; // Super scripts and subscripts, whose precise placement can depend on other +// functions that precede them. + + +defineFunctionBuilders({ + type: "supsub", + htmlBuilder: function htmlBuilder(group, options) { + // Superscript and subscripts are handled in the TeXbook on page + // 445-446, rules 18(a-f). + // Here is where we defer to the inner group if it should handle + // superscripts and subscripts itself. + var builderDelegate = htmlBuilderDelegate(group, options); + + if (builderDelegate) { + return builderDelegate(group, options); + } + + var valueBase = group.base, + valueSup = group.sup, + valueSub = group.sub; + var base = buildGroup(valueBase, options); + var supm; + var subm; + var metrics = options.fontMetrics(); // Rule 18a + + var supShift = 0; + var subShift = 0; + var isCharacterBox = valueBase && utils.isCharacterBox(valueBase); + + if (valueSup) { + var newOptions = options.havingStyle(options.style.sup()); + supm = buildGroup(valueSup, newOptions, options); + + if (!isCharacterBox) { + supShift = base.height - newOptions.fontMetrics().supDrop * newOptions.sizeMultiplier / options.sizeMultiplier; + } + } + + if (valueSub) { + var _newOptions = options.havingStyle(options.style.sub()); + + subm = buildGroup(valueSub, _newOptions, options); + + if (!isCharacterBox) { + subShift = base.depth + _newOptions.fontMetrics().subDrop * _newOptions.sizeMultiplier / options.sizeMultiplier; + } + } // Rule 18c + + + var minSupShift; + + if (options.style === src_Style.DISPLAY) { + minSupShift = metrics.sup1; + } else if (options.style.cramped) { + minSupShift = metrics.sup3; + } else { + minSupShift = metrics.sup2; + } // scriptspace is a font-size-independent size, so scale it + // appropriately for use as the marginRight. + + + var multiplier = options.sizeMultiplier; + var marginRight = makeEm(0.5 / metrics.ptPerEm / multiplier); + var marginLeft = null; + + if (subm) { + // Subscripts shouldn't be shifted by the base's italic correction. + // Account for that by shifting the subscript back the appropriate + // amount. Note we only do this when the base is a single symbol. + var isOiint = group.base && group.base.type === "op" && group.base.name && (group.base.name === "\\oiint" || group.base.name === "\\oiiint"); + + if (base instanceof SymbolNode || isOiint) { + // $FlowFixMe + marginLeft = makeEm(-base.italic); + } + } + + var supsub; + + if (supm && subm) { + supShift = Math.max(supShift, minSupShift, supm.depth + 0.25 * metrics.xHeight); + subShift = Math.max(subShift, metrics.sub2); + var ruleWidth = metrics.defaultRuleThickness; // Rule 18e + + var maxWidth = 4 * ruleWidth; + + if (supShift - supm.depth - (subm.height - subShift) < maxWidth) { + subShift = maxWidth - (supShift - supm.depth) + subm.height; + var psi = 0.8 * metrics.xHeight - (supShift - supm.depth); + + if (psi > 0) { + supShift += psi; + subShift -= psi; + } + } + + var vlistElem = [{ + type: "elem", + elem: subm, + shift: subShift, + marginRight: marginRight, + marginLeft: marginLeft + }, { + type: "elem", + elem: supm, + shift: -supShift, + marginRight: marginRight + }]; + supsub = buildCommon.makeVList({ + positionType: "individualShift", + children: vlistElem + }, options); + } else if (subm) { + // Rule 18b + subShift = Math.max(subShift, metrics.sub1, subm.height - 0.8 * metrics.xHeight); + var _vlistElem = [{ + type: "elem", + elem: subm, + marginLeft: marginLeft, + marginRight: marginRight + }]; + supsub = buildCommon.makeVList({ + positionType: "shift", + positionData: subShift, + children: _vlistElem + }, options); + } else if (supm) { + // Rule 18c, d + supShift = Math.max(supShift, minSupShift, supm.depth + 0.25 * metrics.xHeight); + supsub = buildCommon.makeVList({ + positionType: "shift", + positionData: -supShift, + children: [{ + type: "elem", + elem: supm, + marginRight: marginRight + }] + }, options); + } else { + throw new Error("supsub must have either sup or sub."); + } // Wrap the supsub vlist in a span.msupsub to reset text-align. + + + var mclass = getTypeOfDomTree(base, "right") || "mord"; + return buildCommon.makeSpan([mclass], [base, buildCommon.makeSpan(["msupsub"], [supsub])], options); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + // Is the inner group a relevant horizonal brace? + var isBrace = false; + var isOver; + var isSup; + + if (group.base && group.base.type === "horizBrace") { + isSup = !!group.sup; + + if (isSup === group.base.isOver) { + isBrace = true; + isOver = group.base.isOver; + } + } + + if (group.base && (group.base.type === "op" || group.base.type === "operatorname")) { + group.base.parentIsSupSub = true; + } + + var children = [buildMathML_buildGroup(group.base, options)]; + + if (group.sub) { + children.push(buildMathML_buildGroup(group.sub, options)); + } + + if (group.sup) { + children.push(buildMathML_buildGroup(group.sup, options)); + } + + var nodeType; + + if (isBrace) { + nodeType = isOver ? "mover" : "munder"; + } else if (!group.sub) { + var base = group.base; + + if (base && base.type === "op" && base.limits && (options.style === src_Style.DISPLAY || base.alwaysHandleSupSub)) { + nodeType = "mover"; + } else if (base && base.type === "operatorname" && base.alwaysHandleSupSub && (base.limits || options.style === src_Style.DISPLAY)) { + nodeType = "mover"; + } else { + nodeType = "msup"; + } + } else if (!group.sup) { + var _base = group.base; + + if (_base && _base.type === "op" && _base.limits && (options.style === src_Style.DISPLAY || _base.alwaysHandleSupSub)) { + nodeType = "munder"; + } else if (_base && _base.type === "operatorname" && _base.alwaysHandleSupSub && (_base.limits || options.style === src_Style.DISPLAY)) { + nodeType = "munder"; + } else { + nodeType = "msub"; + } + } else { + var _base2 = group.base; + + if (_base2 && _base2.type === "op" && _base2.limits && options.style === src_Style.DISPLAY) { + nodeType = "munderover"; + } else if (_base2 && _base2.type === "operatorname" && _base2.alwaysHandleSupSub && (options.style === src_Style.DISPLAY || _base2.limits)) { + nodeType = "munderover"; + } else { + nodeType = "msubsup"; + } + } + + return new mathMLTree.MathNode(nodeType, children); + } +}); + + + + // Operator ParseNodes created in Parser.js from symbol Groups in src/symbols.js. + +defineFunctionBuilders({ + type: "atom", + htmlBuilder: function htmlBuilder(group, options) { + return buildCommon.mathsym(group.text, group.mode, options, ["m" + group.family]); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var node = new mathMLTree.MathNode("mo", [makeText(group.text, group.mode)]); + + if (group.family === "bin") { + var variant = getVariant(group, options); + + if (variant === "bold-italic") { + node.setAttribute("mathvariant", variant); + } + } else if (group.family === "punct") { + node.setAttribute("separator", "true"); + } else if (group.family === "open" || group.family === "close") { + // Delims built here should not stretch vertically. + // See delimsizing.js for stretchy delims. + node.setAttribute("stretchy", "false"); + } + + return node; + } +}); + + + + +// "mathord" and "textord" ParseNodes created in Parser.js from symbol Groups in +// src/symbols.js. +var defaultVariant = { + "mi": "italic", + "mn": "normal", + "mtext": "normal" +}; +defineFunctionBuilders({ + type: "mathord", + htmlBuilder: function htmlBuilder(group, options) { + return buildCommon.makeOrd(group, options, "mathord"); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var node = new mathMLTree.MathNode("mi", [makeText(group.text, group.mode, options)]); + var variant = getVariant(group, options) || "italic"; + + if (variant !== defaultVariant[node.type]) { + node.setAttribute("mathvariant", variant); + } + + return node; + } +}); +defineFunctionBuilders({ + type: "textord", + htmlBuilder: function htmlBuilder(group, options) { + return buildCommon.makeOrd(group, options, "textord"); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var text = makeText(group.text, group.mode, options); + var variant = getVariant(group, options) || "normal"; + var node; + + if (group.mode === 'text') { + node = new mathMLTree.MathNode("mtext", [text]); + } else if (/[0-9]/.test(group.text)) { + node = new mathMLTree.MathNode("mn", [text]); + } else if (group.text === "\\prime") { + node = new mathMLTree.MathNode("mo", [text]); + } else { + node = new mathMLTree.MathNode("mi", [text]); + } + + if (variant !== defaultVariant[node.type]) { + node.setAttribute("mathvariant", variant); + } + + return node; + } +}); + + + + // A map of CSS-based spacing functions to their CSS class. + +var cssSpace = { + "\\nobreak": "nobreak", + "\\allowbreak": "allowbreak" +}; // A lookup table to determine whether a spacing function/symbol should be +// treated like a regular space character. If a symbol or command is a key +// in this table, then it should be a regular space character. Furthermore, +// the associated value may have a `className` specifying an extra CSS class +// to add to the created `span`. + +var regularSpace = { + " ": {}, + "\\ ": {}, + "~": { + className: "nobreak" + }, + "\\space": {}, + "\\nobreakspace": { + className: "nobreak" + } +}; // ParseNode<"spacing"> created in Parser.js from the "spacing" symbol Groups in +// src/symbols.js. + +defineFunctionBuilders({ + type: "spacing", + htmlBuilder: function htmlBuilder(group, options) { + if (regularSpace.hasOwnProperty(group.text)) { + var className = regularSpace[group.text].className || ""; // Spaces are generated by adding an actual space. Each of these + // things has an entry in the symbols table, so these will be turned + // into appropriate outputs. + + if (group.mode === "text") { + var ord = buildCommon.makeOrd(group, options, "textord"); + ord.classes.push(className); + return ord; + } else { + return buildCommon.makeSpan(["mspace", className], [buildCommon.mathsym(group.text, group.mode, options)], options); + } + } else if (cssSpace.hasOwnProperty(group.text)) { + // Spaces based on just a CSS class. + return buildCommon.makeSpan(["mspace", cssSpace[group.text]], [], options); + } else { + throw new src_ParseError("Unknown type of space \"" + group.text + "\""); + } + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var node; + + if (regularSpace.hasOwnProperty(group.text)) { + node = new mathMLTree.MathNode("mtext", [new mathMLTree.TextNode("\xA0")]); + } else if (cssSpace.hasOwnProperty(group.text)) { + // CSS-based MathML spaces (\nobreak, \allowbreak) are ignored + return new mathMLTree.MathNode("mspace"); + } else { + throw new src_ParseError("Unknown type of space \"" + group.text + "\""); + } + + return node; + } +}); + + + + +var pad = function pad() { + var padNode = new mathMLTree.MathNode("mtd", []); + padNode.setAttribute("width", "50%"); + return padNode; +}; + +defineFunctionBuilders({ + type: "tag", + mathmlBuilder: function mathmlBuilder(group, options) { + var table = new mathMLTree.MathNode("mtable", [new mathMLTree.MathNode("mtr", [pad(), new mathMLTree.MathNode("mtd", [buildExpressionRow(group.body, options)]), pad(), new mathMLTree.MathNode("mtd", [buildExpressionRow(group.tag, options)])])]); + table.setAttribute("width", "100%"); + return table; // TODO: Left-aligned tags. + // Currently, the group and options passed here do not contain + // enough info to set tag alignment. `leqno` is in Settings but it is + // not passed to Options. On the HTML side, leqno is + // set by a CSS class applied in buildTree.js. That would have worked + // in MathML if browsers supported . Since they don't, we + // need to rewrite the way this function is called. + } +}); + + + + // Non-mathy text, possibly in a font + +var textFontFamilies = { + "\\text": undefined, + "\\textrm": "textrm", + "\\textsf": "textsf", + "\\texttt": "texttt", + "\\textnormal": "textrm" +}; +var textFontWeights = { + "\\textbf": "textbf", + "\\textmd": "textmd" +}; +var textFontShapes = { + "\\textit": "textit", + "\\textup": "textup" +}; + +var optionsWithFont = function optionsWithFont(group, options) { + var font = group.font; // Checks if the argument is a font family or a font style. + + if (!font) { + return options; + } else if (textFontFamilies[font]) { + return options.withTextFontFamily(textFontFamilies[font]); + } else if (textFontWeights[font]) { + return options.withTextFontWeight(textFontWeights[font]); + } else { + return options.withTextFontShape(textFontShapes[font]); + } +}; + +defineFunction({ + type: "text", + names: [// Font families + "\\text", "\\textrm", "\\textsf", "\\texttt", "\\textnormal", // Font weights + "\\textbf", "\\textmd", // Font Shapes + "\\textit", "\\textup"], + props: { + numArgs: 1, + argTypes: ["text"], + allowedInArgument: true, + allowedInText: true + }, + handler: function handler(_ref, args) { + var parser = _ref.parser, + funcName = _ref.funcName; + var body = args[0]; + return { + type: "text", + mode: parser.mode, + body: ordargument(body), + font: funcName + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + var newOptions = optionsWithFont(group, options); + var inner = buildExpression(group.body, newOptions, true); + return buildCommon.makeSpan(["mord", "text"], inner, newOptions); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var newOptions = optionsWithFont(group, options); + return buildExpressionRow(group.body, newOptions); + } +}); + + + + + +defineFunction({ + type: "underline", + names: ["\\underline"], + props: { + numArgs: 1, + allowedInText: true + }, + handler: function handler(_ref, args) { + var parser = _ref.parser; + return { + type: "underline", + mode: parser.mode, + body: args[0] + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + // Underlines are handled in the TeXbook pg 443, Rule 10. + // Build the inner group. + var innerGroup = buildGroup(group.body, options); // Create the line to go below the body + + var line = buildCommon.makeLineSpan("underline-line", options); // Generate the vlist, with the appropriate kerns + + var defaultRuleThickness = options.fontMetrics().defaultRuleThickness; + var vlist = buildCommon.makeVList({ + positionType: "top", + positionData: innerGroup.height, + children: [{ + type: "kern", + size: defaultRuleThickness + }, { + type: "elem", + elem: line + }, { + type: "kern", + size: 3 * defaultRuleThickness + }, { + type: "elem", + elem: innerGroup + }] + }, options); + return buildCommon.makeSpan(["mord", "underline"], [vlist], options); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var operator = new mathMLTree.MathNode("mo", [new mathMLTree.TextNode("\u203E")]); + operator.setAttribute("stretchy", "true"); + var node = new mathMLTree.MathNode("munder", [buildMathML_buildGroup(group.body, options), operator]); + node.setAttribute("accentunder", "true"); + return node; + } +}); + + + + + // \vcenter: Vertically center the argument group on the math axis. + +defineFunction({ + type: "vcenter", + names: ["\\vcenter"], + props: { + numArgs: 1, + argTypes: ["original"], + // In LaTeX, \vcenter can act only on a box. + allowedInText: false + }, + handler: function handler(_ref, args) { + var parser = _ref.parser; + return { + type: "vcenter", + mode: parser.mode, + body: args[0] + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + var body = buildGroup(group.body, options); + var axisHeight = options.fontMetrics().axisHeight; + var dy = 0.5 * (body.height - axisHeight - (body.depth + axisHeight)); + return buildCommon.makeVList({ + positionType: "shift", + positionData: dy, + children: [{ + type: "elem", + elem: body + }] + }, options); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + // There is no way to do this in MathML. + // Write a class as a breadcrumb in case some post-processor wants + // to perform a vcenter adjustment. + return new mathMLTree.MathNode("mpadded", [buildMathML_buildGroup(group.body, options)], ["vcenter"]); + } +}); + + + + +defineFunction({ + type: "verb", + names: ["\\verb"], + props: { + numArgs: 0, + allowedInText: true + }, + handler: function handler(context, args, optArgs) { + // \verb and \verb* are dealt with directly in Parser.js. + // If we end up here, it's because of a failure to match the two delimiters + // in the regex in Lexer.js. LaTeX raises the following error when \verb is + // terminated by end of line (or file). + throw new src_ParseError("\\verb ended by end of line instead of matching delimiter"); + }, + htmlBuilder: function htmlBuilder(group, options) { + var text = makeVerb(group); + var body = []; // \verb enters text mode and therefore is sized like \textstyle + + var newOptions = options.havingStyle(options.style.text()); + + for (var i = 0; i < text.length; i++) { + var c = text[i]; + + if (c === '~') { + c = '\\textasciitilde'; + } + + body.push(buildCommon.makeSymbol(c, "Typewriter-Regular", group.mode, newOptions, ["mord", "texttt"])); + } + + return buildCommon.makeSpan(["mord", "text"].concat(newOptions.sizingClasses(options)), buildCommon.tryCombineChars(body), newOptions); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var text = new mathMLTree.TextNode(makeVerb(group)); + var node = new mathMLTree.MathNode("mtext", [text]); + node.setAttribute("mathvariant", "monospace"); + return node; + } +}); +/** + * Converts verb group into body string. + * + * \verb* replaces each space with an open box \u2423 + * \verb replaces each space with a no-break space \xA0 + */ + +var makeVerb = function makeVerb(group) { + return group.body.replace(/ /g, group.star ? "\u2423" : '\xA0'); +}; +/** Include this to ensure that all functions are defined. */ + +var functions = _functions; +/* harmony default export */ var src_functions = (functions); // TODO(kevinb): have functions return an object and call defineFunction with +/** + * Lexing or parsing positional information for error reporting. + * This object is immutable. + */ +var SourceLocation = /*#__PURE__*/function () { + // The + prefix indicates that these fields aren't writeable + // Lexer holding the input string. + // Start offset, zero-based inclusive. + // End offset, zero-based exclusive. + function SourceLocation(lexer, start, end) { + this.lexer = void 0; + this.start = void 0; + this.end = void 0; + this.lexer = lexer; + this.start = start; + this.end = end; + } + /** + * Merges two `SourceLocation`s from location providers, given they are + * provided in order of appearance. + * - Returns the first one's location if only the first is provided. + * - Returns a merged range of the first and the last if both are provided + * and their lexers match. + * - Otherwise, returns null. + */ + + + SourceLocation.range = function range(first, second) { + if (!second) { + return first && first.loc; + } else if (!first || !first.loc || !second.loc || first.loc.lexer !== second.loc.lexer) { + return null; + } else { + return new SourceLocation(first.loc.lexer, first.loc.start, second.loc.end); + } + }; + + return SourceLocation; +}(); + +/** + * Interface required to break circular dependency between Token, Lexer, and + * ParseError. + */ + +/** + * The resulting token returned from `lex`. + * + * It consists of the token text plus some position information. + * The position information is essentially a range in an input string, + * but instead of referencing the bare input string, we refer to the lexer. + * That way it is possible to attach extra metadata to the input string, + * like for example a file name or similar. + * + * The position information is optional, so it is OK to construct synthetic + * tokens if appropriate. Not providing available position information may + * lead to degraded error reporting, though. + */ +var Token = /*#__PURE__*/function () { + // don't expand the token + // used in \noexpand + function Token(text, // the text of this token + loc) { + this.text = void 0; + this.loc = void 0; + this.noexpand = void 0; + this.treatAsRelax = void 0; + this.text = text; + this.loc = loc; + } + /** + * Given a pair of tokens (this and endToken), compute a `Token` encompassing + * the whole input range enclosed by these two. + */ + + + var _proto = Token.prototype; + + _proto.range = function range(endToken, // last token of the range, inclusive + text // the text of the newly constructed token + ) { + return new Token(text, SourceLocation.range(this, endToken)); + }; + + return Token; +}(); +/** + * The Lexer class handles tokenizing the input in various ways. Since our + * parser expects us to be able to backtrack, the lexer allows lexing from any + * given starting point. + * + * Its main exposed function is the `lex` function, which takes a position to + * lex from and a type of token to lex. It defers to the appropriate `_innerLex` + * function. + * + * The various `_innerLex` functions perform the actual lexing of different + * kinds. + */ + + + + +/* The following tokenRegex + * - matches typical whitespace (but not NBSP etc.) using its first group + * - does not match any control character \x00-\x1f except whitespace + * - does not match a bare backslash + * - matches any ASCII character except those just mentioned + * - does not match the BMP private use area \uE000-\uF8FF + * - does not match bare surrogate code units + * - matches any BMP character except for those just described + * - matches any valid Unicode surrogate pair + * - matches a backslash followed by one or more whitespace characters + * - matches a backslash followed by one or more letters then whitespace + * - matches a backslash followed by any BMP character + * Capturing groups: + * [1] regular whitespace + * [2] backslash followed by whitespace + * [3] anything else, which may include: + * [4] left character of \verb* + * [5] left character of \verb + * [6] backslash followed by word, excluding any trailing whitespace + * Just because the Lexer matches something doesn't mean it's valid input: + * If there is no matching function or symbol definition, the Parser will + * still reject the input. + */ +var spaceRegexString = "[ \r\n\t]"; +var controlWordRegexString = "\\\\[a-zA-Z@]+"; +var controlSymbolRegexString = "\\\\[^\uD800-\uDFFF]"; +var controlWordWhitespaceRegexString = "(" + controlWordRegexString + ")" + spaceRegexString + "*"; +var controlSpaceRegexString = "\\\\(\n|[ \r\t]+\n?)[ \r\t]*"; +var combiningDiacriticalMarkString = "[\u0300-\u036F]"; +var combiningDiacriticalMarksEndRegex = new RegExp(combiningDiacriticalMarkString + "+$"); +var tokenRegexString = "(" + spaceRegexString + "+)|" + ( // whitespace +controlSpaceRegexString + "|") + // \whitespace +"([!-\\[\\]-\u2027\u202A-\uD7FF\uF900-\uFFFF]" + ( // single codepoint +combiningDiacriticalMarkString + "*") + // ...plus accents +"|[\uD800-\uDBFF][\uDC00-\uDFFF]" + ( // surrogate pair +combiningDiacriticalMarkString + "*") + // ...plus accents +"|\\\\verb\\*([^]).*?\\4" + // \verb* +"|\\\\verb([^*a-zA-Z]).*?\\5" + ( // \verb unstarred +"|" + controlWordWhitespaceRegexString) + ( // \macroName + spaces +"|" + controlSymbolRegexString + ")"); // \\, \', etc. + +/** Main Lexer class */ + +var Lexer = /*#__PURE__*/function () { + // Category codes. The lexer only supports comment characters (14) for now. + // MacroExpander additionally distinguishes active (13). + function Lexer(input, settings) { + this.input = void 0; + this.settings = void 0; + this.tokenRegex = void 0; + this.catcodes = void 0; + // Separate accents from characters + this.input = input; + this.settings = settings; + this.tokenRegex = new RegExp(tokenRegexString, 'g'); + this.catcodes = { + "%": 14, + // comment character + "~": 13 // active character + + }; + } + + var _proto = Lexer.prototype; + + _proto.setCatcode = function setCatcode(char, code) { + this.catcodes[char] = code; + } + /** + * This function lexes a single token. + */ + ; + + _proto.lex = function lex() { + var input = this.input; + var pos = this.tokenRegex.lastIndex; + + if (pos === input.length) { + return new Token("EOF", new SourceLocation(this, pos, pos)); + } + + var match = this.tokenRegex.exec(input); + + if (match === null || match.index !== pos) { + throw new src_ParseError("Unexpected character: '" + input[pos] + "'", new Token(input[pos], new SourceLocation(this, pos, pos + 1))); + } + + var text = match[6] || match[3] || (match[2] ? "\\ " : " "); + + if (this.catcodes[text] === 14) { + // comment character + var nlIndex = input.indexOf('\n', this.tokenRegex.lastIndex); + + if (nlIndex === -1) { + this.tokenRegex.lastIndex = input.length; // EOF + + this.settings.reportNonstrict("commentAtEnd", "% comment has no terminating newline; LaTeX would " + "fail because of commenting the end of math mode (e.g. $)"); + } else { + this.tokenRegex.lastIndex = nlIndex + 1; + } + + return this.lex(); + } + + return new Token(text, new SourceLocation(this, pos, this.tokenRegex.lastIndex)); + }; + + return Lexer; +}(); +/** + * A `Namespace` refers to a space of nameable things like macros or lengths, + * which can be `set` either globally or local to a nested group, using an + * undo stack similar to how TeX implements this functionality. + * Performance-wise, `get` and local `set` take constant time, while global + * `set` takes time proportional to the depth of group nesting. + */ + + +var Namespace = /*#__PURE__*/function () { + /** + * Both arguments are optional. The first argument is an object of + * built-in mappings which never change. The second argument is an object + * of initial (global-level) mappings, which will constantly change + * according to any global/top-level `set`s done. + */ + function Namespace(builtins, globalMacros) { + if (builtins === void 0) { + builtins = {}; + } + + if (globalMacros === void 0) { + globalMacros = {}; + } + + this.current = void 0; + this.builtins = void 0; + this.undefStack = void 0; + this.current = globalMacros; + this.builtins = builtins; + this.undefStack = []; + } + /** + * Start a new nested group, affecting future local `set`s. + */ + + + var _proto = Namespace.prototype; + + _proto.beginGroup = function beginGroup() { + this.undefStack.push({}); + } + /** + * End current nested group, restoring values before the group began. + */ + ; + + _proto.endGroup = function endGroup() { + if (this.undefStack.length === 0) { + throw new src_ParseError("Unbalanced namespace destruction: attempt " + "to pop global namespace; please report this as a bug"); + } + + var undefs = this.undefStack.pop(); + + for (var undef in undefs) { + if (undefs.hasOwnProperty(undef)) { + if (undefs[undef] === undefined) { + delete this.current[undef]; + } else { + this.current[undef] = undefs[undef]; + } + } + } + } + /** + * Ends all currently nested groups (if any), restoring values before the + * groups began. Useful in case of an error in the middle of parsing. + */ + ; + + _proto.endGroups = function endGroups() { + while (this.undefStack.length > 0) { + this.endGroup(); + } + } + /** + * Detect whether `name` has a definition. Equivalent to + * `get(name) != null`. + */ + ; + + _proto.has = function has(name) { + return this.current.hasOwnProperty(name) || this.builtins.hasOwnProperty(name); + } + /** + * Get the current value of a name, or `undefined` if there is no value. + * + * Note: Do not use `if (namespace.get(...))` to detect whether a macro + * is defined, as the definition may be the empty string which evaluates + * to `false` in JavaScript. Use `if (namespace.get(...) != null)` or + * `if (namespace.has(...))`. + */ + ; + + _proto.get = function get(name) { + if (this.current.hasOwnProperty(name)) { + return this.current[name]; + } else { + return this.builtins[name]; + } + } + /** + * Set the current value of a name, and optionally set it globally too. + * Local set() sets the current value and (when appropriate) adds an undo + * operation to the undo stack. Global set() may change the undo + * operation at every level, so takes time linear in their number. + */ + ; + + _proto.set = function set(name, value, global) { + if (global === void 0) { + global = false; + } + + if (global) { + // Global set is equivalent to setting in all groups. Simulate this + // by destroying any undos currently scheduled for this name, + // and adding an undo with the *new* value (in case it later gets + // locally reset within this environment). + for (var i = 0; i < this.undefStack.length; i++) { + delete this.undefStack[i][name]; + } + + if (this.undefStack.length > 0) { + this.undefStack[this.undefStack.length - 1][name] = value; + } + } else { + // Undo this set at end of this group (possibly to `undefined`), + // unless an undo is already in place, in which case that older + // value is the correct one. + var top = this.undefStack[this.undefStack.length - 1]; + + if (top && !top.hasOwnProperty(name)) { + top[name] = this.current[name]; + } + } + + this.current[name] = value; + }; + + return Namespace; +}(); +/** + * Predefined macros for KaTeX. + * This can be used to define some commands in terms of others. + */ +// Export global macros object from defineMacro + +var macros = _macros; +/* harmony default export */ var src_macros = (macros); + + + + + + ////////////////////////////////////////////////////////////////////// +// macro tools + +defineMacro("\\noexpand", function (context) { + // The expansion is the token itself; but that token is interpreted + // as if its meaning were ‘\relax’ if it is a control sequence that + // would ordinarily be expanded by TeX’s expansion rules. + var t = context.popToken(); + + if (context.isExpandable(t.text)) { + t.noexpand = true; + t.treatAsRelax = true; + } + + return { + tokens: [t], + numArgs: 0 + }; +}); +defineMacro("\\expandafter", function (context) { + // TeX first reads the token that comes immediately after \expandafter, + // without expanding it; let’s call this token t. Then TeX reads the + // token that comes after t (and possibly more tokens, if that token + // has an argument), replacing it by its expansion. Finally TeX puts + // t back in front of that expansion. + var t = context.popToken(); + context.expandOnce(true); // expand only an expandable token + + return { + tokens: [t], + numArgs: 0 + }; +}); // LaTeX's \@firstoftwo{#1}{#2} expands to #1, skipping #2 +// TeX source: \long\def\@firstoftwo#1#2{#1} + +defineMacro("\\@firstoftwo", function (context) { + var args = context.consumeArgs(2); + return { + tokens: args[0], + numArgs: 0 + }; +}); // LaTeX's \@secondoftwo{#1}{#2} expands to #2, skipping #1 +// TeX source: \long\def\@secondoftwo#1#2{#2} + +defineMacro("\\@secondoftwo", function (context) { + var args = context.consumeArgs(2); + return { + tokens: args[1], + numArgs: 0 + }; +}); // LaTeX's \@ifnextchar{#1}{#2}{#3} looks ahead to the next (unexpanded) +// symbol that isn't a space, consuming any spaces but not consuming the +// first nonspace character. If that nonspace character matches #1, then +// the macro expands to #2; otherwise, it expands to #3. + +defineMacro("\\@ifnextchar", function (context) { + var args = context.consumeArgs(3); // symbol, if, else + + context.consumeSpaces(); + var nextToken = context.future(); + + if (args[0].length === 1 && args[0][0].text === nextToken.text) { + return { + tokens: args[1], + numArgs: 0 + }; + } else { + return { + tokens: args[2], + numArgs: 0 + }; + } +}); // LaTeX's \@ifstar{#1}{#2} looks ahead to the next (unexpanded) symbol. +// If it is `*`, then it consumes the symbol, and the macro expands to #1; +// otherwise, the macro expands to #2 (without consuming the symbol). +// TeX source: \def\@ifstar#1{\@ifnextchar *{\@firstoftwo{#1}}} + +defineMacro("\\@ifstar", "\\@ifnextchar *{\\@firstoftwo{#1}}"); // LaTeX's \TextOrMath{#1}{#2} expands to #1 in text mode, #2 in math mode + +defineMacro("\\TextOrMath", function (context) { + var args = context.consumeArgs(2); + + if (context.mode === 'text') { + return { + tokens: args[0], + numArgs: 0 + }; + } else { + return { + tokens: args[1], + numArgs: 0 + }; + } +}); // Lookup table for parsing numbers in base 8 through 16 + +var digitToNumber = { + "0": 0, + "1": 1, + "2": 2, + "3": 3, + "4": 4, + "5": 5, + "6": 6, + "7": 7, + "8": 8, + "9": 9, + "a": 10, + "A": 10, + "b": 11, + "B": 11, + "c": 12, + "C": 12, + "d": 13, + "D": 13, + "e": 14, + "E": 14, + "f": 15, + "F": 15 +}; // TeX \char makes a literal character (catcode 12) using the following forms: +// (see The TeXBook, p. 43) +// \char123 -- decimal +// \char'123 -- octal +// \char"123 -- hex +// \char`x -- character that can be written (i.e. isn't active) +// \char`\x -- character that cannot be written (e.g. %) +// These all refer to characters from the font, so we turn them into special +// calls to a function \@char dealt with in the Parser. + +defineMacro("\\char", function (context) { + var token = context.popToken(); + var base; + var number = ''; + + if (token.text === "'") { + base = 8; + token = context.popToken(); + } else if (token.text === '"') { + base = 16; + token = context.popToken(); + } else if (token.text === "`") { + token = context.popToken(); + + if (token.text[0] === "\\") { + number = token.text.charCodeAt(1); + } else if (token.text === "EOF") { + throw new src_ParseError("\\char` missing argument"); + } else { + number = token.text.charCodeAt(0); + } + } else { + base = 10; + } + + if (base) { + // Parse a number in the given base, starting with first `token`. + number = digitToNumber[token.text]; + + if (number == null || number >= base) { + throw new src_ParseError("Invalid base-" + base + " digit " + token.text); + } + + var digit; + + while ((digit = digitToNumber[context.future().text]) != null && digit < base) { + number *= base; + number += digit; + context.popToken(); + } + } + + return "\\@char{" + number + "}"; +}); // \newcommand{\macro}[args]{definition} +// \renewcommand{\macro}[args]{definition} +// TODO: Optional arguments: \newcommand{\macro}[args][default]{definition} + +var newcommand = function newcommand(context, existsOK, nonexistsOK) { + var arg = context.consumeArg().tokens; + + if (arg.length !== 1) { + throw new src_ParseError("\\newcommand's first argument must be a macro name"); + } + + var name = arg[0].text; + var exists = context.isDefined(name); + + if (exists && !existsOK) { + throw new src_ParseError("\\newcommand{" + name + "} attempting to redefine " + (name + "; use \\renewcommand")); + } + + if (!exists && !nonexistsOK) { + throw new src_ParseError("\\renewcommand{" + name + "} when command " + name + " " + "does not yet exist; use \\newcommand"); + } + + var numArgs = 0; + arg = context.consumeArg().tokens; + + if (arg.length === 1 && arg[0].text === "[") { + var argText = ''; + var token = context.expandNextToken(); + + while (token.text !== "]" && token.text !== "EOF") { + // TODO: Should properly expand arg, e.g., ignore {}s + argText += token.text; + token = context.expandNextToken(); + } + + if (!argText.match(/^\s*[0-9]+\s*$/)) { + throw new src_ParseError("Invalid number of arguments: " + argText); + } + + numArgs = parseInt(argText); + arg = context.consumeArg().tokens; + } // Final arg is the expansion of the macro + + + context.macros.set(name, { + tokens: arg, + numArgs: numArgs + }); + return ''; +}; + +defineMacro("\\newcommand", function (context) { + return newcommand(context, false, true); +}); +defineMacro("\\renewcommand", function (context) { + return newcommand(context, true, false); +}); +defineMacro("\\providecommand", function (context) { + return newcommand(context, true, true); +}); // terminal (console) tools + +defineMacro("\\message", function (context) { + var arg = context.consumeArgs(1)[0]; // eslint-disable-next-line no-console + + console.log(arg.reverse().map(function (token) { + return token.text; + }).join("")); + return ''; +}); +defineMacro("\\errmessage", function (context) { + var arg = context.consumeArgs(1)[0]; // eslint-disable-next-line no-console + + console.error(arg.reverse().map(function (token) { + return token.text; + }).join("")); + return ''; +}); +defineMacro("\\show", function (context) { + var tok = context.popToken(); + var name = tok.text; // eslint-disable-next-line no-console + + console.log(tok, context.macros.get(name), src_functions[name], src_symbols.math[name], src_symbols.text[name]); + return ''; +}); ////////////////////////////////////////////////////////////////////// +// Grouping +// \let\bgroup={ \let\egroup=} + +defineMacro("\\bgroup", "{"); +defineMacro("\\egroup", "}"); // Symbols from latex.ltx: +// \def~{\nobreakspace{}} +// \def\lq{`} +// \def\rq{'} +// \def \aa {\r a} +// \def \AA {\r A} + +defineMacro("~", "\\nobreakspace"); +defineMacro("\\lq", "`"); +defineMacro("\\rq", "'"); +defineMacro("\\aa", "\\r a"); +defineMacro("\\AA", "\\r A"); // Copyright (C) and registered (R) symbols. Use raw symbol in MathML. +// \DeclareTextCommandDefault{\textcopyright}{\textcircled{c}} +// \DeclareTextCommandDefault{\textregistered}{\textcircled{% +// \check@mathfonts\fontsize\sf@size\z@\math@fontsfalse\selectfont R}} +// \DeclareRobustCommand{\copyright}{% +// \ifmmode{\nfss@text{\textcopyright}}\else\textcopyright\fi} + +defineMacro("\\textcopyright", "\\html@mathml{\\textcircled{c}}{\\char`©}"); +defineMacro("\\copyright", "\\TextOrMath{\\textcopyright}{\\text{\\textcopyright}}"); +defineMacro("\\textregistered", "\\html@mathml{\\textcircled{\\scriptsize R}}{\\char`®}"); // Characters omitted from Unicode range 1D400–1D7FF + +defineMacro("\u212C", "\\mathscr{B}"); // script + +defineMacro("\u2130", "\\mathscr{E}"); +defineMacro("\u2131", "\\mathscr{F}"); +defineMacro("\u210B", "\\mathscr{H}"); +defineMacro("\u2110", "\\mathscr{I}"); +defineMacro("\u2112", "\\mathscr{L}"); +defineMacro("\u2133", "\\mathscr{M}"); +defineMacro("\u211B", "\\mathscr{R}"); +defineMacro("\u212D", "\\mathfrak{C}"); // Fraktur + +defineMacro("\u210C", "\\mathfrak{H}"); +defineMacro("\u2128", "\\mathfrak{Z}"); // Define \Bbbk with a macro that works in both HTML and MathML. + +defineMacro("\\Bbbk", "\\Bbb{k}"); // Unicode middle dot +// The KaTeX fonts do not contain U+00B7. Instead, \cdotp displays +// the dot at U+22C5 and gives it punct spacing. + +defineMacro("\xB7", "\\cdotp"); // \llap and \rlap render their contents in text mode + +defineMacro("\\llap", "\\mathllap{\\textrm{#1}}"); +defineMacro("\\rlap", "\\mathrlap{\\textrm{#1}}"); +defineMacro("\\clap", "\\mathclap{\\textrm{#1}}"); // \mathstrut from the TeXbook, p 360 + +defineMacro("\\mathstrut", "\\vphantom{(}"); // \underbar from TeXbook p 353 + +defineMacro("\\underbar", "\\underline{\\text{#1}}"); // \not is defined by base/fontmath.ltx via +// \DeclareMathSymbol{\not}{\mathrel}{symbols}{"36} +// It's thus treated like a \mathrel, but defined by a symbol that has zero +// width but extends to the right. We use \rlap to get that spacing. +// For MathML we write U+0338 here. buildMathML.js will then do the overlay. + +defineMacro("\\not", '\\html@mathml{\\mathrel{\\mathrlap\\@not}}{\\char"338}'); // Negated symbols from base/fontmath.ltx: +// \def\neq{\not=} \let\ne=\neq +// \DeclareRobustCommand +// \notin{\mathrel{\m@th\mathpalette\c@ncel\in}} +// \def\c@ncel#1#2{\m@th\ooalign{$\hfil#1\mkern1mu/\hfil$\crcr$#1#2$}} + +defineMacro("\\neq", "\\html@mathml{\\mathrel{\\not=}}{\\mathrel{\\char`≠}}"); +defineMacro("\\ne", "\\neq"); +defineMacro("\u2260", "\\neq"); +defineMacro("\\notin", "\\html@mathml{\\mathrel{{\\in}\\mathllap{/\\mskip1mu}}}" + "{\\mathrel{\\char`∉}}"); +defineMacro("\u2209", "\\notin"); // Unicode stacked relations + +defineMacro("\u2258", "\\html@mathml{" + "\\mathrel{=\\kern{-1em}\\raisebox{0.4em}{$\\scriptsize\\frown$}}" + "}{\\mathrel{\\char`\u2258}}"); +defineMacro("\u2259", "\\html@mathml{\\stackrel{\\tiny\\wedge}{=}}{\\mathrel{\\char`\u2258}}"); +defineMacro("\u225A", "\\html@mathml{\\stackrel{\\tiny\\vee}{=}}{\\mathrel{\\char`\u225A}}"); +defineMacro("\u225B", "\\html@mathml{\\stackrel{\\scriptsize\\star}{=}}" + "{\\mathrel{\\char`\u225B}}"); +defineMacro("\u225D", "\\html@mathml{\\stackrel{\\tiny\\mathrm{def}}{=}}" + "{\\mathrel{\\char`\u225D}}"); +defineMacro("\u225E", "\\html@mathml{\\stackrel{\\tiny\\mathrm{m}}{=}}" + "{\\mathrel{\\char`\u225E}}"); +defineMacro("\u225F", "\\html@mathml{\\stackrel{\\tiny?}{=}}{\\mathrel{\\char`\u225F}}"); // Misc Unicode + +defineMacro("\u27C2", "\\perp"); +defineMacro("\u203C", "\\mathclose{!\\mkern-0.8mu!}"); +defineMacro("\u220C", "\\notni"); +defineMacro("\u231C", "\\ulcorner"); +defineMacro("\u231D", "\\urcorner"); +defineMacro("\u231E", "\\llcorner"); +defineMacro("\u231F", "\\lrcorner"); +defineMacro("\xA9", "\\copyright"); +defineMacro("\xAE", "\\textregistered"); +defineMacro("\uFE0F", "\\textregistered"); // The KaTeX fonts have corners at codepoints that don't match Unicode. +// For MathML purposes, use the Unicode code point. + +defineMacro("\\ulcorner", "\\html@mathml{\\@ulcorner}{\\mathop{\\char\"231c}}"); +defineMacro("\\urcorner", "\\html@mathml{\\@urcorner}{\\mathop{\\char\"231d}}"); +defineMacro("\\llcorner", "\\html@mathml{\\@llcorner}{\\mathop{\\char\"231e}}"); +defineMacro("\\lrcorner", "\\html@mathml{\\@lrcorner}{\\mathop{\\char\"231f}}"); ////////////////////////////////////////////////////////////////////// +// LaTeX_2ε +// \vdots{\vbox{\baselineskip4\p@ \lineskiplimit\z@ +// \kern6\p@\hbox{.}\hbox{.}\hbox{.}}} +// We'll call \varvdots, which gets a glyph from symbols.js. +// The zero-width rule gets us an equivalent to the vertical 6pt kern. + +defineMacro("\\vdots", "\\mathord{\\varvdots\\rule{0pt}{15pt}}"); +defineMacro("\u22EE", "\\vdots"); ////////////////////////////////////////////////////////////////////// +// 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}. + +defineMacro("\\varGamma", "\\mathit{\\Gamma}"); +defineMacro("\\varDelta", "\\mathit{\\Delta}"); +defineMacro("\\varTheta", "\\mathit{\\Theta}"); +defineMacro("\\varLambda", "\\mathit{\\Lambda}"); +defineMacro("\\varXi", "\\mathit{\\Xi}"); +defineMacro("\\varPi", "\\mathit{\\Pi}"); +defineMacro("\\varSigma", "\\mathit{\\Sigma}"); +defineMacro("\\varUpsilon", "\\mathit{\\Upsilon}"); +defineMacro("\\varPhi", "\\mathit{\\Phi}"); +defineMacro("\\varPsi", "\\mathit{\\Psi}"); +defineMacro("\\varOmega", "\\mathit{\\Omega}"); //\newcommand{\substack}[1]{\subarray{c}#1\endsubarray} + +defineMacro("\\substack", "\\begin{subarray}{c}#1\\end{subarray}"); // \renewcommand{\colon}{\nobreak\mskip2mu\mathpunct{}\nonscript +// \mkern-\thinmuskip{:}\mskip6muplus1mu\relax} + +defineMacro("\\colon", "\\nobreak\\mskip2mu\\mathpunct{}" + "\\mathchoice{\\mkern-3mu}{\\mkern-3mu}{}{}{:}\\mskip6mu"); // \newcommand{\boxed}[1]{\fbox{\m@th$\displaystyle#1$}} + +defineMacro("\\boxed", "\\fbox{$\\displaystyle{#1}$}"); // \def\iff{\DOTSB\;\Longleftrightarrow\;} +// \def\implies{\DOTSB\;\Longrightarrow\;} +// \def\impliedby{\DOTSB\;\Longleftarrow\;} + +defineMacro("\\iff", "\\DOTSB\\;\\Longleftrightarrow\\;"); +defineMacro("\\implies", "\\DOTSB\\;\\Longrightarrow\\;"); +defineMacro("\\impliedby", "\\DOTSB\\;\\Longleftarrow\\;"); // AMSMath's automatic \dots, based on \mdots@@ macro. + +var dotsByToken = { + ',': '\\dotsc', + '\\not': '\\dotsb', + // \keybin@ checks for the following: + '+': '\\dotsb', + '=': '\\dotsb', + '<': '\\dotsb', + '>': '\\dotsb', + '-': '\\dotsb', + '*': '\\dotsb', + ':': '\\dotsb', + // Symbols whose definition starts with \DOTSB: + '\\DOTSB': '\\dotsb', + '\\coprod': '\\dotsb', + '\\bigvee': '\\dotsb', + '\\bigwedge': '\\dotsb', + '\\biguplus': '\\dotsb', + '\\bigcap': '\\dotsb', + '\\bigcup': '\\dotsb', + '\\prod': '\\dotsb', + '\\sum': '\\dotsb', + '\\bigotimes': '\\dotsb', + '\\bigoplus': '\\dotsb', + '\\bigodot': '\\dotsb', + '\\bigsqcup': '\\dotsb', + '\\And': '\\dotsb', + '\\longrightarrow': '\\dotsb', + '\\Longrightarrow': '\\dotsb', + '\\longleftarrow': '\\dotsb', + '\\Longleftarrow': '\\dotsb', + '\\longleftrightarrow': '\\dotsb', + '\\Longleftrightarrow': '\\dotsb', + '\\mapsto': '\\dotsb', + '\\longmapsto': '\\dotsb', + '\\hookrightarrow': '\\dotsb', + '\\doteq': '\\dotsb', + // Symbols whose definition starts with \mathbin: + '\\mathbin': '\\dotsb', + // Symbols whose definition starts with \mathrel: + '\\mathrel': '\\dotsb', + '\\relbar': '\\dotsb', + '\\Relbar': '\\dotsb', + '\\xrightarrow': '\\dotsb', + '\\xleftarrow': '\\dotsb', + // Symbols whose definition starts with \DOTSI: + '\\DOTSI': '\\dotsi', + '\\int': '\\dotsi', + '\\oint': '\\dotsi', + '\\iint': '\\dotsi', + '\\iiint': '\\dotsi', + '\\iiiint': '\\dotsi', + '\\idotsint': '\\dotsi', + // Symbols whose definition starts with \DOTSX: + '\\DOTSX': '\\dotsx' +}; +defineMacro("\\dots", function (context) { + // TODO: If used in text mode, should expand to \textellipsis. + // However, in KaTeX, \textellipsis and \ldots behave the same + // (in text mode), and it's unlikely we'd see any of the math commands + // that affect the behavior of \dots when in text mode. So fine for now + // (until we support \ifmmode ... \else ... \fi). + var thedots = '\\dotso'; + var next = context.expandAfterFuture().text; + + if (next in dotsByToken) { + thedots = dotsByToken[next]; + } else if (next.substr(0, 4) === '\\not') { + thedots = '\\dotsb'; + } else if (next in src_symbols.math) { + if (utils.contains(['bin', 'rel'], src_symbols.math[next].group)) { + thedots = '\\dotsb'; + } + } + + return thedots; +}); +var spaceAfterDots = { + // \rightdelim@ checks for the following: + ')': true, + ']': true, + '\\rbrack': true, + '\\}': true, + '\\rbrace': true, + '\\rangle': true, + '\\rceil': true, + '\\rfloor': true, + '\\rgroup': true, + '\\rmoustache': true, + '\\right': true, + '\\bigr': true, + '\\biggr': true, + '\\Bigr': true, + '\\Biggr': true, + // \extra@ also tests for the following: + '$': true, + // \extrap@ checks for the following: + ';': true, + '.': true, + ',': true +}; +defineMacro("\\dotso", function (context) { + var next = context.future().text; + + if (next in spaceAfterDots) { + return "\\ldots\\,"; + } else { + return "\\ldots"; + } +}); +defineMacro("\\dotsc", function (context) { + var next = context.future().text; // \dotsc uses \extra@ but not \extrap@, instead specially checking for + // ';' and '.', but doesn't check for ','. + + if (next in spaceAfterDots && next !== ',') { + return "\\ldots\\,"; + } else { + return "\\ldots"; + } +}); +defineMacro("\\cdots", function (context) { + var next = context.future().text; + + if (next in spaceAfterDots) { + return "\\@cdots\\,"; + } else { + return "\\@cdots"; + } +}); +defineMacro("\\dotsb", "\\cdots"); +defineMacro("\\dotsm", "\\cdots"); +defineMacro("\\dotsi", "\\!\\cdots"); // amsmath doesn't actually define \dotsx, but \dots followed by a macro +// starting with \DOTSX implies \dotso, and then \extra@ detects this case +// and forces the added `\,`. + +defineMacro("\\dotsx", "\\ldots\\,"); // \let\DOTSI\relax +// \let\DOTSB\relax +// \let\DOTSX\relax + +defineMacro("\\DOTSI", "\\relax"); +defineMacro("\\DOTSB", "\\relax"); +defineMacro("\\DOTSX", "\\relax"); // Spacing, based on amsmath.sty's override of LaTeX defaults +// \DeclareRobustCommand{\tmspace}[3]{% +// \ifmmode\mskip#1#2\else\kern#1#3\fi\relax} + +defineMacro("\\tmspace", "\\TextOrMath{\\kern#1#3}{\\mskip#1#2}\\relax"); // \renewcommand{\,}{\tmspace+\thinmuskip{.1667em}} +// TODO: math mode should use \thinmuskip + +defineMacro("\\,", "\\tmspace+{3mu}{.1667em}"); // \let\thinspace\, + +defineMacro("\\thinspace", "\\,"); // \def\>{\mskip\medmuskip} +// \renewcommand{\:}{\tmspace+\medmuskip{.2222em}} +// TODO: \> and math mode of \: should use \medmuskip = 4mu plus 2mu minus 4mu + +defineMacro("\\>", "\\mskip{4mu}"); +defineMacro("\\:", "\\tmspace+{4mu}{.2222em}"); // \let\medspace\: + +defineMacro("\\medspace", "\\:"); // \renewcommand{\;}{\tmspace+\thickmuskip{.2777em}} +// TODO: math mode should use \thickmuskip = 5mu plus 5mu + +defineMacro("\\;", "\\tmspace+{5mu}{.2777em}"); // \let\thickspace\; + +defineMacro("\\thickspace", "\\;"); // \renewcommand{\!}{\tmspace-\thinmuskip{.1667em}} +// TODO: math mode should use \thinmuskip + +defineMacro("\\!", "\\tmspace-{3mu}{.1667em}"); // \let\negthinspace\! + +defineMacro("\\negthinspace", "\\!"); // \newcommand{\negmedspace}{\tmspace-\medmuskip{.2222em}} +// TODO: math mode should use \medmuskip + +defineMacro("\\negmedspace", "\\tmspace-{4mu}{.2222em}"); // \newcommand{\negthickspace}{\tmspace-\thickmuskip{.2777em}} +// TODO: math mode should use \thickmuskip + +defineMacro("\\negthickspace", "\\tmspace-{5mu}{.277em}"); // \def\enspace{\kern.5em } + +defineMacro("\\enspace", "\\kern.5em "); // \def\enskip{\hskip.5em\relax} + +defineMacro("\\enskip", "\\hskip.5em\\relax"); // \def\quad{\hskip1em\relax} + +defineMacro("\\quad", "\\hskip1em\\relax"); // \def\qquad{\hskip2em\relax} + +defineMacro("\\qquad", "\\hskip2em\\relax"); // \tag@in@display form of \tag + +defineMacro("\\tag", "\\@ifstar\\tag@literal\\tag@paren"); +defineMacro("\\tag@paren", "\\tag@literal{({#1})}"); +defineMacro("\\tag@literal", function (context) { + if (context.macros.get("\\df@tag")) { + throw new src_ParseError("Multiple \\tag"); + } + + return "\\gdef\\df@tag{\\text{#1}}"; +}); // \renewcommand{\bmod}{\nonscript\mskip-\medmuskip\mkern5mu\mathbin +// {\operator@font mod}\penalty900 +// \mkern5mu\nonscript\mskip-\medmuskip} +// \newcommand{\pod}[1]{\allowbreak +// \if@display\mkern18mu\else\mkern8mu\fi(#1)} +// \renewcommand{\pmod}[1]{\pod{{\operator@font mod}\mkern6mu#1}} +// \newcommand{\mod}[1]{\allowbreak\if@display\mkern18mu +// \else\mkern12mu\fi{\operator@font mod}\,\,#1} +// TODO: math mode should use \medmuskip = 4mu plus 2mu minus 4mu + +defineMacro("\\bmod", "\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}" + "\\mathbin{\\rm mod}" + "\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}"); +defineMacro("\\pod", "\\allowbreak" + "\\mathchoice{\\mkern18mu}{\\mkern8mu}{\\mkern8mu}{\\mkern8mu}(#1)"); +defineMacro("\\pmod", "\\pod{{\\rm mod}\\mkern6mu#1}"); +defineMacro("\\mod", "\\allowbreak" + "\\mathchoice{\\mkern18mu}{\\mkern12mu}{\\mkern12mu}{\\mkern12mu}" + "{\\rm mod}\\,\\,#1"); // \pmb -- A simulation of bold. +// The version in ambsy.sty works by typesetting three copies of the argument +// with small offsets. We use two copies. We omit the vertical offset because +// of rendering problems that makeVList encounters in Safari. + +defineMacro("\\pmb", "\\html@mathml{" + "\\@binrel{#1}{\\mathrlap{#1}\\kern0.5px#1}}" + "{\\mathbf{#1}}"); ////////////////////////////////////////////////////////////////////// +// LaTeX source2e +// \expandafter\let\expandafter\@normalcr +// \csname\expandafter\@gobble\string\\ \endcsname +// \DeclareRobustCommand\newline{\@normalcr\relax} + +defineMacro("\\newline", "\\\\\\relax"); // \def\TeX{T\kern-.1667em\lower.5ex\hbox{E}\kern-.125emX\@} +// TODO: Doesn't normally work in math mode because \@ fails. KaTeX doesn't +// support \@ yet, so that's omitted, and we add \text so that the result +// doesn't look funny in math mode. + +defineMacro("\\TeX", "\\textrm{\\html@mathml{" + "T\\kern-.1667em\\raisebox{-.5ex}{E}\\kern-.125emX" + "}{TeX}}"); // \DeclareRobustCommand{\LaTeX}{L\kern-.36em% +// {\sbox\z@ T% +// \vbox to\ht\z@{\hbox{\check@mathfonts +// \fontsize\sf@size\z@ +// \math@fontsfalse\selectfont +// A}% +// \vss}% +// }% +// \kern-.15em% +// \TeX} +// This code aligns the top of the A with the T (from the perspective of TeX's +// boxes, though visually the A appears to extend above slightly). +// We compute the corresponding \raisebox when A is rendered in \normalsize +// \scriptstyle, which has a scale factor of 0.7 (see Options.js). + +var latexRaiseA = makeEm(fontMetricsData["Main-Regular"]["T".charCodeAt(0)][1] - 0.7 * fontMetricsData["Main-Regular"]["A".charCodeAt(0)][1]); +defineMacro("\\LaTeX", "\\textrm{\\html@mathml{" + ("L\\kern-.36em\\raisebox{" + latexRaiseA + "}{\\scriptstyle A}") + "\\kern-.15em\\TeX}{LaTeX}}"); // New KaTeX logo based on tweaking LaTeX logo + +defineMacro("\\KaTeX", "\\textrm{\\html@mathml{" + ("K\\kern-.17em\\raisebox{" + latexRaiseA + "}{\\scriptstyle A}") + "\\kern-.15em\\TeX}{KaTeX}}"); // \DeclareRobustCommand\hspace{\@ifstar\@hspacer\@hspace} +// \def\@hspace#1{\hskip #1\relax} +// \def\@hspacer#1{\vrule \@width\z@\nobreak +// \hskip #1\hskip \z@skip} + +defineMacro("\\hspace", "\\@ifstar\\@hspacer\\@hspace"); +defineMacro("\\@hspace", "\\hskip #1\\relax"); +defineMacro("\\@hspacer", "\\rule{0pt}{0pt}\\hskip #1\\relax"); ////////////////////////////////////////////////////////////////////// +// mathtools.sty +//\providecommand\ordinarycolon{:} + +defineMacro("\\ordinarycolon", ":"); //\def\vcentcolon{\mathrel{\mathop\ordinarycolon}} +//TODO(edemaine): Not yet centered. Fix via \raisebox or #726 + +defineMacro("\\vcentcolon", "\\mathrel{\\mathop\\ordinarycolon}"); // \providecommand*\dblcolon{\vcentcolon\mathrel{\mkern-.9mu}\vcentcolon} + +defineMacro("\\dblcolon", "\\html@mathml{" + "\\mathrel{\\vcentcolon\\mathrel{\\mkern-.9mu}\\vcentcolon}}" + "{\\mathop{\\char\"2237}}"); // \providecommand*\coloneqq{\vcentcolon\mathrel{\mkern-1.2mu}=} + +defineMacro("\\coloneqq", "\\html@mathml{" + "\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}=}}" + "{\\mathop{\\char\"2254}}"); // ≔ +// \providecommand*\Coloneqq{\dblcolon\mathrel{\mkern-1.2mu}=} + +defineMacro("\\Coloneqq", "\\html@mathml{" + "\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}=}}" + "{\\mathop{\\char\"2237\\char\"3d}}"); // \providecommand*\coloneq{\vcentcolon\mathrel{\mkern-1.2mu}\mathrel{-}} + +defineMacro("\\coloneq", "\\html@mathml{" + "\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}}" + "{\\mathop{\\char\"3a\\char\"2212}}"); // \providecommand*\Coloneq{\dblcolon\mathrel{\mkern-1.2mu}\mathrel{-}} + +defineMacro("\\Coloneq", "\\html@mathml{" + "\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}}" + "{\\mathop{\\char\"2237\\char\"2212}}"); // \providecommand*\eqqcolon{=\mathrel{\mkern-1.2mu}\vcentcolon} + +defineMacro("\\eqqcolon", "\\html@mathml{" + "\\mathrel{=\\mathrel{\\mkern-1.2mu}\\vcentcolon}}" + "{\\mathop{\\char\"2255}}"); // ≕ +// \providecommand*\Eqqcolon{=\mathrel{\mkern-1.2mu}\dblcolon} + +defineMacro("\\Eqqcolon", "\\html@mathml{" + "\\mathrel{=\\mathrel{\\mkern-1.2mu}\\dblcolon}}" + "{\\mathop{\\char\"3d\\char\"2237}}"); // \providecommand*\eqcolon{\mathrel{-}\mathrel{\mkern-1.2mu}\vcentcolon} + +defineMacro("\\eqcolon", "\\html@mathml{" + "\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\vcentcolon}}" + "{\\mathop{\\char\"2239}}"); // \providecommand*\Eqcolon{\mathrel{-}\mathrel{\mkern-1.2mu}\dblcolon} + +defineMacro("\\Eqcolon", "\\html@mathml{" + "\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\dblcolon}}" + "{\\mathop{\\char\"2212\\char\"2237}}"); // \providecommand*\colonapprox{\vcentcolon\mathrel{\mkern-1.2mu}\approx} + +defineMacro("\\colonapprox", "\\html@mathml{" + "\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\approx}}" + "{\\mathop{\\char\"3a\\char\"2248}}"); // \providecommand*\Colonapprox{\dblcolon\mathrel{\mkern-1.2mu}\approx} + +defineMacro("\\Colonapprox", "\\html@mathml{" + "\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\approx}}" + "{\\mathop{\\char\"2237\\char\"2248}}"); // \providecommand*\colonsim{\vcentcolon\mathrel{\mkern-1.2mu}\sim} + +defineMacro("\\colonsim", "\\html@mathml{" + "\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\sim}}" + "{\\mathop{\\char\"3a\\char\"223c}}"); // \providecommand*\Colonsim{\dblcolon\mathrel{\mkern-1.2mu}\sim} + +defineMacro("\\Colonsim", "\\html@mathml{" + "\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\sim}}" + "{\\mathop{\\char\"2237\\char\"223c}}"); // Some Unicode characters are implemented with macros to mathtools functions. + +defineMacro("\u2237", "\\dblcolon"); // :: + +defineMacro("\u2239", "\\eqcolon"); // -: + +defineMacro("\u2254", "\\coloneqq"); // := + +defineMacro("\u2255", "\\eqqcolon"); // =: + +defineMacro("\u2A74", "\\Coloneqq"); // ::= +////////////////////////////////////////////////////////////////////// +// colonequals.sty +// Alternate names for mathtools's macros: + +defineMacro("\\ratio", "\\vcentcolon"); +defineMacro("\\coloncolon", "\\dblcolon"); +defineMacro("\\colonequals", "\\coloneqq"); +defineMacro("\\coloncolonequals", "\\Coloneqq"); +defineMacro("\\equalscolon", "\\eqqcolon"); +defineMacro("\\equalscoloncolon", "\\Eqqcolon"); +defineMacro("\\colonminus", "\\coloneq"); +defineMacro("\\coloncolonminus", "\\Coloneq"); +defineMacro("\\minuscolon", "\\eqcolon"); +defineMacro("\\minuscoloncolon", "\\Eqcolon"); // \colonapprox name is same in mathtools and colonequals. + +defineMacro("\\coloncolonapprox", "\\Colonapprox"); // \colonsim name is same in mathtools and colonequals. + +defineMacro("\\coloncolonsim", "\\Colonsim"); // Additional macros, implemented by analogy with mathtools definitions: + +defineMacro("\\simcolon", "\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\vcentcolon}"); +defineMacro("\\simcoloncolon", "\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\dblcolon}"); +defineMacro("\\approxcolon", "\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\vcentcolon}"); +defineMacro("\\approxcoloncolon", "\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\dblcolon}"); // Present in newtxmath, pxfonts and txfonts + +defineMacro("\\notni", "\\html@mathml{\\not\\ni}{\\mathrel{\\char`\u220C}}"); +defineMacro("\\limsup", "\\DOTSB\\operatorname*{lim\\,sup}"); +defineMacro("\\liminf", "\\DOTSB\\operatorname*{lim\\,inf}"); ////////////////////////////////////////////////////////////////////// +// From amsopn.sty + +defineMacro("\\injlim", "\\DOTSB\\operatorname*{inj\\,lim}"); +defineMacro("\\projlim", "\\DOTSB\\operatorname*{proj\\,lim}"); +defineMacro("\\varlimsup", "\\DOTSB\\operatorname*{\\overline{lim}}"); +defineMacro("\\varliminf", "\\DOTSB\\operatorname*{\\underline{lim}}"); +defineMacro("\\varinjlim", "\\DOTSB\\operatorname*{\\underrightarrow{lim}}"); +defineMacro("\\varprojlim", "\\DOTSB\\operatorname*{\\underleftarrow{lim}}"); ////////////////////////////////////////////////////////////////////// +// MathML alternates for KaTeX glyphs in the Unicode private area + +defineMacro("\\gvertneqq", "\\html@mathml{\\@gvertneqq}{\u2269}"); +defineMacro("\\lvertneqq", "\\html@mathml{\\@lvertneqq}{\u2268}"); +defineMacro("\\ngeqq", "\\html@mathml{\\@ngeqq}{\u2271}"); +defineMacro("\\ngeqslant", "\\html@mathml{\\@ngeqslant}{\u2271}"); +defineMacro("\\nleqq", "\\html@mathml{\\@nleqq}{\u2270}"); +defineMacro("\\nleqslant", "\\html@mathml{\\@nleqslant}{\u2270}"); +defineMacro("\\nshortmid", "\\html@mathml{\\@nshortmid}{∤}"); +defineMacro("\\nshortparallel", "\\html@mathml{\\@nshortparallel}{∦}"); +defineMacro("\\nsubseteqq", "\\html@mathml{\\@nsubseteqq}{\u2288}"); +defineMacro("\\nsupseteqq", "\\html@mathml{\\@nsupseteqq}{\u2289}"); +defineMacro("\\varsubsetneq", "\\html@mathml{\\@varsubsetneq}{⊊}"); +defineMacro("\\varsubsetneqq", "\\html@mathml{\\@varsubsetneqq}{⫋}"); +defineMacro("\\varsupsetneq", "\\html@mathml{\\@varsupsetneq}{⊋}"); +defineMacro("\\varsupsetneqq", "\\html@mathml{\\@varsupsetneqq}{⫌}"); +defineMacro("\\imath", "\\html@mathml{\\@imath}{\u0131}"); +defineMacro("\\jmath", "\\html@mathml{\\@jmath}{\u0237}"); ////////////////////////////////////////////////////////////////////// +// stmaryrd and semantic +// The stmaryrd and semantic packages render the next four items by calling a +// glyph. Those glyphs do not exist in the KaTeX fonts. Hence the macros. + +defineMacro("\\llbracket", "\\html@mathml{" + "\\mathopen{[\\mkern-3.2mu[}}" + "{\\mathopen{\\char`\u27E6}}"); +defineMacro("\\rrbracket", "\\html@mathml{" + "\\mathclose{]\\mkern-3.2mu]}}" + "{\\mathclose{\\char`\u27E7}}"); +defineMacro("\u27E6", "\\llbracket"); // blackboard bold [ + +defineMacro("\u27E7", "\\rrbracket"); // blackboard bold ] + +defineMacro("\\lBrace", "\\html@mathml{" + "\\mathopen{\\{\\mkern-3.2mu[}}" + "{\\mathopen{\\char`\u2983}}"); +defineMacro("\\rBrace", "\\html@mathml{" + "\\mathclose{]\\mkern-3.2mu\\}}}" + "{\\mathclose{\\char`\u2984}}"); +defineMacro("\u2983", "\\lBrace"); // blackboard bold { + +defineMacro("\u2984", "\\rBrace"); // blackboard bold } +// TODO: Create variable sized versions of the last two items. I believe that +// will require new font glyphs. +// The stmaryrd function `\minuso` provides a "Plimsoll" symbol that +// superimposes the characters \circ and \mathminus. Used in chemistry. + +defineMacro("\\minuso", "\\mathbin{\\html@mathml{" + "{\\mathrlap{\\mathchoice{\\kern{0.145em}}{\\kern{0.145em}}" + "{\\kern{0.1015em}}{\\kern{0.0725em}}\\circ}{-}}}" + "{\\char`⦵}}"); +defineMacro("⦵", "\\minuso"); ////////////////////////////////////////////////////////////////////// +// 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} + +defineMacro("\\darr", "\\downarrow"); +defineMacro("\\dArr", "\\Downarrow"); +defineMacro("\\Darr", "\\Downarrow"); +defineMacro("\\lang", "\\langle"); +defineMacro("\\rang", "\\rangle"); +defineMacro("\\uarr", "\\uparrow"); +defineMacro("\\uArr", "\\Uparrow"); +defineMacro("\\Uarr", "\\Uparrow"); +defineMacro("\\N", "\\mathbb{N}"); +defineMacro("\\R", "\\mathbb{R}"); +defineMacro("\\Z", "\\mathbb{Z}"); +defineMacro("\\alef", "\\aleph"); +defineMacro("\\alefsym", "\\aleph"); +defineMacro("\\Alpha", "\\mathrm{A}"); +defineMacro("\\Beta", "\\mathrm{B}"); +defineMacro("\\bull", "\\bullet"); +defineMacro("\\Chi", "\\mathrm{X}"); +defineMacro("\\clubs", "\\clubsuit"); +defineMacro("\\cnums", "\\mathbb{C}"); +defineMacro("\\Complex", "\\mathbb{C}"); +defineMacro("\\Dagger", "\\ddagger"); +defineMacro("\\diamonds", "\\diamondsuit"); +defineMacro("\\empty", "\\emptyset"); +defineMacro("\\Epsilon", "\\mathrm{E}"); +defineMacro("\\Eta", "\\mathrm{H}"); +defineMacro("\\exist", "\\exists"); +defineMacro("\\harr", "\\leftrightarrow"); +defineMacro("\\hArr", "\\Leftrightarrow"); +defineMacro("\\Harr", "\\Leftrightarrow"); +defineMacro("\\hearts", "\\heartsuit"); +defineMacro("\\image", "\\Im"); +defineMacro("\\infin", "\\infty"); +defineMacro("\\Iota", "\\mathrm{I}"); +defineMacro("\\isin", "\\in"); +defineMacro("\\Kappa", "\\mathrm{K}"); +defineMacro("\\larr", "\\leftarrow"); +defineMacro("\\lArr", "\\Leftarrow"); +defineMacro("\\Larr", "\\Leftarrow"); +defineMacro("\\lrarr", "\\leftrightarrow"); +defineMacro("\\lrArr", "\\Leftrightarrow"); +defineMacro("\\Lrarr", "\\Leftrightarrow"); +defineMacro("\\Mu", "\\mathrm{M}"); +defineMacro("\\natnums", "\\mathbb{N}"); +defineMacro("\\Nu", "\\mathrm{N}"); +defineMacro("\\Omicron", "\\mathrm{O}"); +defineMacro("\\plusmn", "\\pm"); +defineMacro("\\rarr", "\\rightarrow"); +defineMacro("\\rArr", "\\Rightarrow"); +defineMacro("\\Rarr", "\\Rightarrow"); +defineMacro("\\real", "\\Re"); +defineMacro("\\reals", "\\mathbb{R}"); +defineMacro("\\Reals", "\\mathbb{R}"); +defineMacro("\\Rho", "\\mathrm{P}"); +defineMacro("\\sdot", "\\cdot"); +defineMacro("\\sect", "\\S"); +defineMacro("\\spades", "\\spadesuit"); +defineMacro("\\sub", "\\subset"); +defineMacro("\\sube", "\\subseteq"); +defineMacro("\\supe", "\\supseteq"); +defineMacro("\\Tau", "\\mathrm{T}"); +defineMacro("\\thetasym", "\\vartheta"); // TODO: defineMacro("\\varcoppa", "\\\mbox{\\coppa}"); + +defineMacro("\\weierp", "\\wp"); +defineMacro("\\Zeta", "\\mathrm{Z}"); ////////////////////////////////////////////////////////////////////// +// statmath.sty +// https://ctan.math.illinois.edu/macros/latex/contrib/statmath/statmath.pdf + +defineMacro("\\argmin", "\\DOTSB\\operatorname*{arg\\,min}"); +defineMacro("\\argmax", "\\DOTSB\\operatorname*{arg\\,max}"); +defineMacro("\\plim", "\\DOTSB\\mathop{\\operatorname{plim}}\\limits"); ////////////////////////////////////////////////////////////////////// +// braket.sty +// http://ctan.math.washington.edu/tex-archive/macros/latex/contrib/braket/braket.pdf + +defineMacro("\\bra", "\\mathinner{\\langle{#1}|}"); +defineMacro("\\ket", "\\mathinner{|{#1}\\rangle}"); +defineMacro("\\braket", "\\mathinner{\\langle{#1}\\rangle}"); +defineMacro("\\Bra", "\\left\\langle#1\\right|"); +defineMacro("\\Ket", "\\left|#1\\right\\rangle"); ////////////////////////////////////////////////////////////////////// +// actuarialangle.dtx + +defineMacro("\\angln", "{\\angl n}"); // Custom Khan Academy colors, should be moved to an optional package + +defineMacro("\\blue", "\\textcolor{##6495ed}{#1}"); +defineMacro("\\orange", "\\textcolor{##ffa500}{#1}"); +defineMacro("\\pink", "\\textcolor{##ff00af}{#1}"); +defineMacro("\\red", "\\textcolor{##df0030}{#1}"); +defineMacro("\\green", "\\textcolor{##28ae7b}{#1}"); +defineMacro("\\gray", "\\textcolor{gray}{#1}"); +defineMacro("\\purple", "\\textcolor{##9d38bd}{#1}"); +defineMacro("\\blueA", "\\textcolor{##ccfaff}{#1}"); +defineMacro("\\blueB", "\\textcolor{##80f6ff}{#1}"); +defineMacro("\\blueC", "\\textcolor{##63d9ea}{#1}"); +defineMacro("\\blueD", "\\textcolor{##11accd}{#1}"); +defineMacro("\\blueE", "\\textcolor{##0c7f99}{#1}"); +defineMacro("\\tealA", "\\textcolor{##94fff5}{#1}"); +defineMacro("\\tealB", "\\textcolor{##26edd5}{#1}"); +defineMacro("\\tealC", "\\textcolor{##01d1c1}{#1}"); +defineMacro("\\tealD", "\\textcolor{##01a995}{#1}"); +defineMacro("\\tealE", "\\textcolor{##208170}{#1}"); +defineMacro("\\greenA", "\\textcolor{##b6ffb0}{#1}"); +defineMacro("\\greenB", "\\textcolor{##8af281}{#1}"); +defineMacro("\\greenC", "\\textcolor{##74cf70}{#1}"); +defineMacro("\\greenD", "\\textcolor{##1fab54}{#1}"); +defineMacro("\\greenE", "\\textcolor{##0d923f}{#1}"); +defineMacro("\\goldA", "\\textcolor{##ffd0a9}{#1}"); +defineMacro("\\goldB", "\\textcolor{##ffbb71}{#1}"); +defineMacro("\\goldC", "\\textcolor{##ff9c39}{#1}"); +defineMacro("\\goldD", "\\textcolor{##e07d10}{#1}"); +defineMacro("\\goldE", "\\textcolor{##a75a05}{#1}"); +defineMacro("\\redA", "\\textcolor{##fca9a9}{#1}"); +defineMacro("\\redB", "\\textcolor{##ff8482}{#1}"); +defineMacro("\\redC", "\\textcolor{##f9685d}{#1}"); +defineMacro("\\redD", "\\textcolor{##e84d39}{#1}"); +defineMacro("\\redE", "\\textcolor{##bc2612}{#1}"); +defineMacro("\\maroonA", "\\textcolor{##ffbde0}{#1}"); +defineMacro("\\maroonB", "\\textcolor{##ff92c6}{#1}"); +defineMacro("\\maroonC", "\\textcolor{##ed5fa6}{#1}"); +defineMacro("\\maroonD", "\\textcolor{##ca337c}{#1}"); +defineMacro("\\maroonE", "\\textcolor{##9e034e}{#1}"); +defineMacro("\\purpleA", "\\textcolor{##ddd7ff}{#1}"); +defineMacro("\\purpleB", "\\textcolor{##c6b9fc}{#1}"); +defineMacro("\\purpleC", "\\textcolor{##aa87ff}{#1}"); +defineMacro("\\purpleD", "\\textcolor{##7854ab}{#1}"); +defineMacro("\\purpleE", "\\textcolor{##543b78}{#1}"); +defineMacro("\\mintA", "\\textcolor{##f5f9e8}{#1}"); +defineMacro("\\mintB", "\\textcolor{##edf2df}{#1}"); +defineMacro("\\mintC", "\\textcolor{##e0e5cc}{#1}"); +defineMacro("\\grayA", "\\textcolor{##f6f7f7}{#1}"); +defineMacro("\\grayB", "\\textcolor{##f0f1f2}{#1}"); +defineMacro("\\grayC", "\\textcolor{##e3e5e6}{#1}"); +defineMacro("\\grayD", "\\textcolor{##d6d8da}{#1}"); +defineMacro("\\grayE", "\\textcolor{##babec2}{#1}"); +defineMacro("\\grayF", "\\textcolor{##888d93}{#1}"); +defineMacro("\\grayG", "\\textcolor{##626569}{#1}"); +defineMacro("\\grayH", "\\textcolor{##3b3e40}{#1}"); +defineMacro("\\grayI", "\\textcolor{##21242c}{#1}"); +defineMacro("\\kaBlue", "\\textcolor{##314453}{#1}"); +defineMacro("\\kaGreen", "\\textcolor{##71B307}{#1}"); +/** + * This file contains the “gullet” where macros are expanded + * until only non-macro tokens remain. + */ + + + + + + + +// List of commands that act like macros but aren't defined as a macro, +// function, or symbol. Used in `isDefined`. +var implicitCommands = { + "\\relax": true, + // MacroExpander.js + "^": true, + // Parser.js + "_": true, + // Parser.js + "\\limits": true, + // Parser.js + "\\nolimits": true // Parser.js + +}; + +var MacroExpander = /*#__PURE__*/function () { + function MacroExpander(input, settings, mode) { + this.settings = void 0; + this.expansionCount = void 0; + this.lexer = void 0; + this.macros = void 0; + this.stack = void 0; + this.mode = void 0; + this.settings = settings; + this.expansionCount = 0; + this.feed(input); // Make new global namespace + + this.macros = new Namespace(src_macros, settings.macros); + this.mode = mode; + this.stack = []; // contains tokens in REVERSE order + } + /** + * Feed a new input string to the same MacroExpander + * (with existing macros etc.). + */ + + + var _proto = MacroExpander.prototype; + + _proto.feed = function feed(input) { + this.lexer = new Lexer(input, this.settings); + } + /** + * Switches between "text" and "math" modes. + */ + ; + + _proto.switchMode = function switchMode(newMode) { + this.mode = newMode; + } + /** + * Start a new group nesting within all namespaces. + */ + ; + + _proto.beginGroup = function beginGroup() { + this.macros.beginGroup(); + } + /** + * End current group nesting within all namespaces. + */ + ; + + _proto.endGroup = function endGroup() { + this.macros.endGroup(); + } + /** + * Ends all currently nested groups (if any), restoring values before the + * groups began. Useful in case of an error in the middle of parsing. + */ + ; + + _proto.endGroups = function endGroups() { + this.macros.endGroups(); + } + /** + * Returns the topmost token on the stack, without expanding it. + * Similar in behavior to TeX's `\futurelet`. + */ + ; + + _proto.future = function future() { + if (this.stack.length === 0) { + this.pushToken(this.lexer.lex()); + } + + return this.stack[this.stack.length - 1]; + } + /** + * Remove and return the next unexpanded token. + */ + ; + + _proto.popToken = function popToken() { + this.future(); // ensure non-empty stack + + return this.stack.pop(); + } + /** + * Add a given token to the token stack. In particular, this get be used + * to put back a token returned from one of the other methods. + */ + ; + + _proto.pushToken = function pushToken(token) { + this.stack.push(token); + } + /** + * Append an array of tokens to the token stack. + */ + ; + + _proto.pushTokens = function pushTokens(tokens) { + var _this$stack; + + (_this$stack = this.stack).push.apply(_this$stack, tokens); + } + /** + * Find an macro argument without expanding tokens and append the array of + * tokens to the token stack. Uses Token as a container for the result. + */ + ; + + _proto.scanArgument = function scanArgument(isOptional) { + var start; + var end; + var tokens; + + if (isOptional) { + this.consumeSpaces(); // \@ifnextchar gobbles any space following it + + if (this.future().text !== "[") { + return null; + } + + start = this.popToken(); // don't include [ in tokens + + var _this$consumeArg = this.consumeArg(["]"]); + + tokens = _this$consumeArg.tokens; + end = _this$consumeArg.end; + } else { + var _this$consumeArg2 = this.consumeArg(); + + tokens = _this$consumeArg2.tokens; + start = _this$consumeArg2.start; + end = _this$consumeArg2.end; + } // indicate the end of an argument + + + this.pushToken(new Token("EOF", end.loc)); + this.pushTokens(tokens); + return start.range(end, ""); + } + /** + * Consume all following space tokens, without expansion. + */ + ; + + _proto.consumeSpaces = function consumeSpaces() { + for (;;) { + var token = this.future(); + + if (token.text === " ") { + this.stack.pop(); + } else { + break; + } + } + } + /** + * Consume an argument from the token stream, and return the resulting array + * of tokens and start/end token. + */ + ; + + _proto.consumeArg = function consumeArg(delims) { + // The argument for a delimited parameter is the shortest (possibly + // empty) sequence of tokens with properly nested {...} groups that is + // followed ... by this particular list of non-parameter tokens. + // The argument for an undelimited parameter is the next nonblank + // token, unless that token is ‘{’, when the argument will be the + // entire {...} group that follows. + var tokens = []; + var isDelimited = delims && delims.length > 0; + + if (!isDelimited) { + // Ignore spaces between arguments. As the TeXbook says: + // "After you have said ‘\def\row#1#2{...}’, you are allowed to + // put spaces between the arguments (e.g., ‘\row x n’), because + // TeX doesn’t use single spaces as undelimited arguments." + this.consumeSpaces(); + } + + var start = this.future(); + var tok; + var depth = 0; + var match = 0; + + do { + tok = this.popToken(); + tokens.push(tok); + + if (tok.text === "{") { + ++depth; + } else if (tok.text === "}") { + --depth; + + if (depth === -1) { + throw new src_ParseError("Extra }", tok); + } + } else if (tok.text === "EOF") { + throw new src_ParseError("Unexpected end of input in a macro argument" + ", expected '" + (delims && isDelimited ? delims[match] : "}") + "'", tok); + } + + if (delims && isDelimited) { + if ((depth === 0 || depth === 1 && delims[match] === "{") && tok.text === delims[match]) { + ++match; + + if (match === delims.length) { + // don't include delims in tokens + tokens.splice(-match, match); + break; + } + } else { + match = 0; + } + } + } while (depth !== 0 || isDelimited); // If the argument found ... has the form ‘{}’, + // ... the outermost braces enclosing the argument are removed + + + if (start.text === "{" && tokens[tokens.length - 1].text === "}") { + tokens.pop(); + tokens.shift(); + } + + tokens.reverse(); // to fit in with stack order + + return { + tokens: tokens, + start: start, + end: tok + }; + } + /** + * Consume the specified number of (delimited) arguments from the token + * stream and return the resulting array of arguments. + */ + ; + + _proto.consumeArgs = function consumeArgs(numArgs, delimiters) { + if (delimiters) { + if (delimiters.length !== numArgs + 1) { + throw new src_ParseError("The length of delimiters doesn't match the number of args!"); + } + + var delims = delimiters[0]; + + for (var i = 0; i < delims.length; i++) { + var tok = this.popToken(); + + if (delims[i] !== tok.text) { + throw new src_ParseError("Use of the macro doesn't match its definition", tok); + } + } + } + + var args = []; + + for (var _i = 0; _i < numArgs; _i++) { + args.push(this.consumeArg(delimiters && delimiters[_i + 1]).tokens); + } + + return args; + } + /** + * Expand the next token only once if possible. + * + * If the token is expanded, the resulting tokens will be pushed onto + * the stack in reverse order and will be returned as an array, + * also in reverse order. + * + * If not, the next token will be returned without removing it + * from the stack. This case can be detected by a `Token` return value + * instead of an `Array` return value. + * + * In either case, the next token will be on the top of the stack, + * or the stack will be empty. + * + * Used to implement `expandAfterFuture` and `expandNextToken`. + * + * If expandableOnly, only expandable tokens are expanded and + * an undefined control sequence results in an error. + */ + ; + + _proto.expandOnce = function expandOnce(expandableOnly) { + var topToken = this.popToken(); + var name = topToken.text; + var expansion = !topToken.noexpand ? this._getExpansion(name) : null; + + if (expansion == null || expandableOnly && expansion.unexpandable) { + if (expandableOnly && expansion == null && name[0] === "\\" && !this.isDefined(name)) { + throw new src_ParseError("Undefined control sequence: " + name); + } + + this.pushToken(topToken); + return topToken; + } + + this.expansionCount++; + + if (this.expansionCount > this.settings.maxExpand) { + throw new src_ParseError("Too many expansions: infinite loop or " + "need to increase maxExpand setting"); + } + + var tokens = expansion.tokens; + var args = this.consumeArgs(expansion.numArgs, expansion.delimiters); + + if (expansion.numArgs) { + // paste arguments in place of the placeholders + tokens = tokens.slice(); // make a shallow copy + + for (var i = tokens.length - 1; i >= 0; --i) { + var tok = tokens[i]; + + if (tok.text === "#") { + if (i === 0) { + throw new src_ParseError("Incomplete placeholder at end of macro body", tok); + } + + tok = tokens[--i]; // next token on stack + + if (tok.text === "#") { + // ## → # + tokens.splice(i + 1, 1); // drop first # + } else if (/^[1-9]$/.test(tok.text)) { + var _tokens; + + // replace the placeholder with the indicated argument + (_tokens = tokens).splice.apply(_tokens, [i, 2].concat(args[+tok.text - 1])); + } else { + throw new src_ParseError("Not a valid argument number", tok); + } + } + } + } // Concatenate expansion onto top of stack. + + + this.pushTokens(tokens); + return tokens; + } + /** + * Expand the next token only once (if possible), and return the resulting + * top token on the stack (without removing anything from the stack). + * Similar in behavior to TeX's `\expandafter\futurelet`. + * Equivalent to expandOnce() followed by future(). + */ + ; + + _proto.expandAfterFuture = function expandAfterFuture() { + this.expandOnce(); + return this.future(); + } + /** + * Recursively expand first token, then return first non-expandable token. + */ + ; + + _proto.expandNextToken = function expandNextToken() { + for (;;) { + var expanded = this.expandOnce(); // expandOnce returns Token if and only if it's fully expanded. + + if (expanded instanceof Token) { + // \relax stops the expansion, but shouldn't get returned (a + // null return value couldn't get implemented as a function). + // the token after \noexpand is interpreted as if its meaning + // were ‘\relax’ + if (expanded.text === "\\relax" || expanded.treatAsRelax) { + this.stack.pop(); + } else { + return this.stack.pop(); // === expanded + } + } + } // Flow unable to figure out that this pathway is impossible. + // https://github.com/facebook/flow/issues/4808 + + + throw new Error(); // eslint-disable-line no-unreachable + } + /** + * Fully expand the given macro name and return the resulting list of + * tokens, or return `undefined` if no such macro is defined. + */ + ; + + _proto.expandMacro = function expandMacro(name) { + return this.macros.has(name) ? this.expandTokens([new Token(name)]) : undefined; + } + /** + * Fully expand the given token stream and return the resulting list of tokens + */ + ; + + _proto.expandTokens = function expandTokens(tokens) { + var output = []; + var oldStackLength = this.stack.length; + this.pushTokens(tokens); + + while (this.stack.length > oldStackLength) { + var expanded = this.expandOnce(true); // expand only expandable tokens + // expandOnce returns Token if and only if it's fully expanded. + + if (expanded instanceof Token) { + if (expanded.treatAsRelax) { + // the expansion of \noexpand is the token itself + expanded.noexpand = false; + expanded.treatAsRelax = false; + } + + output.push(this.stack.pop()); + } + } + + return output; + } + /** + * Fully expand the given macro name and return the result as a string, + * or return `undefined` if no such macro is defined. + */ + ; + + _proto.expandMacroAsText = function expandMacroAsText(name) { + var tokens = this.expandMacro(name); + + if (tokens) { + return tokens.map(function (token) { + return token.text; + }).join(""); + } else { + return tokens; + } + } + /** + * Returns the expanded macro as a reversed array of tokens and a macro + * argument count. Or returns `null` if no such macro. + */ + ; + + _proto._getExpansion = function _getExpansion(name) { + var definition = this.macros.get(name); + + if (definition == null) { + // mainly checking for undefined here + return definition; + } // If a single character has an associated catcode other than 13 + // (active character), then don't expand it. + + + if (name.length === 1) { + var catcode = this.lexer.catcodes[name]; + + if (catcode != null && catcode !== 13) { + return; + } + } + + var expansion = typeof definition === "function" ? definition(this) : definition; + + if (typeof expansion === "string") { + var numArgs = 0; + + if (expansion.indexOf("#") !== -1) { + var stripped = expansion.replace(/##/g, ""); + + while (stripped.indexOf("#" + (numArgs + 1)) !== -1) { + ++numArgs; + } + } + + var bodyLexer = new Lexer(expansion, this.settings); + var tokens = []; + var tok = bodyLexer.lex(); + + while (tok.text !== "EOF") { + tokens.push(tok); + tok = bodyLexer.lex(); + } + + tokens.reverse(); // to fit in with stack using push and pop + + var expanded = { + tokens: tokens, + numArgs: numArgs + }; + return expanded; + } + + return expansion; + } + /** + * Determine whether a command is currently "defined" (has some + * functionality), meaning that it's a macro (in the current group), + * a function, a symbol, or one of the special commands listed in + * `implicitCommands`. + */ + ; + + _proto.isDefined = function isDefined(name) { + return this.macros.has(name) || src_functions.hasOwnProperty(name) || src_symbols.math.hasOwnProperty(name) || src_symbols.text.hasOwnProperty(name) || implicitCommands.hasOwnProperty(name); + } + /** + * Determine whether a command is expandable. + */ + ; + + _proto.isExpandable = function isExpandable(name) { + var macro = this.macros.get(name); + return macro != null ? typeof macro === "string" || typeof macro === "function" || !macro.unexpandable : src_functions.hasOwnProperty(name) && !src_functions[name].primitive; + }; + + return MacroExpander; +}(); +/* eslint no-constant-condition:0 */ + + + + + + + + + + // Pre-evaluate both modules as unicodeSymbols require String.normalize() + +var unicodeAccents = { + "́": { + "text": "\\'", + "math": "\\acute" + }, + "̀": { + "text": "\\`", + "math": "\\grave" + }, + "̈": { + "text": "\\\"", + "math": "\\ddot" + }, + "̃": { + "text": "\\~", + "math": "\\tilde" + }, + "̄": { + "text": "\\=", + "math": "\\bar" + }, + "̆": { + "text": "\\u", + "math": "\\breve" + }, + "̌": { + "text": "\\v", + "math": "\\check" + }, + "̂": { + "text": "\\^", + "math": "\\hat" + }, + "̇": { + "text": "\\.", + "math": "\\dot" + }, + "̊": { + "text": "\\r", + "math": "\\mathring" + }, + "̋": { + "text": "\\H" + }, + "̧": { + "text": "\\c" + } +}; +var unicodeSymbols = { + "á": "á", + "à": "à", + "ä": "ä", + "ǟ": "ǟ", + "ã": "ã", + "ā": "ā", + "ă": "ă", + "ắ": "ắ", + "ằ": "ằ", + "ẵ": "ẵ", + "ǎ": "ǎ", + "â": "â", + "ấ": "ấ", + "ầ": "ầ", + "ẫ": "ẫ", + "ȧ": "ȧ", + "ǡ": "ǡ", + "å": "å", + "ǻ": "ǻ", + "ḃ": "ḃ", + "ć": "ć", + "ḉ": "ḉ", + "č": "č", + "ĉ": "ĉ", + "ċ": "ċ", + "ç": "ç", + "ď": "ď", + "ḋ": "ḋ", + "ḑ": "ḑ", + "é": "é", + "è": "è", + "ë": "ë", + "ẽ": "ẽ", + "ē": "ē", + "ḗ": "ḗ", + "ḕ": "ḕ", + "ĕ": "ĕ", + "ḝ": "ḝ", + "ě": "ě", + "ê": "ê", + "ế": "ế", + "ề": "ề", + "ễ": "ễ", + "ė": "ė", + "ȩ": "ȩ", + "ḟ": "ḟ", + "ǵ": "ǵ", + "ḡ": "ḡ", + "ğ": "ğ", + "ǧ": "ǧ", + "ĝ": "ĝ", + "ġ": "ġ", + "ģ": "ģ", + "ḧ": "ḧ", + "ȟ": "ȟ", + "ĥ": "ĥ", + "ḣ": "ḣ", + "ḩ": "ḩ", + "í": "í", + "ì": "ì", + "ï": "ï", + "ḯ": "ḯ", + "ĩ": "ĩ", + "ī": "ī", + "ĭ": "ĭ", + "ǐ": "ǐ", + "î": "î", + "ǰ": "ǰ", + "ĵ": "ĵ", + "ḱ": "ḱ", + "ǩ": "ǩ", + "ķ": "ķ", + "ĺ": "ĺ", + "ľ": "ľ", + "ļ": "ļ", + "ḿ": "ḿ", + "ṁ": "ṁ", + "ń": "ń", + "ǹ": "ǹ", + "ñ": "ñ", + "ň": "ň", + "ṅ": "ṅ", + "ņ": "ņ", + "ó": "ó", + "ò": "ò", + "ö": "ö", + "ȫ": "ȫ", + "õ": "õ", + "ṍ": "ṍ", + "ṏ": "ṏ", + "ȭ": "ȭ", + "ō": "ō", + "ṓ": "ṓ", + "ṑ": "ṑ", + "ŏ": "ŏ", + "ǒ": "ǒ", + "ô": "ô", + "ố": "ố", + "ồ": "ồ", + "ỗ": "ỗ", + "ȯ": "ȯ", + "ȱ": "ȱ", + "ő": "ő", + "ṕ": "ṕ", + "ṗ": "ṗ", + "ŕ": "ŕ", + "ř": "ř", + "ṙ": "ṙ", + "ŗ": "ŗ", + "ś": "ś", + "ṥ": "ṥ", + "š": "š", + "ṧ": "ṧ", + "ŝ": "ŝ", + "ṡ": "ṡ", + "ş": "ş", + "ẗ": "ẗ", + "ť": "ť", + "ṫ": "ṫ", + "ţ": "ţ", + "ú": "ú", + "ù": "ù", + "ü": "ü", + "ǘ": "ǘ", + "ǜ": "ǜ", + "ǖ": "ǖ", + "ǚ": "ǚ", + "ũ": "ũ", + "ṹ": "ṹ", + "ū": "ū", + "ṻ": "ṻ", + "ŭ": "ŭ", + "ǔ": "ǔ", + "û": "û", + "ů": "ů", + "ű": "ű", + "ṽ": "ṽ", + "ẃ": "ẃ", + "ẁ": "ẁ", + "ẅ": "ẅ", + "ŵ": "ŵ", + "ẇ": "ẇ", + "ẘ": "ẘ", + "ẍ": "ẍ", + "ẋ": "ẋ", + "ý": "ý", + "ỳ": "ỳ", + "ÿ": "ÿ", + "ỹ": "ỹ", + "ȳ": "ȳ", + "ŷ": "ŷ", + "ẏ": "ẏ", + "ẙ": "ẙ", + "ź": "ź", + "ž": "ž", + "ẑ": "ẑ", + "ż": "ż", + "Á": "Á", + "À": "À", + "Ä": "Ä", + "Ǟ": "Ǟ", + "Ã": "Ã", + "Ā": "Ā", + "Ă": "Ă", + "Ắ": "Ắ", + "Ằ": "Ằ", + "Ẵ": "Ẵ", + "Ǎ": "Ǎ", + "Â": "Â", + "Ấ": "Ấ", + "Ầ": "Ầ", + "Ẫ": "Ẫ", + "Ȧ": "Ȧ", + "Ǡ": "Ǡ", + "Å": "Å", + "Ǻ": "Ǻ", + "Ḃ": "Ḃ", + "Ć": "Ć", + "Ḉ": "Ḉ", + "Č": "Č", + "Ĉ": "Ĉ", + "Ċ": "Ċ", + "Ç": "Ç", + "Ď": "Ď", + "Ḋ": "Ḋ", + "Ḑ": "Ḑ", + "É": "É", + "È": "È", + "Ë": "Ë", + "Ẽ": "Ẽ", + "Ē": "Ē", + "Ḗ": "Ḗ", + "Ḕ": "Ḕ", + "Ĕ": "Ĕ", + "Ḝ": "Ḝ", + "Ě": "Ě", + "Ê": "Ê", + "Ế": "Ế", + "Ề": "Ề", + "Ễ": "Ễ", + "Ė": "Ė", + "Ȩ": "Ȩ", + "Ḟ": "Ḟ", + "Ǵ": "Ǵ", + "Ḡ": "Ḡ", + "Ğ": "Ğ", + "Ǧ": "Ǧ", + "Ĝ": "Ĝ", + "Ġ": "Ġ", + "Ģ": "Ģ", + "Ḧ": "Ḧ", + "Ȟ": "Ȟ", + "Ĥ": "Ĥ", + "Ḣ": "Ḣ", + "Ḩ": "Ḩ", + "Í": "Í", + "Ì": "Ì", + "Ï": "Ï", + "Ḯ": "Ḯ", + "Ĩ": "Ĩ", + "Ī": "Ī", + "Ĭ": "Ĭ", + "Ǐ": "Ǐ", + "Î": "Î", + "İ": "İ", + "Ĵ": "Ĵ", + "Ḱ": "Ḱ", + "Ǩ": "Ǩ", + "Ķ": "Ķ", + "Ĺ": "Ĺ", + "Ľ": "Ľ", + "Ļ": "Ļ", + "Ḿ": "Ḿ", + "Ṁ": "Ṁ", + "Ń": "Ń", + "Ǹ": "Ǹ", + "Ñ": "Ñ", + "Ň": "Ň", + "Ṅ": "Ṅ", + "Ņ": "Ņ", + "Ó": "Ó", + "Ò": "Ò", + "Ö": "Ö", + "Ȫ": "Ȫ", + "Õ": "Õ", + "Ṍ": "Ṍ", + "Ṏ": "Ṏ", + "Ȭ": "Ȭ", + "Ō": "Ō", + "Ṓ": "Ṓ", + "Ṑ": "Ṑ", + "Ŏ": "Ŏ", + "Ǒ": "Ǒ", + "Ô": "Ô", + "Ố": "Ố", + "Ồ": "Ồ", + "Ỗ": "Ỗ", + "Ȯ": "Ȯ", + "Ȱ": "Ȱ", + "Ő": "Ő", + "Ṕ": "Ṕ", + "Ṗ": "Ṗ", + "Ŕ": "Ŕ", + "Ř": "Ř", + "Ṙ": "Ṙ", + "Ŗ": "Ŗ", + "Ś": "Ś", + "Ṥ": "Ṥ", + "Š": "Š", + "Ṧ": "Ṧ", + "Ŝ": "Ŝ", + "Ṡ": "Ṡ", + "Ş": "Ş", + "Ť": "Ť", + "Ṫ": "Ṫ", + "Ţ": "Ţ", + "Ú": "Ú", + "Ù": "Ù", + "Ü": "Ü", + "Ǘ": "Ǘ", + "Ǜ": "Ǜ", + "Ǖ": "Ǖ", + "Ǚ": "Ǚ", + "Ũ": "Ũ", + "Ṹ": "Ṹ", + "Ū": "Ū", + "Ṻ": "Ṻ", + "Ŭ": "Ŭ", + "Ǔ": "Ǔ", + "Û": "Û", + "Ů": "Ů", + "Ű": "Ű", + "Ṽ": "Ṽ", + "Ẃ": "Ẃ", + "Ẁ": "Ẁ", + "Ẅ": "Ẅ", + "Ŵ": "Ŵ", + "Ẇ": "Ẇ", + "Ẍ": "Ẍ", + "Ẋ": "Ẋ", + "Ý": "Ý", + "Ỳ": "Ỳ", + "Ÿ": "Ÿ", + "Ỹ": "Ỹ", + "Ȳ": "Ȳ", + "Ŷ": "Ŷ", + "Ẏ": "Ẏ", + "Ź": "Ź", + "Ž": "Ž", + "Ẑ": "Ẑ", + "Ż": "Ż", + "ά": "ά", + "ὰ": "ὰ", + "ᾱ": "ᾱ", + "ᾰ": "ᾰ", + "έ": "έ", + "ὲ": "ὲ", + "ή": "ή", + "ὴ": "ὴ", + "ί": "ί", + "ὶ": "ὶ", + "ϊ": "ϊ", + "ΐ": "ΐ", + "ῒ": "ῒ", + "ῑ": "ῑ", + "ῐ": "ῐ", + "ό": "ό", + "ὸ": "ὸ", + "ύ": "ύ", + "ὺ": "ὺ", + "ϋ": "ϋ", + "ΰ": "ΰ", + "ῢ": "ῢ", + "ῡ": "ῡ", + "ῠ": "ῠ", + "ώ": "ώ", + "ὼ": "ὼ", + "Ύ": "Ύ", + "Ὺ": "Ὺ", + "Ϋ": "Ϋ", + "Ῡ": "Ῡ", + "Ῠ": "Ῠ", + "Ώ": "Ώ", + "Ὼ": "Ὼ" +}; + +/** + * This file contains the parser used to parse out a TeX expression from the + * input. Since TeX isn't context-free, standard parsers don't work particularly + * well. + * + * The strategy of this parser is as such: + * + * The main functions (the `.parse...` ones) take a position in the current + * parse string to parse tokens from. The lexer (found in Lexer.js, stored at + * this.gullet.lexer) also supports pulling out tokens at arbitrary places. When + * individual tokens are needed at a position, the lexer is called to pull out a + * token, which is then used. + * + * The parser has a property called "mode" indicating the mode that + * the parser is currently in. Currently it has to be one of "math" or + * "text", which denotes whether the current environment is a math-y + * one or a text-y one (e.g. inside \text). Currently, this serves to + * limit the functions which can be used in text mode. + * + * The main functions then return an object which contains the useful data that + * was parsed at its given point, and a new position at the end of the parsed + * data. The main functions can call each other and continue the parsing by + * using the returned position as a new starting point. + * + * There are also extra `.handle...` functions, which pull out some reused + * functionality into self-contained functions. + * + * The functions return ParseNodes. + */ +var Parser = /*#__PURE__*/function () { + function Parser(input, settings) { + this.mode = void 0; + this.gullet = void 0; + this.settings = void 0; + this.leftrightDepth = void 0; + this.nextToken = void 0; + // Start in math mode + this.mode = "math"; // Create a new macro expander (gullet) and (indirectly via that) also a + // new lexer (mouth) for this parser (stomach, in the language of TeX) + + this.gullet = new MacroExpander(input, settings, this.mode); // Store the settings for use in parsing + + this.settings = settings; // Count leftright depth (for \middle errors) + + this.leftrightDepth = 0; + } + /** + * Checks a result to make sure it has the right type, and throws an + * appropriate error otherwise. + */ + + + var _proto = Parser.prototype; + + _proto.expect = function expect(text, consume) { + if (consume === void 0) { + consume = true; + } + + if (this.fetch().text !== text) { + throw new src_ParseError("Expected '" + text + "', got '" + this.fetch().text + "'", this.fetch()); + } + + if (consume) { + this.consume(); + } + } + /** + * Discards the current lookahead token, considering it consumed. + */ + ; + + _proto.consume = function consume() { + this.nextToken = null; + } + /** + * Return the current lookahead token, or if there isn't one (at the + * beginning, or if the previous lookahead token was consume()d), + * fetch the next token as the new lookahead token and return it. + */ + ; + + _proto.fetch = function fetch() { + if (this.nextToken == null) { + this.nextToken = this.gullet.expandNextToken(); + } + + return this.nextToken; + } + /** + * Switches between "text" and "math" modes. + */ + ; + + _proto.switchMode = function switchMode(newMode) { + this.mode = newMode; + this.gullet.switchMode(newMode); + } + /** + * Main parsing function, which parses an entire input. + */ + ; + + _proto.parse = function parse() { + if (!this.settings.globalGroup) { + // Create a group namespace for the math expression. + // (LaTeX creates a new group for every $...$, $$...$$, \[...\].) + this.gullet.beginGroup(); + } // Use old \color behavior (same as LaTeX's \textcolor) if requested. + // We do this within the group for the math expression, so it doesn't + // pollute settings.macros. + + + if (this.settings.colorIsTextColor) { + this.gullet.macros.set("\\color", "\\textcolor"); + } + + try { + // Try to parse the input + var parse = this.parseExpression(false); // If we succeeded, make sure there's an EOF at the end + + this.expect("EOF"); // End the group namespace for the expression + + if (!this.settings.globalGroup) { + this.gullet.endGroup(); + } + + return parse; // Close any leftover groups in case of a parse error. + } finally { + this.gullet.endGroups(); + } + }; + + /** + * Parses an "expression", which is a list of atoms. + * + * `breakOnInfix`: Should the parsing stop when we hit infix nodes? This + * happens when functions have higher precendence han infix + * nodes in implicit parses. + * + * `breakOnTokenText`: The text of the token that the expression should end + * with, or `null` if something else should end the + * expression. + */ + _proto.parseExpression = function parseExpression(breakOnInfix, breakOnTokenText) { + var body = []; // Keep adding atoms to the body until we can't parse any more atoms (either + // we reached the end, a }, or a \right) + + while (true) { + // Ignore spaces in math mode + if (this.mode === "math") { + this.consumeSpaces(); + } + + var lex = this.fetch(); + + if (Parser.endOfExpression.indexOf(lex.text) !== -1) { + break; + } + + if (breakOnTokenText && lex.text === breakOnTokenText) { + break; + } + + if (breakOnInfix && src_functions[lex.text] && src_functions[lex.text].infix) { + break; + } + + var atom = this.parseAtom(breakOnTokenText); + + if (!atom) { + break; + } else if (atom.type === "internal") { + continue; + } + + body.push(atom); + } + + if (this.mode === "text") { + this.formLigatures(body); + } + + return this.handleInfixNodes(body); + } + /** + * Rewrites infix operators such as \over with corresponding commands such + * as \frac. + * + * There can only be one infix operator per group. If there's more than one + * then the expression is ambiguous. This can be resolved by adding {}. + */ + ; + + _proto.handleInfixNodes = function handleInfixNodes(body) { + var overIndex = -1; + var funcName; + + for (var i = 0; i < body.length; i++) { + if (body[i].type === "infix") { + if (overIndex !== -1) { + throw new src_ParseError("only one infix operator per group", body[i].token); + } + + overIndex = i; + funcName = body[i].replaceWith; + } + } + + if (overIndex !== -1 && funcName) { + var numerNode; + var denomNode; + var numerBody = body.slice(0, overIndex); + var denomBody = body.slice(overIndex + 1); + + if (numerBody.length === 1 && numerBody[0].type === "ordgroup") { + numerNode = numerBody[0]; + } else { + numerNode = { + type: "ordgroup", + mode: this.mode, + body: numerBody + }; + } + + if (denomBody.length === 1 && denomBody[0].type === "ordgroup") { + denomNode = denomBody[0]; + } else { + denomNode = { + type: "ordgroup", + mode: this.mode, + body: denomBody + }; + } + + var node; + + if (funcName === "\\\\abovefrac") { + node = this.callFunction(funcName, [numerNode, body[overIndex], denomNode], []); + } else { + node = this.callFunction(funcName, [numerNode, denomNode], []); + } + + return [node]; + } else { + return body; + } + } + /** + * Handle a subscript or superscript with nice errors. + */ + ; + + _proto.handleSupSubscript = function handleSupSubscript(name // For error reporting. + ) { + var symbolToken = this.fetch(); + var symbol = symbolToken.text; + this.consume(); + this.consumeSpaces(); // ignore spaces before sup/subscript argument + + var group = this.parseGroup(name); + + if (!group) { + throw new src_ParseError("Expected group after '" + symbol + "'", symbolToken); + } + + return group; + } + /** + * Converts the textual input of an unsupported command into a text node + * contained within a color node whose color is determined by errorColor + */ + ; + + _proto.formatUnsupportedCmd = function formatUnsupportedCmd(text) { + var textordArray = []; + + for (var i = 0; i < text.length; i++) { + textordArray.push({ + type: "textord", + mode: "text", + text: text[i] + }); + } + + var textNode = { + type: "text", + mode: this.mode, + body: textordArray + }; + var colorNode = { + type: "color", + mode: this.mode, + color: this.settings.errorColor, + body: [textNode] + }; + return colorNode; + } + /** + * Parses a group with optional super/subscripts. + */ + ; + + _proto.parseAtom = function parseAtom(breakOnTokenText) { + // The body of an atom is an implicit group, so that things like + // \left(x\right)^2 work correctly. + var base = this.parseGroup("atom", breakOnTokenText); // In text mode, we don't have superscripts or subscripts + + if (this.mode === "text") { + return base; + } // Note that base may be empty (i.e. null) at this point. + + + var superscript; + var subscript; + + while (true) { + // Guaranteed in math mode, so eat any spaces first. + this.consumeSpaces(); // Lex the first token + + var lex = this.fetch(); + + if (lex.text === "\\limits" || lex.text === "\\nolimits") { + // We got a limit control + if (base && base.type === "op") { + var limits = lex.text === "\\limits"; + base.limits = limits; + base.alwaysHandleSupSub = true; + } else if (base && base.type === "operatorname") { + if (base.alwaysHandleSupSub) { + base.limits = lex.text === "\\limits"; + } + } else { + throw new src_ParseError("Limit controls must follow a math operator", lex); + } + + this.consume(); + } else if (lex.text === "^") { + // We got a superscript start + if (superscript) { + throw new src_ParseError("Double superscript", lex); + } + + superscript = this.handleSupSubscript("superscript"); + } else if (lex.text === "_") { + // We got a subscript start + if (subscript) { + throw new src_ParseError("Double subscript", lex); + } + + subscript = this.handleSupSubscript("subscript"); + } else if (lex.text === "'") { + // We got a prime + if (superscript) { + throw new src_ParseError("Double superscript", lex); + } + + var prime = { + type: "textord", + mode: this.mode, + text: "\\prime" + }; // Many primes can be grouped together, so we handle this here + + var primes = [prime]; + this.consume(); // Keep lexing tokens until we get something that's not a prime + + while (this.fetch().text === "'") { + // For each one, add another prime to the list + primes.push(prime); + this.consume(); + } // If there's a superscript following the primes, combine that + // superscript in with the primes. + + + if (this.fetch().text === "^") { + primes.push(this.handleSupSubscript("superscript")); + } // Put everything into an ordgroup as the superscript + + + superscript = { + type: "ordgroup", + mode: this.mode, + body: primes + }; + } else { + // If it wasn't ^, _, or ', stop parsing super/subscripts + break; + } + } // Base must be set if superscript or subscript are set per logic above, + // but need to check here for type check to pass. + + + if (superscript || subscript) { + // If we got either a superscript or subscript, create a supsub + return { + type: "supsub", + mode: this.mode, + base: base, + sup: superscript, + sub: subscript + }; + } else { + // Otherwise return the original body + return base; + } + } + /** + * Parses an entire function, including its base and all of its arguments. + */ + ; + + _proto.parseFunction = function parseFunction(breakOnTokenText, name // For determining its context + ) { + var token = this.fetch(); + var func = token.text; + var funcData = src_functions[func]; + + if (!funcData) { + return null; + } + + this.consume(); // consume command token + + if (name && name !== "atom" && !funcData.allowedInArgument) { + throw new src_ParseError("Got function '" + func + "' with no arguments" + (name ? " as " + name : ""), token); + } else if (this.mode === "text" && !funcData.allowedInText) { + throw new src_ParseError("Can't use function '" + func + "' in text mode", token); + } else if (this.mode === "math" && funcData.allowedInMath === false) { + throw new src_ParseError("Can't use function '" + func + "' in math mode", token); + } + + var _this$parseArguments = this.parseArguments(func, funcData), + args = _this$parseArguments.args, + optArgs = _this$parseArguments.optArgs; + + return this.callFunction(func, args, optArgs, token, breakOnTokenText); + } + /** + * Call a function handler with a suitable context and arguments. + */ + ; + + _proto.callFunction = function callFunction(name, args, optArgs, token, breakOnTokenText) { + var context = { + funcName: name, + parser: this, + token: token, + breakOnTokenText: breakOnTokenText + }; + var func = src_functions[name]; + + if (func && func.handler) { + return func.handler(context, args, optArgs); + } else { + throw new src_ParseError("No function handler for " + name); + } + } + /** + * Parses the arguments of a function or environment + */ + ; + + _proto.parseArguments = function parseArguments(func, // Should look like "\name" or "\begin{name}". + funcData) { + var totalArgs = funcData.numArgs + funcData.numOptionalArgs; + + if (totalArgs === 0) { + return { + args: [], + optArgs: [] + }; + } + + var args = []; + var optArgs = []; + + for (var i = 0; i < totalArgs; i++) { + var argType = funcData.argTypes && funcData.argTypes[i]; + var isOptional = i < funcData.numOptionalArgs; + + if (funcData.primitive && argType == null || // \sqrt expands into primitive if optional argument doesn't exist + funcData.type === "sqrt" && i === 1 && optArgs[0] == null) { + argType = "primitive"; + } + + var arg = this.parseGroupOfType("argument to '" + func + "'", argType, isOptional); + + if (isOptional) { + optArgs.push(arg); + } else if (arg != null) { + args.push(arg); + } else { + // should be unreachable + throw new src_ParseError("Null argument, please report this as a bug"); + } + } + + return { + args: args, + optArgs: optArgs + }; + } + /** + * Parses a group when the mode is changing. + */ + ; + + _proto.parseGroupOfType = function parseGroupOfType(name, type, optional) { + switch (type) { + case "color": + return this.parseColorGroup(optional); + + case "size": + return this.parseSizeGroup(optional); + + case "url": + return this.parseUrlGroup(optional); + + case "math": + case "text": + return this.parseArgumentGroup(optional, type); + + case "hbox": + { + // hbox argument type wraps the argument in the equivalent of + // \hbox, which is like \text but switching to \textstyle size. + var group = this.parseArgumentGroup(optional, "text"); + return group != null ? { + type: "styling", + mode: group.mode, + body: [group], + style: "text" // simulate \textstyle + + } : null; + } + + case "raw": + { + var token = this.parseStringGroup("raw", optional); + return token != null ? { + type: "raw", + mode: "text", + string: token.text + } : null; + } + + case "primitive": + { + if (optional) { + throw new src_ParseError("A primitive argument cannot be optional"); + } + + var _group = this.parseGroup(name); + + if (_group == null) { + throw new src_ParseError("Expected group as " + name, this.fetch()); + } + + return _group; + } + + case "original": + case null: + case undefined: + return this.parseArgumentGroup(optional); + + default: + throw new src_ParseError("Unknown group type as " + name, this.fetch()); + } + } + /** + * Discard any space tokens, fetching the next non-space token. + */ + ; + + _proto.consumeSpaces = function consumeSpaces() { + while (this.fetch().text === " ") { + this.consume(); + } + } + /** + * Parses a group, essentially returning the string formed by the + * brace-enclosed tokens plus some position information. + */ + ; + + _proto.parseStringGroup = function parseStringGroup(modeName, // Used to describe the mode in error messages. + optional) { + var argToken = this.gullet.scanArgument(optional); + + if (argToken == null) { + return null; + } + + var str = ""; + var nextToken; + + while ((nextToken = this.fetch()).text !== "EOF") { + str += nextToken.text; + this.consume(); + } + + this.consume(); // consume the end of the argument + + argToken.text = str; + return argToken; + } + /** + * Parses a regex-delimited group: the largest sequence of tokens + * whose concatenated strings match `regex`. Returns the string + * formed by the tokens plus some position information. + */ + ; + + _proto.parseRegexGroup = function parseRegexGroup(regex, modeName // Used to describe the mode in error messages. + ) { + var firstToken = this.fetch(); + var lastToken = firstToken; + var str = ""; + var nextToken; + + while ((nextToken = this.fetch()).text !== "EOF" && regex.test(str + nextToken.text)) { + lastToken = nextToken; + str += lastToken.text; + this.consume(); + } + + if (str === "") { + throw new src_ParseError("Invalid " + modeName + ": '" + firstToken.text + "'", firstToken); + } + + return firstToken.range(lastToken, str); + } + /** + * Parses a color description. + */ + ; + + _proto.parseColorGroup = function parseColorGroup(optional) { + var res = this.parseStringGroup("color", optional); + + if (res == null) { + return null; + } + + var match = /^(#[a-f0-9]{3}|#?[a-f0-9]{6}|[a-z]+)$/i.exec(res.text); + + if (!match) { + throw new src_ParseError("Invalid color: '" + res.text + "'", res); + } + + var color = match[0]; + + if (/^[0-9a-f]{6}$/i.test(color)) { + // We allow a 6-digit HTML color spec without a leading "#". + // This follows the xcolor package's HTML color model. + // Predefined color names are all missed by this RegEx pattern. + color = "#" + color; + } + + return { + type: "color-token", + mode: this.mode, + color: color + }; + } + /** + * Parses a size specification, consisting of magnitude and unit. + */ + ; + + _proto.parseSizeGroup = function parseSizeGroup(optional) { + var res; + var isBlank = false; // don't expand before parseStringGroup + + this.gullet.consumeSpaces(); + + if (!optional && this.gullet.future().text !== "{") { + res = this.parseRegexGroup(/^[-+]? *(?:$|\d+|\d+\.\d*|\.\d*) *[a-z]{0,2} *$/, "size"); + } else { + res = this.parseStringGroup("size", optional); + } + + if (!res) { + return null; + } + + if (!optional && res.text.length === 0) { + // Because we've tested for what is !optional, this block won't + // affect \kern, \hspace, etc. It will capture the mandatory arguments + // to \genfrac and \above. + res.text = "0pt"; // Enable \above{} + + isBlank = true; // This is here specifically for \genfrac + } + + var match = /([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/.exec(res.text); + + if (!match) { + throw new src_ParseError("Invalid size: '" + res.text + "'", res); + } + + var data = { + number: +(match[1] + match[2]), + // sign + magnitude, cast to number + unit: match[3] + }; + + if (!validUnit(data)) { + throw new src_ParseError("Invalid unit: '" + data.unit + "'", res); + } + + return { + type: "size", + mode: this.mode, + value: data, + isBlank: isBlank + }; + } + /** + * Parses an URL, checking escaped letters and allowed protocols, + * and setting the catcode of % as an active character (as in \hyperref). + */ + ; + + _proto.parseUrlGroup = function parseUrlGroup(optional) { + this.gullet.lexer.setCatcode("%", 13); // active character + + this.gullet.lexer.setCatcode("~", 12); // other character + + var res = this.parseStringGroup("url", optional); + this.gullet.lexer.setCatcode("%", 14); // comment character + + this.gullet.lexer.setCatcode("~", 13); // active character + + if (res == null) { + return null; + } // hyperref package allows backslashes alone in href, but doesn't + // generate valid links in such cases; we interpret this as + // "undefined" behaviour, and keep them as-is. Some browser will + // replace backslashes with forward slashes. + + + var url = res.text.replace(/\\([#$%&~_^{}])/g, '$1'); + return { + type: "url", + mode: this.mode, + url: url + }; + } + /** + * Parses an argument with the mode specified. + */ + ; + + _proto.parseArgumentGroup = function parseArgumentGroup(optional, mode) { + var argToken = this.gullet.scanArgument(optional); + + if (argToken == null) { + return null; + } + + var outerMode = this.mode; + + if (mode) { + // Switch to specified mode + this.switchMode(mode); + } + + this.gullet.beginGroup(); + var expression = this.parseExpression(false, "EOF"); // TODO: find an alternative way to denote the end + + this.expect("EOF"); // expect the end of the argument + + this.gullet.endGroup(); + var result = { + type: "ordgroup", + mode: this.mode, + loc: argToken.loc, + body: expression + }; + + if (mode) { + // Switch mode back + this.switchMode(outerMode); + } + + return result; + } + /** + * Parses an ordinary group, which is either a single nucleus (like "x") + * or an expression in braces (like "{x+y}") or an implicit group, a group + * that starts at the current position, and ends right before a higher explicit + * group ends, or at EOF. + */ + ; + + _proto.parseGroup = function parseGroup(name, // For error reporting. + breakOnTokenText) { + var firstToken = this.fetch(); + var text = firstToken.text; + var result; // Try to parse an open brace or \begingroup + + if (text === "{" || text === "\\begingroup") { + this.consume(); + var groupEnd = text === "{" ? "}" : "\\endgroup"; + this.gullet.beginGroup(); // If we get a brace, parse an expression + + var expression = this.parseExpression(false, groupEnd); + var lastToken = this.fetch(); + this.expect(groupEnd); // Check that we got a matching closing brace + + this.gullet.endGroup(); + result = { + type: "ordgroup", + mode: this.mode, + loc: SourceLocation.range(firstToken, lastToken), + body: expression, + // A group formed by \begingroup...\endgroup is a semi-simple group + // which doesn't affect spacing in math mode, i.e., is transparent. + // https://tex.stackexchange.com/questions/1930/when-should-one- + // use-begingroup-instead-of-bgroup + semisimple: text === "\\begingroup" || undefined + }; + } else { + // If there exists a function with this name, parse the function. + // Otherwise, just return a nucleus + result = this.parseFunction(breakOnTokenText, name) || this.parseSymbol(); + + if (result == null && text[0] === "\\" && !implicitCommands.hasOwnProperty(text)) { + if (this.settings.throwOnError) { + throw new src_ParseError("Undefined control sequence: " + text, firstToken); + } + + result = this.formatUnsupportedCmd(text); + this.consume(); + } + } + + return result; + } + /** + * Form ligature-like combinations of characters for text mode. + * This includes inputs like "--", "---", "``" and "''". + * The result will simply replace multiple textord nodes with a single + * character in each value by a single textord node having multiple + * characters in its value. The representation is still ASCII source. + * The group will be modified in place. + */ + ; + + _proto.formLigatures = function formLigatures(group) { + var n = group.length - 1; + + for (var i = 0; i < n; ++i) { + var a = group[i]; // $FlowFixMe: Not every node type has a `text` property. + + var v = a.text; + + if (v === "-" && group[i + 1].text === "-") { + if (i + 1 < n && group[i + 2].text === "-") { + group.splice(i, 3, { + type: "textord", + mode: "text", + loc: SourceLocation.range(a, group[i + 2]), + text: "---" + }); + n -= 2; + } else { + group.splice(i, 2, { + type: "textord", + mode: "text", + loc: SourceLocation.range(a, group[i + 1]), + text: "--" + }); + n -= 1; + } + } + + if ((v === "'" || v === "`") && group[i + 1].text === v) { + group.splice(i, 2, { + type: "textord", + mode: "text", + loc: SourceLocation.range(a, group[i + 1]), + text: v + v + }); + n -= 1; + } + } + } + /** + * Parse a single symbol out of the string. Here, we handle single character + * symbols and special functions like \verb. + */ + ; + + _proto.parseSymbol = function parseSymbol() { + var nucleus = this.fetch(); + var text = nucleus.text; + + if (/^\\verb[^a-zA-Z]/.test(text)) { + this.consume(); + var arg = text.slice(5); + var star = arg.charAt(0) === "*"; + + if (star) { + arg = arg.slice(1); + } // Lexer's tokenRegex is constructed to always have matching + // first/last characters. + + + if (arg.length < 2 || arg.charAt(0) !== arg.slice(-1)) { + throw new src_ParseError("\\verb assertion failed --\n please report what input caused this bug"); + } + + arg = arg.slice(1, -1); // remove first and last char + + return { + type: "verb", + mode: "text", + body: arg, + star: star + }; + } // At this point, we should have a symbol, possibly with accents. + // First expand any accented base symbol according to unicodeSymbols. + + + if (unicodeSymbols.hasOwnProperty(text[0]) && !src_symbols[this.mode][text[0]]) { + // This behavior is not strict (XeTeX-compatible) in math mode. + if (this.settings.strict && this.mode === "math") { + this.settings.reportNonstrict("unicodeTextInMathMode", "Accented Unicode text character \"" + text[0] + "\" used in " + "math mode", nucleus); + } + + text = unicodeSymbols[text[0]] + text.substr(1); + } // Strip off any combining characters + + + var match = combiningDiacriticalMarksEndRegex.exec(text); + + if (match) { + text = text.substring(0, match.index); + + if (text === 'i') { + text = "\u0131"; // dotless i, in math and text mode + } else if (text === 'j') { + text = "\u0237"; // dotless j, in math and text mode + } + } // Recognize base symbol + + + var symbol; + + if (src_symbols[this.mode][text]) { + if (this.settings.strict && this.mode === 'math' && extraLatin.indexOf(text) >= 0) { + this.settings.reportNonstrict("unicodeTextInMathMode", "Latin-1/Unicode text character \"" + text[0] + "\" used in " + "math mode", nucleus); + } + + var group = src_symbols[this.mode][text].group; + var loc = SourceLocation.range(nucleus); + var s; + + if (ATOMS.hasOwnProperty(group)) { + // $FlowFixMe + var family = group; + s = { + type: "atom", + mode: this.mode, + family: family, + loc: loc, + text: text + }; + } else { + // $FlowFixMe + s = { + type: group, + mode: this.mode, + loc: loc, + text: text + }; + } // $FlowFixMe + + + symbol = s; + } else if (text.charCodeAt(0) >= 0x80) { + // no symbol for e.g. ^ + if (this.settings.strict) { + if (!supportedCodepoint(text.charCodeAt(0))) { + this.settings.reportNonstrict("unknownSymbol", "Unrecognized Unicode character \"" + text[0] + "\"" + (" (" + text.charCodeAt(0) + ")"), nucleus); + } else if (this.mode === "math") { + this.settings.reportNonstrict("unicodeTextInMathMode", "Unicode text character \"" + text[0] + "\" used in math mode", nucleus); + } + } // All nonmathematical Unicode characters are rendered as if they + // are in text mode (wrapped in \text) because that's what it + // takes to render them in LaTeX. Setting `mode: this.mode` is + // another natural choice (the user requested math mode), but + // this makes it more difficult for getCharacterMetrics() to + // distinguish Unicode characters without metrics and those for + // which we want to simulate the letter M. + + + symbol = { + type: "textord", + mode: "text", + loc: SourceLocation.range(nucleus), + text: text + }; + } else { + return null; // EOF, ^, _, {, }, etc. + } + + this.consume(); // Transform combining characters into accents + + if (match) { + for (var i = 0; i < match[0].length; i++) { + var accent = match[0][i]; + + if (!unicodeAccents[accent]) { + throw new src_ParseError("Unknown accent ' " + accent + "'", nucleus); + } + + var command = unicodeAccents[accent][this.mode] || unicodeAccents[accent].text; + + if (!command) { + throw new src_ParseError("Accent " + accent + " unsupported in " + this.mode + " mode", nucleus); + } + + symbol = { + type: "accent", + mode: this.mode, + loc: SourceLocation.range(nucleus), + label: command, + isStretchy: false, + isShifty: true, + // $FlowFixMe + base: symbol + }; + } + } // $FlowFixMe + + + return symbol; + }; + + return Parser; +}(); + +Parser.endOfExpression = ["}", "\\endgroup", "\\end", "\\right", "&"]; +/** + * Provides a single function for parsing an expression using a Parser + * TODO(emily): Remove this + */ + + + +/** + * Parses an expression using a Parser, then returns the parsed result. + */ +var parseTree = function parseTree(toParse, settings) { + if (!(typeof toParse === 'string' || toParse instanceof String)) { + throw new TypeError('KaTeX can only parse string typed expression'); + } + + var parser = new Parser(toParse, settings); // Blank out any \df@tag to avoid spurious "Duplicate \tag" errors + + delete parser.gullet.macros.current["\\df@tag"]; + var tree = parser.parse(); // Prevent a color definition from persisting between calls to katex.render(). + + delete parser.gullet.macros.current["\\current@color"]; + delete parser.gullet.macros.current["\\color"]; // If the input used \tag, it will set the \df@tag macro to the tag. + // In this case, we separately parse the tag and wrap the tree. + + if (parser.gullet.macros.get("\\df@tag")) { + if (!settings.displayMode) { + throw new src_ParseError("\\tag works only in display equations"); + } + + parser.gullet.feed("\\df@tag"); + tree = [{ + type: "tag", + mode: "text", + body: tree, + tag: parser.parse() + }]; + } + + return tree; +}; + +/* harmony default export */ var src_parseTree = (parseTree); +/* eslint no-console:0 */ + +/** + * This is the main entry point for KaTeX. Here, we expose functions for + * rendering expressions either to DOM nodes or to markup strings. + * + * We also expose the ParseError class to check if errors thrown from KaTeX are + * errors in the expression, or errors in javascript handling. + */ + + + + + + + + + + +/** + * Parse and build an expression, and place that expression in the DOM node + * given. + */ +var render = function render(expression, baseNode, options) { + baseNode.textContent = ""; + var node = renderToDomTree(expression, options).toNode(); + baseNode.appendChild(node); +}; // KaTeX's styles don't work properly in quirks mode. Print out an error, and +// disable rendering. + + +if (typeof document !== "undefined") { + if (document.compatMode !== "CSS1Compat") { + typeof console !== "undefined" && console.warn("Warning: KaTeX doesn't work in quirks mode. Make sure your " + "website has a suitable doctype."); + + render = function render() { + throw new src_ParseError("KaTeX doesn't work in quirks mode."); + }; + } +} +/** + * Parse and build an expression, and return the markup for that. + */ + + +var renderToString = function renderToString(expression, options) { + var markup = renderToDomTree(expression, options).toMarkup(); + return markup; +}; +/** + * Parse an expression and return the parse tree. + */ + + +var generateParseTree = function generateParseTree(expression, options) { + var settings = new Settings(options); + return src_parseTree(expression, settings); +}; +/** + * If the given error is a KaTeX ParseError and options.throwOnError is false, + * renders the invalid LaTeX as a span with hover title giving the KaTeX + * error message. Otherwise, simply throws the error. + */ + + +var renderError = function renderError(error, expression, options) { + if (options.throwOnError || !(error instanceof src_ParseError)) { + throw error; + } + + var node = buildCommon.makeSpan(["katex-error"], [new SymbolNode(expression)]); + node.setAttribute("title", error.toString()); + node.setAttribute("style", "color:" + options.errorColor); + return node; +}; +/** + * Generates and returns the katex build tree. This is used for advanced + * use cases (like rendering to custom output). + */ + + +var renderToDomTree = function renderToDomTree(expression, options) { + var settings = new Settings(options); + + try { + var tree = src_parseTree(expression, settings); + return buildTree(tree, expression, settings); + } catch (error) { + return renderError(error, expression, settings); + } +}; +/** + * Generates and returns the katex build tree, with just HTML (no MathML). + * This is used for advanced use cases (like rendering to custom output). + */ + + +var renderToHTMLTree = function renderToHTMLTree(expression, options) { + var settings = new Settings(options); + + try { + var tree = src_parseTree(expression, settings); + return buildHTMLTree(tree, expression, settings); + } catch (error) { + return renderError(error, expression, settings); + } +}; + +/* harmony default export */ var katex = ({ + /** + * Current KaTeX version + */ + version: "0.13.24", + + /** + * Renders the given LaTeX into an HTML+MathML combination, and adds + * it as a child to the specified DOM node. + */ + render: render, + + /** + * Renders the given LaTeX into an HTML+MathML combination string, + * for sending to the client. + */ + renderToString: renderToString, + + /** + * KaTeX error, usually during parsing. + */ + ParseError: src_ParseError, + + /** + * Parses the given LaTeX into KaTeX's internal parse tree structure, + * without rendering to HTML or MathML. + * + * NOTE: This method is not currently recommended for public use. + * The internal tree representation is unstable and is very likely + * to change. Use at your own risk. + */ + __parse: generateParseTree, + + /** + * Renders the given LaTeX into an HTML+MathML internal DOM tree + * representation, without flattening that representation to a string. + * + * NOTE: This method is not currently recommended for public use. + * The internal tree representation is unstable and is very likely + * to change. Use at your own risk. + */ + __renderToDomTree: renderToDomTree, + + /** + * Renders the given LaTeX into an HTML internal DOM tree representation, + * without MathML and without flattening that representation to a string. + * + * NOTE: This method is not currently recommended for public use. + * The internal tree representation is unstable and is very likely + * to change. Use at your own risk. + */ + __renderToHTMLTree: renderToHTMLTree, + + /** + * extends internal font metrics object with a new object + * each key in the new object represents a font name + */ + __setFontMetrics: setFontMetrics, + + /** + * adds a new symbol to builtin symbols table + */ + __defineSymbol: defineSymbol, + + /** + * adds a new macro to builtin macro list + */ + __defineMacro: defineMacro, + + /** + * Expose the dom tree node types, which can be useful for type checking nodes. + * + * NOTE: This method is not currently recommended for public use. + * The internal tree representation is unstable and is very likely + * to change. Use at your own risk. + */ + __domTree: { + Span: Span, + Anchor: Anchor, + SymbolNode: SymbolNode, + SvgNode: SvgNode, + PathNode: PathNode, + LineNode: LineNode + } +}); +/** + * This is the webpack entry point for KaTeX. As ECMAScript, flow[1] and jest[2] + * doesn't support CSS modules natively, a separate entry point is used and + * it is not flowtyped. + * + * [1] https://gist.github.com/lambdahands/d19e0da96285b749f0ef + * [2] https://facebook.github.io/jest/docs/en/webpack.html + */ + + +/* harmony default export */ var katex_webpack = (katex); +__webpack_exports__ = __webpack_exports__["default"]; +/******/ return __webpack_exports__; +/******/ })() +; +}); +}); + +/*! markmap-lib v0.11.6 | MIT License */ + +var browser = createCommonjsModule(function (module, exports) { +(function (global, factory) { +factory(exports, katex) ; +}(commonjsGlobal, (function (exports, require$$0) { +function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e['default'] : e; } + +var require$$0__default = /*#__PURE__*/_interopDefaultLegacy(require$$0); + +function _extends$1() { + _extends$1 = Object.assign || function (target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i]; + + for (var key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + } + + return target; + }; + + return _extends$1.apply(this, arguments); +} + +/*! markmap-common v0.1.5 | MIT License */ +class Hook { + constructor() { + this.listeners = []; + } + + tap(fn) { + this.listeners.push(fn); + return () => this.revoke(fn); + } + + revoke(fn) { + const i = this.listeners.indexOf(fn); + if (i >= 0) this.listeners.splice(i, 1); + } + + revokeAll() { + this.listeners.splice(0); + } + + call(...args) { + for (const fn of this.listeners) { + fn(...args); + } + } + +} + +function _extends() { + _extends = Object.assign || function (target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i]; + + for (var key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + } + + return target; + }; + + return _extends.apply(this, arguments); +} + +function escapeHtml$1(html) { + return html.replace(/[&<"]/g, m => ({ + '&': '&', + '<': '<', + '"': '"' + })[m]); +} + +function escapeScript(content) { + return content.replace(/<(\/script>)/g, '\\x3c$2'); +} + +function htmlOpen(tagName, attrs) { + const attrStr = attrs ? Object.entries(attrs).map(([key, value]) => { + if (value == null || value === false) return; + key = ` ${escapeHtml$1(key)}`; + if (value === true) return key; + return `${key}="${escapeHtml$1(value)}"`; + }).filter(Boolean).join('') : ''; + return `<${tagName}${attrStr}>`; +} + +function htmlClose(tagName) { + return ``; +} + +function wrapHtml(tagName, content, attrs) { + if (content == null) return htmlOpen(tagName, attrs); + return htmlOpen(tagName, attrs) + (content || '') + htmlClose(tagName); +} + +function buildCode(fn, args) { + const params = args.map(arg => { + if (typeof arg === 'function') return arg.toString(); + return JSON.stringify(arg != null ? arg : null); + }).join(','); + return `(${fn.toString()})(${params})`; +} + +function persistJS(items, context) { + return items.map(item => { + if (item.type === 'script') return wrapHtml('script', '', item.data); + + if (item.type === 'iife') { + const { + fn, + getParams + } = item.data; + return wrapHtml('script', escapeScript(buildCode(fn, (getParams == null ? void 0 : getParams(context)) || []))); + } + + return ''; + }); +} + +function persistCSS(items) { + return items.map(item => { + if (item.type === 'stylesheet') { + return wrapHtml('link', null, _extends({ + rel: 'stylesheet' + }, item.data)); + } + /* else if (item.type === 'style') */ + + + return wrapHtml('style', item.data); + }); +} + +Math.random().toString(36).slice(2, 8); + +function wrapFunction(fn, { + before, + after +}) { + return function wrapped(...args) { + const ctx = { + args + }; + + try { + if (before) before(ctx); + } catch (_unused) {// ignore + } + + ctx.result = fn(...args); + + try { + if (after) after(ctx); + } catch (_unused2) {// ignore + } + + return ctx.result; + }; +} + +function memoize(fn) { + const cache = {}; + return function memoized(...args) { + const key = `${args[0]}`; + let data = cache[key]; + + if (!data) { + data = { + value: fn(...args) + }; + cache[key] = data; + } + + return data.value; + }; +} + +function createElement(tagName, props, attrs) { + const el = document.createElement(tagName); + + if (props) { + Object.entries(props).forEach(([key, value]) => { + el[key] = value; + }); + } + + if (attrs) { + Object.entries(attrs).forEach(([key, value]) => { + el.setAttribute(key, value); + }); + } + + return el; +} + +const memoizedPreloadJS = memoize(url => { + document.head.append(createElement('link', { + rel: 'preload', + as: 'script', + href: url + })); +}); + +function loadJSItem(item, context) { + if (item.type === 'script') { + return new Promise((resolve, reject) => { + var _item$data; + + document.head.append(createElement('script', _extends({}, item.data, { + onload: resolve, + onerror: reject + }))); // Run inline script synchronously + + if (!((_item$data = item.data) != null && _item$data.src)) resolve(); + }); + } + + if (item.type === 'iife') { + const { + fn, + getParams + } = item.data; + fn(...((getParams == null ? void 0 : getParams(context)) || [])); + } +} + +async function loadJS(items, context) { + const needPreload = items.filter(item => { + var _item$data2; + + return item.type === 'script' && ((_item$data2 = item.data) == null ? void 0 : _item$data2.src); + }); + if (needPreload.length > 1) needPreload.forEach(item => memoizedPreloadJS(item.data.src)); + context = _extends({ + getMarkmap: () => window.markmap + }, context); + + for (const item of items) { + await loadJSItem(item, context); + } +} + +const template = "\n\n\n\n\n\nMarkmap\n\n\n\n\n\n\n\n\n"; +const BASE_JS = [`https://cdn.jsdelivr.net/npm/d3@${"6.6.0"}`, `https://cdn.jsdelivr.net/npm/markmap-view@${"0.2.6"}`].map(src => ({ + type: 'script', + data: { + src + } +})); +function fillTemplate(data, assets, extra) { + if (typeof extra === 'function') { + extra = { + getOptions: extra + }; + } + + extra = _extends$1({ + baseJs: BASE_JS + }, extra); + const { + scripts, + styles + } = assets; + const cssList = [...(styles ? persistCSS(styles) : [])]; + const context = { + getMarkmap: () => window.markmap, + getOptions: extra.getOptions, + data + }; + const jsList = [...persistJS(extra.baseJs), ...persistJS([...(scripts || []), { + type: 'iife', + data: { + fn: (getMarkmap, getOptions, data) => { + const { + Markmap + } = getMarkmap(); + window.mm = Markmap.create('svg#mindmap', getOptions == null ? void 0 : getOptions(), data); + }, + getParams: ({ + getMarkmap, + getOptions, + data + }) => { + return [getMarkmap, getOptions, data]; + } + } + }], context)]; + const html = template.replace('', () => cssList.join('')).replace('', () => jsList.join('')); + return html; +} + +// List of valid entities +// +// Generate with ./support/entities.js script +// + +/*eslint quotes:0*/ +var entities = { + "Aacute": "\xC1", + "aacute": "\xE1", + "Abreve": "\u0102", + "abreve": "\u0103", + "ac": "\u223E", + "acd": "\u223F", + "acE": "\u223E\u0333", + "Acirc": "\xC2", + "acirc": "\xE2", + "acute": "\xB4", + "Acy": "\u0410", + "acy": "\u0430", + "AElig": "\xC6", + "aelig": "\xE6", + "af": "\u2061", + "Afr": "\uD835\uDD04", + "afr": "\uD835\uDD1E", + "Agrave": "\xC0", + "agrave": "\xE0", + "alefsym": "\u2135", + "aleph": "\u2135", + "Alpha": "\u0391", + "alpha": "\u03B1", + "Amacr": "\u0100", + "amacr": "\u0101", + "amalg": "\u2A3F", + "AMP": "&", + "amp": "&", + "And": "\u2A53", + "and": "\u2227", + "andand": "\u2A55", + "andd": "\u2A5C", + "andslope": "\u2A58", + "andv": "\u2A5A", + "ang": "\u2220", + "ange": "\u29A4", + "angle": "\u2220", + "angmsd": "\u2221", + "angmsdaa": "\u29A8", + "angmsdab": "\u29A9", + "angmsdac": "\u29AA", + "angmsdad": "\u29AB", + "angmsdae": "\u29AC", + "angmsdaf": "\u29AD", + "angmsdag": "\u29AE", + "angmsdah": "\u29AF", + "angrt": "\u221F", + "angrtvb": "\u22BE", + "angrtvbd": "\u299D", + "angsph": "\u2222", + "angst": "\xC5", + "angzarr": "\u237C", + "Aogon": "\u0104", + "aogon": "\u0105", + "Aopf": "\uD835\uDD38", + "aopf": "\uD835\uDD52", + "ap": "\u2248", + "apacir": "\u2A6F", + "apE": "\u2A70", + "ape": "\u224A", + "apid": "\u224B", + "apos": "'", + "ApplyFunction": "\u2061", + "approx": "\u2248", + "approxeq": "\u224A", + "Aring": "\xC5", + "aring": "\xE5", + "Ascr": "\uD835\uDC9C", + "ascr": "\uD835\uDCB6", + "Assign": "\u2254", + "ast": "*", + "asymp": "\u2248", + "asympeq": "\u224D", + "Atilde": "\xC3", + "atilde": "\xE3", + "Auml": "\xC4", + "auml": "\xE4", + "awconint": "\u2233", + "awint": "\u2A11", + "backcong": "\u224C", + "backepsilon": "\u03F6", + "backprime": "\u2035", + "backsim": "\u223D", + "backsimeq": "\u22CD", + "Backslash": "\u2216", + "Barv": "\u2AE7", + "barvee": "\u22BD", + "Barwed": "\u2306", + "barwed": "\u2305", + "barwedge": "\u2305", + "bbrk": "\u23B5", + "bbrktbrk": "\u23B6", + "bcong": "\u224C", + "Bcy": "\u0411", + "bcy": "\u0431", + "bdquo": "\u201E", + "becaus": "\u2235", + "Because": "\u2235", + "because": "\u2235", + "bemptyv": "\u29B0", + "bepsi": "\u03F6", + "bernou": "\u212C", + "Bernoullis": "\u212C", + "Beta": "\u0392", + "beta": "\u03B2", + "beth": "\u2136", + "between": "\u226C", + "Bfr": "\uD835\uDD05", + "bfr": "\uD835\uDD1F", + "bigcap": "\u22C2", + "bigcirc": "\u25EF", + "bigcup": "\u22C3", + "bigodot": "\u2A00", + "bigoplus": "\u2A01", + "bigotimes": "\u2A02", + "bigsqcup": "\u2A06", + "bigstar": "\u2605", + "bigtriangledown": "\u25BD", + "bigtriangleup": "\u25B3", + "biguplus": "\u2A04", + "bigvee": "\u22C1", + "bigwedge": "\u22C0", + "bkarow": "\u290D", + "blacklozenge": "\u29EB", + "blacksquare": "\u25AA", + "blacktriangle": "\u25B4", + "blacktriangledown": "\u25BE", + "blacktriangleleft": "\u25C2", + "blacktriangleright": "\u25B8", + "blank": "\u2423", + "blk12": "\u2592", + "blk14": "\u2591", + "blk34": "\u2593", + "block": "\u2588", + "bne": "=\u20E5", + "bnequiv": "\u2261\u20E5", + "bNot": "\u2AED", + "bnot": "\u2310", + "Bopf": "\uD835\uDD39", + "bopf": "\uD835\uDD53", + "bot": "\u22A5", + "bottom": "\u22A5", + "bowtie": "\u22C8", + "boxbox": "\u29C9", + "boxDL": "\u2557", + "boxDl": "\u2556", + "boxdL": "\u2555", + "boxdl": "\u2510", + "boxDR": "\u2554", + "boxDr": "\u2553", + "boxdR": "\u2552", + "boxdr": "\u250C", + "boxH": "\u2550", + "boxh": "\u2500", + "boxHD": "\u2566", + "boxHd": "\u2564", + "boxhD": "\u2565", + "boxhd": "\u252C", + "boxHU": "\u2569", + "boxHu": "\u2567", + "boxhU": "\u2568", + "boxhu": "\u2534", + "boxminus": "\u229F", + "boxplus": "\u229E", + "boxtimes": "\u22A0", + "boxUL": "\u255D", + "boxUl": "\u255C", + "boxuL": "\u255B", + "boxul": "\u2518", + "boxUR": "\u255A", + "boxUr": "\u2559", + "boxuR": "\u2558", + "boxur": "\u2514", + "boxV": "\u2551", + "boxv": "\u2502", + "boxVH": "\u256C", + "boxVh": "\u256B", + "boxvH": "\u256A", + "boxvh": "\u253C", + "boxVL": "\u2563", + "boxVl": "\u2562", + "boxvL": "\u2561", + "boxvl": "\u2524", + "boxVR": "\u2560", + "boxVr": "\u255F", + "boxvR": "\u255E", + "boxvr": "\u251C", + "bprime": "\u2035", + "Breve": "\u02D8", + "breve": "\u02D8", + "brvbar": "\xA6", + "Bscr": "\u212C", + "bscr": "\uD835\uDCB7", + "bsemi": "\u204F", + "bsim": "\u223D", + "bsime": "\u22CD", + "bsol": "\\", + "bsolb": "\u29C5", + "bsolhsub": "\u27C8", + "bull": "\u2022", + "bullet": "\u2022", + "bump": "\u224E", + "bumpE": "\u2AAE", + "bumpe": "\u224F", + "Bumpeq": "\u224E", + "bumpeq": "\u224F", + "Cacute": "\u0106", + "cacute": "\u0107", + "Cap": "\u22D2", + "cap": "\u2229", + "capand": "\u2A44", + "capbrcup": "\u2A49", + "capcap": "\u2A4B", + "capcup": "\u2A47", + "capdot": "\u2A40", + "CapitalDifferentialD": "\u2145", + "caps": "\u2229\uFE00", + "caret": "\u2041", + "caron": "\u02C7", + "Cayleys": "\u212D", + "ccaps": "\u2A4D", + "Ccaron": "\u010C", + "ccaron": "\u010D", + "Ccedil": "\xC7", + "ccedil": "\xE7", + "Ccirc": "\u0108", + "ccirc": "\u0109", + "Cconint": "\u2230", + "ccups": "\u2A4C", + "ccupssm": "\u2A50", + "Cdot": "\u010A", + "cdot": "\u010B", + "cedil": "\xB8", + "Cedilla": "\xB8", + "cemptyv": "\u29B2", + "cent": "\xA2", + "CenterDot": "\xB7", + "centerdot": "\xB7", + "Cfr": "\u212D", + "cfr": "\uD835\uDD20", + "CHcy": "\u0427", + "chcy": "\u0447", + "check": "\u2713", + "checkmark": "\u2713", + "Chi": "\u03A7", + "chi": "\u03C7", + "cir": "\u25CB", + "circ": "\u02C6", + "circeq": "\u2257", + "circlearrowleft": "\u21BA", + "circlearrowright": "\u21BB", + "circledast": "\u229B", + "circledcirc": "\u229A", + "circleddash": "\u229D", + "CircleDot": "\u2299", + "circledR": "\xAE", + "circledS": "\u24C8", + "CircleMinus": "\u2296", + "CirclePlus": "\u2295", + "CircleTimes": "\u2297", + "cirE": "\u29C3", + "cire": "\u2257", + "cirfnint": "\u2A10", + "cirmid": "\u2AEF", + "cirscir": "\u29C2", + "ClockwiseContourIntegral": "\u2232", + "CloseCurlyDoubleQuote": "\u201D", + "CloseCurlyQuote": "\u2019", + "clubs": "\u2663", + "clubsuit": "\u2663", + "Colon": "\u2237", + "colon": ":", + "Colone": "\u2A74", + "colone": "\u2254", + "coloneq": "\u2254", + "comma": ",", + "commat": "@", + "comp": "\u2201", + "compfn": "\u2218", + "complement": "\u2201", + "complexes": "\u2102", + "cong": "\u2245", + "congdot": "\u2A6D", + "Congruent": "\u2261", + "Conint": "\u222F", + "conint": "\u222E", + "ContourIntegral": "\u222E", + "Copf": "\u2102", + "copf": "\uD835\uDD54", + "coprod": "\u2210", + "Coproduct": "\u2210", + "COPY": "\xA9", + "copy": "\xA9", + "copysr": "\u2117", + "CounterClockwiseContourIntegral": "\u2233", + "crarr": "\u21B5", + "Cross": "\u2A2F", + "cross": "\u2717", + "Cscr": "\uD835\uDC9E", + "cscr": "\uD835\uDCB8", + "csub": "\u2ACF", + "csube": "\u2AD1", + "csup": "\u2AD0", + "csupe": "\u2AD2", + "ctdot": "\u22EF", + "cudarrl": "\u2938", + "cudarrr": "\u2935", + "cuepr": "\u22DE", + "cuesc": "\u22DF", + "cularr": "\u21B6", + "cularrp": "\u293D", + "Cup": "\u22D3", + "cup": "\u222A", + "cupbrcap": "\u2A48", + "CupCap": "\u224D", + "cupcap": "\u2A46", + "cupcup": "\u2A4A", + "cupdot": "\u228D", + "cupor": "\u2A45", + "cups": "\u222A\uFE00", + "curarr": "\u21B7", + "curarrm": "\u293C", + "curlyeqprec": "\u22DE", + "curlyeqsucc": "\u22DF", + "curlyvee": "\u22CE", + "curlywedge": "\u22CF", + "curren": "\xA4", + "curvearrowleft": "\u21B6", + "curvearrowright": "\u21B7", + "cuvee": "\u22CE", + "cuwed": "\u22CF", + "cwconint": "\u2232", + "cwint": "\u2231", + "cylcty": "\u232D", + "Dagger": "\u2021", + "dagger": "\u2020", + "daleth": "\u2138", + "Darr": "\u21A1", + "dArr": "\u21D3", + "darr": "\u2193", + "dash": "\u2010", + "Dashv": "\u2AE4", + "dashv": "\u22A3", + "dbkarow": "\u290F", + "dblac": "\u02DD", + "Dcaron": "\u010E", + "dcaron": "\u010F", + "Dcy": "\u0414", + "dcy": "\u0434", + "DD": "\u2145", + "dd": "\u2146", + "ddagger": "\u2021", + "ddarr": "\u21CA", + "DDotrahd": "\u2911", + "ddotseq": "\u2A77", + "deg": "\xB0", + "Del": "\u2207", + "Delta": "\u0394", + "delta": "\u03B4", + "demptyv": "\u29B1", + "dfisht": "\u297F", + "Dfr": "\uD835\uDD07", + "dfr": "\uD835\uDD21", + "dHar": "\u2965", + "dharl": "\u21C3", + "dharr": "\u21C2", + "DiacriticalAcute": "\xB4", + "DiacriticalDot": "\u02D9", + "DiacriticalDoubleAcute": "\u02DD", + "DiacriticalGrave": "`", + "DiacriticalTilde": "\u02DC", + "diam": "\u22C4", + "Diamond": "\u22C4", + "diamond": "\u22C4", + "diamondsuit": "\u2666", + "diams": "\u2666", + "die": "\xA8", + "DifferentialD": "\u2146", + "digamma": "\u03DD", + "disin": "\u22F2", + "div": "\xF7", + "divide": "\xF7", + "divideontimes": "\u22C7", + "divonx": "\u22C7", + "DJcy": "\u0402", + "djcy": "\u0452", + "dlcorn": "\u231E", + "dlcrop": "\u230D", + "dollar": "$", + "Dopf": "\uD835\uDD3B", + "dopf": "\uD835\uDD55", + "Dot": "\xA8", + "dot": "\u02D9", + "DotDot": "\u20DC", + "doteq": "\u2250", + "doteqdot": "\u2251", + "DotEqual": "\u2250", + "dotminus": "\u2238", + "dotplus": "\u2214", + "dotsquare": "\u22A1", + "doublebarwedge": "\u2306", + "DoubleContourIntegral": "\u222F", + "DoubleDot": "\xA8", + "DoubleDownArrow": "\u21D3", + "DoubleLeftArrow": "\u21D0", + "DoubleLeftRightArrow": "\u21D4", + "DoubleLeftTee": "\u2AE4", + "DoubleLongLeftArrow": "\u27F8", + "DoubleLongLeftRightArrow": "\u27FA", + "DoubleLongRightArrow": "\u27F9", + "DoubleRightArrow": "\u21D2", + "DoubleRightTee": "\u22A8", + "DoubleUpArrow": "\u21D1", + "DoubleUpDownArrow": "\u21D5", + "DoubleVerticalBar": "\u2225", + "DownArrow": "\u2193", + "Downarrow": "\u21D3", + "downarrow": "\u2193", + "DownArrowBar": "\u2913", + "DownArrowUpArrow": "\u21F5", + "DownBreve": "\u0311", + "downdownarrows": "\u21CA", + "downharpoonleft": "\u21C3", + "downharpoonright": "\u21C2", + "DownLeftRightVector": "\u2950", + "DownLeftTeeVector": "\u295E", + "DownLeftVector": "\u21BD", + "DownLeftVectorBar": "\u2956", + "DownRightTeeVector": "\u295F", + "DownRightVector": "\u21C1", + "DownRightVectorBar": "\u2957", + "DownTee": "\u22A4", + "DownTeeArrow": "\u21A7", + "drbkarow": "\u2910", + "drcorn": "\u231F", + "drcrop": "\u230C", + "Dscr": "\uD835\uDC9F", + "dscr": "\uD835\uDCB9", + "DScy": "\u0405", + "dscy": "\u0455", + "dsol": "\u29F6", + "Dstrok": "\u0110", + "dstrok": "\u0111", + "dtdot": "\u22F1", + "dtri": "\u25BF", + "dtrif": "\u25BE", + "duarr": "\u21F5", + "duhar": "\u296F", + "dwangle": "\u29A6", + "DZcy": "\u040F", + "dzcy": "\u045F", + "dzigrarr": "\u27FF", + "Eacute": "\xC9", + "eacute": "\xE9", + "easter": "\u2A6E", + "Ecaron": "\u011A", + "ecaron": "\u011B", + "ecir": "\u2256", + "Ecirc": "\xCA", + "ecirc": "\xEA", + "ecolon": "\u2255", + "Ecy": "\u042D", + "ecy": "\u044D", + "eDDot": "\u2A77", + "Edot": "\u0116", + "eDot": "\u2251", + "edot": "\u0117", + "ee": "\u2147", + "efDot": "\u2252", + "Efr": "\uD835\uDD08", + "efr": "\uD835\uDD22", + "eg": "\u2A9A", + "Egrave": "\xC8", + "egrave": "\xE8", + "egs": "\u2A96", + "egsdot": "\u2A98", + "el": "\u2A99", + "Element": "\u2208", + "elinters": "\u23E7", + "ell": "\u2113", + "els": "\u2A95", + "elsdot": "\u2A97", + "Emacr": "\u0112", + "emacr": "\u0113", + "empty": "\u2205", + "emptyset": "\u2205", + "EmptySmallSquare": "\u25FB", + "emptyv": "\u2205", + "EmptyVerySmallSquare": "\u25AB", + "emsp": "\u2003", + "emsp13": "\u2004", + "emsp14": "\u2005", + "ENG": "\u014A", + "eng": "\u014B", + "ensp": "\u2002", + "Eogon": "\u0118", + "eogon": "\u0119", + "Eopf": "\uD835\uDD3C", + "eopf": "\uD835\uDD56", + "epar": "\u22D5", + "eparsl": "\u29E3", + "eplus": "\u2A71", + "epsi": "\u03B5", + "Epsilon": "\u0395", + "epsilon": "\u03B5", + "epsiv": "\u03F5", + "eqcirc": "\u2256", + "eqcolon": "\u2255", + "eqsim": "\u2242", + "eqslantgtr": "\u2A96", + "eqslantless": "\u2A95", + "Equal": "\u2A75", + "equals": "=", + "EqualTilde": "\u2242", + "equest": "\u225F", + "Equilibrium": "\u21CC", + "equiv": "\u2261", + "equivDD": "\u2A78", + "eqvparsl": "\u29E5", + "erarr": "\u2971", + "erDot": "\u2253", + "Escr": "\u2130", + "escr": "\u212F", + "esdot": "\u2250", + "Esim": "\u2A73", + "esim": "\u2242", + "Eta": "\u0397", + "eta": "\u03B7", + "ETH": "\xD0", + "eth": "\xF0", + "Euml": "\xCB", + "euml": "\xEB", + "euro": "\u20AC", + "excl": "!", + "exist": "\u2203", + "Exists": "\u2203", + "expectation": "\u2130", + "ExponentialE": "\u2147", + "exponentiale": "\u2147", + "fallingdotseq": "\u2252", + "Fcy": "\u0424", + "fcy": "\u0444", + "female": "\u2640", + "ffilig": "\uFB03", + "fflig": "\uFB00", + "ffllig": "\uFB04", + "Ffr": "\uD835\uDD09", + "ffr": "\uD835\uDD23", + "filig": "\uFB01", + "FilledSmallSquare": "\u25FC", + "FilledVerySmallSquare": "\u25AA", + "fjlig": "fj", + "flat": "\u266D", + "fllig": "\uFB02", + "fltns": "\u25B1", + "fnof": "\u0192", + "Fopf": "\uD835\uDD3D", + "fopf": "\uD835\uDD57", + "ForAll": "\u2200", + "forall": "\u2200", + "fork": "\u22D4", + "forkv": "\u2AD9", + "Fouriertrf": "\u2131", + "fpartint": "\u2A0D", + "frac12": "\xBD", + "frac13": "\u2153", + "frac14": "\xBC", + "frac15": "\u2155", + "frac16": "\u2159", + "frac18": "\u215B", + "frac23": "\u2154", + "frac25": "\u2156", + "frac34": "\xBE", + "frac35": "\u2157", + "frac38": "\u215C", + "frac45": "\u2158", + "frac56": "\u215A", + "frac58": "\u215D", + "frac78": "\u215E", + "frasl": "\u2044", + "frown": "\u2322", + "Fscr": "\u2131", + "fscr": "\uD835\uDCBB", + "gacute": "\u01F5", + "Gamma": "\u0393", + "gamma": "\u03B3", + "Gammad": "\u03DC", + "gammad": "\u03DD", + "gap": "\u2A86", + "Gbreve": "\u011E", + "gbreve": "\u011F", + "Gcedil": "\u0122", + "Gcirc": "\u011C", + "gcirc": "\u011D", + "Gcy": "\u0413", + "gcy": "\u0433", + "Gdot": "\u0120", + "gdot": "\u0121", + "gE": "\u2267", + "ge": "\u2265", + "gEl": "\u2A8C", + "gel": "\u22DB", + "geq": "\u2265", + "geqq": "\u2267", + "geqslant": "\u2A7E", + "ges": "\u2A7E", + "gescc": "\u2AA9", + "gesdot": "\u2A80", + "gesdoto": "\u2A82", + "gesdotol": "\u2A84", + "gesl": "\u22DB\uFE00", + "gesles": "\u2A94", + "Gfr": "\uD835\uDD0A", + "gfr": "\uD835\uDD24", + "Gg": "\u22D9", + "gg": "\u226B", + "ggg": "\u22D9", + "gimel": "\u2137", + "GJcy": "\u0403", + "gjcy": "\u0453", + "gl": "\u2277", + "gla": "\u2AA5", + "glE": "\u2A92", + "glj": "\u2AA4", + "gnap": "\u2A8A", + "gnapprox": "\u2A8A", + "gnE": "\u2269", + "gne": "\u2A88", + "gneq": "\u2A88", + "gneqq": "\u2269", + "gnsim": "\u22E7", + "Gopf": "\uD835\uDD3E", + "gopf": "\uD835\uDD58", + "grave": "`", + "GreaterEqual": "\u2265", + "GreaterEqualLess": "\u22DB", + "GreaterFullEqual": "\u2267", + "GreaterGreater": "\u2AA2", + "GreaterLess": "\u2277", + "GreaterSlantEqual": "\u2A7E", + "GreaterTilde": "\u2273", + "Gscr": "\uD835\uDCA2", + "gscr": "\u210A", + "gsim": "\u2273", + "gsime": "\u2A8E", + "gsiml": "\u2A90", + "GT": ">", + "Gt": "\u226B", + "gt": ">", + "gtcc": "\u2AA7", + "gtcir": "\u2A7A", + "gtdot": "\u22D7", + "gtlPar": "\u2995", + "gtquest": "\u2A7C", + "gtrapprox": "\u2A86", + "gtrarr": "\u2978", + "gtrdot": "\u22D7", + "gtreqless": "\u22DB", + "gtreqqless": "\u2A8C", + "gtrless": "\u2277", + "gtrsim": "\u2273", + "gvertneqq": "\u2269\uFE00", + "gvnE": "\u2269\uFE00", + "Hacek": "\u02C7", + "hairsp": "\u200A", + "half": "\xBD", + "hamilt": "\u210B", + "HARDcy": "\u042A", + "hardcy": "\u044A", + "hArr": "\u21D4", + "harr": "\u2194", + "harrcir": "\u2948", + "harrw": "\u21AD", + "Hat": "^", + "hbar": "\u210F", + "Hcirc": "\u0124", + "hcirc": "\u0125", + "hearts": "\u2665", + "heartsuit": "\u2665", + "hellip": "\u2026", + "hercon": "\u22B9", + "Hfr": "\u210C", + "hfr": "\uD835\uDD25", + "HilbertSpace": "\u210B", + "hksearow": "\u2925", + "hkswarow": "\u2926", + "hoarr": "\u21FF", + "homtht": "\u223B", + "hookleftarrow": "\u21A9", + "hookrightarrow": "\u21AA", + "Hopf": "\u210D", + "hopf": "\uD835\uDD59", + "horbar": "\u2015", + "HorizontalLine": "\u2500", + "Hscr": "\u210B", + "hscr": "\uD835\uDCBD", + "hslash": "\u210F", + "Hstrok": "\u0126", + "hstrok": "\u0127", + "HumpDownHump": "\u224E", + "HumpEqual": "\u224F", + "hybull": "\u2043", + "hyphen": "\u2010", + "Iacute": "\xCD", + "iacute": "\xED", + "ic": "\u2063", + "Icirc": "\xCE", + "icirc": "\xEE", + "Icy": "\u0418", + "icy": "\u0438", + "Idot": "\u0130", + "IEcy": "\u0415", + "iecy": "\u0435", + "iexcl": "\xA1", + "iff": "\u21D4", + "Ifr": "\u2111", + "ifr": "\uD835\uDD26", + "Igrave": "\xCC", + "igrave": "\xEC", + "ii": "\u2148", + "iiiint": "\u2A0C", + "iiint": "\u222D", + "iinfin": "\u29DC", + "iiota": "\u2129", + "IJlig": "\u0132", + "ijlig": "\u0133", + "Im": "\u2111", + "Imacr": "\u012A", + "imacr": "\u012B", + "image": "\u2111", + "ImaginaryI": "\u2148", + "imagline": "\u2110", + "imagpart": "\u2111", + "imath": "\u0131", + "imof": "\u22B7", + "imped": "\u01B5", + "Implies": "\u21D2", + "in": "\u2208", + "incare": "\u2105", + "infin": "\u221E", + "infintie": "\u29DD", + "inodot": "\u0131", + "Int": "\u222C", + "int": "\u222B", + "intcal": "\u22BA", + "integers": "\u2124", + "Integral": "\u222B", + "intercal": "\u22BA", + "Intersection": "\u22C2", + "intlarhk": "\u2A17", + "intprod": "\u2A3C", + "InvisibleComma": "\u2063", + "InvisibleTimes": "\u2062", + "IOcy": "\u0401", + "iocy": "\u0451", + "Iogon": "\u012E", + "iogon": "\u012F", + "Iopf": "\uD835\uDD40", + "iopf": "\uD835\uDD5A", + "Iota": "\u0399", + "iota": "\u03B9", + "iprod": "\u2A3C", + "iquest": "\xBF", + "Iscr": "\u2110", + "iscr": "\uD835\uDCBE", + "isin": "\u2208", + "isindot": "\u22F5", + "isinE": "\u22F9", + "isins": "\u22F4", + "isinsv": "\u22F3", + "isinv": "\u2208", + "it": "\u2062", + "Itilde": "\u0128", + "itilde": "\u0129", + "Iukcy": "\u0406", + "iukcy": "\u0456", + "Iuml": "\xCF", + "iuml": "\xEF", + "Jcirc": "\u0134", + "jcirc": "\u0135", + "Jcy": "\u0419", + "jcy": "\u0439", + "Jfr": "\uD835\uDD0D", + "jfr": "\uD835\uDD27", + "jmath": "\u0237", + "Jopf": "\uD835\uDD41", + "jopf": "\uD835\uDD5B", + "Jscr": "\uD835\uDCA5", + "jscr": "\uD835\uDCBF", + "Jsercy": "\u0408", + "jsercy": "\u0458", + "Jukcy": "\u0404", + "jukcy": "\u0454", + "Kappa": "\u039A", + "kappa": "\u03BA", + "kappav": "\u03F0", + "Kcedil": "\u0136", + "kcedil": "\u0137", + "Kcy": "\u041A", + "kcy": "\u043A", + "Kfr": "\uD835\uDD0E", + "kfr": "\uD835\uDD28", + "kgreen": "\u0138", + "KHcy": "\u0425", + "khcy": "\u0445", + "KJcy": "\u040C", + "kjcy": "\u045C", + "Kopf": "\uD835\uDD42", + "kopf": "\uD835\uDD5C", + "Kscr": "\uD835\uDCA6", + "kscr": "\uD835\uDCC0", + "lAarr": "\u21DA", + "Lacute": "\u0139", + "lacute": "\u013A", + "laemptyv": "\u29B4", + "lagran": "\u2112", + "Lambda": "\u039B", + "lambda": "\u03BB", + "Lang": "\u27EA", + "lang": "\u27E8", + "langd": "\u2991", + "langle": "\u27E8", + "lap": "\u2A85", + "Laplacetrf": "\u2112", + "laquo": "\xAB", + "Larr": "\u219E", + "lArr": "\u21D0", + "larr": "\u2190", + "larrb": "\u21E4", + "larrbfs": "\u291F", + "larrfs": "\u291D", + "larrhk": "\u21A9", + "larrlp": "\u21AB", + "larrpl": "\u2939", + "larrsim": "\u2973", + "larrtl": "\u21A2", + "lat": "\u2AAB", + "lAtail": "\u291B", + "latail": "\u2919", + "late": "\u2AAD", + "lates": "\u2AAD\uFE00", + "lBarr": "\u290E", + "lbarr": "\u290C", + "lbbrk": "\u2772", + "lbrace": "{", + "lbrack": "[", + "lbrke": "\u298B", + "lbrksld": "\u298F", + "lbrkslu": "\u298D", + "Lcaron": "\u013D", + "lcaron": "\u013E", + "Lcedil": "\u013B", + "lcedil": "\u013C", + "lceil": "\u2308", + "lcub": "{", + "Lcy": "\u041B", + "lcy": "\u043B", + "ldca": "\u2936", + "ldquo": "\u201C", + "ldquor": "\u201E", + "ldrdhar": "\u2967", + "ldrushar": "\u294B", + "ldsh": "\u21B2", + "lE": "\u2266", + "le": "\u2264", + "LeftAngleBracket": "\u27E8", + "LeftArrow": "\u2190", + "Leftarrow": "\u21D0", + "leftarrow": "\u2190", + "LeftArrowBar": "\u21E4", + "LeftArrowRightArrow": "\u21C6", + "leftarrowtail": "\u21A2", + "LeftCeiling": "\u2308", + "LeftDoubleBracket": "\u27E6", + "LeftDownTeeVector": "\u2961", + "LeftDownVector": "\u21C3", + "LeftDownVectorBar": "\u2959", + "LeftFloor": "\u230A", + "leftharpoondown": "\u21BD", + "leftharpoonup": "\u21BC", + "leftleftarrows": "\u21C7", + "LeftRightArrow": "\u2194", + "Leftrightarrow": "\u21D4", + "leftrightarrow": "\u2194", + "leftrightarrows": "\u21C6", + "leftrightharpoons": "\u21CB", + "leftrightsquigarrow": "\u21AD", + "LeftRightVector": "\u294E", + "LeftTee": "\u22A3", + "LeftTeeArrow": "\u21A4", + "LeftTeeVector": "\u295A", + "leftthreetimes": "\u22CB", + "LeftTriangle": "\u22B2", + "LeftTriangleBar": "\u29CF", + "LeftTriangleEqual": "\u22B4", + "LeftUpDownVector": "\u2951", + "LeftUpTeeVector": "\u2960", + "LeftUpVector": "\u21BF", + "LeftUpVectorBar": "\u2958", + "LeftVector": "\u21BC", + "LeftVectorBar": "\u2952", + "lEg": "\u2A8B", + "leg": "\u22DA", + "leq": "\u2264", + "leqq": "\u2266", + "leqslant": "\u2A7D", + "les": "\u2A7D", + "lescc": "\u2AA8", + "lesdot": "\u2A7F", + "lesdoto": "\u2A81", + "lesdotor": "\u2A83", + "lesg": "\u22DA\uFE00", + "lesges": "\u2A93", + "lessapprox": "\u2A85", + "lessdot": "\u22D6", + "lesseqgtr": "\u22DA", + "lesseqqgtr": "\u2A8B", + "LessEqualGreater": "\u22DA", + "LessFullEqual": "\u2266", + "LessGreater": "\u2276", + "lessgtr": "\u2276", + "LessLess": "\u2AA1", + "lesssim": "\u2272", + "LessSlantEqual": "\u2A7D", + "LessTilde": "\u2272", + "lfisht": "\u297C", + "lfloor": "\u230A", + "Lfr": "\uD835\uDD0F", + "lfr": "\uD835\uDD29", + "lg": "\u2276", + "lgE": "\u2A91", + "lHar": "\u2962", + "lhard": "\u21BD", + "lharu": "\u21BC", + "lharul": "\u296A", + "lhblk": "\u2584", + "LJcy": "\u0409", + "ljcy": "\u0459", + "Ll": "\u22D8", + "ll": "\u226A", + "llarr": "\u21C7", + "llcorner": "\u231E", + "Lleftarrow": "\u21DA", + "llhard": "\u296B", + "lltri": "\u25FA", + "Lmidot": "\u013F", + "lmidot": "\u0140", + "lmoust": "\u23B0", + "lmoustache": "\u23B0", + "lnap": "\u2A89", + "lnapprox": "\u2A89", + "lnE": "\u2268", + "lne": "\u2A87", + "lneq": "\u2A87", + "lneqq": "\u2268", + "lnsim": "\u22E6", + "loang": "\u27EC", + "loarr": "\u21FD", + "lobrk": "\u27E6", + "LongLeftArrow": "\u27F5", + "Longleftarrow": "\u27F8", + "longleftarrow": "\u27F5", + "LongLeftRightArrow": "\u27F7", + "Longleftrightarrow": "\u27FA", + "longleftrightarrow": "\u27F7", + "longmapsto": "\u27FC", + "LongRightArrow": "\u27F6", + "Longrightarrow": "\u27F9", + "longrightarrow": "\u27F6", + "looparrowleft": "\u21AB", + "looparrowright": "\u21AC", + "lopar": "\u2985", + "Lopf": "\uD835\uDD43", + "lopf": "\uD835\uDD5D", + "loplus": "\u2A2D", + "lotimes": "\u2A34", + "lowast": "\u2217", + "lowbar": "_", + "LowerLeftArrow": "\u2199", + "LowerRightArrow": "\u2198", + "loz": "\u25CA", + "lozenge": "\u25CA", + "lozf": "\u29EB", + "lpar": "(", + "lparlt": "\u2993", + "lrarr": "\u21C6", + "lrcorner": "\u231F", + "lrhar": "\u21CB", + "lrhard": "\u296D", + "lrm": "\u200E", + "lrtri": "\u22BF", + "lsaquo": "\u2039", + "Lscr": "\u2112", + "lscr": "\uD835\uDCC1", + "Lsh": "\u21B0", + "lsh": "\u21B0", + "lsim": "\u2272", + "lsime": "\u2A8D", + "lsimg": "\u2A8F", + "lsqb": "[", + "lsquo": "\u2018", + "lsquor": "\u201A", + "Lstrok": "\u0141", + "lstrok": "\u0142", + "LT": "<", + "Lt": "\u226A", + "lt": "<", + "ltcc": "\u2AA6", + "ltcir": "\u2A79", + "ltdot": "\u22D6", + "lthree": "\u22CB", + "ltimes": "\u22C9", + "ltlarr": "\u2976", + "ltquest": "\u2A7B", + "ltri": "\u25C3", + "ltrie": "\u22B4", + "ltrif": "\u25C2", + "ltrPar": "\u2996", + "lurdshar": "\u294A", + "luruhar": "\u2966", + "lvertneqq": "\u2268\uFE00", + "lvnE": "\u2268\uFE00", + "macr": "\xAF", + "male": "\u2642", + "malt": "\u2720", + "maltese": "\u2720", + "Map": "\u2905", + "map": "\u21A6", + "mapsto": "\u21A6", + "mapstodown": "\u21A7", + "mapstoleft": "\u21A4", + "mapstoup": "\u21A5", + "marker": "\u25AE", + "mcomma": "\u2A29", + "Mcy": "\u041C", + "mcy": "\u043C", + "mdash": "\u2014", + "mDDot": "\u223A", + "measuredangle": "\u2221", + "MediumSpace": "\u205F", + "Mellintrf": "\u2133", + "Mfr": "\uD835\uDD10", + "mfr": "\uD835\uDD2A", + "mho": "\u2127", + "micro": "\xB5", + "mid": "\u2223", + "midast": "*", + "midcir": "\u2AF0", + "middot": "\xB7", + "minus": "\u2212", + "minusb": "\u229F", + "minusd": "\u2238", + "minusdu": "\u2A2A", + "MinusPlus": "\u2213", + "mlcp": "\u2ADB", + "mldr": "\u2026", + "mnplus": "\u2213", + "models": "\u22A7", + "Mopf": "\uD835\uDD44", + "mopf": "\uD835\uDD5E", + "mp": "\u2213", + "Mscr": "\u2133", + "mscr": "\uD835\uDCC2", + "mstpos": "\u223E", + "Mu": "\u039C", + "mu": "\u03BC", + "multimap": "\u22B8", + "mumap": "\u22B8", + "nabla": "\u2207", + "Nacute": "\u0143", + "nacute": "\u0144", + "nang": "\u2220\u20D2", + "nap": "\u2249", + "napE": "\u2A70\u0338", + "napid": "\u224B\u0338", + "napos": "\u0149", + "napprox": "\u2249", + "natur": "\u266E", + "natural": "\u266E", + "naturals": "\u2115", + "nbsp": "\xA0", + "nbump": "\u224E\u0338", + "nbumpe": "\u224F\u0338", + "ncap": "\u2A43", + "Ncaron": "\u0147", + "ncaron": "\u0148", + "Ncedil": "\u0145", + "ncedil": "\u0146", + "ncong": "\u2247", + "ncongdot": "\u2A6D\u0338", + "ncup": "\u2A42", + "Ncy": "\u041D", + "ncy": "\u043D", + "ndash": "\u2013", + "ne": "\u2260", + "nearhk": "\u2924", + "neArr": "\u21D7", + "nearr": "\u2197", + "nearrow": "\u2197", + "nedot": "\u2250\u0338", + "NegativeMediumSpace": "\u200B", + "NegativeThickSpace": "\u200B", + "NegativeThinSpace": "\u200B", + "NegativeVeryThinSpace": "\u200B", + "nequiv": "\u2262", + "nesear": "\u2928", + "nesim": "\u2242\u0338", + "NestedGreaterGreater": "\u226B", + "NestedLessLess": "\u226A", + "NewLine": "\n", + "nexist": "\u2204", + "nexists": "\u2204", + "Nfr": "\uD835\uDD11", + "nfr": "\uD835\uDD2B", + "ngE": "\u2267\u0338", + "nge": "\u2271", + "ngeq": "\u2271", + "ngeqq": "\u2267\u0338", + "ngeqslant": "\u2A7E\u0338", + "nges": "\u2A7E\u0338", + "nGg": "\u22D9\u0338", + "ngsim": "\u2275", + "nGt": "\u226B\u20D2", + "ngt": "\u226F", + "ngtr": "\u226F", + "nGtv": "\u226B\u0338", + "nhArr": "\u21CE", + "nharr": "\u21AE", + "nhpar": "\u2AF2", + "ni": "\u220B", + "nis": "\u22FC", + "nisd": "\u22FA", + "niv": "\u220B", + "NJcy": "\u040A", + "njcy": "\u045A", + "nlArr": "\u21CD", + "nlarr": "\u219A", + "nldr": "\u2025", + "nlE": "\u2266\u0338", + "nle": "\u2270", + "nLeftarrow": "\u21CD", + "nleftarrow": "\u219A", + "nLeftrightarrow": "\u21CE", + "nleftrightarrow": "\u21AE", + "nleq": "\u2270", + "nleqq": "\u2266\u0338", + "nleqslant": "\u2A7D\u0338", + "nles": "\u2A7D\u0338", + "nless": "\u226E", + "nLl": "\u22D8\u0338", + "nlsim": "\u2274", + "nLt": "\u226A\u20D2", + "nlt": "\u226E", + "nltri": "\u22EA", + "nltrie": "\u22EC", + "nLtv": "\u226A\u0338", + "nmid": "\u2224", + "NoBreak": "\u2060", + "NonBreakingSpace": "\xA0", + "Nopf": "\u2115", + "nopf": "\uD835\uDD5F", + "Not": "\u2AEC", + "not": "\xAC", + "NotCongruent": "\u2262", + "NotCupCap": "\u226D", + "NotDoubleVerticalBar": "\u2226", + "NotElement": "\u2209", + "NotEqual": "\u2260", + "NotEqualTilde": "\u2242\u0338", + "NotExists": "\u2204", + "NotGreater": "\u226F", + "NotGreaterEqual": "\u2271", + "NotGreaterFullEqual": "\u2267\u0338", + "NotGreaterGreater": "\u226B\u0338", + "NotGreaterLess": "\u2279", + "NotGreaterSlantEqual": "\u2A7E\u0338", + "NotGreaterTilde": "\u2275", + "NotHumpDownHump": "\u224E\u0338", + "NotHumpEqual": "\u224F\u0338", + "notin": "\u2209", + "notindot": "\u22F5\u0338", + "notinE": "\u22F9\u0338", + "notinva": "\u2209", + "notinvb": "\u22F7", + "notinvc": "\u22F6", + "NotLeftTriangle": "\u22EA", + "NotLeftTriangleBar": "\u29CF\u0338", + "NotLeftTriangleEqual": "\u22EC", + "NotLess": "\u226E", + "NotLessEqual": "\u2270", + "NotLessGreater": "\u2278", + "NotLessLess": "\u226A\u0338", + "NotLessSlantEqual": "\u2A7D\u0338", + "NotLessTilde": "\u2274", + "NotNestedGreaterGreater": "\u2AA2\u0338", + "NotNestedLessLess": "\u2AA1\u0338", + "notni": "\u220C", + "notniva": "\u220C", + "notnivb": "\u22FE", + "notnivc": "\u22FD", + "NotPrecedes": "\u2280", + "NotPrecedesEqual": "\u2AAF\u0338", + "NotPrecedesSlantEqual": "\u22E0", + "NotReverseElement": "\u220C", + "NotRightTriangle": "\u22EB", + "NotRightTriangleBar": "\u29D0\u0338", + "NotRightTriangleEqual": "\u22ED", + "NotSquareSubset": "\u228F\u0338", + "NotSquareSubsetEqual": "\u22E2", + "NotSquareSuperset": "\u2290\u0338", + "NotSquareSupersetEqual": "\u22E3", + "NotSubset": "\u2282\u20D2", + "NotSubsetEqual": "\u2288", + "NotSucceeds": "\u2281", + "NotSucceedsEqual": "\u2AB0\u0338", + "NotSucceedsSlantEqual": "\u22E1", + "NotSucceedsTilde": "\u227F\u0338", + "NotSuperset": "\u2283\u20D2", + "NotSupersetEqual": "\u2289", + "NotTilde": "\u2241", + "NotTildeEqual": "\u2244", + "NotTildeFullEqual": "\u2247", + "NotTildeTilde": "\u2249", + "NotVerticalBar": "\u2224", + "npar": "\u2226", + "nparallel": "\u2226", + "nparsl": "\u2AFD\u20E5", + "npart": "\u2202\u0338", + "npolint": "\u2A14", + "npr": "\u2280", + "nprcue": "\u22E0", + "npre": "\u2AAF\u0338", + "nprec": "\u2280", + "npreceq": "\u2AAF\u0338", + "nrArr": "\u21CF", + "nrarr": "\u219B", + "nrarrc": "\u2933\u0338", + "nrarrw": "\u219D\u0338", + "nRightarrow": "\u21CF", + "nrightarrow": "\u219B", + "nrtri": "\u22EB", + "nrtrie": "\u22ED", + "nsc": "\u2281", + "nsccue": "\u22E1", + "nsce": "\u2AB0\u0338", + "Nscr": "\uD835\uDCA9", + "nscr": "\uD835\uDCC3", + "nshortmid": "\u2224", + "nshortparallel": "\u2226", + "nsim": "\u2241", + "nsime": "\u2244", + "nsimeq": "\u2244", + "nsmid": "\u2224", + "nspar": "\u2226", + "nsqsube": "\u22E2", + "nsqsupe": "\u22E3", + "nsub": "\u2284", + "nsubE": "\u2AC5\u0338", + "nsube": "\u2288", + "nsubset": "\u2282\u20D2", + "nsubseteq": "\u2288", + "nsubseteqq": "\u2AC5\u0338", + "nsucc": "\u2281", + "nsucceq": "\u2AB0\u0338", + "nsup": "\u2285", + "nsupE": "\u2AC6\u0338", + "nsupe": "\u2289", + "nsupset": "\u2283\u20D2", + "nsupseteq": "\u2289", + "nsupseteqq": "\u2AC6\u0338", + "ntgl": "\u2279", + "Ntilde": "\xD1", + "ntilde": "\xF1", + "ntlg": "\u2278", + "ntriangleleft": "\u22EA", + "ntrianglelefteq": "\u22EC", + "ntriangleright": "\u22EB", + "ntrianglerighteq": "\u22ED", + "Nu": "\u039D", + "nu": "\u03BD", + "num": "#", + "numero": "\u2116", + "numsp": "\u2007", + "nvap": "\u224D\u20D2", + "nVDash": "\u22AF", + "nVdash": "\u22AE", + "nvDash": "\u22AD", + "nvdash": "\u22AC", + "nvge": "\u2265\u20D2", + "nvgt": ">\u20D2", + "nvHarr": "\u2904", + "nvinfin": "\u29DE", + "nvlArr": "\u2902", + "nvle": "\u2264\u20D2", + "nvlt": "<\u20D2", + "nvltrie": "\u22B4\u20D2", + "nvrArr": "\u2903", + "nvrtrie": "\u22B5\u20D2", + "nvsim": "\u223C\u20D2", + "nwarhk": "\u2923", + "nwArr": "\u21D6", + "nwarr": "\u2196", + "nwarrow": "\u2196", + "nwnear": "\u2927", + "Oacute": "\xD3", + "oacute": "\xF3", + "oast": "\u229B", + "ocir": "\u229A", + "Ocirc": "\xD4", + "ocirc": "\xF4", + "Ocy": "\u041E", + "ocy": "\u043E", + "odash": "\u229D", + "Odblac": "\u0150", + "odblac": "\u0151", + "odiv": "\u2A38", + "odot": "\u2299", + "odsold": "\u29BC", + "OElig": "\u0152", + "oelig": "\u0153", + "ofcir": "\u29BF", + "Ofr": "\uD835\uDD12", + "ofr": "\uD835\uDD2C", + "ogon": "\u02DB", + "Ograve": "\xD2", + "ograve": "\xF2", + "ogt": "\u29C1", + "ohbar": "\u29B5", + "ohm": "\u03A9", + "oint": "\u222E", + "olarr": "\u21BA", + "olcir": "\u29BE", + "olcross": "\u29BB", + "oline": "\u203E", + "olt": "\u29C0", + "Omacr": "\u014C", + "omacr": "\u014D", + "Omega": "\u03A9", + "omega": "\u03C9", + "Omicron": "\u039F", + "omicron": "\u03BF", + "omid": "\u29B6", + "ominus": "\u2296", + "Oopf": "\uD835\uDD46", + "oopf": "\uD835\uDD60", + "opar": "\u29B7", + "OpenCurlyDoubleQuote": "\u201C", + "OpenCurlyQuote": "\u2018", + "operp": "\u29B9", + "oplus": "\u2295", + "Or": "\u2A54", + "or": "\u2228", + "orarr": "\u21BB", + "ord": "\u2A5D", + "order": "\u2134", + "orderof": "\u2134", + "ordf": "\xAA", + "ordm": "\xBA", + "origof": "\u22B6", + "oror": "\u2A56", + "orslope": "\u2A57", + "orv": "\u2A5B", + "oS": "\u24C8", + "Oscr": "\uD835\uDCAA", + "oscr": "\u2134", + "Oslash": "\xD8", + "oslash": "\xF8", + "osol": "\u2298", + "Otilde": "\xD5", + "otilde": "\xF5", + "Otimes": "\u2A37", + "otimes": "\u2297", + "otimesas": "\u2A36", + "Ouml": "\xD6", + "ouml": "\xF6", + "ovbar": "\u233D", + "OverBar": "\u203E", + "OverBrace": "\u23DE", + "OverBracket": "\u23B4", + "OverParenthesis": "\u23DC", + "par": "\u2225", + "para": "\xB6", + "parallel": "\u2225", + "parsim": "\u2AF3", + "parsl": "\u2AFD", + "part": "\u2202", + "PartialD": "\u2202", + "Pcy": "\u041F", + "pcy": "\u043F", + "percnt": "%", + "period": ".", + "permil": "\u2030", + "perp": "\u22A5", + "pertenk": "\u2031", + "Pfr": "\uD835\uDD13", + "pfr": "\uD835\uDD2D", + "Phi": "\u03A6", + "phi": "\u03C6", + "phiv": "\u03D5", + "phmmat": "\u2133", + "phone": "\u260E", + "Pi": "\u03A0", + "pi": "\u03C0", + "pitchfork": "\u22D4", + "piv": "\u03D6", + "planck": "\u210F", + "planckh": "\u210E", + "plankv": "\u210F", + "plus": "+", + "plusacir": "\u2A23", + "plusb": "\u229E", + "pluscir": "\u2A22", + "plusdo": "\u2214", + "plusdu": "\u2A25", + "pluse": "\u2A72", + "PlusMinus": "\xB1", + "plusmn": "\xB1", + "plussim": "\u2A26", + "plustwo": "\u2A27", + "pm": "\xB1", + "Poincareplane": "\u210C", + "pointint": "\u2A15", + "Popf": "\u2119", + "popf": "\uD835\uDD61", + "pound": "\xA3", + "Pr": "\u2ABB", + "pr": "\u227A", + "prap": "\u2AB7", + "prcue": "\u227C", + "prE": "\u2AB3", + "pre": "\u2AAF", + "prec": "\u227A", + "precapprox": "\u2AB7", + "preccurlyeq": "\u227C", + "Precedes": "\u227A", + "PrecedesEqual": "\u2AAF", + "PrecedesSlantEqual": "\u227C", + "PrecedesTilde": "\u227E", + "preceq": "\u2AAF", + "precnapprox": "\u2AB9", + "precneqq": "\u2AB5", + "precnsim": "\u22E8", + "precsim": "\u227E", + "Prime": "\u2033", + "prime": "\u2032", + "primes": "\u2119", + "prnap": "\u2AB9", + "prnE": "\u2AB5", + "prnsim": "\u22E8", + "prod": "\u220F", + "Product": "\u220F", + "profalar": "\u232E", + "profline": "\u2312", + "profsurf": "\u2313", + "prop": "\u221D", + "Proportion": "\u2237", + "Proportional": "\u221D", + "propto": "\u221D", + "prsim": "\u227E", + "prurel": "\u22B0", + "Pscr": "\uD835\uDCAB", + "pscr": "\uD835\uDCC5", + "Psi": "\u03A8", + "psi": "\u03C8", + "puncsp": "\u2008", + "Qfr": "\uD835\uDD14", + "qfr": "\uD835\uDD2E", + "qint": "\u2A0C", + "Qopf": "\u211A", + "qopf": "\uD835\uDD62", + "qprime": "\u2057", + "Qscr": "\uD835\uDCAC", + "qscr": "\uD835\uDCC6", + "quaternions": "\u210D", + "quatint": "\u2A16", + "quest": "?", + "questeq": "\u225F", + "QUOT": "\"", + "quot": "\"", + "rAarr": "\u21DB", + "race": "\u223D\u0331", + "Racute": "\u0154", + "racute": "\u0155", + "radic": "\u221A", + "raemptyv": "\u29B3", + "Rang": "\u27EB", + "rang": "\u27E9", + "rangd": "\u2992", + "range": "\u29A5", + "rangle": "\u27E9", + "raquo": "\xBB", + "Rarr": "\u21A0", + "rArr": "\u21D2", + "rarr": "\u2192", + "rarrap": "\u2975", + "rarrb": "\u21E5", + "rarrbfs": "\u2920", + "rarrc": "\u2933", + "rarrfs": "\u291E", + "rarrhk": "\u21AA", + "rarrlp": "\u21AC", + "rarrpl": "\u2945", + "rarrsim": "\u2974", + "Rarrtl": "\u2916", + "rarrtl": "\u21A3", + "rarrw": "\u219D", + "rAtail": "\u291C", + "ratail": "\u291A", + "ratio": "\u2236", + "rationals": "\u211A", + "RBarr": "\u2910", + "rBarr": "\u290F", + "rbarr": "\u290D", + "rbbrk": "\u2773", + "rbrace": "}", + "rbrack": "]", + "rbrke": "\u298C", + "rbrksld": "\u298E", + "rbrkslu": "\u2990", + "Rcaron": "\u0158", + "rcaron": "\u0159", + "Rcedil": "\u0156", + "rcedil": "\u0157", + "rceil": "\u2309", + "rcub": "}", + "Rcy": "\u0420", + "rcy": "\u0440", + "rdca": "\u2937", + "rdldhar": "\u2969", + "rdquo": "\u201D", + "rdquor": "\u201D", + "rdsh": "\u21B3", + "Re": "\u211C", + "real": "\u211C", + "realine": "\u211B", + "realpart": "\u211C", + "reals": "\u211D", + "rect": "\u25AD", + "REG": "\xAE", + "reg": "\xAE", + "ReverseElement": "\u220B", + "ReverseEquilibrium": "\u21CB", + "ReverseUpEquilibrium": "\u296F", + "rfisht": "\u297D", + "rfloor": "\u230B", + "Rfr": "\u211C", + "rfr": "\uD835\uDD2F", + "rHar": "\u2964", + "rhard": "\u21C1", + "rharu": "\u21C0", + "rharul": "\u296C", + "Rho": "\u03A1", + "rho": "\u03C1", + "rhov": "\u03F1", + "RightAngleBracket": "\u27E9", + "RightArrow": "\u2192", + "Rightarrow": "\u21D2", + "rightarrow": "\u2192", + "RightArrowBar": "\u21E5", + "RightArrowLeftArrow": "\u21C4", + "rightarrowtail": "\u21A3", + "RightCeiling": "\u2309", + "RightDoubleBracket": "\u27E7", + "RightDownTeeVector": "\u295D", + "RightDownVector": "\u21C2", + "RightDownVectorBar": "\u2955", + "RightFloor": "\u230B", + "rightharpoondown": "\u21C1", + "rightharpoonup": "\u21C0", + "rightleftarrows": "\u21C4", + "rightleftharpoons": "\u21CC", + "rightrightarrows": "\u21C9", + "rightsquigarrow": "\u219D", + "RightTee": "\u22A2", + "RightTeeArrow": "\u21A6", + "RightTeeVector": "\u295B", + "rightthreetimes": "\u22CC", + "RightTriangle": "\u22B3", + "RightTriangleBar": "\u29D0", + "RightTriangleEqual": "\u22B5", + "RightUpDownVector": "\u294F", + "RightUpTeeVector": "\u295C", + "RightUpVector": "\u21BE", + "RightUpVectorBar": "\u2954", + "RightVector": "\u21C0", + "RightVectorBar": "\u2953", + "ring": "\u02DA", + "risingdotseq": "\u2253", + "rlarr": "\u21C4", + "rlhar": "\u21CC", + "rlm": "\u200F", + "rmoust": "\u23B1", + "rmoustache": "\u23B1", + "rnmid": "\u2AEE", + "roang": "\u27ED", + "roarr": "\u21FE", + "robrk": "\u27E7", + "ropar": "\u2986", + "Ropf": "\u211D", + "ropf": "\uD835\uDD63", + "roplus": "\u2A2E", + "rotimes": "\u2A35", + "RoundImplies": "\u2970", + "rpar": ")", + "rpargt": "\u2994", + "rppolint": "\u2A12", + "rrarr": "\u21C9", + "Rrightarrow": "\u21DB", + "rsaquo": "\u203A", + "Rscr": "\u211B", + "rscr": "\uD835\uDCC7", + "Rsh": "\u21B1", + "rsh": "\u21B1", + "rsqb": "]", + "rsquo": "\u2019", + "rsquor": "\u2019", + "rthree": "\u22CC", + "rtimes": "\u22CA", + "rtri": "\u25B9", + "rtrie": "\u22B5", + "rtrif": "\u25B8", + "rtriltri": "\u29CE", + "RuleDelayed": "\u29F4", + "ruluhar": "\u2968", + "rx": "\u211E", + "Sacute": "\u015A", + "sacute": "\u015B", + "sbquo": "\u201A", + "Sc": "\u2ABC", + "sc": "\u227B", + "scap": "\u2AB8", + "Scaron": "\u0160", + "scaron": "\u0161", + "sccue": "\u227D", + "scE": "\u2AB4", + "sce": "\u2AB0", + "Scedil": "\u015E", + "scedil": "\u015F", + "Scirc": "\u015C", + "scirc": "\u015D", + "scnap": "\u2ABA", + "scnE": "\u2AB6", + "scnsim": "\u22E9", + "scpolint": "\u2A13", + "scsim": "\u227F", + "Scy": "\u0421", + "scy": "\u0441", + "sdot": "\u22C5", + "sdotb": "\u22A1", + "sdote": "\u2A66", + "searhk": "\u2925", + "seArr": "\u21D8", + "searr": "\u2198", + "searrow": "\u2198", + "sect": "\xA7", + "semi": ";", + "seswar": "\u2929", + "setminus": "\u2216", + "setmn": "\u2216", + "sext": "\u2736", + "Sfr": "\uD835\uDD16", + "sfr": "\uD835\uDD30", + "sfrown": "\u2322", + "sharp": "\u266F", + "SHCHcy": "\u0429", + "shchcy": "\u0449", + "SHcy": "\u0428", + "shcy": "\u0448", + "ShortDownArrow": "\u2193", + "ShortLeftArrow": "\u2190", + "shortmid": "\u2223", + "shortparallel": "\u2225", + "ShortRightArrow": "\u2192", + "ShortUpArrow": "\u2191", + "shy": "\xAD", + "Sigma": "\u03A3", + "sigma": "\u03C3", + "sigmaf": "\u03C2", + "sigmav": "\u03C2", + "sim": "\u223C", + "simdot": "\u2A6A", + "sime": "\u2243", + "simeq": "\u2243", + "simg": "\u2A9E", + "simgE": "\u2AA0", + "siml": "\u2A9D", + "simlE": "\u2A9F", + "simne": "\u2246", + "simplus": "\u2A24", + "simrarr": "\u2972", + "slarr": "\u2190", + "SmallCircle": "\u2218", + "smallsetminus": "\u2216", + "smashp": "\u2A33", + "smeparsl": "\u29E4", + "smid": "\u2223", + "smile": "\u2323", + "smt": "\u2AAA", + "smte": "\u2AAC", + "smtes": "\u2AAC\uFE00", + "SOFTcy": "\u042C", + "softcy": "\u044C", + "sol": "/", + "solb": "\u29C4", + "solbar": "\u233F", + "Sopf": "\uD835\uDD4A", + "sopf": "\uD835\uDD64", + "spades": "\u2660", + "spadesuit": "\u2660", + "spar": "\u2225", + "sqcap": "\u2293", + "sqcaps": "\u2293\uFE00", + "sqcup": "\u2294", + "sqcups": "\u2294\uFE00", + "Sqrt": "\u221A", + "sqsub": "\u228F", + "sqsube": "\u2291", + "sqsubset": "\u228F", + "sqsubseteq": "\u2291", + "sqsup": "\u2290", + "sqsupe": "\u2292", + "sqsupset": "\u2290", + "sqsupseteq": "\u2292", + "squ": "\u25A1", + "Square": "\u25A1", + "square": "\u25A1", + "SquareIntersection": "\u2293", + "SquareSubset": "\u228F", + "SquareSubsetEqual": "\u2291", + "SquareSuperset": "\u2290", + "SquareSupersetEqual": "\u2292", + "SquareUnion": "\u2294", + "squarf": "\u25AA", + "squf": "\u25AA", + "srarr": "\u2192", + "Sscr": "\uD835\uDCAE", + "sscr": "\uD835\uDCC8", + "ssetmn": "\u2216", + "ssmile": "\u2323", + "sstarf": "\u22C6", + "Star": "\u22C6", + "star": "\u2606", + "starf": "\u2605", + "straightepsilon": "\u03F5", + "straightphi": "\u03D5", + "strns": "\xAF", + "Sub": "\u22D0", + "sub": "\u2282", + "subdot": "\u2ABD", + "subE": "\u2AC5", + "sube": "\u2286", + "subedot": "\u2AC3", + "submult": "\u2AC1", + "subnE": "\u2ACB", + "subne": "\u228A", + "subplus": "\u2ABF", + "subrarr": "\u2979", + "Subset": "\u22D0", + "subset": "\u2282", + "subseteq": "\u2286", + "subseteqq": "\u2AC5", + "SubsetEqual": "\u2286", + "subsetneq": "\u228A", + "subsetneqq": "\u2ACB", + "subsim": "\u2AC7", + "subsub": "\u2AD5", + "subsup": "\u2AD3", + "succ": "\u227B", + "succapprox": "\u2AB8", + "succcurlyeq": "\u227D", + "Succeeds": "\u227B", + "SucceedsEqual": "\u2AB0", + "SucceedsSlantEqual": "\u227D", + "SucceedsTilde": "\u227F", + "succeq": "\u2AB0", + "succnapprox": "\u2ABA", + "succneqq": "\u2AB6", + "succnsim": "\u22E9", + "succsim": "\u227F", + "SuchThat": "\u220B", + "Sum": "\u2211", + "sum": "\u2211", + "sung": "\u266A", + "Sup": "\u22D1", + "sup": "\u2283", + "sup1": "\xB9", + "sup2": "\xB2", + "sup3": "\xB3", + "supdot": "\u2ABE", + "supdsub": "\u2AD8", + "supE": "\u2AC6", + "supe": "\u2287", + "supedot": "\u2AC4", + "Superset": "\u2283", + "SupersetEqual": "\u2287", + "suphsol": "\u27C9", + "suphsub": "\u2AD7", + "suplarr": "\u297B", + "supmult": "\u2AC2", + "supnE": "\u2ACC", + "supne": "\u228B", + "supplus": "\u2AC0", + "Supset": "\u22D1", + "supset": "\u2283", + "supseteq": "\u2287", + "supseteqq": "\u2AC6", + "supsetneq": "\u228B", + "supsetneqq": "\u2ACC", + "supsim": "\u2AC8", + "supsub": "\u2AD4", + "supsup": "\u2AD6", + "swarhk": "\u2926", + "swArr": "\u21D9", + "swarr": "\u2199", + "swarrow": "\u2199", + "swnwar": "\u292A", + "szlig": "\xDF", + "Tab": "\t", + "target": "\u2316", + "Tau": "\u03A4", + "tau": "\u03C4", + "tbrk": "\u23B4", + "Tcaron": "\u0164", + "tcaron": "\u0165", + "Tcedil": "\u0162", + "tcedil": "\u0163", + "Tcy": "\u0422", + "tcy": "\u0442", + "tdot": "\u20DB", + "telrec": "\u2315", + "Tfr": "\uD835\uDD17", + "tfr": "\uD835\uDD31", + "there4": "\u2234", + "Therefore": "\u2234", + "therefore": "\u2234", + "Theta": "\u0398", + "theta": "\u03B8", + "thetasym": "\u03D1", + "thetav": "\u03D1", + "thickapprox": "\u2248", + "thicksim": "\u223C", + "ThickSpace": "\u205F\u200A", + "thinsp": "\u2009", + "ThinSpace": "\u2009", + "thkap": "\u2248", + "thksim": "\u223C", + "THORN": "\xDE", + "thorn": "\xFE", + "Tilde": "\u223C", + "tilde": "\u02DC", + "TildeEqual": "\u2243", + "TildeFullEqual": "\u2245", + "TildeTilde": "\u2248", + "times": "\xD7", + "timesb": "\u22A0", + "timesbar": "\u2A31", + "timesd": "\u2A30", + "tint": "\u222D", + "toea": "\u2928", + "top": "\u22A4", + "topbot": "\u2336", + "topcir": "\u2AF1", + "Topf": "\uD835\uDD4B", + "topf": "\uD835\uDD65", + "topfork": "\u2ADA", + "tosa": "\u2929", + "tprime": "\u2034", + "TRADE": "\u2122", + "trade": "\u2122", + "triangle": "\u25B5", + "triangledown": "\u25BF", + "triangleleft": "\u25C3", + "trianglelefteq": "\u22B4", + "triangleq": "\u225C", + "triangleright": "\u25B9", + "trianglerighteq": "\u22B5", + "tridot": "\u25EC", + "trie": "\u225C", + "triminus": "\u2A3A", + "TripleDot": "\u20DB", + "triplus": "\u2A39", + "trisb": "\u29CD", + "tritime": "\u2A3B", + "trpezium": "\u23E2", + "Tscr": "\uD835\uDCAF", + "tscr": "\uD835\uDCC9", + "TScy": "\u0426", + "tscy": "\u0446", + "TSHcy": "\u040B", + "tshcy": "\u045B", + "Tstrok": "\u0166", + "tstrok": "\u0167", + "twixt": "\u226C", + "twoheadleftarrow": "\u219E", + "twoheadrightarrow": "\u21A0", + "Uacute": "\xDA", + "uacute": "\xFA", + "Uarr": "\u219F", + "uArr": "\u21D1", + "uarr": "\u2191", + "Uarrocir": "\u2949", + "Ubrcy": "\u040E", + "ubrcy": "\u045E", + "Ubreve": "\u016C", + "ubreve": "\u016D", + "Ucirc": "\xDB", + "ucirc": "\xFB", + "Ucy": "\u0423", + "ucy": "\u0443", + "udarr": "\u21C5", + "Udblac": "\u0170", + "udblac": "\u0171", + "udhar": "\u296E", + "ufisht": "\u297E", + "Ufr": "\uD835\uDD18", + "ufr": "\uD835\uDD32", + "Ugrave": "\xD9", + "ugrave": "\xF9", + "uHar": "\u2963", + "uharl": "\u21BF", + "uharr": "\u21BE", + "uhblk": "\u2580", + "ulcorn": "\u231C", + "ulcorner": "\u231C", + "ulcrop": "\u230F", + "ultri": "\u25F8", + "Umacr": "\u016A", + "umacr": "\u016B", + "uml": "\xA8", + "UnderBar": "_", + "UnderBrace": "\u23DF", + "UnderBracket": "\u23B5", + "UnderParenthesis": "\u23DD", + "Union": "\u22C3", + "UnionPlus": "\u228E", + "Uogon": "\u0172", + "uogon": "\u0173", + "Uopf": "\uD835\uDD4C", + "uopf": "\uD835\uDD66", + "UpArrow": "\u2191", + "Uparrow": "\u21D1", + "uparrow": "\u2191", + "UpArrowBar": "\u2912", + "UpArrowDownArrow": "\u21C5", + "UpDownArrow": "\u2195", + "Updownarrow": "\u21D5", + "updownarrow": "\u2195", + "UpEquilibrium": "\u296E", + "upharpoonleft": "\u21BF", + "upharpoonright": "\u21BE", + "uplus": "\u228E", + "UpperLeftArrow": "\u2196", + "UpperRightArrow": "\u2197", + "Upsi": "\u03D2", + "upsi": "\u03C5", + "upsih": "\u03D2", + "Upsilon": "\u03A5", + "upsilon": "\u03C5", + "UpTee": "\u22A5", + "UpTeeArrow": "\u21A5", + "upuparrows": "\u21C8", + "urcorn": "\u231D", + "urcorner": "\u231D", + "urcrop": "\u230E", + "Uring": "\u016E", + "uring": "\u016F", + "urtri": "\u25F9", + "Uscr": "\uD835\uDCB0", + "uscr": "\uD835\uDCCA", + "utdot": "\u22F0", + "Utilde": "\u0168", + "utilde": "\u0169", + "utri": "\u25B5", + "utrif": "\u25B4", + "uuarr": "\u21C8", + "Uuml": "\xDC", + "uuml": "\xFC", + "uwangle": "\u29A7", + "vangrt": "\u299C", + "varepsilon": "\u03F5", + "varkappa": "\u03F0", + "varnothing": "\u2205", + "varphi": "\u03D5", + "varpi": "\u03D6", + "varpropto": "\u221D", + "vArr": "\u21D5", + "varr": "\u2195", + "varrho": "\u03F1", + "varsigma": "\u03C2", + "varsubsetneq": "\u228A\uFE00", + "varsubsetneqq": "\u2ACB\uFE00", + "varsupsetneq": "\u228B\uFE00", + "varsupsetneqq": "\u2ACC\uFE00", + "vartheta": "\u03D1", + "vartriangleleft": "\u22B2", + "vartriangleright": "\u22B3", + "Vbar": "\u2AEB", + "vBar": "\u2AE8", + "vBarv": "\u2AE9", + "Vcy": "\u0412", + "vcy": "\u0432", + "VDash": "\u22AB", + "Vdash": "\u22A9", + "vDash": "\u22A8", + "vdash": "\u22A2", + "Vdashl": "\u2AE6", + "Vee": "\u22C1", + "vee": "\u2228", + "veebar": "\u22BB", + "veeeq": "\u225A", + "vellip": "\u22EE", + "Verbar": "\u2016", + "verbar": "|", + "Vert": "\u2016", + "vert": "|", + "VerticalBar": "\u2223", + "VerticalLine": "|", + "VerticalSeparator": "\u2758", + "VerticalTilde": "\u2240", + "VeryThinSpace": "\u200A", + "Vfr": "\uD835\uDD19", + "vfr": "\uD835\uDD33", + "vltri": "\u22B2", + "vnsub": "\u2282\u20D2", + "vnsup": "\u2283\u20D2", + "Vopf": "\uD835\uDD4D", + "vopf": "\uD835\uDD67", + "vprop": "\u221D", + "vrtri": "\u22B3", + "Vscr": "\uD835\uDCB1", + "vscr": "\uD835\uDCCB", + "vsubnE": "\u2ACB\uFE00", + "vsubne": "\u228A\uFE00", + "vsupnE": "\u2ACC\uFE00", + "vsupne": "\u228B\uFE00", + "Vvdash": "\u22AA", + "vzigzag": "\u299A", + "Wcirc": "\u0174", + "wcirc": "\u0175", + "wedbar": "\u2A5F", + "Wedge": "\u22C0", + "wedge": "\u2227", + "wedgeq": "\u2259", + "weierp": "\u2118", + "Wfr": "\uD835\uDD1A", + "wfr": "\uD835\uDD34", + "Wopf": "\uD835\uDD4E", + "wopf": "\uD835\uDD68", + "wp": "\u2118", + "wr": "\u2240", + "wreath": "\u2240", + "Wscr": "\uD835\uDCB2", + "wscr": "\uD835\uDCCC", + "xcap": "\u22C2", + "xcirc": "\u25EF", + "xcup": "\u22C3", + "xdtri": "\u25BD", + "Xfr": "\uD835\uDD1B", + "xfr": "\uD835\uDD35", + "xhArr": "\u27FA", + "xharr": "\u27F7", + "Xi": "\u039E", + "xi": "\u03BE", + "xlArr": "\u27F8", + "xlarr": "\u27F5", + "xmap": "\u27FC", + "xnis": "\u22FB", + "xodot": "\u2A00", + "Xopf": "\uD835\uDD4F", + "xopf": "\uD835\uDD69", + "xoplus": "\u2A01", + "xotime": "\u2A02", + "xrArr": "\u27F9", + "xrarr": "\u27F6", + "Xscr": "\uD835\uDCB3", + "xscr": "\uD835\uDCCD", + "xsqcup": "\u2A06", + "xuplus": "\u2A04", + "xutri": "\u25B3", + "xvee": "\u22C1", + "xwedge": "\u22C0", + "Yacute": "\xDD", + "yacute": "\xFD", + "YAcy": "\u042F", + "yacy": "\u044F", + "Ycirc": "\u0176", + "ycirc": "\u0177", + "Ycy": "\u042B", + "ycy": "\u044B", + "yen": "\xA5", + "Yfr": "\uD835\uDD1C", + "yfr": "\uD835\uDD36", + "YIcy": "\u0407", + "yicy": "\u0457", + "Yopf": "\uD835\uDD50", + "yopf": "\uD835\uDD6A", + "Yscr": "\uD835\uDCB4", + "yscr": "\uD835\uDCCE", + "YUcy": "\u042E", + "yucy": "\u044E", + "Yuml": "\u0178", + "yuml": "\xFF", + "Zacute": "\u0179", + "zacute": "\u017A", + "Zcaron": "\u017D", + "zcaron": "\u017E", + "Zcy": "\u0417", + "zcy": "\u0437", + "Zdot": "\u017B", + "zdot": "\u017C", + "zeetrf": "\u2128", + "ZeroWidthSpace": "\u200B", + "Zeta": "\u0396", + "zeta": "\u03B6", + "Zfr": "\u2128", + "zfr": "\uD835\uDD37", + "ZHcy": "\u0416", + "zhcy": "\u0436", + "zigrarr": "\u21DD", + "Zopf": "\u2124", + "zopf": "\uD835\uDD6B", + "Zscr": "\uD835\uDCB5", + "zscr": "\uD835\uDCCF", + "zwj": "\u200D", + "zwnj": "\u200C" +}; +var hasOwn = Object.prototype.hasOwnProperty; + +function has(object, key) { + return object ? hasOwn.call(object, key) : false; +} + +function decodeEntity(name) { + if (has(entities, name)) { + return entities[name]; + } else { + return name; + } +} + +var hasOwn$1 = Object.prototype.hasOwnProperty; + +function has$1(object, key) { + return object ? hasOwn$1.call(object, key) : false; +} // Extend objects +// + + +function assign(obj +/*from1, from2, from3, ...*/ +) { + var sources = [].slice.call(arguments, 1); + sources.forEach(function (source) { + if (!source) { + return; + } + + if (typeof source !== 'object') { + throw new TypeError(source + 'must be object'); + } + + Object.keys(source).forEach(function (key) { + obj[key] = source[key]; + }); + }); + return obj; +} //////////////////////////////////////////////////////////////////////////////// + + +var UNESCAPE_MD_RE = /\\([\\!"#$%&'()*+,.\/:;<=>?@[\]^_`{|}~-])/g; + +function unescapeMd(str) { + if (str.indexOf('\\') < 0) { + return str; + } + + return str.replace(UNESCAPE_MD_RE, '$1'); +} //////////////////////////////////////////////////////////////////////////////// + + +function isValidEntityCode(c) { + /*eslint no-bitwise:0*/ + // broken sequence + if (c >= 0xD800 && c <= 0xDFFF) { + return false; + } // never used + + + if (c >= 0xFDD0 && c <= 0xFDEF) { + return false; + } + + if ((c & 0xFFFF) === 0xFFFF || (c & 0xFFFF) === 0xFFFE) { + return false; + } // control codes + + + if (c >= 0x00 && c <= 0x08) { + return false; + } + + if (c === 0x0B) { + return false; + } + + if (c >= 0x0E && c <= 0x1F) { + return false; + } + + if (c >= 0x7F && c <= 0x9F) { + return false; + } // out of range + + + if (c > 0x10FFFF) { + return false; + } + + return true; +} + +function fromCodePoint(c) { + /*eslint no-bitwise:0*/ + if (c > 0xffff) { + c -= 0x10000; + var surrogate1 = 0xd800 + (c >> 10), + surrogate2 = 0xdc00 + (c & 0x3ff); + return String.fromCharCode(surrogate1, surrogate2); + } + + return String.fromCharCode(c); +} + +var NAMED_ENTITY_RE = /&([a-z#][a-z0-9]{1,31});/gi; +var DIGITAL_ENTITY_TEST_RE = /^#((?:x[a-f0-9]{1,8}|[0-9]{1,8}))/i; + +function replaceEntityPattern(match, name) { + var code = 0; + var decoded = decodeEntity(name); + + if (name !== decoded) { + return decoded; + } else if (name.charCodeAt(0) === 0x23 + /* # */ + && DIGITAL_ENTITY_TEST_RE.test(name)) { + code = name[1].toLowerCase() === 'x' ? parseInt(name.slice(2), 16) : parseInt(name.slice(1), 10); + + if (isValidEntityCode(code)) { + return fromCodePoint(code); + } + } + + return match; +} + +function replaceEntities(str) { + if (str.indexOf('&') < 0) { + return str; + } + + return str.replace(NAMED_ENTITY_RE, replaceEntityPattern); +} //////////////////////////////////////////////////////////////////////////////// + + +var HTML_ESCAPE_TEST_RE = /[&<>"]/; +var HTML_ESCAPE_REPLACE_RE = /[&<>"]/g; +var HTML_REPLACEMENTS = { + '&': '&', + '<': '<', + '>': '>', + '"': '"' +}; + +function replaceUnsafeChar(ch) { + return HTML_REPLACEMENTS[ch]; +} + +function escapeHtml(str) { + if (HTML_ESCAPE_TEST_RE.test(str)) { + return str.replace(HTML_ESCAPE_REPLACE_RE, replaceUnsafeChar); + } + + return str; +} +/** + * Renderer rules cache + */ + +var rules = {}; +/** + * Blockquotes + */ + +rules.blockquote_open = function () +/* tokens, idx, options, env */ +{ + return '
\n'; +}; + +rules.blockquote_close = function (tokens, idx +/*, options, env */ +) { + return '
' + getBreak(tokens, idx); +}; +/** + * Code + */ + + +rules.code = function (tokens, idx +/*, options, env */ +) { + if (tokens[idx].block) { + return '
' + escapeHtml(tokens[idx].content) + '
' + getBreak(tokens, idx); + } + + return '' + escapeHtml(tokens[idx].content) + ''; +}; +/** + * Fenced code blocks + */ + + +rules.fence = function (tokens, idx, options, env, instance) { + var token = tokens[idx]; + var langClass = ''; + var langPrefix = options.langPrefix; + var langName = '', + fences, + fenceName; + var highlighted; + + if (token.params) { + // + // ```foo bar + // + // Try custom renderer "foo" first. That will simplify overwrite + // for diagrams, latex, and any other fenced block with custom look + // + fences = token.params.split(/\s+/g); + fenceName = fences.join(' '); + + if (has$1(instance.rules.fence_custom, fences[0])) { + return instance.rules.fence_custom[fences[0]](tokens, idx, options, env, instance); + } + + langName = escapeHtml(replaceEntities(unescapeMd(fenceName))); + langClass = ' class="' + langPrefix + langName + '"'; + } + + if (options.highlight) { + highlighted = options.highlight.apply(options.highlight, [token.content].concat(fences)) || escapeHtml(token.content); + } else { + highlighted = escapeHtml(token.content); + } + + return '
' + highlighted + '
' + getBreak(tokens, idx); +}; + +rules.fence_custom = {}; +/** + * Headings + */ + +rules.heading_open = function (tokens, idx +/*, options, env */ +) { + return ''; +}; + +rules.heading_close = function (tokens, idx +/*, options, env */ +) { + return '\n'; +}; +/** + * Horizontal rules + */ + + +rules.hr = function (tokens, idx, options +/*, env */ +) { + return (options.xhtmlOut ? '
' : '
') + getBreak(tokens, idx); +}; +/** + * Bullets + */ + + +rules.bullet_list_open = function () +/* tokens, idx, options, env */ +{ + return '
    \n'; +}; + +rules.bullet_list_close = function (tokens, idx +/*, options, env */ +) { + return '
' + getBreak(tokens, idx); +}; +/** + * List items + */ + + +rules.list_item_open = function () +/* tokens, idx, options, env */ +{ + return '
  • '; +}; + +rules.list_item_close = function () +/* tokens, idx, options, env */ +{ + return '
  • \n'; +}; +/** + * Ordered list items + */ + + +rules.ordered_list_open = function (tokens, idx +/*, options, env */ +) { + var token = tokens[idx]; + var order = token.order > 1 ? ' start="' + token.order + '"' : ''; + return '\n'; +}; + +rules.ordered_list_close = function (tokens, idx +/*, options, env */ +) { + return '' + getBreak(tokens, idx); +}; +/** + * Paragraphs + */ + + +rules.paragraph_open = function (tokens, idx +/*, options, env */ +) { + return tokens[idx].tight ? '' : '

    '; +}; + +rules.paragraph_close = function (tokens, idx +/*, options, env */ +) { + var addBreak = !(tokens[idx].tight && idx && tokens[idx - 1].type === 'inline' && !tokens[idx - 1].content); + return (tokens[idx].tight ? '' : '

    ') + (addBreak ? getBreak(tokens, idx) : ''); +}; +/** + * Links + */ + + +rules.link_open = function (tokens, idx, options +/* env */ +) { + var title = tokens[idx].title ? ' title="' + escapeHtml(replaceEntities(tokens[idx].title)) + '"' : ''; + var target = options.linkTarget ? ' target="' + options.linkTarget + '"' : ''; + return ''; +}; + +rules.link_close = function () +/* tokens, idx, options, env */ +{ + return ''; +}; +/** + * Images + */ + + +rules.image = function (tokens, idx, options +/*, env */ +) { + var src = ' src="' + escapeHtml(tokens[idx].src) + '"'; + var title = tokens[idx].title ? ' title="' + escapeHtml(replaceEntities(tokens[idx].title)) + '"' : ''; + var alt = ' alt="' + (tokens[idx].alt ? escapeHtml(replaceEntities(unescapeMd(tokens[idx].alt))) : '') + '"'; + var suffix = options.xhtmlOut ? ' /' : ''; + return ''; +}; +/** + * Tables + */ + + +rules.table_open = function () +/* tokens, idx, options, env */ +{ + return '\n'; +}; + +rules.table_close = function () +/* tokens, idx, options, env */ +{ + return '
    \n'; +}; + +rules.thead_open = function () +/* tokens, idx, options, env */ +{ + return '\n'; +}; + +rules.thead_close = function () +/* tokens, idx, options, env */ +{ + return '\n'; +}; + +rules.tbody_open = function () +/* tokens, idx, options, env */ +{ + return '\n'; +}; + +rules.tbody_close = function () +/* tokens, idx, options, env */ +{ + return '\n'; +}; + +rules.tr_open = function () +/* tokens, idx, options, env */ +{ + return ''; +}; + +rules.tr_close = function () +/* tokens, idx, options, env */ +{ + return '\n'; +}; + +rules.th_open = function (tokens, idx +/*, options, env */ +) { + var token = tokens[idx]; + return ''; +}; + +rules.th_close = function () +/* tokens, idx, options, env */ +{ + return ''; +}; + +rules.td_open = function (tokens, idx +/*, options, env */ +) { + var token = tokens[idx]; + return ''; +}; + +rules.td_close = function () +/* tokens, idx, options, env */ +{ + return ''; +}; +/** + * Bold + */ + + +rules.strong_open = function () +/* tokens, idx, options, env */ +{ + return ''; +}; + +rules.strong_close = function () +/* tokens, idx, options, env */ +{ + return ''; +}; +/** + * Italicize + */ + + +rules.em_open = function () +/* tokens, idx, options, env */ +{ + return ''; +}; + +rules.em_close = function () +/* tokens, idx, options, env */ +{ + return ''; +}; +/** + * Strikethrough + */ + + +rules.del_open = function () +/* tokens, idx, options, env */ +{ + return ''; +}; + +rules.del_close = function () +/* tokens, idx, options, env */ +{ + return ''; +}; +/** + * Insert + */ + + +rules.ins_open = function () +/* tokens, idx, options, env */ +{ + return ''; +}; + +rules.ins_close = function () +/* tokens, idx, options, env */ +{ + return ''; +}; +/** + * Highlight + */ + + +rules.mark_open = function () +/* tokens, idx, options, env */ +{ + return ''; +}; + +rules.mark_close = function () +/* tokens, idx, options, env */ +{ + return ''; +}; +/** + * Super- and sub-script + */ + + +rules.sub = function (tokens, idx +/*, options, env */ +) { + return '' + escapeHtml(tokens[idx].content) + ''; +}; + +rules.sup = function (tokens, idx +/*, options, env */ +) { + return '' + escapeHtml(tokens[idx].content) + ''; +}; +/** + * Breaks + */ + + +rules.hardbreak = function (tokens, idx, options +/*, env */ +) { + return options.xhtmlOut ? '
    \n' : '
    \n'; +}; + +rules.softbreak = function (tokens, idx, options +/*, env */ +) { + return options.breaks ? options.xhtmlOut ? '
    \n' : '
    \n' : '\n'; +}; +/** + * Text + */ + + +rules.text = function (tokens, idx +/*, options, env */ +) { + return escapeHtml(tokens[idx].content); +}; +/** + * Content + */ + + +rules.htmlblock = function (tokens, idx +/*, options, env */ +) { + return tokens[idx].content; +}; + +rules.htmltag = function (tokens, idx +/*, options, env */ +) { + return tokens[idx].content; +}; +/** + * Abbreviations, initialism + */ + + +rules.abbr_open = function (tokens, idx +/*, options, env */ +) { + return ''; +}; + +rules.abbr_close = function () +/* tokens, idx, options, env */ +{ + return ''; +}; +/** + * Footnotes + */ + + +rules.footnote_ref = function (tokens, idx) { + var n = Number(tokens[idx].id + 1).toString(); + var id = 'fnref' + n; + + if (tokens[idx].subId > 0) { + id += ':' + tokens[idx].subId; + } + + return '[' + n + ']'; +}; + +rules.footnote_block_open = function (tokens, idx, options) { + var hr = options.xhtmlOut ? '
    \n' : '
    \n'; + return hr + '
    \n
      \n'; +}; + +rules.footnote_block_close = function () { + return '
    \n
    \n'; +}; + +rules.footnote_open = function (tokens, idx) { + var id = Number(tokens[idx].id + 1).toString(); + return '
  • '; +}; + +rules.footnote_close = function () { + return '
  • \n'; +}; + +rules.footnote_anchor = function (tokens, idx) { + var n = Number(tokens[idx].id + 1).toString(); + var id = 'fnref' + n; + + if (tokens[idx].subId > 0) { + id += ':' + tokens[idx].subId; + } + + return ' ↩'; +}; +/** + * Definition lists + */ + + +rules.dl_open = function () { + return '
    \n'; +}; + +rules.dt_open = function () { + return '
    '; +}; + +rules.dd_open = function () { + return '
    '; +}; + +rules.dl_close = function () { + return '
    \n'; +}; + +rules.dt_close = function () { + return '\n'; +}; + +rules.dd_close = function () { + return '\n'; +}; +/** + * Helper functions + */ + + +function nextToken(tokens, idx) { + if (++idx >= tokens.length - 2) { + return idx; + } + + if (tokens[idx].type === 'paragraph_open' && tokens[idx].tight && tokens[idx + 1].type === 'inline' && tokens[idx + 1].content.length === 0 && tokens[idx + 2].type === 'paragraph_close' && tokens[idx + 2].tight) { + return nextToken(tokens, idx + 2); + } + + return idx; +} +/** + * Check to see if `\n` is needed before the next token. + * + * @param {Array} `tokens` + * @param {Number} `idx` + * @return {String} Empty string or newline + * @api private + */ + + +var getBreak = rules.getBreak = function getBreak(tokens, idx) { + idx = nextToken(tokens, idx); + + if (idx < tokens.length && tokens[idx].type === 'list_item_close') { + return ''; + } + + return '\n'; +}; +/** + * Renderer class. Renders HTML and exposes `rules` to allow + * local modifications. + */ + + +function Renderer() { + this.rules = assign({}, rules); // exported helper, for custom rules only + + this.getBreak = rules.getBreak; +} +/** + * Render a string of inline HTML with the given `tokens` and + * `options`. + * + * @param {Array} `tokens` + * @param {Object} `options` + * @param {Object} `env` + * @return {String} + * @api public + */ + + +Renderer.prototype.renderInline = function (tokens, options, env) { + var _rules = this.rules; + var len = tokens.length, + i = 0; + var result = ''; + + while (len--) { + result += _rules[tokens[i].type](tokens, i++, options, env, this); + } + + return result; +}; +/** + * Render a string of HTML with the given `tokens` and + * `options`. + * + * @param {Array} `tokens` + * @param {Object} `options` + * @param {Object} `env` + * @return {String} + * @api public + */ + + +Renderer.prototype.render = function (tokens, options, env) { + var _rules = this.rules; + var len = tokens.length, + i = -1; + var result = ''; + + while (++i < len) { + if (tokens[i].type === 'inline') { + result += this.renderInline(tokens[i].children, options, env); + } else { + result += _rules[tokens[i].type](tokens, i, options, env, this); + } + } + + return result; +}; +/** + * Ruler is a helper class for building responsibility chains from + * parse rules. It allows: + * + * - easy stack rules chains + * - getting main chain and named chains content (as arrays of functions) + * + * Helper methods, should not be used directly. + * @api private + */ + + +function Ruler() { + // List of added rules. Each element is: + // + // { name: XXX, + // enabled: Boolean, + // fn: Function(), + // alt: [ name2, name3 ] } + // + this.__rules__ = []; // Cached rule chains. + // + // First level - chain name, '' for default. + // Second level - digital anchor for fast filtering by charcodes. + // + + this.__cache__ = null; +} +/** + * Find the index of a rule by `name`. + * + * @param {String} `name` + * @return {Number} Index of the given `name` + * @api private + */ + + +Ruler.prototype.__find__ = function (name) { + var len = this.__rules__.length; + var i = -1; + + while (len--) { + if (this.__rules__[++i].name === name) { + return i; + } + } + + return -1; +}; +/** + * Build the rules lookup cache + * + * @api private + */ + + +Ruler.prototype.__compile__ = function () { + var self = this; + var chains = ['']; // collect unique names + + self.__rules__.forEach(function (rule) { + if (!rule.enabled) { + return; + } + + rule.alt.forEach(function (altName) { + if (chains.indexOf(altName) < 0) { + chains.push(altName); + } + }); + }); + + self.__cache__ = {}; + chains.forEach(function (chain) { + self.__cache__[chain] = []; + + self.__rules__.forEach(function (rule) { + if (!rule.enabled) { + return; + } + + if (chain && rule.alt.indexOf(chain) < 0) { + return; + } + + self.__cache__[chain].push(rule.fn); + }); + }); +}; +/** + * Ruler public methods + * ------------------------------------------------ + */ + +/** + * Replace rule function + * + * @param {String} `name` Rule name + * @param {Function `fn` + * @param {Object} `options` + * @api private + */ + + +Ruler.prototype.at = function (name, fn, options) { + var idx = this.__find__(name); + + var opt = options || {}; + + if (idx === -1) { + throw new Error('Parser rule not found: ' + name); + } + + this.__rules__[idx].fn = fn; + this.__rules__[idx].alt = opt.alt || []; + this.__cache__ = null; +}; +/** + * Add a rule to the chain before given the `ruleName`. + * + * @param {String} `beforeName` + * @param {String} `ruleName` + * @param {Function} `fn` + * @param {Object} `options` + * @api private + */ + + +Ruler.prototype.before = function (beforeName, ruleName, fn, options) { + var idx = this.__find__(beforeName); + + var opt = options || {}; + + if (idx === -1) { + throw new Error('Parser rule not found: ' + beforeName); + } + + this.__rules__.splice(idx, 0, { + name: ruleName, + enabled: true, + fn: fn, + alt: opt.alt || [] + }); + + this.__cache__ = null; +}; +/** + * Add a rule to the chain after the given `ruleName`. + * + * @param {String} `afterName` + * @param {String} `ruleName` + * @param {Function} `fn` + * @param {Object} `options` + * @api private + */ + + +Ruler.prototype.after = function (afterName, ruleName, fn, options) { + var idx = this.__find__(afterName); + + var opt = options || {}; + + if (idx === -1) { + throw new Error('Parser rule not found: ' + afterName); + } + + this.__rules__.splice(idx + 1, 0, { + name: ruleName, + enabled: true, + fn: fn, + alt: opt.alt || [] + }); + + this.__cache__ = null; +}; +/** + * Add a rule to the end of chain. + * + * @param {String} `ruleName` + * @param {Function} `fn` + * @param {Object} `options` + * @return {String} + */ + + +Ruler.prototype.push = function (ruleName, fn, options) { + var opt = options || {}; + + this.__rules__.push({ + name: ruleName, + enabled: true, + fn: fn, + alt: opt.alt || [] + }); + + this.__cache__ = null; +}; +/** + * Enable a rule or list of rules. + * + * @param {String|Array} `list` Name or array of rule names to enable + * @param {Boolean} `strict` If `true`, all non listed rules will be disabled. + * @api private + */ + + +Ruler.prototype.enable = function (list, strict) { + list = !Array.isArray(list) ? [list] : list; // In strict mode disable all existing rules first + + if (strict) { + this.__rules__.forEach(function (rule) { + rule.enabled = false; + }); + } // Search by name and enable + + + list.forEach(function (name) { + var idx = this.__find__(name); + + if (idx < 0) { + throw new Error('Rules manager: invalid rule name ' + name); + } + + this.__rules__[idx].enabled = true; + }, this); + this.__cache__ = null; +}; +/** + * Disable a rule or list of rules. + * + * @param {String|Array} `list` Name or array of rule names to disable + * @api private + */ + + +Ruler.prototype.disable = function (list) { + list = !Array.isArray(list) ? [list] : list; // Search by name and disable + + list.forEach(function (name) { + var idx = this.__find__(name); + + if (idx < 0) { + throw new Error('Rules manager: invalid rule name ' + name); + } + + this.__rules__[idx].enabled = false; + }, this); + this.__cache__ = null; +}; +/** + * Get a rules list as an array of functions. + * + * @param {String} `chainName` + * @return {Object} + * @api private + */ + + +Ruler.prototype.getRules = function (chainName) { + if (this.__cache__ === null) { + this.__compile__(); + } + + return this.__cache__[chainName] || []; +}; + +function block(state) { + if (state.inlineMode) { + state.tokens.push({ + type: 'inline', + content: state.src.replace(/\n/g, ' ').trim(), + level: 0, + lines: [0, 1], + children: [] + }); + } else { + state.block.parse(state.src, state.options, state.env, state.tokens); + } +} // Inline parser state + + +function StateInline(src, parserInline, options, env, outTokens) { + this.src = src; + this.env = env; + this.options = options; + this.parser = parserInline; + this.tokens = outTokens; + this.pos = 0; + this.posMax = this.src.length; + this.level = 0; + this.pending = ''; + this.pendingLevel = 0; + this.cache = []; // Stores { start: end } pairs. Useful for backtrack + // optimization of pairs parse (emphasis, strikes). + // Link parser state vars + + this.isInLabel = false; // Set true when seek link label - we should disable + // "paired" rules (emphasis, strikes) to not skip + // tailing `]` + + this.linkLevel = 0; // Increment for each nesting link. Used to prevent + // nesting in definitions + + this.linkContent = ''; // Temporary storage for link url + + this.labelUnmatchedScopes = 0; // Track unpaired `[` for link labels + // (backtrack optimization) +} // Flush pending text +// + + +StateInline.prototype.pushPending = function () { + this.tokens.push({ + type: 'text', + content: this.pending, + level: this.pendingLevel + }); + this.pending = ''; +}; // Push new token to "stream". +// If pending text exists - flush it as text token +// + + +StateInline.prototype.push = function (token) { + if (this.pending) { + this.pushPending(); + } + + this.tokens.push(token); + this.pendingLevel = this.level; +}; // Store value to cache. +// !!! Implementation has parser-specific optimizations +// !!! keys MUST be integer, >= 0; values MUST be integer, > 0 +// + + +StateInline.prototype.cacheSet = function (key, val) { + for (var i = this.cache.length; i <= key; i++) { + this.cache.push(0); + } + + this.cache[key] = val; +}; // Get cache value +// + + +StateInline.prototype.cacheGet = function (key) { + return key < this.cache.length ? this.cache[key] : 0; +}; +/** + * Parse link labels + * + * This function assumes that first character (`[`) already matches; + * returns the end of the label. + * + * @param {Object} state + * @param {Number} start + * @api private + */ + + +function parseLinkLabel(state, start) { + var level, + found, + marker, + labelEnd = -1, + max = state.posMax, + oldPos = state.pos, + oldFlag = state.isInLabel; + + if (state.isInLabel) { + return -1; + } + + if (state.labelUnmatchedScopes) { + state.labelUnmatchedScopes--; + return -1; + } + + state.pos = start + 1; + state.isInLabel = true; + level = 1; + + while (state.pos < max) { + marker = state.src.charCodeAt(state.pos); + + if (marker === 0x5B + /* [ */ + ) { + level++; + } else if (marker === 0x5D + /* ] */ + ) { + level--; + + if (level === 0) { + found = true; + break; + } + } + + state.parser.skipToken(state); + } + + if (found) { + labelEnd = state.pos; + state.labelUnmatchedScopes = 0; + } else { + state.labelUnmatchedScopes = level - 1; + } // restore old state + + + state.pos = oldPos; + state.isInLabel = oldFlag; + return labelEnd; +} // Parse abbreviation definitions, i.e. `*[abbr]: description` + + +function parseAbbr(str, parserInline, options, env) { + var state, labelEnd, pos, max, label, title; + + if (str.charCodeAt(0) !== 0x2A + /* * */ + ) { + return -1; + } + + if (str.charCodeAt(1) !== 0x5B + /* [ */ + ) { + return -1; + } + + if (str.indexOf(']:') === -1) { + return -1; + } + + state = new StateInline(str, parserInline, options, env, []); + labelEnd = parseLinkLabel(state, 1); + + if (labelEnd < 0 || str.charCodeAt(labelEnd + 1) !== 0x3A + /* : */ + ) { + return -1; + } + + max = state.posMax; // abbr title is always one line, so looking for ending "\n" here + + for (pos = labelEnd + 2; pos < max; pos++) { + if (state.src.charCodeAt(pos) === 0x0A) { + break; + } + } + + label = str.slice(2, labelEnd); + title = str.slice(labelEnd + 2, pos).trim(); + + if (title.length === 0) { + return -1; + } + + if (!env.abbreviations) { + env.abbreviations = {}; + } // prepend ':' to avoid conflict with Object.prototype members + + + if (typeof env.abbreviations[':' + label] === 'undefined') { + env.abbreviations[':' + label] = title; + } + + return pos; +} + +function abbr(state) { + var tokens = state.tokens, + i, + l, + content, + pos; + + if (state.inlineMode) { + return; + } // Parse inlines + + + for (i = 1, l = tokens.length - 1; i < l; i++) { + if (tokens[i - 1].type === 'paragraph_open' && tokens[i].type === 'inline' && tokens[i + 1].type === 'paragraph_close') { + content = tokens[i].content; + + while (content.length) { + pos = parseAbbr(content, state.inline, state.options, state.env); + + if (pos < 0) { + break; + } + + content = content.slice(pos).trim(); + } + + tokens[i].content = content; + + if (!content.length) { + tokens[i - 1].tight = true; + tokens[i + 1].tight = true; + } + } + } +} + +function normalizeLink(url) { + var normalized = replaceEntities(url); // We shouldn't care about the result of malformed URIs, + // and should not throw an exception. + + try { + normalized = decodeURI(normalized); + } catch (err) {} + + return encodeURI(normalized); +} +/** + * Parse link destination + * + * - on success it returns a string and updates state.pos; + * - on failure it returns null + * + * @param {Object} state + * @param {Number} pos + * @api private + */ + + +function parseLinkDestination(state, pos) { + var code, + level, + link, + start = pos, + max = state.posMax; + + if (state.src.charCodeAt(pos) === 0x3C + /* < */ + ) { + pos++; + + while (pos < max) { + code = state.src.charCodeAt(pos); + + if (code === 0x0A + /* \n */ + ) { + return false; + } + + if (code === 0x3E + /* > */ + ) { + link = normalizeLink(unescapeMd(state.src.slice(start + 1, pos))); + + if (!state.parser.validateLink(link)) { + return false; + } + + state.pos = pos + 1; + state.linkContent = link; + return true; + } + + if (code === 0x5C + /* \ */ + && pos + 1 < max) { + pos += 2; + continue; + } + + pos++; + } // no closing '>' + + + return false; + } // this should be ... } else { ... branch + + + level = 0; + + while (pos < max) { + code = state.src.charCodeAt(pos); + + if (code === 0x20) { + break; + } // ascii control chars + + + if (code < 0x20 || code === 0x7F) { + break; + } + + if (code === 0x5C + /* \ */ + && pos + 1 < max) { + pos += 2; + continue; + } + + if (code === 0x28 + /* ( */ + ) { + level++; + + if (level > 1) { + break; + } + } + + if (code === 0x29 + /* ) */ + ) { + level--; + + if (level < 0) { + break; + } + } + + pos++; + } + + if (start === pos) { + return false; + } + + link = unescapeMd(state.src.slice(start, pos)); + + if (!state.parser.validateLink(link)) { + return false; + } + + state.linkContent = link; + state.pos = pos; + return true; +} +/** + * Parse link title + * + * - on success it returns a string and updates state.pos; + * - on failure it returns null + * + * @param {Object} state + * @param {Number} pos + * @api private + */ + + +function parseLinkTitle(state, pos) { + var code, + start = pos, + max = state.posMax, + marker = state.src.charCodeAt(pos); + + if (marker !== 0x22 + /* " */ + && marker !== 0x27 + /* ' */ + && marker !== 0x28 + /* ( */ + ) { + return false; + } + + pos++; // if opening marker is "(", switch it to closing marker ")" + + if (marker === 0x28) { + marker = 0x29; + } + + while (pos < max) { + code = state.src.charCodeAt(pos); + + if (code === marker) { + state.pos = pos + 1; + state.linkContent = unescapeMd(state.src.slice(start + 1, pos)); + return true; + } + + if (code === 0x5C + /* \ */ + && pos + 1 < max) { + pos += 2; + continue; + } + + pos++; + } + + return false; +} + +function normalizeReference(str) { + // use .toUpperCase() instead of .toLowerCase() + // here to avoid a conflict with Object.prototype + // members (most notably, `__proto__`) + return str.trim().replace(/\s+/g, ' ').toUpperCase(); +} + +function parseReference(str, parser, options, env) { + var state, labelEnd, pos, max, code, start, href, title, label; + + if (str.charCodeAt(0) !== 0x5B + /* [ */ + ) { + return -1; + } + + if (str.indexOf(']:') === -1) { + return -1; + } + + state = new StateInline(str, parser, options, env, []); + labelEnd = parseLinkLabel(state, 0); + + if (labelEnd < 0 || str.charCodeAt(labelEnd + 1) !== 0x3A + /* : */ + ) { + return -1; + } + + max = state.posMax; // [label]: destination 'title' + // ^^^ skip optional whitespace here + + for (pos = labelEnd + 2; pos < max; pos++) { + code = state.src.charCodeAt(pos); + + if (code !== 0x20 && code !== 0x0A) { + break; + } + } // [label]: destination 'title' + // ^^^^^^^^^^^ parse this + + + if (!parseLinkDestination(state, pos)) { + return -1; + } + + href = state.linkContent; + pos = state.pos; // [label]: destination 'title' + // ^^^ skipping those spaces + + start = pos; + + for (pos = pos + 1; pos < max; pos++) { + code = state.src.charCodeAt(pos); + + if (code !== 0x20 && code !== 0x0A) { + break; + } + } // [label]: destination 'title' + // ^^^^^^^ parse this + + + if (pos < max && start !== pos && parseLinkTitle(state, pos)) { + title = state.linkContent; + pos = state.pos; + } else { + title = ''; + pos = start; + } // ensure that the end of the line is empty + + + while (pos < max && state.src.charCodeAt(pos) === 0x20 + /* space */ + ) { + pos++; + } + + if (pos < max && state.src.charCodeAt(pos) !== 0x0A) { + return -1; + } + + label = normalizeReference(str.slice(1, labelEnd)); + + if (typeof env.references[label] === 'undefined') { + env.references[label] = { + title: title, + href: href + }; + } + + return pos; +} + +function references(state) { + var tokens = state.tokens, + i, + l, + content, + pos; + state.env.references = state.env.references || {}; + + if (state.inlineMode) { + return; + } // Scan definitions in paragraph inlines + + + for (i = 1, l = tokens.length - 1; i < l; i++) { + if (tokens[i].type === 'inline' && tokens[i - 1].type === 'paragraph_open' && tokens[i + 1].type === 'paragraph_close') { + content = tokens[i].content; + + while (content.length) { + pos = parseReference(content, state.inline, state.options, state.env); + + if (pos < 0) { + break; + } + + content = content.slice(pos).trim(); + } + + tokens[i].content = content; + + if (!content.length) { + tokens[i - 1].tight = true; + tokens[i + 1].tight = true; + } + } + } +} + +function inline(state) { + var tokens = state.tokens, + tok, + i, + l; // Parse inlines + + for (i = 0, l = tokens.length; i < l; i++) { + tok = tokens[i]; + + if (tok.type === 'inline') { + state.inline.parse(tok.content, state.options, state.env, tok.children); + } + } +} + +function footnote_block(state) { + var i, + l, + j, + t, + lastParagraph, + list, + tokens, + current, + currentLabel, + level = 0, + insideRef = false, + refTokens = {}; + + if (!state.env.footnotes) { + return; + } + + state.tokens = state.tokens.filter(function (tok) { + if (tok.type === 'footnote_reference_open') { + insideRef = true; + current = []; + currentLabel = tok.label; + return false; + } + + if (tok.type === 'footnote_reference_close') { + insideRef = false; // prepend ':' to avoid conflict with Object.prototype members + + refTokens[':' + currentLabel] = current; + return false; + } + + if (insideRef) { + current.push(tok); + } + + return !insideRef; + }); + + if (!state.env.footnotes.list) { + return; + } + + list = state.env.footnotes.list; + state.tokens.push({ + type: 'footnote_block_open', + level: level++ + }); + + for (i = 0, l = list.length; i < l; i++) { + state.tokens.push({ + type: 'footnote_open', + id: i, + level: level++ + }); + + if (list[i].tokens) { + tokens = []; + tokens.push({ + type: 'paragraph_open', + tight: false, + level: level++ + }); + tokens.push({ + type: 'inline', + content: '', + level: level, + children: list[i].tokens + }); + tokens.push({ + type: 'paragraph_close', + tight: false, + level: --level + }); + } else if (list[i].label) { + tokens = refTokens[':' + list[i].label]; + } + + state.tokens = state.tokens.concat(tokens); + + if (state.tokens[state.tokens.length - 1].type === 'paragraph_close') { + lastParagraph = state.tokens.pop(); + } else { + lastParagraph = null; + } + + t = list[i].count > 0 ? list[i].count : 1; + + for (j = 0; j < t; j++) { + state.tokens.push({ + type: 'footnote_anchor', + id: i, + subId: j, + level: level + }); + } + + if (lastParagraph) { + state.tokens.push(lastParagraph); + } + + state.tokens.push({ + type: 'footnote_close', + level: --level + }); + } + + state.tokens.push({ + type: 'footnote_block_close', + level: --level + }); +} // Enclose abbreviations in tags +// + + +var PUNCT_CHARS = ' \n()[]\'".,!?-'; // from Google closure library +// http://closure-library.googlecode.com/git-history/docs/local_closure_goog_string_string.js.source.html#line1021 + +function regEscape(s) { + return s.replace(/([-()\[\]{}+?*.$\^|,:#= 0; i--) { + token = tokens[i]; + + if (token.type !== 'text') { + continue; + } + + pos = 0; + text = token.content; + reg.lastIndex = 0; + level = token.level; + nodes = []; + + while (m = reg.exec(text)) { + if (reg.lastIndex > pos) { + nodes.push({ + type: 'text', + content: text.slice(pos, m.index + m[1].length), + level: level + }); + } + + nodes.push({ + type: 'abbr_open', + title: state.env.abbreviations[':' + m[2]], + level: level++ + }); + nodes.push({ + type: 'text', + content: m[2], + level: level + }); + nodes.push({ + type: 'abbr_close', + level: --level + }); + pos = reg.lastIndex - m[3].length; + } + + if (!nodes.length) { + continue; + } + + if (pos < text.length) { + nodes.push({ + type: 'text', + content: text.slice(pos), + level: level + }); + } // replace current node + + + blockTokens[j].children = tokens = [].concat(tokens.slice(0, i), nodes, tokens.slice(i + 1)); + } + } +} // Simple typographical replacements +// +// TODO: +// - fractionals 1/2, 1/4, 3/4 -> ½, ¼, ¾ +// - miltiplication 2 x 4 -> 2 × 4 + + +var RARE_RE = /\+-|\.\.|\?\?\?\?|!!!!|,,|--/; +var SCOPED_ABBR_RE = /\((c|tm|r|p)\)/ig; +var SCOPED_ABBR = { + 'c': '©', + 'r': '®', + 'p': '§', + 'tm': '™' +}; + +function replaceScopedAbbr(str) { + if (str.indexOf('(') < 0) { + return str; + } + + return str.replace(SCOPED_ABBR_RE, function (match, name) { + return SCOPED_ABBR[name.toLowerCase()]; + }); +} + +function replace(state) { + var i, token, text, inlineTokens, blkIdx; + + if (!state.options.typographer) { + return; + } + + for (blkIdx = state.tokens.length - 1; blkIdx >= 0; blkIdx--) { + if (state.tokens[blkIdx].type !== 'inline') { + continue; + } + + inlineTokens = state.tokens[blkIdx].children; + + for (i = inlineTokens.length - 1; i >= 0; i--) { + token = inlineTokens[i]; + + if (token.type === 'text') { + text = token.content; + text = replaceScopedAbbr(text); + + if (RARE_RE.test(text)) { + text = text.replace(/\+-/g, '±') // .., ..., ....... -> … + // but ?..... & !..... -> ?.. & !.. + .replace(/\.{2,}/g, '…').replace(/([?!])…/g, '$1..').replace(/([?!]){4,}/g, '$1$1$1').replace(/,{2,}/g, ',') // em-dash + .replace(/(^|[^-])---([^-]|$)/mg, "$1\u2014$2") // en-dash + .replace(/(^|\s)--(\s|$)/mg, "$1\u2013$2").replace(/(^|[^-\s])--([^-\s]|$)/mg, "$1\u2013$2"); + } + + token.content = text; + } + } + } +} // Convert straight quotation marks to typographic ones +// + + +var QUOTE_TEST_RE = /['"]/; +var QUOTE_RE = /['"]/g; +var PUNCT_RE = /[-\s()\[\]]/; +var APOSTROPHE = '’'; // This function returns true if the character at `pos` +// could be inside a word. + +function isLetter(str, pos) { + if (pos < 0 || pos >= str.length) { + return false; + } + + return !PUNCT_RE.test(str[pos]); +} + +function replaceAt(str, index, ch) { + return str.substr(0, index) + ch + str.substr(index + 1); +} + +function smartquotes(state) { + /*eslint max-depth:0*/ + var i, token, text, t, pos, max, thisLevel, lastSpace, nextSpace, item, canOpen, canClose, j, isSingle, blkIdx, tokens, stack; + + if (!state.options.typographer) { + return; + } + + stack = []; + + for (blkIdx = state.tokens.length - 1; blkIdx >= 0; blkIdx--) { + if (state.tokens[blkIdx].type !== 'inline') { + continue; + } + + tokens = state.tokens[blkIdx].children; + stack.length = 0; + + for (i = 0; i < tokens.length; i++) { + token = tokens[i]; + + if (token.type !== 'text' || QUOTE_TEST_RE.test(token.text)) { + continue; + } + + thisLevel = tokens[i].level; + + for (j = stack.length - 1; j >= 0; j--) { + if (stack[j].level <= thisLevel) { + break; + } + } + + stack.length = j + 1; + text = token.content; + pos = 0; + max = text.length; + /*eslint no-labels:0,block-scoped-var:0*/ + + OUTER: while (pos < max) { + QUOTE_RE.lastIndex = pos; + t = QUOTE_RE.exec(text); + + if (!t) { + break; + } + + lastSpace = !isLetter(text, t.index - 1); + pos = t.index + 1; + isSingle = t[0] === "'"; + nextSpace = !isLetter(text, pos); + + if (!nextSpace && !lastSpace) { + // middle of word + if (isSingle) { + token.content = replaceAt(token.content, t.index, APOSTROPHE); + } + + continue; + } + + canOpen = !nextSpace; + canClose = !lastSpace; + + if (canClose) { + // this could be a closing quote, rewind the stack to get a match + for (j = stack.length - 1; j >= 0; j--) { + item = stack[j]; + + if (stack[j].level < thisLevel) { + break; + } + + if (item.single === isSingle && stack[j].level === thisLevel) { + item = stack[j]; + + if (isSingle) { + tokens[item.token].content = replaceAt(tokens[item.token].content, item.pos, state.options.quotes[2]); + token.content = replaceAt(token.content, t.index, state.options.quotes[3]); + } else { + tokens[item.token].content = replaceAt(tokens[item.token].content, item.pos, state.options.quotes[0]); + token.content = replaceAt(token.content, t.index, state.options.quotes[1]); + } + + stack.length = j; + continue OUTER; + } + } + } + + if (canOpen) { + stack.push({ + token: i, + pos: t.index, + single: isSingle, + level: thisLevel + }); + } else if (canClose && isSingle) { + token.content = replaceAt(token.content, t.index, APOSTROPHE); + } + } + } + } +} +/** + * Core parser `rules` + */ + + +var _rules = [['block', block], ['abbr', abbr], ['references', references], ['inline', inline], ['footnote_tail', footnote_block], ['abbr2', abbr2], ['replacements', replace], ['smartquotes', smartquotes]]; +/** + * Class for top level (`core`) parser rules + * + * @api private + */ + +function Core() { + this.options = {}; + this.ruler = new Ruler(); + + for (var i = 0; i < _rules.length; i++) { + this.ruler.push(_rules[i][0], _rules[i][1]); + } +} +/** + * Process rules with the given `state` + * + * @param {Object} `state` + * @api private + */ + + +Core.prototype.process = function (state) { + var i, l, rules; + rules = this.ruler.getRules(''); + + for (i = 0, l = rules.length; i < l; i++) { + rules[i](state); + } +}; // Parser state class + + +function StateBlock(src, parser, options, env, tokens) { + var ch, s, start, pos, len, indent, indent_found; + this.src = src; // Shortcuts to simplify nested calls + + this.parser = parser; + this.options = options; + this.env = env; // + // Internal state vartiables + // + + this.tokens = tokens; + this.bMarks = []; // line begin offsets for fast jumps + + this.eMarks = []; // line end offsets for fast jumps + + this.tShift = []; // indent for each line + // block parser variables + + this.blkIndent = 0; // required block content indent + // (for example, if we are in list) + + this.line = 0; // line index in src + + this.lineMax = 0; // lines count + + this.tight = false; // loose/tight mode for lists + + this.parentType = 'root'; // if `list`, block parser stops on two newlines + + this.ddIndent = -1; // indent of the current dd block (-1 if there isn't any) + + this.level = 0; // renderer + + this.result = ''; // Create caches + // Generate markers. + + s = this.src; + indent = 0; + indent_found = false; + + for (start = pos = indent = 0, len = s.length; pos < len; pos++) { + ch = s.charCodeAt(pos); + + if (!indent_found) { + if (ch === 0x20 + /* space */ + ) { + indent++; + continue; + } else { + indent_found = true; + } + } + + if (ch === 0x0A || pos === len - 1) { + if (ch !== 0x0A) { + pos++; + } + + this.bMarks.push(start); + this.eMarks.push(pos); + this.tShift.push(indent); + indent_found = false; + indent = 0; + start = pos + 1; + } + } // Push fake entry to simplify cache bounds checks + + + this.bMarks.push(s.length); + this.eMarks.push(s.length); + this.tShift.push(0); + this.lineMax = this.bMarks.length - 1; // don't count last fake line +} + +StateBlock.prototype.isEmpty = function isEmpty(line) { + return this.bMarks[line] + this.tShift[line] >= this.eMarks[line]; +}; + +StateBlock.prototype.skipEmptyLines = function skipEmptyLines(from) { + for (var max = this.lineMax; from < max; from++) { + if (this.bMarks[from] + this.tShift[from] < this.eMarks[from]) { + break; + } + } + + return from; +}; // Skip spaces from given position. + + +StateBlock.prototype.skipSpaces = function skipSpaces(pos) { + for (var max = this.src.length; pos < max; pos++) { + if (this.src.charCodeAt(pos) !== 0x20 + /* space */ + ) { + break; + } + } + + return pos; +}; // Skip char codes from given position + + +StateBlock.prototype.skipChars = function skipChars(pos, code) { + for (var max = this.src.length; pos < max; pos++) { + if (this.src.charCodeAt(pos) !== code) { + break; + } + } + + return pos; +}; // Skip char codes reverse from given position - 1 + + +StateBlock.prototype.skipCharsBack = function skipCharsBack(pos, code, min) { + if (pos <= min) { + return pos; + } + + while (pos > min) { + if (code !== this.src.charCodeAt(--pos)) { + return pos + 1; + } + } + + return pos; +}; // cut lines range from source. + + +StateBlock.prototype.getLines = function getLines(begin, end, indent, keepLastLF) { + var i, + first, + last, + queue, + shift, + line = begin; + + if (begin >= end) { + return ''; + } // Opt: don't use push queue for single line; + + + if (line + 1 === end) { + first = this.bMarks[line] + Math.min(this.tShift[line], indent); + last = keepLastLF ? this.eMarks[line] + 1 : this.eMarks[line]; + return this.src.slice(first, last); + } + + queue = new Array(end - begin); + + for (i = 0; line < end; line++, i++) { + shift = this.tShift[line]; + + if (shift > indent) { + shift = indent; + } + + if (shift < 0) { + shift = 0; + } + + first = this.bMarks[line] + shift; + + if (line + 1 < end || keepLastLF) { + // No need for bounds check because we have fake entry on tail. + last = this.eMarks[line] + 1; + } else { + last = this.eMarks[line]; + } + + queue[i] = this.src.slice(first, last); + } + + return queue.join(''); +}; // Code block (4 spaces padded) + + +function code(state, startLine, endLine +/*, silent*/ +) { + var nextLine, last; + + if (state.tShift[startLine] - state.blkIndent < 4) { + return false; + } + + last = nextLine = startLine + 1; + + while (nextLine < endLine) { + if (state.isEmpty(nextLine)) { + nextLine++; + continue; + } + + if (state.tShift[nextLine] - state.blkIndent >= 4) { + nextLine++; + last = nextLine; + continue; + } + + break; + } + + state.line = nextLine; + state.tokens.push({ + type: 'code', + content: state.getLines(startLine, last, 4 + state.blkIndent, true), + block: true, + lines: [startLine, state.line], + level: state.level + }); + return true; +} // fences (``` lang, ~~~ lang) + + +function fences(state, startLine, endLine, silent) { + var marker, + len, + params, + nextLine, + mem, + haveEndMarker = false, + pos = state.bMarks[startLine] + state.tShift[startLine], + max = state.eMarks[startLine]; + + if (pos + 3 > max) { + return false; + } + + marker = state.src.charCodeAt(pos); + + if (marker !== 0x7E + /* ~ */ + && marker !== 0x60 + /* ` */ + ) { + return false; + } // scan marker length + + + mem = pos; + pos = state.skipChars(pos, marker); + len = pos - mem; + + if (len < 3) { + return false; + } + + params = state.src.slice(pos, max).trim(); + + if (params.indexOf('`') >= 0) { + return false; + } // Since start is found, we can report success here in validation mode + + + if (silent) { + return true; + } // search end of block + + + nextLine = startLine; + + for (;;) { + nextLine++; + + if (nextLine >= endLine) { + // unclosed block should be autoclosed by end of document. + // also block seems to be autoclosed by end of parent + break; + } + + pos = mem = state.bMarks[nextLine] + state.tShift[nextLine]; + max = state.eMarks[nextLine]; + + if (pos < max && state.tShift[nextLine] < state.blkIndent) { + // non-empty line with negative indent should stop the list: + // - ``` + // test + break; + } + + if (state.src.charCodeAt(pos) !== marker) { + continue; + } + + if (state.tShift[nextLine] - state.blkIndent >= 4) { + // closing fence should be indented less than 4 spaces + continue; + } + + pos = state.skipChars(pos, marker); // closing code fence must be at least as long as the opening one + + if (pos - mem < len) { + continue; + } // make sure tail has spaces only + + + pos = state.skipSpaces(pos); + + if (pos < max) { + continue; + } + + haveEndMarker = true; // found! + + break; + } // If a fence has heading spaces, they should be removed from its inner block + + + len = state.tShift[startLine]; + state.line = nextLine + (haveEndMarker ? 1 : 0); + state.tokens.push({ + type: 'fence', + params: params, + content: state.getLines(startLine + 1, nextLine, len, true), + lines: [startLine, state.line], + level: state.level + }); + return true; +} // Block quotes + + +function blockquote(state, startLine, endLine, silent) { + var nextLine, + lastLineEmpty, + oldTShift, + oldBMarks, + oldIndent, + oldParentType, + lines, + terminatorRules, + i, + l, + terminate, + pos = state.bMarks[startLine] + state.tShift[startLine], + max = state.eMarks[startLine]; + + if (pos > max) { + return false; + } // check the block quote marker + + + if (state.src.charCodeAt(pos++) !== 0x3E + /* > */ + ) { + return false; + } + + if (state.level >= state.options.maxNesting) { + return false; + } // we know that it's going to be a valid blockquote, + // so no point trying to find the end of it in silent mode + + + if (silent) { + return true; + } // skip one optional space after '>' + + + if (state.src.charCodeAt(pos) === 0x20) { + pos++; + } + + oldIndent = state.blkIndent; + state.blkIndent = 0; + oldBMarks = [state.bMarks[startLine]]; + state.bMarks[startLine] = pos; // check if we have an empty blockquote + + pos = pos < max ? state.skipSpaces(pos) : pos; + lastLineEmpty = pos >= max; + oldTShift = [state.tShift[startLine]]; + state.tShift[startLine] = pos - state.bMarks[startLine]; + terminatorRules = state.parser.ruler.getRules('blockquote'); // Search the end of the block + // + // Block ends with either: + // 1. an empty line outside: + // ``` + // > test + // + // ``` + // 2. an empty line inside: + // ``` + // > + // test + // ``` + // 3. another tag + // ``` + // > test + // - - - + // ``` + + for (nextLine = startLine + 1; nextLine < endLine; nextLine++) { + pos = state.bMarks[nextLine] + state.tShift[nextLine]; + max = state.eMarks[nextLine]; + + if (pos >= max) { + // Case 1: line is not inside the blockquote, and this line is empty. + break; + } + + if (state.src.charCodeAt(pos++) === 0x3E + /* > */ + ) { + // This line is inside the blockquote. + // skip one optional space after '>' + if (state.src.charCodeAt(pos) === 0x20) { + pos++; + } + + oldBMarks.push(state.bMarks[nextLine]); + state.bMarks[nextLine] = pos; + pos = pos < max ? state.skipSpaces(pos) : pos; + lastLineEmpty = pos >= max; + oldTShift.push(state.tShift[nextLine]); + state.tShift[nextLine] = pos - state.bMarks[nextLine]; + continue; + } // Case 2: line is not inside the blockquote, and the last line was empty. + + + if (lastLineEmpty) { + break; + } // Case 3: another tag found. + + + terminate = false; + + for (i = 0, l = terminatorRules.length; i < l; i++) { + if (terminatorRules[i](state, nextLine, endLine, true)) { + terminate = true; + break; + } + } + + if (terminate) { + break; + } + + oldBMarks.push(state.bMarks[nextLine]); + oldTShift.push(state.tShift[nextLine]); // A negative number means that this is a paragraph continuation; + // + // Any negative number will do the job here, but it's better for it + // to be large enough to make any bugs obvious. + + state.tShift[nextLine] = -1337; + } + + oldParentType = state.parentType; + state.parentType = 'blockquote'; + state.tokens.push({ + type: 'blockquote_open', + lines: lines = [startLine, 0], + level: state.level++ + }); + state.parser.tokenize(state, startLine, nextLine); + state.tokens.push({ + type: 'blockquote_close', + level: --state.level + }); + state.parentType = oldParentType; + lines[1] = state.line; // Restore original tShift; this might not be necessary since the parser + // has already been here, but just to make sure we can do that. + + for (i = 0; i < oldTShift.length; i++) { + state.bMarks[i + startLine] = oldBMarks[i]; + state.tShift[i + startLine] = oldTShift[i]; + } + + state.blkIndent = oldIndent; + return true; +} // Horizontal rule + + +function hr(state, startLine, endLine, silent) { + var marker, + cnt, + ch, + pos = state.bMarks[startLine], + max = state.eMarks[startLine]; + pos += state.tShift[startLine]; + + if (pos > max) { + return false; + } + + marker = state.src.charCodeAt(pos++); // Check hr marker + + if (marker !== 0x2A + /* * */ + && marker !== 0x2D + /* - */ + && marker !== 0x5F + /* _ */ + ) { + return false; + } // markers can be mixed with spaces, but there should be at least 3 one + + + cnt = 1; + + while (pos < max) { + ch = state.src.charCodeAt(pos++); + + if (ch !== marker && ch !== 0x20 + /* space */ + ) { + return false; + } + + if (ch === marker) { + cnt++; + } + } + + if (cnt < 3) { + return false; + } + + if (silent) { + return true; + } + + state.line = startLine + 1; + state.tokens.push({ + type: 'hr', + lines: [startLine, state.line], + level: state.level + }); + return true; +} // Lists +// Search `[-+*][\n ]`, returns next pos arter marker on success +// or -1 on fail. + + +function skipBulletListMarker(state, startLine) { + var marker, pos, max; + pos = state.bMarks[startLine] + state.tShift[startLine]; + max = state.eMarks[startLine]; + + if (pos >= max) { + return -1; + } + + marker = state.src.charCodeAt(pos++); // Check bullet + + if (marker !== 0x2A + /* * */ + && marker !== 0x2D + /* - */ + && marker !== 0x2B + /* + */ + ) { + return -1; + } + + if (pos < max && state.src.charCodeAt(pos) !== 0x20) { + // " 1.test " - is not a list item + return -1; + } + + return pos; +} // Search `\d+[.)][\n ]`, returns next pos arter marker on success +// or -1 on fail. + + +function skipOrderedListMarker(state, startLine) { + var ch, + pos = state.bMarks[startLine] + state.tShift[startLine], + max = state.eMarks[startLine]; + + if (pos + 1 >= max) { + return -1; + } + + ch = state.src.charCodeAt(pos++); + + if (ch < 0x30 + /* 0 */ + || ch > 0x39 + /* 9 */ + ) { + return -1; + } + + for (;;) { + // EOL -> fail + if (pos >= max) { + return -1; + } + + ch = state.src.charCodeAt(pos++); + + if (ch >= 0x30 + /* 0 */ + && ch <= 0x39 + /* 9 */ + ) { + continue; + } // found valid marker + + + if (ch === 0x29 + /* ) */ + || ch === 0x2e + /* . */ + ) { + break; + } + + return -1; + } + + if (pos < max && state.src.charCodeAt(pos) !== 0x20 + /* space */ + ) { + // " 1.test " - is not a list item + return -1; + } + + return pos; +} + +function markTightParagraphs(state, idx) { + var i, + l, + level = state.level + 2; + + for (i = idx + 2, l = state.tokens.length - 2; i < l; i++) { + if (state.tokens[i].level === level && state.tokens[i].type === 'paragraph_open') { + state.tokens[i + 2].tight = true; + state.tokens[i].tight = true; + i += 2; + } + } +} + +function list(state, startLine, endLine, silent) { + var nextLine, + indent, + oldTShift, + oldIndent, + oldTight, + oldParentType, + start, + posAfterMarker, + max, + indentAfterMarker, + markerValue, + markerCharCode, + isOrdered, + contentStart, + listTokIdx, + prevEmptyEnd, + listLines, + itemLines, + tight = true, + terminatorRules, + i, + l, + terminate; // Detect list type and position after marker + + if ((posAfterMarker = skipOrderedListMarker(state, startLine)) >= 0) { + isOrdered = true; + } else if ((posAfterMarker = skipBulletListMarker(state, startLine)) >= 0) { + isOrdered = false; + } else { + return false; + } + + if (state.level >= state.options.maxNesting) { + return false; + } // We should terminate list on style change. Remember first one to compare. + + + markerCharCode = state.src.charCodeAt(posAfterMarker - 1); // For validation mode we can terminate immediately + + if (silent) { + return true; + } // Start list + + + listTokIdx = state.tokens.length; + + if (isOrdered) { + start = state.bMarks[startLine] + state.tShift[startLine]; + markerValue = Number(state.src.substr(start, posAfterMarker - start - 1)); + state.tokens.push({ + type: 'ordered_list_open', + order: markerValue, + lines: listLines = [startLine, 0], + level: state.level++ + }); + } else { + state.tokens.push({ + type: 'bullet_list_open', + lines: listLines = [startLine, 0], + level: state.level++ + }); + } // + // Iterate list items + // + + + nextLine = startLine; + prevEmptyEnd = false; + terminatorRules = state.parser.ruler.getRules('list'); + + while (nextLine < endLine) { + contentStart = state.skipSpaces(posAfterMarker); + max = state.eMarks[nextLine]; + + if (contentStart >= max) { + // trimming space in "- \n 3" case, indent is 1 here + indentAfterMarker = 1; + } else { + indentAfterMarker = contentStart - posAfterMarker; + } // If we have more than 4 spaces, the indent is 1 + // (the rest is just indented code block) + + + if (indentAfterMarker > 4) { + indentAfterMarker = 1; + } // If indent is less than 1, assume that it's one, example: + // "-\n test" + + + if (indentAfterMarker < 1) { + indentAfterMarker = 1; + } // " - test" + // ^^^^^ - calculating total length of this thing + + + indent = posAfterMarker - state.bMarks[nextLine] + indentAfterMarker; // Run subparser & write tokens + + state.tokens.push({ + type: 'list_item_open', + lines: itemLines = [startLine, 0], + level: state.level++ + }); + oldIndent = state.blkIndent; + oldTight = state.tight; + oldTShift = state.tShift[startLine]; + oldParentType = state.parentType; + state.tShift[startLine] = contentStart - state.bMarks[startLine]; + state.blkIndent = indent; + state.tight = true; + state.parentType = 'list'; + state.parser.tokenize(state, startLine, endLine, true); // If any of list item is tight, mark list as tight + + if (!state.tight || prevEmptyEnd) { + tight = false; + } // Item become loose if finish with empty line, + // but we should filter last element, because it means list finish + + + prevEmptyEnd = state.line - startLine > 1 && state.isEmpty(state.line - 1); + state.blkIndent = oldIndent; + state.tShift[startLine] = oldTShift; + state.tight = oldTight; + state.parentType = oldParentType; + state.tokens.push({ + type: 'list_item_close', + level: --state.level + }); + nextLine = startLine = state.line; + itemLines[1] = nextLine; + contentStart = state.bMarks[startLine]; + + if (nextLine >= endLine) { + break; + } + + if (state.isEmpty(nextLine)) { + break; + } // + // Try to check if list is terminated or continued. + // + + + if (state.tShift[nextLine] < state.blkIndent) { + break; + } // fail if terminating block found + + + terminate = false; + + for (i = 0, l = terminatorRules.length; i < l; i++) { + if (terminatorRules[i](state, nextLine, endLine, true)) { + terminate = true; + break; + } + } + + if (terminate) { + break; + } // fail if list has another type + + + if (isOrdered) { + posAfterMarker = skipOrderedListMarker(state, nextLine); + + if (posAfterMarker < 0) { + break; + } + } else { + posAfterMarker = skipBulletListMarker(state, nextLine); + + if (posAfterMarker < 0) { + break; + } + } + + if (markerCharCode !== state.src.charCodeAt(posAfterMarker - 1)) { + break; + } + } // Finilize list + + + state.tokens.push({ + type: isOrdered ? 'ordered_list_close' : 'bullet_list_close', + level: --state.level + }); + listLines[1] = nextLine; + state.line = nextLine; // mark paragraphs tight if needed + + if (tight) { + markTightParagraphs(state, listTokIdx); + } + + return true; +} // Process footnote reference list + + +function footnote(state, startLine, endLine, silent) { + var oldBMark, + oldTShift, + oldParentType, + pos, + label, + start = state.bMarks[startLine] + state.tShift[startLine], + max = state.eMarks[startLine]; // line should be at least 5 chars - "[^x]:" + + if (start + 4 > max) { + return false; + } + + if (state.src.charCodeAt(start) !== 0x5B + /* [ */ + ) { + return false; + } + + if (state.src.charCodeAt(start + 1) !== 0x5E + /* ^ */ + ) { + return false; + } + + if (state.level >= state.options.maxNesting) { + return false; + } + + for (pos = start + 2; pos < max; pos++) { + if (state.src.charCodeAt(pos) === 0x20) { + return false; + } + + if (state.src.charCodeAt(pos) === 0x5D + /* ] */ + ) { + break; + } + } + + if (pos === start + 2) { + return false; + } // no empty footnote labels + + + if (pos + 1 >= max || state.src.charCodeAt(++pos) !== 0x3A + /* : */ + ) { + return false; + } + + if (silent) { + return true; + } + + pos++; + + if (!state.env.footnotes) { + state.env.footnotes = {}; + } + + if (!state.env.footnotes.refs) { + state.env.footnotes.refs = {}; + } + + label = state.src.slice(start + 2, pos - 2); + state.env.footnotes.refs[':' + label] = -1; + state.tokens.push({ + type: 'footnote_reference_open', + label: label, + level: state.level++ + }); + oldBMark = state.bMarks[startLine]; + oldTShift = state.tShift[startLine]; + oldParentType = state.parentType; + state.tShift[startLine] = state.skipSpaces(pos) - pos; + state.bMarks[startLine] = pos; + state.blkIndent += 4; + state.parentType = 'footnote'; + + if (state.tShift[startLine] < state.blkIndent) { + state.tShift[startLine] += state.blkIndent; + state.bMarks[startLine] -= state.blkIndent; + } + + state.parser.tokenize(state, startLine, endLine, true); + state.parentType = oldParentType; + state.blkIndent -= 4; + state.tShift[startLine] = oldTShift; + state.bMarks[startLine] = oldBMark; + state.tokens.push({ + type: 'footnote_reference_close', + level: --state.level + }); + return true; +} // heading (#, ##, ...) + + +function heading(state, startLine, endLine, silent) { + var ch, + level, + tmp, + pos = state.bMarks[startLine] + state.tShift[startLine], + max = state.eMarks[startLine]; + + if (pos >= max) { + return false; + } + + ch = state.src.charCodeAt(pos); + + if (ch !== 0x23 + /* # */ + || pos >= max) { + return false; + } // count heading level + + + level = 1; + ch = state.src.charCodeAt(++pos); + + while (ch === 0x23 + /* # */ + && pos < max && level <= 6) { + level++; + ch = state.src.charCodeAt(++pos); + } + + if (level > 6 || pos < max && ch !== 0x20 + /* space */ + ) { + return false; + } + + if (silent) { + return true; + } // Let's cut tails like ' ### ' from the end of string + + + max = state.skipCharsBack(max, 0x20, pos); // space + + tmp = state.skipCharsBack(max, 0x23, pos); // # + + if (tmp > pos && state.src.charCodeAt(tmp - 1) === 0x20 + /* space */ + ) { + max = tmp; + } + + state.line = startLine + 1; + state.tokens.push({ + type: 'heading_open', + hLevel: level, + lines: [startLine, state.line], + level: state.level + }); // only if header is not empty + + if (pos < max) { + state.tokens.push({ + type: 'inline', + content: state.src.slice(pos, max).trim(), + level: state.level + 1, + lines: [startLine, state.line], + children: [] + }); + } + + state.tokens.push({ + type: 'heading_close', + hLevel: level, + level: state.level + }); + return true; +} // lheading (---, ===) + + +function lheading(state, startLine, endLine +/*, silent*/ +) { + var marker, + pos, + max, + next = startLine + 1; + + if (next >= endLine) { + return false; + } + + if (state.tShift[next] < state.blkIndent) { + return false; + } // Scan next line + + + if (state.tShift[next] - state.blkIndent > 3) { + return false; + } + + pos = state.bMarks[next] + state.tShift[next]; + max = state.eMarks[next]; + + if (pos >= max) { + return false; + } + + marker = state.src.charCodeAt(pos); + + if (marker !== 0x2D + /* - */ + && marker !== 0x3D + /* = */ + ) { + return false; + } + + pos = state.skipChars(pos, marker); + pos = state.skipSpaces(pos); + + if (pos < max) { + return false; + } + + pos = state.bMarks[startLine] + state.tShift[startLine]; + state.line = next + 1; + state.tokens.push({ + type: 'heading_open', + hLevel: marker === 0x3D + /* = */ + ? 1 : 2, + lines: [startLine, state.line], + level: state.level + }); + state.tokens.push({ + type: 'inline', + content: state.src.slice(pos, state.eMarks[startLine]).trim(), + level: state.level + 1, + lines: [startLine, state.line - 1], + children: [] + }); + state.tokens.push({ + type: 'heading_close', + hLevel: marker === 0x3D + /* = */ + ? 1 : 2, + level: state.level + }); + return true; +} // List of valid html blocks names, accorting to commonmark spec +// http://jgm.github.io/CommonMark/spec.html#html-blocks + + +var html_blocks = {}; +['article', 'aside', 'button', 'blockquote', 'body', 'canvas', 'caption', 'col', 'colgroup', 'dd', 'div', 'dl', 'dt', 'embed', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hgroup', 'hr', 'iframe', 'li', 'map', 'object', 'ol', 'output', 'p', 'pre', 'progress', 'script', 'section', 'style', 'table', 'tbody', 'td', 'textarea', 'tfoot', 'th', 'tr', 'thead', 'ul', 'video'].forEach(function (name) { + html_blocks[name] = true; +}); // HTML block + +var HTML_TAG_OPEN_RE = /^<([a-zA-Z]{1,15})[\s\/>]/; +var HTML_TAG_CLOSE_RE = /^<\/([a-zA-Z]{1,15})[\s>]/; + +function isLetter$1(ch) { + /*eslint no-bitwise:0*/ + var lc = ch | 0x20; // to lower case + + return lc >= 0x61 + /* a */ + && lc <= 0x7a + /* z */ + ; +} + +function htmlblock(state, startLine, endLine, silent) { + var ch, + match, + nextLine, + pos = state.bMarks[startLine], + max = state.eMarks[startLine], + shift = state.tShift[startLine]; + pos += shift; + + if (!state.options.html) { + return false; + } + + if (shift > 3 || pos + 2 >= max) { + return false; + } + + if (state.src.charCodeAt(pos) !== 0x3C + /* < */ + ) { + return false; + } + + ch = state.src.charCodeAt(pos + 1); + + if (ch === 0x21 + /* ! */ + || ch === 0x3F + /* ? */ + ) { + // Directive start / comment start / processing instruction start + if (silent) { + return true; + } + } else if (ch === 0x2F + /* / */ + || isLetter$1(ch)) { + // Probably start or end of tag + if (ch === 0x2F + /* \ */ + ) { + // closing tag + match = state.src.slice(pos, max).match(HTML_TAG_CLOSE_RE); + + if (!match) { + return false; + } + } else { + // opening tag + match = state.src.slice(pos, max).match(HTML_TAG_OPEN_RE); + + if (!match) { + return false; + } + } // Make sure tag name is valid + + + if (html_blocks[match[1].toLowerCase()] !== true) { + return false; + } + + if (silent) { + return true; + } + } else { + return false; + } // If we are here - we detected HTML block. + // Let's roll down till empty line (block end). + + + nextLine = startLine + 1; + + while (nextLine < state.lineMax && !state.isEmpty(nextLine)) { + nextLine++; + } + + state.line = nextLine; + state.tokens.push({ + type: 'htmlblock', + level: state.level, + lines: [startLine, state.line], + content: state.getLines(startLine, nextLine, 0, true) + }); + return true; +} // GFM table, non-standard + + +function getLine(state, line) { + var pos = state.bMarks[line] + state.blkIndent, + max = state.eMarks[line]; + return state.src.substr(pos, max - pos); +} + +function table(state, startLine, endLine, silent) { + var ch, lineText, pos, i, nextLine, rows, cell, aligns, t, tableLines, tbodyLines; // should have at least three lines + + if (startLine + 2 > endLine) { + return false; + } + + nextLine = startLine + 1; + + if (state.tShift[nextLine] < state.blkIndent) { + return false; + } // first character of the second line should be '|' or '-' + + + pos = state.bMarks[nextLine] + state.tShift[nextLine]; + + if (pos >= state.eMarks[nextLine]) { + return false; + } + + ch = state.src.charCodeAt(pos); + + if (ch !== 0x7C + /* | */ + && ch !== 0x2D + /* - */ + && ch !== 0x3A + /* : */ + ) { + return false; + } + + lineText = getLine(state, startLine + 1); + + if (!/^[-:| ]+$/.test(lineText)) { + return false; + } + + rows = lineText.split('|'); + + if (rows <= 2) { + return false; + } + + aligns = []; + + for (i = 0; i < rows.length; i++) { + t = rows[i].trim(); + + if (!t) { + // allow empty columns before and after table, but not in between columns; + // e.g. allow ` |---| `, disallow ` ---||--- ` + if (i === 0 || i === rows.length - 1) { + continue; + } else { + return false; + } + } + + if (!/^:?-+:?$/.test(t)) { + return false; + } + + if (t.charCodeAt(t.length - 1) === 0x3A + /* : */ + ) { + aligns.push(t.charCodeAt(0) === 0x3A + /* : */ + ? 'center' : 'right'); + } else if (t.charCodeAt(0) === 0x3A + /* : */ + ) { + aligns.push('left'); + } else { + aligns.push(''); + } + } + + lineText = getLine(state, startLine).trim(); + + if (lineText.indexOf('|') === -1) { + return false; + } + + rows = lineText.replace(/^\||\|$/g, '').split('|'); + + if (aligns.length !== rows.length) { + return false; + } + + if (silent) { + return true; + } + + state.tokens.push({ + type: 'table_open', + lines: tableLines = [startLine, 0], + level: state.level++ + }); + state.tokens.push({ + type: 'thead_open', + lines: [startLine, startLine + 1], + level: state.level++ + }); + state.tokens.push({ + type: 'tr_open', + lines: [startLine, startLine + 1], + level: state.level++ + }); + + for (i = 0; i < rows.length; i++) { + state.tokens.push({ + type: 'th_open', + align: aligns[i], + lines: [startLine, startLine + 1], + level: state.level++ + }); + state.tokens.push({ + type: 'inline', + content: rows[i].trim(), + lines: [startLine, startLine + 1], + level: state.level, + children: [] + }); + state.tokens.push({ + type: 'th_close', + level: --state.level + }); + } + + state.tokens.push({ + type: 'tr_close', + level: --state.level + }); + state.tokens.push({ + type: 'thead_close', + level: --state.level + }); + state.tokens.push({ + type: 'tbody_open', + lines: tbodyLines = [startLine + 2, 0], + level: state.level++ + }); + + for (nextLine = startLine + 2; nextLine < endLine; nextLine++) { + if (state.tShift[nextLine] < state.blkIndent) { + break; + } + + lineText = getLine(state, nextLine).trim(); + + if (lineText.indexOf('|') === -1) { + break; + } + + rows = lineText.replace(/^\||\|$/g, '').split('|'); + state.tokens.push({ + type: 'tr_open', + level: state.level++ + }); + + for (i = 0; i < rows.length; i++) { + state.tokens.push({ + type: 'td_open', + align: aligns[i], + level: state.level++ + }); // 0x7c === '|' + + cell = rows[i].substring(rows[i].charCodeAt(0) === 0x7c ? 1 : 0, rows[i].charCodeAt(rows[i].length - 1) === 0x7c ? rows[i].length - 1 : rows[i].length).trim(); + state.tokens.push({ + type: 'inline', + content: cell, + level: state.level, + children: [] + }); + state.tokens.push({ + type: 'td_close', + level: --state.level + }); + } + + state.tokens.push({ + type: 'tr_close', + level: --state.level + }); + } + + state.tokens.push({ + type: 'tbody_close', + level: --state.level + }); + state.tokens.push({ + type: 'table_close', + level: --state.level + }); + tableLines[1] = tbodyLines[1] = nextLine; + state.line = nextLine; + return true; +} // Definition lists +// Search `[:~][\n ]`, returns next pos after marker on success +// or -1 on fail. + + +function skipMarker(state, line) { + var pos, + marker, + start = state.bMarks[line] + state.tShift[line], + max = state.eMarks[line]; + + if (start >= max) { + return -1; + } // Check bullet + + + marker = state.src.charCodeAt(start++); + + if (marker !== 0x7E + /* ~ */ + && marker !== 0x3A + /* : */ + ) { + return -1; + } + + pos = state.skipSpaces(start); // require space after ":" + + if (start === pos) { + return -1; + } // no empty definitions, e.g. " : " + + + if (pos >= max) { + return -1; + } + + return pos; +} + +function markTightParagraphs$1(state, idx) { + var i, + l, + level = state.level + 2; + + for (i = idx + 2, l = state.tokens.length - 2; i < l; i++) { + if (state.tokens[i].level === level && state.tokens[i].type === 'paragraph_open') { + state.tokens[i + 2].tight = true; + state.tokens[i].tight = true; + i += 2; + } + } +} + +function deflist(state, startLine, endLine, silent) { + var contentStart, ddLine, dtLine, itemLines, listLines, listTokIdx, nextLine, oldIndent, oldDDIndent, oldParentType, oldTShift, oldTight, prevEmptyEnd, tight; + + if (silent) { + // quirk: validation mode validates a dd block only, not a whole deflist + if (state.ddIndent < 0) { + return false; + } + + return skipMarker(state, startLine) >= 0; + } + + nextLine = startLine + 1; + + if (state.isEmpty(nextLine)) { + if (++nextLine > endLine) { + return false; + } + } + + if (state.tShift[nextLine] < state.blkIndent) { + return false; + } + + contentStart = skipMarker(state, nextLine); + + if (contentStart < 0) { + return false; + } + + if (state.level >= state.options.maxNesting) { + return false; + } // Start list + + + listTokIdx = state.tokens.length; + state.tokens.push({ + type: 'dl_open', + lines: listLines = [startLine, 0], + level: state.level++ + }); // + // Iterate list items + // + + dtLine = startLine; + ddLine = nextLine; // One definition list can contain multiple DTs, + // and one DT can be followed by multiple DDs. + // + // Thus, there is two loops here, and label is + // needed to break out of the second one + // + + /*eslint no-labels:0,block-scoped-var:0*/ + + OUTER: for (;;) { + tight = true; + prevEmptyEnd = false; + state.tokens.push({ + type: 'dt_open', + lines: [dtLine, dtLine], + level: state.level++ + }); + state.tokens.push({ + type: 'inline', + content: state.getLines(dtLine, dtLine + 1, state.blkIndent, false).trim(), + level: state.level + 1, + lines: [dtLine, dtLine], + children: [] + }); + state.tokens.push({ + type: 'dt_close', + level: --state.level + }); + + for (;;) { + state.tokens.push({ + type: 'dd_open', + lines: itemLines = [nextLine, 0], + level: state.level++ + }); + oldTight = state.tight; + oldDDIndent = state.ddIndent; + oldIndent = state.blkIndent; + oldTShift = state.tShift[ddLine]; + oldParentType = state.parentType; + state.blkIndent = state.ddIndent = state.tShift[ddLine] + 2; + state.tShift[ddLine] = contentStart - state.bMarks[ddLine]; + state.tight = true; + state.parentType = 'deflist'; + state.parser.tokenize(state, ddLine, endLine, true); // If any of list item is tight, mark list as tight + + if (!state.tight || prevEmptyEnd) { + tight = false; + } // Item become loose if finish with empty line, + // but we should filter last element, because it means list finish + + + prevEmptyEnd = state.line - ddLine > 1 && state.isEmpty(state.line - 1); + state.tShift[ddLine] = oldTShift; + state.tight = oldTight; + state.parentType = oldParentType; + state.blkIndent = oldIndent; + state.ddIndent = oldDDIndent; + state.tokens.push({ + type: 'dd_close', + level: --state.level + }); + itemLines[1] = nextLine = state.line; + + if (nextLine >= endLine) { + break OUTER; + } + + if (state.tShift[nextLine] < state.blkIndent) { + break OUTER; + } + + contentStart = skipMarker(state, nextLine); + + if (contentStart < 0) { + break; + } + + ddLine = nextLine; // go to the next loop iteration: + // insert DD tag and repeat checking + } + + if (nextLine >= endLine) { + break; + } + + dtLine = nextLine; + + if (state.isEmpty(dtLine)) { + break; + } + + if (state.tShift[dtLine] < state.blkIndent) { + break; + } + + ddLine = dtLine + 1; + + if (ddLine >= endLine) { + break; + } + + if (state.isEmpty(ddLine)) { + ddLine++; + } + + if (ddLine >= endLine) { + break; + } + + if (state.tShift[ddLine] < state.blkIndent) { + break; + } + + contentStart = skipMarker(state, ddLine); + + if (contentStart < 0) { + break; + } // go to the next loop iteration: + // insert DT and DD tags and repeat checking + + } // Finilize list + + + state.tokens.push({ + type: 'dl_close', + level: --state.level + }); + listLines[1] = nextLine; + state.line = nextLine; // mark paragraphs tight if needed + + if (tight) { + markTightParagraphs$1(state, listTokIdx); + } + + return true; +} // Paragraph + + +function paragraph(state, startLine +/*, endLine*/ +) { + var endLine, + content, + terminate, + i, + l, + nextLine = startLine + 1, + terminatorRules; + endLine = state.lineMax; // jump line-by-line until empty one or EOF + + if (nextLine < endLine && !state.isEmpty(nextLine)) { + terminatorRules = state.parser.ruler.getRules('paragraph'); + + for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) { + // this would be a code block normally, but after paragraph + // it's considered a lazy continuation regardless of what's there + if (state.tShift[nextLine] - state.blkIndent > 3) { + continue; + } // Some tags can terminate paragraph without empty line. + + + terminate = false; + + for (i = 0, l = terminatorRules.length; i < l; i++) { + if (terminatorRules[i](state, nextLine, endLine, true)) { + terminate = true; + break; + } + } + + if (terminate) { + break; + } + } + } + + content = state.getLines(startLine, nextLine, state.blkIndent, false).trim(); + state.line = nextLine; + + if (content.length) { + state.tokens.push({ + type: 'paragraph_open', + tight: false, + lines: [startLine, state.line], + level: state.level + }); + state.tokens.push({ + type: 'inline', + content: content, + level: state.level + 1, + lines: [startLine, state.line], + children: [] + }); + state.tokens.push({ + type: 'paragraph_close', + tight: false, + level: state.level + }); + } + + return true; +} +/** + * Parser rules + */ + + +var _rules$1 = [['code', code], ['fences', fences, ['paragraph', 'blockquote', 'list']], ['blockquote', blockquote, ['paragraph', 'blockquote', 'list']], ['hr', hr, ['paragraph', 'blockquote', 'list']], ['list', list, ['paragraph', 'blockquote']], ['footnote', footnote, ['paragraph']], ['heading', heading, ['paragraph', 'blockquote']], ['lheading', lheading], ['htmlblock', htmlblock, ['paragraph', 'blockquote']], ['table', table, ['paragraph']], ['deflist', deflist, ['paragraph']], ['paragraph', paragraph]]; +/** + * Block Parser class + * + * @api private + */ + +function ParserBlock() { + this.ruler = new Ruler(); + + for (var i = 0; i < _rules$1.length; i++) { + this.ruler.push(_rules$1[i][0], _rules$1[i][1], { + alt: (_rules$1[i][2] || []).slice() + }); + } +} +/** + * Generate tokens for the given input range. + * + * @param {Object} `state` Has properties like `src`, `parser`, `options` etc + * @param {Number} `startLine` + * @param {Number} `endLine` + * @api private + */ + + +ParserBlock.prototype.tokenize = function (state, startLine, endLine) { + var rules = this.ruler.getRules(''); + var len = rules.length; + var line = startLine; + var hasEmptyLines = false; + var ok, i; + + while (line < endLine) { + state.line = line = state.skipEmptyLines(line); + + if (line >= endLine) { + break; + } // Termination condition for nested calls. + // Nested calls currently used for blockquotes & lists + + + if (state.tShift[line] < state.blkIndent) { + break; + } // Try all possible rules. + // On success, rule should: + // + // - update `state.line` + // - update `state.tokens` + // - return true + + + for (i = 0; i < len; i++) { + ok = rules[i](state, line, endLine, false); + + if (ok) { + break; + } + } // set state.tight iff we had an empty line before current tag + // i.e. latest empty line should not count + + + state.tight = !hasEmptyLines; // paragraph might "eat" one newline after it in nested lists + + if (state.isEmpty(state.line - 1)) { + hasEmptyLines = true; + } + + line = state.line; + + if (line < endLine && state.isEmpty(line)) { + hasEmptyLines = true; + line++; // two empty lines should stop the parser in list mode + + if (line < endLine && state.parentType === 'list' && state.isEmpty(line)) { + break; + } + + state.line = line; + } + } +}; + +var TABS_SCAN_RE = /[\n\t]/g; +var NEWLINES_RE = /\r[\n\u0085]|[\u2424\u2028\u0085]/g; +var SPACES_RE = /\u00a0/g; +/** + * Tokenize the given `str`. + * + * @param {String} `str` Source string + * @param {Object} `options` + * @param {Object} `env` + * @param {Array} `outTokens` + * @api private + */ + +ParserBlock.prototype.parse = function (str, options, env, outTokens) { + var state, + lineStart = 0, + lastTabPos = 0; + + if (!str) { + return []; + } // Normalize spaces + + + str = str.replace(SPACES_RE, ' '); // Normalize newlines + + str = str.replace(NEWLINES_RE, '\n'); // Replace tabs with proper number of spaces (1..4) + + if (str.indexOf('\t') >= 0) { + str = str.replace(TABS_SCAN_RE, function (match, offset) { + var result; + + if (str.charCodeAt(offset) === 0x0A) { + lineStart = offset + 1; + lastTabPos = 0; + return match; + } + + result = ' '.slice((offset - lineStart - lastTabPos) % 4); + lastTabPos = offset - lineStart + 1; + return result; + }); + } + + state = new StateBlock(str, this, options, env, outTokens); + this.tokenize(state, state.line, state.lineMax); +}; // Skip text characters for text token, place those to pending buffer +// and increment current pos +// Rule to skip pure text +// '{}$%@~+=:' reserved for extentions + + +function isTerminatorChar(ch) { + switch (ch) { + case 0x0A + /* \n */ + : + case 0x5C + /* \ */ + : + case 0x60 + /* ` */ + : + case 0x2A + /* * */ + : + case 0x5F + /* _ */ + : + case 0x5E + /* ^ */ + : + case 0x5B + /* [ */ + : + case 0x5D + /* ] */ + : + case 0x21 + /* ! */ + : + case 0x26 + /* & */ + : + case 0x3C + /* < */ + : + case 0x3E + /* > */ + : + case 0x7B + /* { */ + : + case 0x7D + /* } */ + : + case 0x24 + /* $ */ + : + case 0x25 + /* % */ + : + case 0x40 + /* @ */ + : + case 0x7E + /* ~ */ + : + case 0x2B + /* + */ + : + case 0x3D + /* = */ + : + case 0x3A + /* : */ + : + return true; + + default: + return false; + } +} + +function text(state, silent) { + var pos = state.pos; + + while (pos < state.posMax && !isTerminatorChar(state.src.charCodeAt(pos))) { + pos++; + } + + if (pos === state.pos) { + return false; + } + + if (!silent) { + state.pending += state.src.slice(state.pos, pos); + } + + state.pos = pos; + return true; +} // Proceess '\n' + + +function newline(state, silent) { + var pmax, + max, + pos = state.pos; + + if (state.src.charCodeAt(pos) !== 0x0A + /* \n */ + ) { + return false; + } + + pmax = state.pending.length - 1; + max = state.posMax; // ' \n' -> hardbreak + // Lookup in pending chars is bad practice! Don't copy to other rules! + // Pending string is stored in concat mode, indexed lookups will cause + // convertion to flat mode. + + if (!silent) { + if (pmax >= 0 && state.pending.charCodeAt(pmax) === 0x20) { + if (pmax >= 1 && state.pending.charCodeAt(pmax - 1) === 0x20) { + // Strip out all trailing spaces on this line. + for (var i = pmax - 2; i >= 0; i--) { + if (state.pending.charCodeAt(i) !== 0x20) { + state.pending = state.pending.substring(0, i + 1); + break; + } + } + + state.push({ + type: 'hardbreak', + level: state.level + }); + } else { + state.pending = state.pending.slice(0, -1); + state.push({ + type: 'softbreak', + level: state.level + }); + } + } else { + state.push({ + type: 'softbreak', + level: state.level + }); + } + } + + pos++; // skip heading spaces for next line + + while (pos < max && state.src.charCodeAt(pos) === 0x20) { + pos++; + } + + state.pos = pos; + return true; +} // Proceess escaped chars and hardbreaks + + +var ESCAPED = []; + +for (var i = 0; i < 256; i++) { + ESCAPED.push(0); +} + +'\\!"#$%&\'()*+,./:;<=>?@[]^_`{|}~-'.split('').forEach(function (ch) { + ESCAPED[ch.charCodeAt(0)] = 1; +}); + +function escape(state, silent) { + var ch, + pos = state.pos, + max = state.posMax; + + if (state.src.charCodeAt(pos) !== 0x5C + /* \ */ + ) { + return false; + } + + pos++; + + if (pos < max) { + ch = state.src.charCodeAt(pos); + + if (ch < 256 && ESCAPED[ch] !== 0) { + if (!silent) { + state.pending += state.src[pos]; + } + + state.pos += 2; + return true; + } + + if (ch === 0x0A) { + if (!silent) { + state.push({ + type: 'hardbreak', + level: state.level + }); + } + + pos++; // skip leading whitespaces from next line + + while (pos < max && state.src.charCodeAt(pos) === 0x20) { + pos++; + } + + state.pos = pos; + return true; + } + } + + if (!silent) { + state.pending += '\\'; + } + + state.pos++; + return true; +} // Parse backticks + + +function backticks(state, silent) { + var start, + max, + marker, + matchStart, + matchEnd, + pos = state.pos, + ch = state.src.charCodeAt(pos); + + if (ch !== 0x60 + /* ` */ + ) { + return false; + } + + start = pos; + pos++; + max = state.posMax; + + while (pos < max && state.src.charCodeAt(pos) === 0x60 + /* ` */ + ) { + pos++; + } + + marker = state.src.slice(start, pos); + matchStart = matchEnd = pos; + + while ((matchStart = state.src.indexOf('`', matchEnd)) !== -1) { + matchEnd = matchStart + 1; + + while (matchEnd < max && state.src.charCodeAt(matchEnd) === 0x60 + /* ` */ + ) { + matchEnd++; + } + + if (matchEnd - matchStart === marker.length) { + if (!silent) { + state.push({ + type: 'code', + content: state.src.slice(pos, matchStart).replace(/[ \n]+/g, ' ').trim(), + block: false, + level: state.level + }); + } + + state.pos = matchEnd; + return true; + } + } + + if (!silent) { + state.pending += marker; + } + + state.pos += marker.length; + return true; +} // Process ~~deleted text~~ + + +function del(state, silent) { + var found, + pos, + stack, + max = state.posMax, + start = state.pos, + lastChar, + nextChar; + + if (state.src.charCodeAt(start) !== 0x7E + /* ~ */ + ) { + return false; + } + + if (silent) { + return false; + } // don't run any pairs in validation mode + + + if (start + 4 >= max) { + return false; + } + + if (state.src.charCodeAt(start + 1) !== 0x7E + /* ~ */ + ) { + return false; + } + + if (state.level >= state.options.maxNesting) { + return false; + } + + lastChar = start > 0 ? state.src.charCodeAt(start - 1) : -1; + nextChar = state.src.charCodeAt(start + 2); + + if (lastChar === 0x7E + /* ~ */ + ) { + return false; + } + + if (nextChar === 0x7E + /* ~ */ + ) { + return false; + } + + if (nextChar === 0x20 || nextChar === 0x0A) { + return false; + } + + pos = start + 2; + + while (pos < max && state.src.charCodeAt(pos) === 0x7E + /* ~ */ + ) { + pos++; + } + + if (pos > start + 3) { + // sequence of 4+ markers taking as literal, same as in a emphasis + state.pos += pos - start; + + if (!silent) { + state.pending += state.src.slice(start, pos); + } + + return true; + } + + state.pos = start + 2; + stack = 1; + + while (state.pos + 1 < max) { + if (state.src.charCodeAt(state.pos) === 0x7E + /* ~ */ + ) { + if (state.src.charCodeAt(state.pos + 1) === 0x7E + /* ~ */ + ) { + lastChar = state.src.charCodeAt(state.pos - 1); + nextChar = state.pos + 2 < max ? state.src.charCodeAt(state.pos + 2) : -1; + + if (nextChar !== 0x7E + /* ~ */ + && lastChar !== 0x7E + /* ~ */ + ) { + if (lastChar !== 0x20 && lastChar !== 0x0A) { + // closing '~~' + stack--; + } else if (nextChar !== 0x20 && nextChar !== 0x0A) { + // opening '~~' + stack++; + } // else { + // // standalone ' ~~ ' indented with spaces + // } + + + if (stack <= 0) { + found = true; + break; + } + } + } + } + + state.parser.skipToken(state); + } + + if (!found) { + // parser failed to find ending tag, so it's not valid emphasis + state.pos = start; + return false; + } // found! + + + state.posMax = state.pos; + state.pos = start + 2; + + if (!silent) { + state.push({ + type: 'del_open', + level: state.level++ + }); + state.parser.tokenize(state); + state.push({ + type: 'del_close', + level: --state.level + }); + } + + state.pos = state.posMax + 2; + state.posMax = max; + return true; +} // Process ++inserted text++ + + +function ins(state, silent) { + var found, + pos, + stack, + max = state.posMax, + start = state.pos, + lastChar, + nextChar; + + if (state.src.charCodeAt(start) !== 0x2B + /* + */ + ) { + return false; + } + + if (silent) { + return false; + } // don't run any pairs in validation mode + + + if (start + 4 >= max) { + return false; + } + + if (state.src.charCodeAt(start + 1) !== 0x2B + /* + */ + ) { + return false; + } + + if (state.level >= state.options.maxNesting) { + return false; + } + + lastChar = start > 0 ? state.src.charCodeAt(start - 1) : -1; + nextChar = state.src.charCodeAt(start + 2); + + if (lastChar === 0x2B + /* + */ + ) { + return false; + } + + if (nextChar === 0x2B + /* + */ + ) { + return false; + } + + if (nextChar === 0x20 || nextChar === 0x0A) { + return false; + } + + pos = start + 2; + + while (pos < max && state.src.charCodeAt(pos) === 0x2B + /* + */ + ) { + pos++; + } + + if (pos !== start + 2) { + // sequence of 3+ markers taking as literal, same as in a emphasis + state.pos += pos - start; + + if (!silent) { + state.pending += state.src.slice(start, pos); + } + + return true; + } + + state.pos = start + 2; + stack = 1; + + while (state.pos + 1 < max) { + if (state.src.charCodeAt(state.pos) === 0x2B + /* + */ + ) { + if (state.src.charCodeAt(state.pos + 1) === 0x2B + /* + */ + ) { + lastChar = state.src.charCodeAt(state.pos - 1); + nextChar = state.pos + 2 < max ? state.src.charCodeAt(state.pos + 2) : -1; + + if (nextChar !== 0x2B + /* + */ + && lastChar !== 0x2B + /* + */ + ) { + if (lastChar !== 0x20 && lastChar !== 0x0A) { + // closing '++' + stack--; + } else if (nextChar !== 0x20 && nextChar !== 0x0A) { + // opening '++' + stack++; + } // else { + // // standalone ' ++ ' indented with spaces + // } + + + if (stack <= 0) { + found = true; + break; + } + } + } + } + + state.parser.skipToken(state); + } + + if (!found) { + // parser failed to find ending tag, so it's not valid emphasis + state.pos = start; + return false; + } // found! + + + state.posMax = state.pos; + state.pos = start + 2; + + if (!silent) { + state.push({ + type: 'ins_open', + level: state.level++ + }); + state.parser.tokenize(state); + state.push({ + type: 'ins_close', + level: --state.level + }); + } + + state.pos = state.posMax + 2; + state.posMax = max; + return true; +} // Process ==highlighted text== + + +function mark(state, silent) { + var found, + pos, + stack, + max = state.posMax, + start = state.pos, + lastChar, + nextChar; + + if (state.src.charCodeAt(start) !== 0x3D + /* = */ + ) { + return false; + } + + if (silent) { + return false; + } // don't run any pairs in validation mode + + + if (start + 4 >= max) { + return false; + } + + if (state.src.charCodeAt(start + 1) !== 0x3D + /* = */ + ) { + return false; + } + + if (state.level >= state.options.maxNesting) { + return false; + } + + lastChar = start > 0 ? state.src.charCodeAt(start - 1) : -1; + nextChar = state.src.charCodeAt(start + 2); + + if (lastChar === 0x3D + /* = */ + ) { + return false; + } + + if (nextChar === 0x3D + /* = */ + ) { + return false; + } + + if (nextChar === 0x20 || nextChar === 0x0A) { + return false; + } + + pos = start + 2; + + while (pos < max && state.src.charCodeAt(pos) === 0x3D + /* = */ + ) { + pos++; + } + + if (pos !== start + 2) { + // sequence of 3+ markers taking as literal, same as in a emphasis + state.pos += pos - start; + + if (!silent) { + state.pending += state.src.slice(start, pos); + } + + return true; + } + + state.pos = start + 2; + stack = 1; + + while (state.pos + 1 < max) { + if (state.src.charCodeAt(state.pos) === 0x3D + /* = */ + ) { + if (state.src.charCodeAt(state.pos + 1) === 0x3D + /* = */ + ) { + lastChar = state.src.charCodeAt(state.pos - 1); + nextChar = state.pos + 2 < max ? state.src.charCodeAt(state.pos + 2) : -1; + + if (nextChar !== 0x3D + /* = */ + && lastChar !== 0x3D + /* = */ + ) { + if (lastChar !== 0x20 && lastChar !== 0x0A) { + // closing '==' + stack--; + } else if (nextChar !== 0x20 && nextChar !== 0x0A) { + // opening '==' + stack++; + } // else { + // // standalone ' == ' indented with spaces + // } + + + if (stack <= 0) { + found = true; + break; + } + } + } + } + + state.parser.skipToken(state); + } + + if (!found) { + // parser failed to find ending tag, so it's not valid emphasis + state.pos = start; + return false; + } // found! + + + state.posMax = state.pos; + state.pos = start + 2; + + if (!silent) { + state.push({ + type: 'mark_open', + level: state.level++ + }); + state.parser.tokenize(state); + state.push({ + type: 'mark_close', + level: --state.level + }); + } + + state.pos = state.posMax + 2; + state.posMax = max; + return true; +} // Process *this* and _that_ + + +function isAlphaNum(code) { + return code >= 0x30 + /* 0 */ + && code <= 0x39 + /* 9 */ + || code >= 0x41 + /* A */ + && code <= 0x5A + /* Z */ + || code >= 0x61 + /* a */ + && code <= 0x7A + /* z */ + ; +} // parse sequence of emphasis markers, +// "start" should point at a valid marker + + +function scanDelims(state, start) { + var pos = start, + lastChar, + nextChar, + count, + can_open = true, + can_close = true, + max = state.posMax, + marker = state.src.charCodeAt(start); + lastChar = start > 0 ? state.src.charCodeAt(start - 1) : -1; + + while (pos < max && state.src.charCodeAt(pos) === marker) { + pos++; + } + + if (pos >= max) { + can_open = false; + } + + count = pos - start; + + if (count >= 4) { + // sequence of four or more unescaped markers can't start/end an emphasis + can_open = can_close = false; + } else { + nextChar = pos < max ? state.src.charCodeAt(pos) : -1; // check whitespace conditions + + if (nextChar === 0x20 || nextChar === 0x0A) { + can_open = false; + } + + if (lastChar === 0x20 || lastChar === 0x0A) { + can_close = false; + } + + if (marker === 0x5F + /* _ */ + ) { + // check if we aren't inside the word + if (isAlphaNum(lastChar)) { + can_open = false; + } + + if (isAlphaNum(nextChar)) { + can_close = false; + } + } + } + + return { + can_open: can_open, + can_close: can_close, + delims: count + }; +} + +function emphasis(state, silent) { + var startCount, + count, + found, + oldCount, + newCount, + stack, + res, + max = state.posMax, + start = state.pos, + marker = state.src.charCodeAt(start); + + if (marker !== 0x5F + /* _ */ + && marker !== 0x2A + /* * */ + ) { + return false; + } + + if (silent) { + return false; + } // don't run any pairs in validation mode + + + res = scanDelims(state, start); + startCount = res.delims; + + if (!res.can_open) { + state.pos += startCount; + + if (!silent) { + state.pending += state.src.slice(start, state.pos); + } + + return true; + } + + if (state.level >= state.options.maxNesting) { + return false; + } + + state.pos = start + startCount; + stack = [startCount]; + + while (state.pos < max) { + if (state.src.charCodeAt(state.pos) === marker) { + res = scanDelims(state, state.pos); + count = res.delims; + + if (res.can_close) { + oldCount = stack.pop(); + newCount = count; + + while (oldCount !== newCount) { + if (newCount < oldCount) { + stack.push(oldCount - newCount); + break; + } // assert(newCount > oldCount) + + + newCount -= oldCount; + + if (stack.length === 0) { + break; + } + + state.pos += oldCount; + oldCount = stack.pop(); + } + + if (stack.length === 0) { + startCount = oldCount; + found = true; + break; + } + + state.pos += count; + continue; + } + + if (res.can_open) { + stack.push(count); + } + + state.pos += count; + continue; + } + + state.parser.skipToken(state); + } + + if (!found) { + // parser failed to find ending tag, so it's not valid emphasis + state.pos = start; + return false; + } // found! + + + state.posMax = state.pos; + state.pos = start + startCount; + + if (!silent) { + if (startCount === 2 || startCount === 3) { + state.push({ + type: 'strong_open', + level: state.level++ + }); + } + + if (startCount === 1 || startCount === 3) { + state.push({ + type: 'em_open', + level: state.level++ + }); + } + + state.parser.tokenize(state); + + if (startCount === 1 || startCount === 3) { + state.push({ + type: 'em_close', + level: --state.level + }); + } + + if (startCount === 2 || startCount === 3) { + state.push({ + type: 'strong_close', + level: --state.level + }); + } + } + + state.pos = state.posMax + startCount; + state.posMax = max; + return true; +} // Process ~subscript~ +// same as UNESCAPE_MD_RE plus a space + + +var UNESCAPE_RE = /\\([ \\!"#$%&'()*+,.\/:;<=>?@[\]^_`{|}~-])/g; + +function sub(state, silent) { + var found, + content, + max = state.posMax, + start = state.pos; + + if (state.src.charCodeAt(start) !== 0x7E + /* ~ */ + ) { + return false; + } + + if (silent) { + return false; + } // don't run any pairs in validation mode + + + if (start + 2 >= max) { + return false; + } + + if (state.level >= state.options.maxNesting) { + return false; + } + + state.pos = start + 1; + + while (state.pos < max) { + if (state.src.charCodeAt(state.pos) === 0x7E + /* ~ */ + ) { + found = true; + break; + } + + state.parser.skipToken(state); + } + + if (!found || start + 1 === state.pos) { + state.pos = start; + return false; + } + + content = state.src.slice(start + 1, state.pos); // don't allow unescaped spaces/newlines inside + + if (content.match(/(^|[^\\])(\\\\)*\s/)) { + state.pos = start; + return false; + } // found! + + + state.posMax = state.pos; + state.pos = start + 1; + + if (!silent) { + state.push({ + type: 'sub', + level: state.level, + content: content.replace(UNESCAPE_RE, '$1') + }); + } + + state.pos = state.posMax + 1; + state.posMax = max; + return true; +} // Process ^superscript^ +// same as UNESCAPE_MD_RE plus a space + + +var UNESCAPE_RE$1 = /\\([ \\!"#$%&'()*+,.\/:;<=>?@[\]^_`{|}~-])/g; + +function sup(state, silent) { + var found, + content, + max = state.posMax, + start = state.pos; + + if (state.src.charCodeAt(start) !== 0x5E + /* ^ */ + ) { + return false; + } + + if (silent) { + return false; + } // don't run any pairs in validation mode + + + if (start + 2 >= max) { + return false; + } + + if (state.level >= state.options.maxNesting) { + return false; + } + + state.pos = start + 1; + + while (state.pos < max) { + if (state.src.charCodeAt(state.pos) === 0x5E + /* ^ */ + ) { + found = true; + break; + } + + state.parser.skipToken(state); + } + + if (!found || start + 1 === state.pos) { + state.pos = start; + return false; + } + + content = state.src.slice(start + 1, state.pos); // don't allow unescaped spaces/newlines inside + + if (content.match(/(^|[^\\])(\\\\)*\s/)) { + state.pos = start; + return false; + } // found! + + + state.posMax = state.pos; + state.pos = start + 1; + + if (!silent) { + state.push({ + type: 'sup', + level: state.level, + content: content.replace(UNESCAPE_RE$1, '$1') + }); + } + + state.pos = state.posMax + 1; + state.posMax = max; + return true; +} // Process [links]( "stuff") + + +function links(state, silent) { + var labelStart, + labelEnd, + label, + href, + title, + pos, + ref, + code, + isImage = false, + oldPos = state.pos, + max = state.posMax, + start = state.pos, + marker = state.src.charCodeAt(start); + + if (marker === 0x21 + /* ! */ + ) { + isImage = true; + marker = state.src.charCodeAt(++start); + } + + if (marker !== 0x5B + /* [ */ + ) { + return false; + } + + if (state.level >= state.options.maxNesting) { + return false; + } + + labelStart = start + 1; + labelEnd = parseLinkLabel(state, start); // parser failed to find ']', so it's not a valid link + + if (labelEnd < 0) { + return false; + } + + pos = labelEnd + 1; + + if (pos < max && state.src.charCodeAt(pos) === 0x28 + /* ( */ + ) { + // + // Inline link + // + // [link]( "title" ) + // ^^ skipping these spaces + pos++; + + for (; pos < max; pos++) { + code = state.src.charCodeAt(pos); + + if (code !== 0x20 && code !== 0x0A) { + break; + } + } + + if (pos >= max) { + return false; + } // [link]( "title" ) + // ^^^^^^ parsing link destination + + + start = pos; + + if (parseLinkDestination(state, pos)) { + href = state.linkContent; + pos = state.pos; + } else { + href = ''; + } // [link]( "title" ) + // ^^ skipping these spaces + + + start = pos; + + for (; pos < max; pos++) { + code = state.src.charCodeAt(pos); + + if (code !== 0x20 && code !== 0x0A) { + break; + } + } // [link]( "title" ) + // ^^^^^^^ parsing link title + + + if (pos < max && start !== pos && parseLinkTitle(state, pos)) { + title = state.linkContent; + pos = state.pos; // [link]( "title" ) + // ^^ skipping these spaces + + for (; pos < max; pos++) { + code = state.src.charCodeAt(pos); + + if (code !== 0x20 && code !== 0x0A) { + break; + } + } + } else { + title = ''; + } + + if (pos >= max || state.src.charCodeAt(pos) !== 0x29 + /* ) */ + ) { + state.pos = oldPos; + return false; + } + + pos++; + } else { + // + // Link reference + // + // do not allow nested reference links + if (state.linkLevel > 0) { + return false; + } // [foo] [bar] + // ^^ optional whitespace (can include newlines) + + + for (; pos < max; pos++) { + code = state.src.charCodeAt(pos); + + if (code !== 0x20 && code !== 0x0A) { + break; + } + } + + if (pos < max && state.src.charCodeAt(pos) === 0x5B + /* [ */ + ) { + start = pos + 1; + pos = parseLinkLabel(state, pos); + + if (pos >= 0) { + label = state.src.slice(start, pos++); + } else { + pos = start - 1; + } + } // covers label === '' and label === undefined + // (collapsed reference link and shortcut reference link respectively) + + + if (!label) { + if (typeof label === 'undefined') { + pos = labelEnd + 1; + } + + label = state.src.slice(labelStart, labelEnd); + } + + ref = state.env.references[normalizeReference(label)]; + + if (!ref) { + state.pos = oldPos; + return false; + } + + href = ref.href; + title = ref.title; + } // + // We found the end of the link, and know for a fact it's a valid link; + // so all that's left to do is to call tokenizer. + // + + + if (!silent) { + state.pos = labelStart; + state.posMax = labelEnd; + + if (isImage) { + state.push({ + type: 'image', + src: href, + title: title, + alt: state.src.substr(labelStart, labelEnd - labelStart), + level: state.level + }); + } else { + state.push({ + type: 'link_open', + href: href, + title: title, + level: state.level++ + }); + state.linkLevel++; + state.parser.tokenize(state); + state.linkLevel--; + state.push({ + type: 'link_close', + level: --state.level + }); + } + } + + state.pos = pos; + state.posMax = max; + return true; +} // Process inline footnotes (^[...]) + + +function footnote_inline(state, silent) { + var labelStart, + labelEnd, + footnoteId, + oldLength, + max = state.posMax, + start = state.pos; + + if (start + 2 >= max) { + return false; + } + + if (state.src.charCodeAt(start) !== 0x5E + /* ^ */ + ) { + return false; + } + + if (state.src.charCodeAt(start + 1) !== 0x5B + /* [ */ + ) { + return false; + } + + if (state.level >= state.options.maxNesting) { + return false; + } + + labelStart = start + 2; + labelEnd = parseLinkLabel(state, start + 1); // parser failed to find ']', so it's not a valid note + + if (labelEnd < 0) { + return false; + } // We found the end of the link, and know for a fact it's a valid link; + // so all that's left to do is to call tokenizer. + // + + + if (!silent) { + if (!state.env.footnotes) { + state.env.footnotes = {}; + } + + if (!state.env.footnotes.list) { + state.env.footnotes.list = []; + } + + footnoteId = state.env.footnotes.list.length; + state.pos = labelStart; + state.posMax = labelEnd; + state.push({ + type: 'footnote_ref', + id: footnoteId, + level: state.level + }); + state.linkLevel++; + oldLength = state.tokens.length; + state.parser.tokenize(state); + state.env.footnotes.list[footnoteId] = { + tokens: state.tokens.splice(oldLength) + }; + state.linkLevel--; + } + + state.pos = labelEnd + 1; + state.posMax = max; + return true; +} // Process footnote references ([^...]) + + +function footnote_ref(state, silent) { + var label, + pos, + footnoteId, + footnoteSubId, + max = state.posMax, + start = state.pos; // should be at least 4 chars - "[^x]" + + if (start + 3 > max) { + return false; + } + + if (!state.env.footnotes || !state.env.footnotes.refs) { + return false; + } + + if (state.src.charCodeAt(start) !== 0x5B + /* [ */ + ) { + return false; + } + + if (state.src.charCodeAt(start + 1) !== 0x5E + /* ^ */ + ) { + return false; + } + + if (state.level >= state.options.maxNesting) { + return false; + } + + for (pos = start + 2; pos < max; pos++) { + if (state.src.charCodeAt(pos) === 0x20) { + return false; + } + + if (state.src.charCodeAt(pos) === 0x0A) { + return false; + } + + if (state.src.charCodeAt(pos) === 0x5D + /* ] */ + ) { + break; + } + } + + if (pos === start + 2) { + return false; + } // no empty footnote labels + + + if (pos >= max) { + return false; + } + + pos++; + label = state.src.slice(start + 2, pos - 1); + + if (typeof state.env.footnotes.refs[':' + label] === 'undefined') { + return false; + } + + if (!silent) { + if (!state.env.footnotes.list) { + state.env.footnotes.list = []; + } + + if (state.env.footnotes.refs[':' + label] < 0) { + footnoteId = state.env.footnotes.list.length; + state.env.footnotes.list[footnoteId] = { + label: label, + count: 0 + }; + state.env.footnotes.refs[':' + label] = footnoteId; + } else { + footnoteId = state.env.footnotes.refs[':' + label]; + } + + footnoteSubId = state.env.footnotes.list[footnoteId].count; + state.env.footnotes.list[footnoteId].count++; + state.push({ + type: 'footnote_ref', + id: footnoteId, + subId: footnoteSubId, + level: state.level + }); + } + + state.pos = pos; + state.posMax = max; + return true; +} // List of valid url schemas, accorting to commonmark spec +// http://jgm.github.io/CommonMark/spec.html#autolinks + + +var url_schemas = ['coap', 'doi', 'javascript', 'aaa', 'aaas', 'about', 'acap', 'cap', 'cid', 'crid', 'data', 'dav', 'dict', 'dns', 'file', 'ftp', 'geo', 'go', 'gopher', 'h323', 'http', 'https', 'iax', 'icap', 'im', 'imap', 'info', 'ipp', 'iris', 'iris.beep', 'iris.xpc', 'iris.xpcs', 'iris.lwz', 'ldap', 'mailto', 'mid', 'msrp', 'msrps', 'mtqp', 'mupdate', 'news', 'nfs', 'ni', 'nih', 'nntp', 'opaquelocktoken', 'pop', 'pres', 'rtsp', 'service', 'session', 'shttp', 'sieve', 'sip', 'sips', 'sms', 'snmp', 'soap.beep', 'soap.beeps', 'tag', 'tel', 'telnet', 'tftp', 'thismessage', 'tn3270', 'tip', 'tv', 'urn', 'vemmi', 'ws', 'wss', 'xcon', 'xcon-userid', 'xmlrpc.beep', 'xmlrpc.beeps', 'xmpp', 'z39.50r', 'z39.50s', 'adiumxtra', 'afp', 'afs', 'aim', 'apt', 'attachment', 'aw', 'beshare', 'bitcoin', 'bolo', 'callto', 'chrome', 'chrome-extension', 'com-eventbrite-attendee', 'content', 'cvs', 'dlna-playsingle', 'dlna-playcontainer', 'dtn', 'dvb', 'ed2k', 'facetime', 'feed', 'finger', 'fish', 'gg', 'git', 'gizmoproject', 'gtalk', 'hcp', 'icon', 'ipn', 'irc', 'irc6', 'ircs', 'itms', 'jar', 'jms', 'keyparc', 'lastfm', 'ldaps', 'magnet', 'maps', 'market', 'message', 'mms', 'ms-help', 'msnim', 'mumble', 'mvn', 'notes', 'oid', 'palm', 'paparazzi', 'platform', 'proxy', 'psyc', 'query', 'res', 'resource', 'rmi', 'rsync', 'rtmp', 'secondlife', 'sftp', 'sgn', 'skype', 'smb', 'soldat', 'spotify', 'ssh', 'steam', 'svn', 'teamspeak', 'things', 'udp', 'unreal', 'ut2004', 'ventrilo', 'view-source', 'webcal', 'wtai', 'wyciwyg', 'xfire', 'xri', 'ymsgr']; // Process autolinks '' + +/*eslint max-len:0*/ + +var EMAIL_RE = /^<([a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)>/; +var AUTOLINK_RE = /^<([a-zA-Z.\-]{1,25}):([^<>\x00-\x20]*)>/; + +function autolink(state, silent) { + var tail, + linkMatch, + emailMatch, + url, + fullUrl, + pos = state.pos; + + if (state.src.charCodeAt(pos) !== 0x3C + /* < */ + ) { + return false; + } + + tail = state.src.slice(pos); + + if (tail.indexOf('>') < 0) { + return false; + } + + linkMatch = tail.match(AUTOLINK_RE); + + if (linkMatch) { + if (url_schemas.indexOf(linkMatch[1].toLowerCase()) < 0) { + return false; + } + + url = linkMatch[0].slice(1, -1); + fullUrl = normalizeLink(url); + + if (!state.parser.validateLink(url)) { + return false; + } + + if (!silent) { + state.push({ + type: 'link_open', + href: fullUrl, + level: state.level + }); + state.push({ + type: 'text', + content: url, + level: state.level + 1 + }); + state.push({ + type: 'link_close', + level: state.level + }); + } + + state.pos += linkMatch[0].length; + return true; + } + + emailMatch = tail.match(EMAIL_RE); + + if (emailMatch) { + url = emailMatch[0].slice(1, -1); + fullUrl = normalizeLink('mailto:' + url); + + if (!state.parser.validateLink(fullUrl)) { + return false; + } + + if (!silent) { + state.push({ + type: 'link_open', + href: fullUrl, + level: state.level + }); + state.push({ + type: 'text', + content: url, + level: state.level + 1 + }); + state.push({ + type: 'link_close', + level: state.level + }); + } + + state.pos += emailMatch[0].length; + return true; + } + + return false; +} // Regexps to match html elements + + +function replace$1(regex, options) { + regex = regex.source; + options = options || ''; + return function self(name, val) { + if (!name) { + return new RegExp(regex, options); + } + + val = val.source || val; + regex = regex.replace(name, val); + return self; + }; +} + +var attr_name = /[a-zA-Z_:][a-zA-Z0-9:._-]*/; +var unquoted = /[^"'=<>`\x00-\x20]+/; +var single_quoted = /'[^']*'/; +var double_quoted = /"[^"]*"/; +/*eslint no-spaced-func:0*/ + +var attr_value = replace$1(/(?:unquoted|single_quoted|double_quoted)/)('unquoted', unquoted)('single_quoted', single_quoted)('double_quoted', double_quoted)(); +var attribute = replace$1(/(?:\s+attr_name(?:\s*=\s*attr_value)?)/)('attr_name', attr_name)('attr_value', attr_value)(); +var open_tag = replace$1(/<[A-Za-z][A-Za-z0-9]*attribute*\s*\/?>/)('attribute', attribute)(); +var close_tag = /<\/[A-Za-z][A-Za-z0-9]*\s*>/; +var comment = /|/; +var processing = /<[?].*?[?]>/; +var declaration = /]*>/; +var cdata = //; +var HTML_TAG_RE = replace$1(/^(?:open_tag|close_tag|comment|processing|declaration|cdata)/)('open_tag', open_tag)('close_tag', close_tag)('comment', comment)('processing', processing)('declaration', declaration)('cdata', cdata)(); // Process html tags + +function isLetter$2(ch) { + /*eslint no-bitwise:0*/ + var lc = ch | 0x20; // to lower case + + return lc >= 0x61 + /* a */ + && lc <= 0x7a + /* z */ + ; +} + +function htmltag(state, silent) { + var ch, + match, + max, + pos = state.pos; + + if (!state.options.html) { + return false; + } // Check start + + + max = state.posMax; + + if (state.src.charCodeAt(pos) !== 0x3C + /* < */ + || pos + 2 >= max) { + return false; + } // Quick fail on second char + + + ch = state.src.charCodeAt(pos + 1); + + if (ch !== 0x21 + /* ! */ + && ch !== 0x3F + /* ? */ + && ch !== 0x2F + /* / */ + && !isLetter$2(ch)) { + return false; + } + + match = state.src.slice(pos).match(HTML_TAG_RE); + + if (!match) { + return false; + } + + if (!silent) { + state.push({ + type: 'htmltag', + content: state.src.slice(pos, pos + match[0].length), + level: state.level + }); + } + + state.pos += match[0].length; + return true; +} // Process html entity - {, ¯, ", ... + + +var DIGITAL_RE = /^&#((?:x[a-f0-9]{1,8}|[0-9]{1,8}));/i; +var NAMED_RE = /^&([a-z][a-z0-9]{1,31});/i; + +function entity(state, silent) { + var ch, + code, + match, + pos = state.pos, + max = state.posMax; + + if (state.src.charCodeAt(pos) !== 0x26 + /* & */ + ) { + return false; + } + + if (pos + 1 < max) { + ch = state.src.charCodeAt(pos + 1); + + if (ch === 0x23 + /* # */ + ) { + match = state.src.slice(pos).match(DIGITAL_RE); + + if (match) { + if (!silent) { + code = match[1][0].toLowerCase() === 'x' ? parseInt(match[1].slice(1), 16) : parseInt(match[1], 10); + state.pending += isValidEntityCode(code) ? fromCodePoint(code) : fromCodePoint(0xFFFD); + } + + state.pos += match[0].length; + return true; + } + } else { + match = state.src.slice(pos).match(NAMED_RE); + + if (match) { + var decoded = decodeEntity(match[1]); + + if (match[1] !== decoded) { + if (!silent) { + state.pending += decoded; + } + + state.pos += match[0].length; + return true; + } + } + } + } + + if (!silent) { + state.pending += '&'; + } + + state.pos++; + return true; +} +/** + * Inline Parser `rules` + */ + + +var _rules$2 = [['text', text], ['newline', newline], ['escape', escape], ['backticks', backticks], ['del', del], ['ins', ins], ['mark', mark], ['emphasis', emphasis], ['sub', sub], ['sup', sup], ['links', links], ['footnote_inline', footnote_inline], ['footnote_ref', footnote_ref], ['autolink', autolink], ['htmltag', htmltag], ['entity', entity]]; +/** + * Inline Parser class. Note that link validation is stricter + * in Remarkable than what is specified by CommonMark. If you + * want to change this you can use a custom validator. + * + * @api private + */ + +function ParserInline() { + this.ruler = new Ruler(); + + for (var i = 0; i < _rules$2.length; i++) { + this.ruler.push(_rules$2[i][0], _rules$2[i][1]); + } // Can be overridden with a custom validator + + + this.validateLink = validateLink; +} +/** + * Skip a single token by running all rules in validation mode. + * Returns `true` if any rule reports success. + * + * @param {Object} `state` + * @api privage + */ + + +ParserInline.prototype.skipToken = function (state) { + var rules = this.ruler.getRules(''); + var len = rules.length; + var pos = state.pos; + var i, cached_pos; + + if ((cached_pos = state.cacheGet(pos)) > 0) { + state.pos = cached_pos; + return; + } + + for (i = 0; i < len; i++) { + if (rules[i](state, true)) { + state.cacheSet(pos, state.pos); + return; + } + } + + state.pos++; + state.cacheSet(pos, state.pos); +}; +/** + * Generate tokens for the given input range. + * + * @param {Object} `state` + * @api private + */ + + +ParserInline.prototype.tokenize = function (state) { + var rules = this.ruler.getRules(''); + var len = rules.length; + var end = state.posMax; + var ok, i; + + while (state.pos < end) { + // Try all possible rules. + // On success, the rule should: + // + // - update `state.pos` + // - update `state.tokens` + // - return true + for (i = 0; i < len; i++) { + ok = rules[i](state, false); + + if (ok) { + break; + } + } + + if (ok) { + if (state.pos >= end) { + break; + } + + continue; + } + + state.pending += state.src[state.pos++]; + } + + if (state.pending) { + state.pushPending(); + } +}; +/** + * Parse the given input string. + * + * @param {String} `str` + * @param {Object} `options` + * @param {Object} `env` + * @param {Array} `outTokens` + * @api private + */ + + +ParserInline.prototype.parse = function (str, options, env, outTokens) { + var state = new StateInline(str, this, options, env, outTokens); + this.tokenize(state); +}; +/** + * Validate the given `url` by checking for bad protocols. + * + * @param {String} `url` + * @return {Boolean} + */ + + +function validateLink(url) { + var BAD_PROTOCOLS = ['vbscript', 'javascript', 'file', 'data']; + var str = url.trim().toLowerCase(); // Care about digital entities "javascript:alert(1)" + + str = replaceEntities(str); + + if (str.indexOf(':') !== -1 && BAD_PROTOCOLS.indexOf(str.split(':')[0]) !== -1) { + return false; + } + + return true; +} // Remarkable default options + + +var defaultConfig = { + options: { + html: false, + // Enable HTML tags in source + xhtmlOut: false, + // Use '/' to close single tags (
    ) + breaks: false, + // Convert '\n' in paragraphs into
    + langPrefix: 'language-', + // CSS language prefix for fenced blocks + linkTarget: '', + // set target to open link in + // Enable some language-neutral replacements + quotes beautification + typographer: false, + // Double + single quotes replacement pairs, when typographer enabled, + // and smartquotes on. Set doubles to '«»' for Russian, '„“' for German. + quotes: '“”‘’', + // Highlighter function. Should return escaped HTML, + // or '' if input not changed + // + // function (/*str, lang*/) { return ''; } + // + highlight: null, + maxNesting: 20 // Internal protection, recursion limit + + }, + components: { + core: { + rules: ['block', 'inline', 'references', 'replacements', 'smartquotes', 'references', 'abbr2', 'footnote_tail'] + }, + block: { + rules: ['blockquote', 'code', 'fences', 'footnote', 'heading', 'hr', 'htmlblock', 'lheading', 'list', 'paragraph', 'table'] + }, + inline: { + rules: ['autolink', 'backticks', 'del', 'emphasis', 'entity', 'escape', 'footnote_ref', 'htmltag', 'links', 'newline', 'text'] + } + } +}; // Remarkable default options + +var fullConfig = { + options: { + html: false, + // Enable HTML tags in source + xhtmlOut: false, + // Use '/' to close single tags (
    ) + breaks: false, + // Convert '\n' in paragraphs into
    + langPrefix: 'language-', + // CSS language prefix for fenced blocks + linkTarget: '', + // set target to open link in + // Enable some language-neutral replacements + quotes beautification + typographer: false, + // Double + single quotes replacement pairs, when typographer enabled, + // and smartquotes on. Set doubles to '«»' for Russian, '„“' for German. + quotes: '“”‘’', + // Highlighter function. Should return escaped HTML, + // or '' if input not changed + // + // function (/*str, lang*/) { return ''; } + // + highlight: null, + maxNesting: 20 // Internal protection, recursion limit + + }, + components: { + // Don't restrict core/block/inline rules + core: {}, + block: {}, + inline: {} + } +}; // Commonmark default options + +var commonmarkConfig = { + options: { + html: true, + // Enable HTML tags in source + xhtmlOut: true, + // Use '/' to close single tags (
    ) + breaks: false, + // Convert '\n' in paragraphs into
    + langPrefix: 'language-', + // CSS language prefix for fenced blocks + linkTarget: '', + // set target to open link in + // Enable some language-neutral replacements + quotes beautification + typographer: false, + // Double + single quotes replacement pairs, when typographer enabled, + // and smartquotes on. Set doubles to '«»' for Russian, '„“' for German. + quotes: '“”‘’', + // Highlighter function. Should return escaped HTML, + // or '' if input not changed + // + // function (/*str, lang*/) { return ''; } + // + highlight: null, + maxNesting: 20 // Internal protection, recursion limit + + }, + components: { + core: { + rules: ['block', 'inline', 'references', 'abbr2'] + }, + block: { + rules: ['blockquote', 'code', 'fences', 'heading', 'hr', 'htmlblock', 'lheading', 'list', 'paragraph'] + }, + inline: { + rules: ['autolink', 'backticks', 'emphasis', 'entity', 'escape', 'htmltag', 'links', 'newline', 'text'] + } + } +}; +/** + * Preset configs + */ + +var config = { + 'default': defaultConfig, + 'full': fullConfig, + 'commonmark': commonmarkConfig +}; +/** + * The `StateCore` class manages state. + * + * @param {Object} `instance` Remarkable instance + * @param {String} `str` Markdown string + * @param {Object} `env` + */ + +function StateCore(instance, str, env) { + this.src = str; + this.env = env; + this.options = instance.options; + this.tokens = []; + this.inlineMode = false; + this.inline = instance.inline; + this.block = instance.block; + this.renderer = instance.renderer; + this.typographer = instance.typographer; +} +/** + * The main `Remarkable` class. Create an instance of + * `Remarkable` with a `preset` and/or `options`. + * + * @param {String} `preset` If no preset is given, `default` is used. + * @param {Object} `options` + */ + + +function Remarkable(preset, options) { + if (typeof preset !== 'string') { + options = preset; + preset = 'default'; + } + + if (options && options.linkify != null) { + console.warn('linkify option is removed. Use linkify plugin instead:\n\n' + 'import Remarkable from \'remarkable\';\n' + 'import linkify from \'remarkable/linkify\';\n' + 'new Remarkable().use(linkify)\n'); + } + + this.inline = new ParserInline(); + this.block = new ParserBlock(); + this.core = new Core(); + this.renderer = new Renderer(); + this.ruler = new Ruler(); + this.options = {}; + this.configure(config[preset]); + this.set(options || {}); +} +/** + * Set options as an alternative to passing them + * to the constructor. + * + * ```js + * md.set({typographer: true}); + * ``` + * @param {Object} `options` + * @api public + */ + + +Remarkable.prototype.set = function (options) { + assign(this.options, options); +}; +/** + * Batch loader for components rules states, and options + * + * @param {Object} `presets` + */ + + +Remarkable.prototype.configure = function (presets) { + var self = this; + + if (!presets) { + throw new Error('Wrong `remarkable` preset, check name/content'); + } + + if (presets.options) { + self.set(presets.options); + } + + if (presets.components) { + Object.keys(presets.components).forEach(function (name) { + if (presets.components[name].rules) { + self[name].ruler.enable(presets.components[name].rules, true); + } + }); + } +}; +/** + * Use a plugin. + * + * ```js + * var md = new Remarkable(); + * + * md.use(plugin1) + * .use(plugin2, opts) + * .use(plugin3); + * ``` + * + * @param {Function} `plugin` + * @param {Object} `options` + * @return {Object} `Remarkable` for chaining + */ + + +Remarkable.prototype.use = function (plugin, options) { + plugin(this, options); + return this; +}; +/** + * Parse the input `string` and return a tokens array. + * Modifies `env` with definitions data. + * + * @param {String} `string` + * @param {Object} `env` + * @return {Array} Array of tokens + */ + + +Remarkable.prototype.parse = function (str, env) { + var state = new StateCore(this, str, env); + this.core.process(state); + return state.tokens; +}; +/** + * The main `.render()` method that does all the magic :) + * + * @param {String} `string` + * @param {Object} `env` + * @return {String} Rendered HTML. + */ + + +Remarkable.prototype.render = function (str, env) { + env = env || {}; + return this.renderer.render(this.parse(str, env), this.options, env); +}; +/** + * Parse the given content `string` as a single string. + * + * @param {String} `string` + * @param {Object} `env` + * @return {Array} Array of tokens + */ + + +Remarkable.prototype.parseInline = function (str, env) { + var state = new StateCore(this, str, env); + state.inlineMode = true; + this.core.process(state); + return state.tokens; +}; +/** + * Render a single content `string`, without wrapping it + * to paragraphs + * + * @param {String} `str` + * @param {Object} `env` + * @return {String} + */ + + +Remarkable.prototype.renderInline = function (str, env) { + env = env || {}; + return this.renderer.render(this.parseInline(str, env), this.options, env); +}; + +/** + * Plugin for Remarkable Markdown processor which transforms $..$ and $$..$$ sequences into math HTML using the + * Katex package. + */ + +var rkatex = function rkatex(md, options) { + var dollar = '$'; + var opts = options || {}; + var delimiter = opts.delimiter || dollar; + + if (delimiter.length !== 1) { + throw new Error('invalid delimiter'); + } + + var katex = require$$0__default; + /** + * Render the contents as KaTeX + */ + + + var renderKatex = function renderKatex(source, displayMode) { + return katex.renderToString(source, { + displayMode: displayMode, + throwOnError: false + }); + }; + /** + * Parse '$$' as a block. Based off of similar method in remarkable. + */ + + + var parseBlockKatex = function parseBlockKatex(state, startLine, endLine) { + var haveEndMarker = false; + var pos = state.bMarks[startLine] + state.tShift[startLine]; + var max = state.eMarks[startLine]; + + if (pos + 1 > max) { + return false; + } + + var marker = state.src.charAt(pos); + + if (marker !== delimiter) { + return false; + } // scan marker length + + + var mem = pos; + pos = state.skipChars(pos, marker); + var len = pos - mem; + + if (len !== 2) { + return false; + } // search end of block + + + var nextLine = startLine; + + for (;;) { + ++nextLine; + + if (nextLine >= endLine) { + break; + } + + pos = mem = state.bMarks[nextLine] + state.tShift[nextLine]; + max = state.eMarks[nextLine]; + + if (pos < max && state.tShift[nextLine] < state.blkIndent) { + break; + } + + if (state.src.charAt(pos) !== delimiter) { + continue; + } + + if (state.tShift[nextLine] - state.blkIndent >= 4) { + continue; + } + + pos = state.skipChars(pos, marker); + + if (pos - mem < len) { + continue; + } + + pos = state.skipSpaces(pos); + + if (pos < max) { + continue; + } + + haveEndMarker = true; + break; + } // If a fence has heading spaces, they should be removed from its inner block + + + len = state.tShift[startLine]; + state.line = nextLine + (haveEndMarker ? 1 : 0); + var content = state.getLines(startLine + 1, nextLine, len, true).replace(/[ \n]+/g, ' ').trim(); + state.tokens.push({ + type: 'katex', + params: null, + content: content, + lines: [startLine, state.line], + level: state.level, + block: true + }); + return true; + }; + /** + * Look for '$' or '$$' spans in Markdown text. Based off of the 'fenced' parser in remarkable. + */ + + + var parseInlineKatex = function parseInlineKatex(state, silent) { + var start = state.pos; + var max = state.posMax; + var pos = start; // Unexpected starting character + + if (state.src.charAt(pos) !== delimiter) { + return false; + } + + ++pos; + + while (pos < max && state.src.charAt(pos) === delimiter) { + ++pos; + } // Capture the length of the starting delimiter -- closing one must match in size + + + var marker = state.src.slice(start, pos); + + if (marker.length > 2) { + return false; + } + + var spanStart = pos; + var escapedDepth = 0; + + while (pos < max) { + var _char = state.src.charAt(pos); + + if (_char === '{') { + escapedDepth += 1; + } else if (_char === '}') { + escapedDepth -= 1; + + if (escapedDepth < 0) { + return false; + } + } else if (_char === delimiter && escapedDepth === 0) { + var matchStart = pos; + var matchEnd = pos + 1; + + while (matchEnd < max && state.src.charAt(matchEnd) === delimiter) { + ++matchEnd; + } + + if (matchEnd - matchStart === marker.length) { + if (!silent) { + var content = state.src.slice(spanStart, matchStart).replace(/[ \n]+/g, ' ').trim(); + state.push({ + type: 'katex', + content: content, + block: marker.length > 1, + level: state.level + }); + } + + state.pos = matchEnd; + return true; + } + } + + pos += 1; + } + + if (!silent) { + state.pending += marker; + } + + state.pos += marker.length; + return true; + }; + + md.inline.ruler.push('katex', parseInlineKatex, options); + md.block.ruler.push('katex', parseBlockKatex, options); + + md.renderer.rules.katex = function (tokens, idx) { + return renderKatex(tokens[idx].content, tokens[idx].block); + }; + + md.renderer.rules.katex.delimiter = delimiter; +}; + +var remarkableKatex = rkatex; + +let loading$1; + +const autoload$1 = () => { + loading$1 = loading$1 || loadJS([{ + type: 'script', + data: { + src: 'https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js' + } + }]); + return loading$1; +}; + +const name$1 = 'katex'; +function transform$1(transformHooks) { + transformHooks.parser.tap((md, features) => { + md.use(remarkableKatex); + + const renderKatex = (source, displayMode) => { + const { + katex + } = window; + if (katex) return katex.renderToString(source, { + displayMode, + throwOnError: false + }); + autoload$1().then(() => { + transformHooks.retransform.call(); + }); + return source; + }; + + md.renderer.rules.katex = (tokens, idx) => { + const result = renderKatex(tokens[idx].content, tokens[idx].block); + features[name$1] = true; + return result; + }; + }); + return { + styles: [{ + type: 'stylesheet', + data: { + href: 'https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css' + } + }], + scripts: [{ + type: 'iife', + data: { + fn: getMarkmap => { + window.WebFontConfig = { + custom: { + families: ['KaTeX_AMS', 'KaTeX_Caligraphic:n4,n7', 'KaTeX_Fraktur:n4,n7', 'KaTeX_Main:n4,n7,i4,i7', 'KaTeX_Math:i4,i7', 'KaTeX_Script', 'KaTeX_SansSerif:n4,n7,i4', 'KaTeX_Size1', 'KaTeX_Size2', 'KaTeX_Size3', 'KaTeX_Size4', 'KaTeX_Typewriter'] + }, + active: () => { + getMarkmap().refreshHook.call(); + } + }; + }, + + getParams({ + getMarkmap + }) { + return [getMarkmap]; + } + + } + }, { + type: 'script', + data: { + src: 'https://cdn.jsdelivr.net/npm/webfontloader@1.6.28/webfontloader.js', + defer: true + } + }] + }; +} + +var katex = /*#__PURE__*/Object.freeze({ +__proto__: null, +name: name$1, +transform: transform$1 +}); + +let loading; + +const autoload = () => { + loading = loading || loadJS([{ + type: 'script', + data: { + src: `https://cdn.jsdelivr.net/npm/prismjs@${"1.23.0"}/components/prism-core.min.js` + } + }, { + type: 'script', + data: { + src: `https://cdn.jsdelivr.net/npm/prismjs@${"1.23.0"}/plugins/autoloader/prism-autoloader.min.js` + } + }]); + return loading; +}; + +function loadLanguageAndRefresh(lang, transformHooks) { + autoload().then(() => { + window.Prism.plugins.autoloader.loadLanguages([lang], () => { + transformHooks.retransform.call(); + }); + }); +} + +const name = 'prism'; +function transform(transformHooks) { + transformHooks.parser.tap((md, features) => { + md.set({ + highlight: (str, lang) => { + var _Prism$languages; + + features[name] = true; + const { + Prism + } = window; + const grammar = Prism == null ? void 0 : (_Prism$languages = Prism.languages) == null ? void 0 : _Prism$languages[lang]; + + if (!grammar) { + loadLanguageAndRefresh(lang, transformHooks); + return ''; + } + + return Prism.highlight(str, grammar, lang); + } + }); + }); + return { + styles: [{ + type: 'stylesheet', + data: { + href: `https://cdn.jsdelivr.net/npm/prismjs@${"1.23.0"}/themes/prism.css` + } + }] + }; +} + +var prism = /*#__PURE__*/Object.freeze({ +__proto__: null, +name: name, +transform: transform +}); + +function createTransformHooks() { + return { + parser: new Hook(), + htmltag: new Hook(), + + /** + * Indicate that the last transformation is not complete for reasons like + * lack of resources and is called when it is ready for a new transformation. + */ + retransform: new Hook() + }; +} + +const plugins = [katex, prism]; + +function cleanNode(node, depth = 0) { + if (node.t === 'heading') { + // drop all paragraphs + node.c = node.c.filter(item => item.t !== 'paragraph'); + } else if (node.t === 'list_item') { + var _node$p; + + // keep first paragraph as content of list_item, drop others + node.c = node.c.filter(item => { + if (['paragraph', 'fence'].includes(item.t)) { + if (!node.v) { + node.v = item.v; + node.p = _extends$1({}, node.p, item.p); + } + + return false; + } + + return true; + }); + + if (((_node$p = node.p) == null ? void 0 : _node$p.index) != null) { + node.v = `${node.p.index}. ${node.v}`; + } + } else if (node.t === 'ordered_list') { + var _node$p$start, _node$p2; + + let index = (_node$p$start = (_node$p2 = node.p) == null ? void 0 : _node$p2.start) != null ? _node$p$start : 1; + node.c.forEach(item => { + if (item.t === 'list_item') { + item.p = _extends$1({}, item.p, { + index + }); + index += 1; + } + }); + } + + if (node.c.length === 0) { + delete node.c; + } else { + node.c.forEach(child => cleanNode(child, depth + 1)); + + if (node.c.length === 1 && !node.c[0].v) { + node.c = node.c[0].c; + } + } + + node.d = depth; +} + +class Transformer { + constructor(plugins$1 = plugins) { + this.plugins = void 0; + this.hooks = void 0; + this.md = void 0; + this.assetsMap = void 0; + this.plugins = plugins$1; + this.hooks = createTransformHooks(); + const md = new Remarkable({ + html: true, + breaks: true, + maxNesting: Infinity + }); + md.block.ruler.enable(['deflist']); + md.renderer.rules.htmltag = wrapFunction(md.renderer.rules.htmltag, { + after: ctx => { + this.hooks.htmltag.call(ctx); + } + }); + this.md = md; + const assetsMap = {}; + + for (const { + name, + transform + } of plugins$1) { + assetsMap[name] = transform(this.hooks); + } + + this.assetsMap = assetsMap; + } + + buildTree(tokens) { + + const root = { + t: 'root', + d: 0, + v: '', + c: [], + p: {} + }; + const stack = [root]; + let depth = 0; + + for (const token of tokens) { + let current = stack[stack.length - 1]; + + if (token.type.endsWith('_open')) { + const type = token.type.slice(0, -5); + const payload = {}; + + if (token.lines) { + payload.lines = token.lines; + } + + if (type === 'heading') { + depth = token.hLevel; + + while (((_current = current) == null ? void 0 : _current.d) >= depth) { + var _current; + + stack.pop(); + current = stack[stack.length - 1]; + } + } else { + var _current2; + + depth = Math.max(depth, ((_current2 = current) == null ? void 0 : _current2.d) || 0) + 1; + + if (type === 'ordered_list') { + payload.start = token.order; + } + } + + const item = { + t: type, + d: depth, + p: payload, + v: '', + c: [] + }; + current.c.push(item); + stack.push(item); + } else if (!current) { + continue; + } else if (token.type === `${current.t}_close`) { + if (current.t === 'heading') { + depth = current.d; + } else { + stack.pop(); + depth = 0; + } + } else if (token.type === 'inline') { + const revoke = this.hooks.htmltag.tap(ctx => { + const comment = ctx.result.match(/^$/); + const data = comment == null ? void 0 : comment[1].trim(); + + if (data === 'fold') { + current.p.f = true; + ctx.result = ''; + } + }); + // const text = md.renderer.render([token], md.options, {}); + let text=''; + if(token.children&&token.children.length==1){ + if(token.children[0].type&&token.children[0].type=='link_open'){ + text = `[${token.children[1].content}](${token.children[0].href})`; + }else { + text = token.content.trim(); + // text = text.replace(/\n/g,"
    "); + } + }else { + text = token.content.trim(); + // text = text.replace(/\n/g,"
    "); + } + revoke(); + current.v = `${current.v || ''}${text}`; + } else if (token.type === 'fence') { + // let result = md.renderer.render([token], md.options, {}); // Remarkable only adds className to `` but not `
    `, copy it to make PrismJS style work.
    +
    +        // const matches = result.match(//);
    +        // if (matches) result = result.replace('
    ', ``);
    +        let result = '```'+ token.params + '\n';
    +        result = result + token.content;
    +        result = result+'```\n';
    +
    +        current.c.push({
    +          t: token.type,
    +          d: depth + 1,
    +          v: result,
    +          c: []
    +        });
    +      } else ;
    +    }
    +
    +    return root;
    +  }
    +
    +  transform(content) {
    +    var _root$c;
    +
    +    const features = {};
    +    this.hooks.parser.call(this.md, features);
    +    const tokens = this.md.parse(content || '', {});
    +    let root = this.buildTree(tokens);
    +    cleanNode(root);
    +    if (((_root$c = root.c) == null ? void 0 : _root$c.length) === 1) root = root.c[0];
    +    return {
    +      root,
    +      features
    +    };
    +  }
    +  /**
    +   * Get all assets from enabled plugins or filter them by plugin names as keys.
    +   */
    +
    +
    +  getAssets(keys) {
    +    var _keys;
    +
    +    const styles = [];
    +    const scripts = [];
    +    (_keys = keys) != null ? _keys : keys = Object.keys(this.assetsMap);
    +
    +    for (const assets of keys.map(key => this.assetsMap[key])) {
    +      if (assets) {
    +        if (assets.styles) styles.push(...assets.styles);
    +        if (assets.scripts) scripts.push(...assets.scripts);
    +      }
    +    }
    +
    +    return {
    +      styles,
    +      scripts
    +    };
    +  }
    +  /**
    +   * Get used assets by features object returned by `transform`.
    +   */
    +
    +
    +  getUsedAssets(features) {
    +    return this.getAssets(Object.keys(features).filter(key => features[key]));
    +  }
    +
    +}
    +
    +exports.Transformer = Transformer;
    +exports.builtInPlugins = plugins;
    +exports.fillTemplate = fillTemplate;
    +
    +})));
    +});
    +
    +var domtoimage = createCommonjsModule(function (module) {
    +(function (global) {
    +
    +    var util = newUtil();
    +    var inliner = newInliner();
    +    var fontFaces = newFontFaces();
    +    var images = newImages();
    +
    +    // Default impl options
    +    var defaultOptions = {
    +        // Default is to fail on error, no placeholder
    +        imagePlaceholder: undefined,
    +        // Default cache bust is false, it will use the cache
    +        cacheBust: false
    +    };
    +
    +    var domtoimage = {
    +        toSvg: toSvg,
    +        toPng: toPng,
    +        toJpeg: toJpeg,
    +        toBlob: toBlob,
    +        toPixelData: toPixelData,
    +        impl: {
    +            fontFaces: fontFaces,
    +            images: images,
    +            util: util,
    +            inliner: inliner,
    +            options: {}
    +        }
    +    };
    +
    +    module.exports = domtoimage;
    +
    +
    +    /**
    +     * @param {Node} node - The DOM Node object to render
    +     * @param {Object} options - Rendering options
    +     * @param {Function} options.filter - Should return true if passed node should be included in the output
    +     *          (excluding node means excluding it's children as well). Not called on the root node.
    +     * @param {String} options.bgcolor - color for the background, any valid CSS color value.
    +     * @param {Number} options.width - width to be applied to node before rendering.
    +     * @param {Number} options.height - height to be applied to node before rendering.
    +     * @param {Object} options.style - an object whose properties to be copied to node's style before rendering.
    +     * @param {Number} options.quality - a Number between 0 and 1 indicating image quality (applicable to JPEG only),
    +                defaults to 1.0.
    +     * @param {String} options.imagePlaceholder - dataURL to use as a placeholder for failed images, default behaviour is to fail fast on images we can't fetch
    +     * @param {Boolean} options.cacheBust - set to true to cache bust by appending the time to the request url
    +     * @return {Promise} - A promise that is fulfilled with a SVG image data URL
    +     * */
    +    function toSvg(node, options) {
    +        options = options || {};
    +        copyOptions(options);
    +        return Promise.resolve(node)
    +            .then(function (node) {
    +                return cloneNode(node, options.filter, true);
    +            })
    +            .then(embedFonts)
    +            .then(inlineImages)
    +            .then(applyOptions)
    +            .then(function (clone) {
    +                return makeSvgDataUri(clone,
    +                    options.width || util.width(node),
    +                    options.height || util.height(node)
    +                );
    +            });
    +
    +        function applyOptions(clone) {
    +            if (options.bgcolor) clone.style.backgroundColor = options.bgcolor;
    +
    +            if (options.width) clone.style.width = options.width + 'px';
    +            if (options.height) clone.style.height = options.height + 'px';
    +
    +            if (options.style)
    +                Object.keys(options.style).forEach(function (property) {
    +                    clone.style[property] = options.style[property];
    +                });
    +
    +            return clone;
    +        }
    +    }
    +
    +    /**
    +     * @param {Node} node - The DOM Node object to render
    +     * @param {Object} options - Rendering options, @see {@link toSvg}
    +     * @return {Promise} - A promise that is fulfilled with a Uint8Array containing RGBA pixel data.
    +     * */
    +    function toPixelData(node, options) {
    +        return draw(node, options || {})
    +            .then(function (canvas) {
    +                return canvas.getContext('2d').getImageData(
    +                    0,
    +                    0,
    +                    util.width(node),
    +                    util.height(node)
    +                ).data;
    +            });
    +    }
    +
    +    /**
    +     * @param {Node} node - The DOM Node object to render
    +     * @param {Object} options - Rendering options, @see {@link toSvg}
    +     * @return {Promise} - A promise that is fulfilled with a PNG image data URL
    +     * */
    +    function toPng(node, options) {
    +        return draw(node, options || {})
    +            .then(function (canvas) {
    +                return canvas.toDataURL();
    +            });
    +    }
    +
    +    /**
    +     * @param {Node} node - The DOM Node object to render
    +     * @param {Object} options - Rendering options, @see {@link toSvg}
    +     * @return {Promise} - A promise that is fulfilled with a JPEG image data URL
    +     * */
    +    function toJpeg(node, options) {
    +        options = options || {};
    +        return draw(node, options)
    +            .then(function (canvas) {
    +                return canvas.toDataURL('image/jpeg', options.quality || 1.0);
    +            });
    +    }
    +
    +    /**
    +     * @param {Node} node - The DOM Node object to render
    +     * @param {Object} options - Rendering options, @see {@link toSvg}
    +     * @return {Promise} - A promise that is fulfilled with a PNG image blob
    +     * */
    +    function toBlob(node, options) {
    +        return draw(node, options || {})
    +            .then(util.canvasToBlob);
    +    }
    +
    +    function copyOptions(options) {
    +        // Copy options to impl options for use in impl
    +        if(typeof(options.imagePlaceholder) === 'undefined') {
    +            domtoimage.impl.options.imagePlaceholder = defaultOptions.imagePlaceholder;
    +        } else {
    +            domtoimage.impl.options.imagePlaceholder = options.imagePlaceholder;
    +        }
    +
    +        if(typeof(options.cacheBust) === 'undefined') {
    +            domtoimage.impl.options.cacheBust = defaultOptions.cacheBust;
    +        } else {
    +            domtoimage.impl.options.cacheBust = options.cacheBust;
    +        }
    +    }
    +
    +    function draw(domNode, options) {
    +        return toSvg(domNode, options)
    +            .then(util.makeImage)
    +            .then(util.delay(100))
    +            .then(function (image) {
    +                var canvas = newCanvas(domNode);
    +                canvas.getContext('2d').drawImage(image, 0, 0);
    +                return canvas;
    +            });
    +
    +        function newCanvas(domNode) {
    +            var canvas = document.createElement('canvas');
    +            canvas.width = options.width || util.width(domNode);
    +            canvas.height = options.height || util.height(domNode);
    +
    +            if (options.bgcolor) {
    +                var ctx = canvas.getContext('2d');
    +                ctx.fillStyle = options.bgcolor;
    +                ctx.fillRect(0, 0, canvas.width, canvas.height);
    +            }
    +
    +            return canvas;
    +        }
    +    }
    +
    +    function cloneNode(node, filter, root) {
    +        if (!root && filter && !filter(node)) return Promise.resolve();
    +
    +        return Promise.resolve(node)
    +            .then(makeNodeCopy)
    +            .then(function (clone) {
    +                return cloneChildren(node, clone, filter);
    +            })
    +            .then(function (clone) {
    +                return processClone(node, clone);
    +            });
    +
    +        function makeNodeCopy(node) {
    +            if (node instanceof HTMLCanvasElement) return util.makeImage(node.toDataURL());
    +            return node.cloneNode(false);
    +        }
    +
    +        function cloneChildren(original, clone, filter) {
    +            var children = original.childNodes;
    +            if (children.length === 0) return Promise.resolve(clone);
    +
    +            return cloneChildrenInOrder(clone, util.asArray(children), filter)
    +                .then(function () {
    +                    return clone;
    +                });
    +
    +            function cloneChildrenInOrder(parent, children, filter) {
    +                var done = Promise.resolve();
    +                children.forEach(function (child) {
    +                    done = done
    +                        .then(function () {
    +                            return cloneNode(child, filter);
    +                        })
    +                        .then(function (childClone) {
    +                            if (childClone) parent.appendChild(childClone);
    +                        });
    +                });
    +                return done;
    +            }
    +        }
    +
    +        function processClone(original, clone) {
    +            if (!(clone instanceof Element)) return clone;
    +            if(!clone.style) return clone;
    +
    +            return Promise.resolve()
    +                .then(cloneStyle)
    +                .then(clonePseudoElements)
    +                .then(copyUserInput)
    +                .then(fixSvg)
    +                .then(function () {
    +                    return clone;
    +                });
    +
    +            function cloneStyle() {
    +
    +                copyStyle(window.getComputedStyle(original), clone.style);
    +
    +                function copyStyle(source, target) {
    +                    if (source.cssText) target.cssText = source.cssText;
    +                    else copyProperties(source, target);
    +
    +                    function copyProperties(source, target) {
    +                        if(target.setProperty){
    +                            util.asArray(source).forEach(function (name) {
    +                                if(!source) return;
    +                                target.setProperty(
    +                                    name,
    +                                    source.getPropertyValue(name),
    +                                    source.getPropertyPriority(name)
    +                                );
    +                            });
    +                        }
    +                    }
    +                }
    +            }
    +
    +            function clonePseudoElements() {
    +                [':before', ':after'].forEach(function (element) {
    +                    clonePseudoElement(element);
    +                });
    +
    +                function clonePseudoElement(element) {
    +                    var style = window.getComputedStyle(original, element);
    +                    var content = style.getPropertyValue('content');
    +
    +                    if (content === '' || content === 'none') return;
    +
    +                    var className = util.uid();
    +                    clone.className = clone.className + ' ' + className;
    +                    var styleElement = document.createElement('style');
    +                    styleElement.appendChild(formatPseudoElementStyle(className, element, style));
    +                    clone.appendChild(styleElement);
    +
    +                    function formatPseudoElementStyle(className, element, style) {
    +                        var selector = '.' + className + ':' + element;
    +                        var cssText = style.cssText ? formatCssText(style) : formatCssProperties(style);
    +                        return document.createTextNode(selector + '{' + cssText + '}');
    +
    +                        function formatCssText(style) {
    +                            var content = style.getPropertyValue('content');
    +                            return style.cssText + ' content: ' + content + ';';
    +                        }
    +
    +                        function formatCssProperties(style) {
    +
    +                            return util.asArray(style)
    +                                .map(formatProperty)
    +                                .join('; ') + ';';
    +
    +                            function formatProperty(name) {
    +                                return name + ': ' +
    +                                    style.getPropertyValue(name) +
    +                                    (style.getPropertyPriority(name) ? ' !important' : '');
    +                            }
    +                        }
    +                    }
    +                }
    +            }
    +
    +            function copyUserInput() {
    +                if (original instanceof HTMLTextAreaElement) clone.innerHTML = original.value;
    +                if (original instanceof HTMLInputElement) clone.setAttribute("value", original.value);
    +            }
    +
    +            function fixSvg() {
    +                if (!(clone instanceof SVGElement)) return;
    +                clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
    +
    +                if (!(clone instanceof SVGRectElement)) return;
    +                ['width', 'height'].forEach(function (attribute) {
    +                    var value = clone.getAttribute(attribute);
    +                    if (!value) return;
    +                     if(clone.style&&clone.style.setProperty){
    +                         clone.style.setProperty(attribute, value);
    +                     }
    +                });
    +            }
    +        }
    +    }
    +
    +    function embedFonts(node) {
    +        return fontFaces.resolveAll()
    +            .then(function (cssText) {
    +                var styleNode = document.createElement('style');
    +                node.appendChild(styleNode);
    +                styleNode.appendChild(document.createTextNode(cssText));
    +                return node;
    +            });
    +    }
    +
    +    function inlineImages(node) {
    +        return images.inlineAll(node)
    +            .then(function () {
    +                return node;
    +            });
    +    }
    +
    +    function makeSvgDataUri(node, width, height) {
    +        return Promise.resolve(node)
    +            .then(function (node) {
    +                node.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml');
    +                return new XMLSerializer().serializeToString(node);
    +            })
    +            .then(util.escapeXhtml)
    +            .then(function (xhtml) {
    +                return '' + xhtml + '';
    +            })
    +            .then(function (foreignObject) {
    +                return '' +
    +                    foreignObject + '';
    +            })
    +            .then(function (svg) {
    +                return 'data:image/svg+xml;charset=utf-8,' + svg;
    +            });
    +    }
    +
    +    function newUtil() {
    +        return {
    +            escape: escape,
    +            parseExtension: parseExtension,
    +            mimeType: mimeType,
    +            dataAsUrl: dataAsUrl,
    +            isDataUrl: isDataUrl,
    +            canvasToBlob: canvasToBlob,
    +            resolveUrl: resolveUrl,
    +            getAndEncode: getAndEncode,
    +            uid: uid(),
    +            delay: delay,
    +            asArray: asArray,
    +            escapeXhtml: escapeXhtml,
    +            makeImage: makeImage,
    +            width: width,
    +            height: height
    +        };
    +
    +        function mimes() {
    +            /*
    +             * Only WOFF and EOT mime types for fonts are 'real'
    +             * see http://www.iana.org/assignments/media-types/media-types.xhtml
    +             */
    +            var WOFF = 'application/font-woff';
    +            var JPEG = 'image/jpeg';
    +
    +            return {
    +                'woff': WOFF,
    +                'woff2': WOFF,
    +                'ttf': 'application/font-truetype',
    +                'eot': 'application/vnd.ms-fontobject',
    +                'png': 'image/png',
    +                'jpg': JPEG,
    +                'jpeg': JPEG,
    +                'gif': 'image/gif',
    +                'tiff': 'image/tiff',
    +                'svg': 'image/svg+xml'
    +            };
    +        }
    +
    +        function parseExtension(url) {
    +            var match = /\.([^\.\/]*?)$/g.exec(url);
    +            if (match) return match[1];
    +            else return '';
    +        }
    +
    +        function mimeType(url) {
    +            var extension = parseExtension(url).toLowerCase();
    +            return mimes()[extension] || '';
    +        }
    +
    +        function isDataUrl(url) {
    +            return url.search(/^(data:)/) !== -1;
    +        }
    +
    +        function toBlob(canvas) {
    +            return new Promise(function (resolve) {
    +                var binaryString = window.atob(canvas.toDataURL().split(',')[1]);
    +                var length = binaryString.length;
    +                var binaryArray = new Uint8Array(length);
    +
    +                for (var i = 0; i < length; i++)
    +                    binaryArray[i] = binaryString.charCodeAt(i);
    +
    +                resolve(new Blob([binaryArray], {
    +                    type: 'image/png'
    +                }));
    +            });
    +        }
    +
    +        function canvasToBlob(canvas) {
    +            if (canvas.toBlob)
    +                return new Promise(function (resolve) {
    +                    canvas.toBlob(resolve);
    +                });
    +
    +            return toBlob(canvas);
    +        }
    +
    +        function resolveUrl(url, baseUrl) {
    +            var doc = document.implementation.createHTMLDocument();
    +            var base = doc.createElement('base');
    +            doc.head.appendChild(base);
    +            var a = doc.createElement('a');
    +            doc.body.appendChild(a);
    +            base.href = baseUrl;
    +            a.href = url;
    +            return a.href;
    +        }
    +
    +        function uid() {
    +            var index = 0;
    +
    +            return function () {
    +                return 'u' + fourRandomChars() + index++;
    +
    +                function fourRandomChars() {
    +                    /* see http://stackoverflow.com/a/6248722/2519373 */
    +                    return ('0000' + (Math.random() * Math.pow(36, 4) << 0).toString(36)).slice(-4);
    +                }
    +            };
    +        }
    +
    +        function makeImage(uri) {
    +            return new Promise(function (resolve, reject) {
    +                var image = new Image();
    +                image.onload = function () {
    +                    resolve(image);
    +                };
    +                image.onerror = reject;
    +                image.src = uri;
    +            });
    +        }
    +
    +        function getAndEncode(url) {
    +            var TIMEOUT = 30000;
    +            if(domtoimage.impl.options.cacheBust) {
    +                // Cache bypass so we dont have CORS issues with cached images
    +                // Source: https://developer.mozilla.org/en/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Bypassing_the_cache
    +                url += ((/\?/).test(url) ? "&" : "?") + (new Date()).getTime();
    +            }
    +
    +            return new Promise(function (resolve) {
    +                var request = new XMLHttpRequest();
    +
    +                request.onreadystatechange = done;
    +                request.ontimeout = timeout;
    +                request.responseType = 'blob';
    +                request.timeout = TIMEOUT;
    +                request.open('GET', url, true);
    +                request.send();
    +
    +                var placeholder;
    +                if(domtoimage.impl.options.imagePlaceholder) {
    +                    var split = domtoimage.impl.options.imagePlaceholder.split(/,/);
    +                    if(split && split[1]) {
    +                        placeholder = split[1];
    +                    }
    +                }
    +
    +                function done() {
    +                    if (request.readyState !== 4) return;
    +
    +                    if (request.status !== 200) {
    +                        if(placeholder) {
    +                            resolve(placeholder);
    +                        } else {
    +                            fail('cannot fetch resource: ' + url + ', status: ' + request.status);
    +                        }
    +
    +                        return;
    +                    }
    +
    +                    var encoder = new FileReader();
    +                    encoder.onloadend = function () {
    +                        var content = encoder.result.split(/,/)[1];
    +                        resolve(content);
    +                    };
    +                    encoder.readAsDataURL(request.response);
    +                }
    +
    +                function timeout() {
    +                    if(placeholder) {
    +                        resolve(placeholder);
    +                    } else {
    +                        fail('timeout of ' + TIMEOUT + 'ms occured while fetching resource: ' + url);
    +                    }
    +                }
    +
    +                function fail(message) {
    +                    console.error(message);
    +                    resolve('');
    +                }
    +            });
    +        }
    +
    +        function dataAsUrl(content, type) {
    +            return 'data:' + type + ';base64,' + content;
    +        }
    +
    +        function escape(string) {
    +            return string.replace(/([.*+?^${}()|\[\]\/\\])/g, '\\$1');
    +        }
    +
    +        function delay(ms) {
    +            return function (arg) {
    +                return new Promise(function (resolve) {
    +                    setTimeout(function () {
    +                        resolve(arg);
    +                    }, ms);
    +                });
    +            };
    +        }
    +
    +        function asArray(arrayLike) {
    +            var array = [];
    +            var length = arrayLike.length;
    +            for (var i = 0; i < length; i++) array.push(arrayLike[i]);
    +            return array;
    +        }
    +
    +        function escapeXhtml(string) {
    +            return string.replace(/#/g, '%23').replace(/\n/g, '%0A');
    +        }
    +
    +        function width(node) {
    +            var leftBorder = px(node, 'border-left-width');
    +            var rightBorder = px(node, 'border-right-width');
    +            return node.scrollWidth + leftBorder + rightBorder;
    +        }
    +
    +        function height(node) {
    +            var topBorder = px(node, 'border-top-width');
    +            var bottomBorder = px(node, 'border-bottom-width');
    +            return node.scrollHeight + topBorder + bottomBorder;
    +        }
    +
    +        function px(node, styleProperty) {
    +            var value = window.getComputedStyle(node).getPropertyValue(styleProperty);
    +            return parseFloat(value.replace('px', ''));
    +        }
    +    }
    +
    +    function newInliner() {
    +        var URL_REGEX = /url\(['"]?([^'"]+?)['"]?\)/g;
    +
    +        return {
    +            inlineAll: inlineAll,
    +            shouldProcess: shouldProcess,
    +            impl: {
    +                readUrls: readUrls,
    +                inline: inline
    +            }
    +        };
    +
    +        function shouldProcess(string) {
    +            return string.search(URL_REGEX) !== -1;
    +        }
    +
    +        function readUrls(string) {
    +            var result = [];
    +            var match;
    +            while ((match = URL_REGEX.exec(string)) !== null) {
    +                result.push(match[1]);
    +            }
    +            return result.filter(function (url) {
    +                return !util.isDataUrl(url);
    +            });
    +        }
    +
    +        function inline(string, url, baseUrl, get) {
    +            return Promise.resolve(url)
    +                .then(function (url) {
    +                    return baseUrl ? util.resolveUrl(url, baseUrl) : url;
    +                })
    +                .then(get || util.getAndEncode)
    +                .then(function (data) {
    +                    return util.dataAsUrl(data, util.mimeType(url));
    +                })
    +                .then(function (dataUrl) {
    +                    return string.replace(urlAsRegex(url), '$1' + dataUrl + '$3');
    +                });
    +
    +            function urlAsRegex(url) {
    +                return new RegExp('(url\\([\'"]?)(' + util.escape(url) + ')([\'"]?\\))', 'g');
    +            }
    +        }
    +
    +        function inlineAll(string, baseUrl, get) {
    +            if (nothingToInline()) return Promise.resolve(string);
    +
    +            return Promise.resolve(string)
    +                .then(readUrls)
    +                .then(function (urls) {
    +                    var done = Promise.resolve(string);
    +                    urls.forEach(function (url) {
    +                        done = done.then(function (string) {
    +                            return inline(string, url, baseUrl, get);
    +                        });
    +                    });
    +                    return done;
    +                });
    +
    +            function nothingToInline() {
    +                return !shouldProcess(string);
    +            }
    +        }
    +    }
    +
    +    function newFontFaces() {
    +        return {
    +            resolveAll: resolveAll,
    +            impl: {
    +                readAll: readAll
    +            }
    +        };
    +
    +        function resolveAll() {
    +            return readAll()
    +                .then(function (webFonts) {
    +                    return Promise.all(
    +                        webFonts.map(function (webFont) {
    +                            return webFont.resolve();
    +                        })
    +                    );
    +                })
    +                .then(function (cssStrings) {
    +                    return cssStrings.join('\n');
    +                });
    +        }
    +
    +        function readAll() {
    +            return Promise.resolve(util.asArray(document.styleSheets))
    +                .then(getCssRules)
    +                .then(selectWebFontRules)
    +                .then(function (rules) {
    +                    return rules.map(newWebFont);
    +                });
    +
    +            function selectWebFontRules(cssRules) {
    +                return cssRules
    +                    .filter(function (rule) {
    +                        return rule.type === CSSRule.FONT_FACE_RULE;
    +                    })
    +                    .filter(function (rule) {
    +                        return inliner.shouldProcess(rule.style.getPropertyValue('src'));
    +                    });
    +            }
    +
    +            function getCssRules(styleSheets) {
    +                var cssRules = [];
    +                styleSheets.forEach(function (sheet) {
    +                    try {
    +                        util.asArray(sheet.cssRules || []).forEach(cssRules.push.bind(cssRules));
    +                    } catch (e) {
    +                        console.log('Error while reading CSS rules from ' + sheet.href, e.toString());
    +                    }
    +                });
    +                return cssRules;
    +            }
    +
    +            function newWebFont(webFontRule) {
    +                return {
    +                    resolve: function resolve() {
    +                        var baseUrl = (webFontRule.parentStyleSheet || {}).href;
    +                        return inliner.inlineAll(webFontRule.cssText, baseUrl);
    +                    },
    +                    src: function () {
    +                        return webFontRule.style.getPropertyValue('src');
    +                    }
    +                };
    +            }
    +        }
    +    }
    +
    +    function newImages() {
    +        return {
    +            inlineAll: inlineAll,
    +            impl: {
    +                newImage: newImage
    +            }
    +        };
    +
    +        function newImage(element) {
    +            return {
    +                inline: inline
    +            };
    +
    +            function inline(get) {
    +                if (util.isDataUrl(element.src)) return Promise.resolve();
    +
    +                return Promise.resolve(element.src)
    +                    .then(get || util.getAndEncode)
    +                    .then(function (data) {
    +                        return util.dataAsUrl(data, util.mimeType(element.src));
    +                    })
    +                    .then(function (dataUrl) {
    +                        return new Promise(function (resolve, reject) {
    +                            element.onload = resolve;
    +                            element.onerror = reject;
    +                            element.src = dataUrl;
    +                        });
    +                    });
    +            }
    +        }
    +
    +        function inlineAll(node) {
    +            if (!(node instanceof Element)) return Promise.resolve(node);
    +
    +            return inlineBackground(node)
    +                .then(function () {
    +                    if (node instanceof HTMLImageElement)
    +                        return newImage(node).inline();
    +                    else
    +                        return Promise.all(
    +                            util.asArray(node.childNodes).map(function (child) {
    +                                return inlineAll(child);
    +                            })
    +                        );
    +                });
    +
    +            function inlineBackground(node) {
    +                if(!node.style){
    +                    return Promise.resolve(node);
    +                }
    +                var background = node.style.getPropertyValue('background');
    +
    +                if (!background) return Promise.resolve(node);
    +
    +                return inliner.inlineAll(background)
    +                    .then(function (inlined) {
    +                        if(node.style&&node.style.setProperty){
    +                            node.style.setProperty(
    +                                'background',
    +                                inlined,
    +                                node.style.getPropertyPriority('background')
    +                            );
    +                        }
    +                    })
    +                    .then(function () {
    +                        return node;
    +                    });
    +            }
    +        }
    +    }
    +})();
    +});
    +
    +function uuid() {
    +    function S4() {
    +        return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
    +    }
    +    return (S4() + S4() + '-' + S4() + '-' + S4());
    +}
    +const transformer = new browser.Transformer();
    +const mindmapViewType = "mindmapView";
    +const mindmapIcon = "blocks";
    +class MindMapView extends obsidian.TextFileView {
    +    getViewType() {
    +        return mindmapViewType;
    +    }
    +    getIcon() {
    +        return mindmapIcon;
    +    }
    +    getDisplayText() {
    +        var _a;
    +        return ((_a = this.file) === null || _a === void 0 ? void 0 : _a.basename) || "mindmap";
    +    }
    +    setColors() {
    +        var colors = [];
    +        try {
    +            if (this.plugin.settings.strokeArray) {
    +                colors = this.plugin.settings.strokeArray.split(',');
    +            }
    +        }
    +        catch (err) {
    +            console.log(err, 'stroke array is error');
    +        }
    +        this.colors = this.colors.concat(colors);
    +        for (var i = 0; i < 50; i++) {
    +            this.colors.push(randomColor());
    +        }
    +    }
    +    exportToSvg() {
    +        if (!this.mindmap) {
    +            return;
    +        }
    +        // this.mindmap.contentEL.style.visibility='hidden';
    +        var nodes = [];
    +        this.mindmap.traverseDF((n) => {
    +            if (n.isShow()) {
    +                nodes.push(n);
    +            }
    +        });
    +        var oldScrollLeft = this.mindmap.containerEL.scrollLeft;
    +        var oldScrollTop = this.mindmap.containerEL.scrollTop;
    +        var box = this.mindmap.getBoundingRect(nodes);
    +        var rootBox = this.mindmap.root.getPosition();
    +        var disX = 0, disY = 0;
    +        if (box.x > 60) {
    +            disX = box.x - 60;
    +        }
    +        if (box.y > 60) {
    +            disY = box.y - 60;
    +        }
    +        this.mindmap.root.setPosition(rootBox.x - disX, rootBox.y - disY);
    +        this.mindmap.refresh();
    +        var w = box.width + 120;
    +        var h = box.height + 120;
    +        this.mindmap.contentEL.style.width = w + 'px';
    +        this.mindmap.contentEL.style.height = h + 'px';
    +        setTimeout(() => {
    +            domtoimage.toPng(this.mindmap.contentEL).then(dataUrl => {
    +                var img = new Image();
    +                img.src = dataUrl;
    +                var str = img.outerHTML;
    +                var p = this.mindmap.path.substr(0, this.mindmap.path.length - 2);
    +                try {
    +                    new obsidian.Notice(p + 'html');
    +                    this.app.vault.adapter.write(p + 'html', str);
    +                    this.restoreMindmap(rootBox, oldScrollLeft, oldScrollTop);
    +                }
    +                catch (err) {
    +                    this.restoreMindmap(rootBox, oldScrollLeft, oldScrollTop);
    +                    new obsidian.Notice(err);
    +                }
    +            }).catch(err => {
    +                this.restoreMindmap(rootBox, oldScrollLeft, oldScrollTop);
    +                new obsidian.Notice(err);
    +            });
    +        }, 200);
    +    }
    +    restoreMindmap(rootBox, left, top) {
    +        if (this.mindmap) {
    +            var size = this.plugin.settings.canvasSize;
    +            this.mindmap.contentEL.style.width = size + 'px';
    +            this.mindmap.contentEL.style.height = size + 'px';
    +            this.mindmap.containerEL.scrollTop = top;
    +            this.mindmap.containerEL.scrollLeft = left;
    +            this.mindmap.root.setPosition(rootBox.x, rootBox.y);
    +            this.mindmap.refresh();
    +            //   this.mindmap.contentEL.style.visibility='visible';
    +        }
    +    }
    +    mindMapChange() {
    +        if (this.mindmap) {
    +            var md = this.mindmap.getMarkdown();
    +            //  var matchArray: string[] = []
    +            // var collapsedIds: string[] = []
    +            // const idRegexMultiline = /.+ \^([a-z0-9\-]+)$/gim
    +            // while ((matchArray = idRegexMultiline.exec(md)) != null) {
    +            //   collapsedIds = [...collapsedIds, ...matchArray.slice(1, 2)];
    +            // }
    +            // this.fileCache.frontmatter.collapsedIds='';
    +            // if (collapsedIds.length > 0) {
    +            //   this.fileCache.frontmatter.collapsedIds = collapsedIds;
    +            // }
    +            //var frontMatter = this.getFrontMatter();
    +            this.data = this.yamlString + md;
    +            // console.log(this.mindmap.path);
    +            // this.app.vault.adapter.write(this.mindmap.path, this.data);
    +            try {
    +                this.requestSave();
    +                //new Notice(`${t("Save success")}`);
    +            }
    +            catch (err) {
    +                console.log(err);
    +                new obsidian.Notice(`${t("Save fail")}`);
    +            }
    +        }
    +    }
    +    getFrontMatter() {
    +        var frontMatter = '---\n\n';
    +        //  var v: any = '';
    +        if (this.fileCache.frontmatter) {
    +            // for (var k in this.fileCache.frontmatter) {
    +            //   if (k != 'position') {
    +            //     if (Object.prototype.toString.call(this.fileCache.frontmatter[k]) == '[object Array]' || Object.prototype.toString.call(this.fileCache.frontmatter[k]) == '[object Object]') {
    +            //       v = JSON.stringify(this.fileCache.frontmatter[k]);
    +            //     } else if (Object.prototype.toString.call(this.fileCache.frontmatter[k]) == '[object Number]' || Object.prototype.toString.call(this.fileCache.frontmatter[k]) == "[object String]") {
    +            //       v = this.fileCache.frontmatter[k];
    +            //     }
    +            //     if (v) {
    +            //       frontMatter += `${k}: ${v}\n`;
    +            //     }
    +            //   }
    +            // }
    +            var position = this.fileCache.frontmatterPosition;
    +            var end = position['end'].offset;
    +            frontMatter = this.data.substr(0, end);
    +        }
    +        frontMatter += '\n\n';
    +        //frontMatter += `\n---\n\n`;
    +        return frontMatter;
    +    }
    +    constructor(leaf, plugin) {
    +        super(leaf);
    +        this.id = this.leaf.id;
    +        this.colors = [];
    +        this.timeOut = null;
    +        this.firstInit = true;
    +        this.yamlString = '';
    +        this.plugin = plugin;
    +        this.setColors();
    +        this.fileCache = {
    +            'frontmatter': {
    +                'mindmap-plugin': 'basic'
    +            }
    +        };
    +    }
    +    onClose() {
    +        return __awaiter(this, void 0, void 0, function* () {
    +            // Remove draggables from render, as the DOM has already detached
    +            //this.plugin.removeView(this);
    +            if (this.mindmap) {
    +                this.mindmap.clear();
    +                this.contentEl.innerHTML = '';
    +                this.mindmap = null;
    +            }
    +        });
    +    }
    +    clear() {
    +    }
    +    getViewData() {
    +        return this.data;
    +    }
    +    setViewData(data) {
    +        if (this.mindmap) {
    +            this.mindmap.clear();
    +            this.contentEl.innerHTML = '';
    +        }
    +        this.data = data;
    +        var mdText = this.getMdText(this.data);
    +        var mindData = this.mdToData(mdText);
    +        mindData.isRoot = true;
    +        // const frontmatterContentRegExResult = /^---$(.+?)^---$.+?/mis.exec(data)
    +        // if (frontmatterContentRegExResult != null && frontmatterContentRegExResult[1]) {
    +        //   frontmatterContentRegExResult[1].split('\n').forEach((frontmatterLine) => {
    +        //     const keyValue = frontmatterLine.split(': ')
    +        //     if (keyValue.length === 2) {
    +        //       const value = /^[{\[].+[}\]]$/.test(keyValue[1]) ? JSON.parse(keyValue[1]) : keyValue[1]
    +        //       this.fileCache.frontmatter[keyValue[0]] = value
    +        //     }
    +        //   });
    +        // }
    +        this.mindmap = new MindMap(mindData, this.contentEl, this.plugin.settings);
    +        this.mindmap.colors = this.colors;
    +        if (this.firstInit) {
    +            setTimeout(() => {
    +                var leaf = this.leaf;
    +                if (leaf) {
    +                    var view = leaf.view;
    +                    this.mindmap.path = view === null || view === void 0 ? void 0 : view.file.path;
    +                    if (view.file) {
    +                        this.fileCache = this.app.metadataCache.getFileCache(view.file);
    +                        this.yamlString = this.getFrontMatter();
    +                    }
    +                }
    +                this.mindmap.init();
    +                this.mindmap.refresh();
    +                this.mindmap.view = this;
    +                this.firstInit = false;
    +            }, 100);
    +        }
    +        else {
    +            var view = this.leaf.view;
    +            this.fileCache = this.app.metadataCache.getFileCache(view.file);
    +            this.yamlString = this.getFrontMatter();
    +            this.mindmap.path = view === null || view === void 0 ? void 0 : view.file.path;
    +            this.mindmap.init();
    +            this.mindmap.refresh();
    +            this.mindmap.view = this;
    +        }
    +    }
    +    onunload() {
    +        this.app.workspace.offref("quick-preview");
    +        this.app.workspace.offref("resize");
    +        if (this.mindmap) {
    +            this.mindmap.clear();
    +            this.contentEl.innerHTML = '';
    +            this.mindmap = null;
    +        }
    +        this.plugin.setMarkdownView(this.leaf);
    +    }
    +    onload() {
    +        super.onload();
    +        this.registerEvent(this.app.workspace.on("quick-preview", () => this.onQuickPreview, this));
    +        this.registerEvent(this.app.workspace.on('resize', () => this.updateMindMap(), this));
    +    }
    +    onQuickPreview(file, data) {
    +        if (file === this.file && data !== this.data) {
    +            this.setViewData(data);
    +            this.fileCache = this.app.metadataCache.getFileCache(file);
    +        }
    +    }
    +    updateMindMap() {
    +        if (this.mindmap) {
    +            if (obsidian.Platform.isDesktopApp) {
    +                this.mindmap.center();
    +            }
    +        }
    +    }
    +    onFileMetadataChange(file) {
    +        return __awaiter(this, void 0, void 0, function* () {
    +            var path = file.path;
    +            let md = yield this.app.vault.adapter.read(path);
    +            this.onQuickPreview(file, md);
    +        });
    +    }
    +    getMdText(str) {
    +        var md = str.trim().replace(FRONT_MATTER_REGEX, '');
    +        return md.trim();
    +    }
    +    mdToData(str) {
    +        var _a;
    +        function transformData(mapData) {
    +            var flag = true;
    +            if (mapData.t == 'blockquote') {
    +                mapData = mapData.c[0];
    +                flag = false;
    +                mapData.v = '> ' + mapData.v;
    +            }
    +            const regexResult = /^.+ \^([a-z0-9\-]+)$/gim.exec(mapData.v);
    +            const id = regexResult != null ? regexResult[1] : null;
    +            // console.log(id);
    +            var map = {
    +                id: id || uuid(),
    +                text: id ? mapData.v.replace(` ^${id}`, '') : mapData.v,
    +                children: [],
    +                expanded: id ? false : true
    +            };
    +            if (flag && mapData.c && mapData.c.length) {
    +                mapData.c.forEach((data) => {
    +                    map.children.push(transformData(data));
    +                });
    +            }
    +            return map;
    +        }
    +        if (str) {
    +            const { root } = transformer.transform(str);
    +            const data = transformData(root);
    +            return data;
    +        }
    +        else {
    +            return {
    +                id: uuid(),
    +                text: ((_a = this.app.workspace.getActiveFile()) === null || _a === void 0 ? void 0 : _a.basename) || `${t('Untitled mindmap')}`
    +            };
    +        }
    +    }
    +    onMoreOptionsMenu(menu) {
    +        // Add a menu item to force the board to markdown view
    +        menu
    +            .addItem((item) => {
    +            item
    +                .setTitle(`${t("Open as markdown")}`)
    +                .setIcon("document")
    +                .onClick(() => {
    +                this.plugin.mindmapFileModes[this.id || this.file.path] = "markdown";
    +                this.plugin.setMarkdownView(this.leaf);
    +            });
    +        });
    +        // .addItem((item)=>{
    +        //    item
    +        //    .setTitle(`${t("Export to opml")}`)
    +        //    .setIcon('image-file')
    +        //    .onClick(()=>{
    +        //       const targetFolder = this.plugin.app.fileManager.getNewFileParent(
    +        //        this.plugin.app.workspace.getActiveFile()?.path || ""
    +        //       );
    +        //       if(targetFolder){
    +        //         console.log(targetFolder,this.plugin.app.fileManager);
    +        //       }
    +        //    })
    +        // })
    +        super.onMoreOptionsMenu(menu);
    +    }
    +}
    +
    +class MindMapSettingsTab extends obsidian.PluginSettingTab {
    +    constructor(app, plugin) {
    +        super(app, plugin);
    +        this.plugin = plugin;
    +    }
    +    display() {
    +        const { containerEl } = this;
    +        containerEl.empty();
    +        new obsidian.Setting(containerEl)
    +            .setName(`${t('Canvas size')}`)
    +            .setDesc(`${t('Canvas size desc')}`)
    +            .addDropdown(dropDown => dropDown
    +            .addOption('4000', '4000')
    +            .addOption('6000', '6000')
    +            .addOption('8000', '8000')
    +            .addOption('10000', '10000')
    +            .addOption('12000', '12000')
    +            .addOption('16000', '16000')
    +            .addOption('20000', '20000')
    +            .addOption('30000', '30000')
    +            .addOption('36000', '36000')
    +            .setValue(this.plugin.settings.canvasSize.toString() || '8000')
    +            .onChange((value) => {
    +            var _v = Number.parseInt(value);
    +            this.plugin.settings.canvasSize = _v;
    +            this.plugin.saveData(this.plugin.settings);
    +            const mindmapLeaves = this.app.workspace.getLeavesOfType(mindmapViewType);
    +            mindmapLeaves.forEach((leaf) => {
    +                var v = leaf.view;
    +                v.mindmap.setting.canvasSize = _v;
    +                v.mindmap.setAppSetting();
    +                var box = v.mindmap.root.getBox();
    +                v.mindmap.root.setPosition(_v / 2 - box.width / 2, _v / 2 - box.height / 2);
    +                v.mindmap.refresh();
    +                v.mindmap.center();
    +            });
    +        }));
    +        new obsidian.Setting(containerEl)
    +            .setName(`${t('Canvas background')}`)
    +            .setDesc(`${t('Canvas background desc')}`)
    +            .addText(text => text
    +            .setValue(this.plugin.settings.background || 'transparent')
    +            .setPlaceholder('Example: black|white|#ccc')
    +            .onChange((value) => {
    +            this.plugin.settings.background = value;
    +            this.plugin.saveData(this.plugin.settings);
    +            const mindmapLeaves = this.app.workspace.getLeavesOfType(mindmapViewType);
    +            mindmapLeaves.forEach((leaf) => {
    +                var v = leaf.view;
    +                v.mindmap.setting.background = this.plugin.settings.background;
    +                v.mindmap.setAppSetting();
    +            });
    +        }));
    +        new obsidian.Setting(containerEl)
    +            .setName(`${t('Max level of node to markdown head')}`)
    +            .setDesc(`${t('Max level of node to markdown head desc')}`)
    +            .addDropdown(dropDown => dropDown
    +            .addOption('0', '0')
    +            .addOption('1', '1')
    +            .addOption('2', '2')
    +            .addOption('3', '3')
    +            .addOption('4', '4')
    +            .addOption('5', '5')
    +            .addOption('6', '6')
    +            .setValue(this.plugin.settings.headLevel.toString() || '2')
    +            .onChange((value) => {
    +            this.plugin.settings.headLevel = Number.parseInt(value);
    +            this.plugin.saveData(this.plugin.settings);
    +            const mindmapLeaves = this.app.workspace.getLeavesOfType(mindmapViewType);
    +            mindmapLeaves.forEach((leaf) => {
    +                var v = leaf.view;
    +                v.mindmap.setting.headLevel = this.plugin.settings.headLevel;
    +            });
    +        }));
    +        new obsidian.Setting(containerEl)
    +            .setName(`${t('Font size')}`)
    +            .setDesc(`${t('Font size desc')}`)
    +            .addText(text => {
    +            var _a;
    +            return text
    +                .setValue(((_a = this.plugin.settings.fontSize) === null || _a === void 0 ? void 0 : _a.toString()) || '16')
    +                .setPlaceholder('Example: 16')
    +                .onChange((value) => {
    +                this.plugin.settings.fontSize = Number.parseInt(value);
    +                this.plugin.saveData(this.plugin.settings);
    +                const mindmapLeaves = this.app.workspace.getLeavesOfType(mindmapViewType);
    +                mindmapLeaves.forEach((leaf) => {
    +                    var v = leaf.view;
    +                    v.mindmap.setting.fontSize = this.plugin.settings.fontSize;
    +                    v.mindmap.setAppSetting();
    +                    v.mindmap.traverseBF((n) => {
    +                        n.boundingRect = null;
    +                        n.refreshBox();
    +                    });
    +                    v.mindmap.refresh();
    +                });
    +            });
    +        });
    +        new obsidian.Setting(containerEl)
    +            .setName(`${t('Mind map layout direct')}`)
    +            .setDesc(`${t('Mind map layout direct desc')}`)
    +            .addDropdown(dropDown => dropDown
    +            .addOption('mind map', t('Centered'))
    +            .addOption('right', t('Right'))
    +            .addOption('left', t('Left'))
    +            .addOption('clockwise', t('Clockwise'))
    +            .setValue(this.plugin.settings.layoutDirect.toString() || 'mind map')
    +            .onChange((value) => {
    +            this.plugin.settings.layoutDirect = value;
    +            this.plugin.saveData(this.plugin.settings);
    +            const mindmapLeaves = this.app.workspace.getLeavesOfType(mindmapViewType);
    +            mindmapLeaves.forEach((leaf) => {
    +                var v = leaf.view;
    +                v.mindmap.setting.layoutDirect = this.plugin.settings.layoutDirect;
    +                v.mindmap.refresh();
    +            });
    +        }));
    +        new obsidian.Setting(containerEl)
    +            .setName(`${t('Stroke Array')}`)
    +            .setDesc(`${t('Stroke Array Desc')}`)
    +            .addText(text => {
    +            var _a;
    +            return text
    +                .setValue(((_a = this.plugin.settings.strokeArray) === null || _a === void 0 ? void 0 : _a.toString()) || '')
    +                .setPlaceholder('Example: red,orange,blue ...')
    +                .onChange((value) => {
    +                this.plugin.settings.strokeArray = value;
    +                this.plugin.saveData(this.plugin.settings);
    +                const mindmapLeaves = this.app.workspace.getLeavesOfType(mindmapViewType);
    +                mindmapLeaves.forEach((leaf) => {
    +                    var v = leaf.view;
    +                    v.mindmap.setting.strokeArray = this.plugin.settings.strokeArray.split(',');
    +                    if (v.mindmap.mmLayout) {
    +                        v.mindmap.mmLayout.colors = v.mindmap.setting.strokeArray;
    +                    }
    +                    v.mindmap.traverseBF((n) => {
    +                        n.boundingRect = null;
    +                        n.refreshBox();
    +                    });
    +                    v.mindmap.refresh();
    +                });
    +            });
    +        });
    +    }
    +}
    +
    +class MindMapPlugin extends obsidian.Plugin {
    +    constructor() {
    +        super(...arguments);
    +        this.mindmapFileModes = {};
    +        this._loaded = false;
    +        this.timeOut = null;
    +    }
    +    onload() {
    +        return __awaiter(this, void 0, void 0, function* () {
    +            yield this.loadSettings();
    +            this.addCommand({
    +                id: 'Create New MindMap',
    +                name: `${t('Create new mindmap')}`,
    +                checkCallback: (checking) => {
    +                    var _a;
    +                    let leaf = this.app.workspace.activeLeaf;
    +                    if (leaf) {
    +                        if (!checking) {
    +                            const targetFolder = this.app.fileManager.getNewFileParent(((_a = this.app.workspace.getActiveFile()) === null || _a === void 0 ? void 0 : _a.path) || "");
    +                            if (targetFolder) {
    +                                this.newMindMap(targetFolder);
    +                            }
    +                        }
    +                        return true;
    +                    }
    +                    return false;
    +                }
    +            });
    +            this.addCommand({
    +                id: 'Toggle to markdown or mindmap',
    +                name: `${t('Toggle markdown/mindmap')}`,
    +                mobileOnly: false,
    +                callback: () => {
    +                    const mindmapView = this.app.workspace.getActiveViewOfType(MindMapView);
    +                    const markdownView = this.app.workspace.getActiveViewOfType(obsidian.MarkdownView);
    +                    if (mindmapView != null) {
    +                        this.mindmapFileModes[mindmapView.leaf.id || mindmapView.file.path] = 'markdown';
    +                        this.setMarkdownView(mindmapView.leaf);
    +                    }
    +                    else if (markdownView != null) {
    +                        this.mindmapFileModes[markdownView.leaf.id || markdownView.file.path] = mindmapViewType;
    +                        this.setMarkdownView(markdownView.leaf);
    +                    }
    +                }
    +            });
    +            this.addCommand({
    +                id: 'Copy Node',
    +                name: `${t('Copy node')}`,
    +                callback: () => {
    +                    const mindmapView = this.app.workspace.getActiveViewOfType(MindMapView);
    +                    if (mindmapView) {
    +                        var mindmap = mindmapView.mindmap;
    +                        navigator.clipboard.writeText('');
    +                        var node = mindmap.selectNode;
    +                        if (node) {
    +                            var text = mindmap.copyNode(node);
    +                            navigator.clipboard.writeText(text);
    +                        }
    +                    }
    +                }
    +            });
    +            this.addCommand({
    +                id: 'Paste Node',
    +                name: `${t('Paste node')}`,
    +                callback: () => {
    +                    const mindmapView = this.app.workspace.getActiveViewOfType(MindMapView);
    +                    if (mindmapView) {
    +                        var mindmap = mindmapView.mindmap;
    +                        navigator.clipboard.readText().then(text => {
    +                            mindmap.pasteNode(text);
    +                        });
    +                    }
    +                }
    +            });
    +            this.addCommand({
    +                id: 'Export to html',
    +                name: `${t('Export to html')}`,
    +                callback: () => {
    +                    const mindmapView = this.app.workspace.getActiveViewOfType(MindMapView);
    +                    if (mindmapView) {
    +                        mindmapView.exportToSvg();
    +                    }
    +                }
    +            });
    +            this.registerView(mindmapViewType, (leaf) => new MindMapView(leaf, this));
    +            this.registerEvents();
    +            this.registerMonkeyAround();
    +            this.addSettingTab(new MindMapSettingsTab(this.app, this));
    +        });
    +    }
    +    onunload() {
    +        this.app.workspace.detachLeavesOfType(mindmapViewType);
    +        //this.app.workspace.unregisterHoverLinkSource(frontMatterKey);
    +    }
    +    newMindMap(folder) {
    +        var _a;
    +        return __awaiter(this, void 0, void 0, function* () {
    +            const targetFolder = folder
    +                ? folder
    +                : this.app.fileManager.getNewFileParent(((_a = this.app.workspace.getActiveFile()) === null || _a === void 0 ? void 0 : _a.path) || "");
    +            try {
    +                // @ts-ignore
    +                const mindmap = yield this.app.fileManager.createNewMarkdownFile(targetFolder, `${t('Untitled mindmap')}`);
    +                yield this.app.vault.modify(mindmap, basicFrontmatter);
    +                setTimeout(() => __awaiter(this, void 0, void 0, function* () {
    +                    yield this.app.workspace.getLeaf().setViewState({
    +                        type: mindmapViewType,
    +                        state: { file: mindmap.path },
    +                    });
    +                }), 100);
    +            }
    +            catch (e) {
    +                console.error("Error creating mindmap board:", e);
    +            }
    +        });
    +    }
    +    loadSettings() {
    +        return __awaiter(this, void 0, void 0, function* () {
    +            this.settings = Object.assign({
    +                canvasSize: 8000,
    +                headLevel: 2,
    +                fontSize: 16,
    +                background: 'transparent',
    +                layout: 'mindmap',
    +                layoutDirect: 'mindmap'
    +            }, yield this.loadData());
    +        });
    +    }
    +    saveSettings() {
    +        return __awaiter(this, void 0, void 0, function* () {
    +            yield this.saveData(this.settings);
    +        });
    +    }
    +    setMarkdownView(leaf) {
    +        return __awaiter(this, void 0, void 0, function* () {
    +            yield leaf.setViewState({
    +                type: "markdown",
    +                state: leaf.view.getState(),
    +                popstate: true,
    +            }, { focus: true });
    +        });
    +    }
    +    setMindMapView(leaf) {
    +        return __awaiter(this, void 0, void 0, function* () {
    +            yield leaf.setViewState({
    +                type: mindmapViewType,
    +                state: leaf.view.getState(),
    +                popstate: true,
    +            });
    +        });
    +    }
    +    registerEvents() {
    +        this.registerEvent(this.app.workspace.on("file-menu", (menu, file, source, leaf) => {
    +            // Add a menu item to the folder context menu to create a board
    +            if (file instanceof obsidian.TFolder) {
    +                menu.addItem((item) => {
    +                    item
    +                        .setTitle(`${t('New mindmap board')}`)
    +                        .setIcon('document')
    +                        .onClick(() => this.newMindMap(file));
    +                });
    +            }
    +            //add markdown view menu  open as mind map view
    +            if (leaf && this.mindmapFileModes[leaf.id || file.path] == 'markdown') {
    +                const cache = this.app.metadataCache.getFileCache(file);
    +                if ((cache === null || cache === void 0 ? void 0 : cache.frontmatter) && cache.frontmatter[frontMatterKey]) {
    +                    menu.addItem((item) => {
    +                        item
    +                            .setTitle(`${t('Open as mindmap board')}`)
    +                            .setIcon("document")
    +                            .onClick(() => {
    +                            this.mindmapFileModes[leaf.id || file.path] = mindmapViewType;
    +                            this.setMindMapView(leaf);
    +                        });
    +                    }).addSeparator();
    +                }
    +            }
    +        }));
    +        this.registerEvent(this.app.metadataCache.on("changed", (file) => {
    +            this.app.workspace.getLeavesOfType(mindmapViewType).forEach((leaf) => {
    +                const view = leaf.view;
    +                view.onFileMetadataChange(file);
    +            });
    +        }));
    +        // @ts-ignore
    +        // this.app.workspace.registerHoverLinkSource(frontMatterKey, {
    +        //   display: mindmapViewType,
    +        //   defaultMod: true,
    +        // });
    +    }
    +    registerMonkeyAround() {
    +        const self = this;
    +        this.register(around(obsidian.WorkspaceLeaf.prototype, {
    +            // Kanbans can be viewed as markdown or kanban, and we keep track of the mode
    +            // while the file is open. When the file closes, we no longer need to keep track of it.
    +            detach(next) {
    +                return function () {
    +                    var _a;
    +                    const state = (_a = this.view) === null || _a === void 0 ? void 0 : _a.getState();
    +                    if ((state === null || state === void 0 ? void 0 : state.file) && self.mindmapFileModes[this.id || state.file]) {
    +                        delete self.mindmapFileModes[this.id || state.file];
    +                    }
    +                    return next.apply(this);
    +                };
    +            },
    +            setViewState(next) {
    +                return function (state, ...rest) {
    +                    var _a;
    +                    // new Notice( state.type);
    +                    if (self._loaded &&
    +                        state.type === "markdown" &&
    +                        ((_a = state.state) === null || _a === void 0 ? void 0 : _a.file) &&
    +                        // And the current mode of the file is not set to markdown
    +                        self.mindmapFileModes[this.id || state.state.file] !== "markdown") {
    +                        // Then check for the kanban frontMatterKey
    +                        const cache = self.app.metadataCache.getCache(state.state.file);
    +                        //   new Notice(cache.frontmatter[frontMatterKey]);
    +                        if ((cache === null || cache === void 0 ? void 0 : cache.frontmatter) && cache.frontmatter[frontMatterKey]) {
    +                            // If we have it, force the view type to kanban
    +                            const newState = Object.assign(Object.assign({}, state), { type: mindmapViewType });
    +                            self.mindmapFileModes[state.state.file] = mindmapViewType;
    +                            return next.apply(this, [newState, ...rest]);
    +                        }
    +                    }
    +                    return next.apply(this, [state, ...rest]);
    +                };
    +            },
    +        }));
    +        // this.register(
    +        //   around(MarkdownView.prototype, {
    +        //     onMoreOptionsMenu(next) {
    +        //       return function (menu: Menu) {
    +        //         const file = this.file;
    +        //         const cache = file
    +        //           ? self.app.metadataCache.getFileCache(file)
    +        //           : null;
    +        //         if (
    +        //           !file ||
    +        //           !cache?.frontmatter ||
    +        //           !cache.frontmatter[frontMatterKey]
    +        //         ) {
    +        //           return next.call(this, menu);
    +        //         }
    +        //         menu
    +        //           .addItem((item) => {
    +        //             item
    +        //               .setTitle(`${t('Open as mindmap board')}`)
    +        //               .setIcon("document")
    +        //               .onClick(() => {
    +        //                 self.mindmapFileModes[this.leaf.id || file.path] =
    +        //                   mindmapViewType;
    +        //                 self.setMindMapView(this.leaf);
    +        //               });
    +        //           })
    +        //           .addSeparator();
    +        //         next.call(this, menu);
    +        //       };
    +        //     },
    +        //   })
    +        // );
    +    }
    +}
    +
    +module.exports = MindMapPlugin;
    +
    +/* nosourcemap */
    \ No newline at end of file
    diff --git a/.obsidian/plugins/obsidian-enhancing-mindmap/styles.css b/.obsidian/plugins/obsidian-enhancing-mindmap/styles.css
    new file mode 100644
    index 00000000..927517ab
    --- /dev/null
    +++ b/.obsidian/plugins/obsidian-enhancing-mindmap/styles.css
    @@ -0,0 +1,175 @@
    +.mm-mindmap {
    +  color: #666;
    +  font-size: 16px;
    +  width: 8000px;
    +  height: 8000px;
    +}
    +
    +.theme-dark .mm-node {
    +  color: #f5f5f5;
    +}
    +
    +.theme-dark .mm-node.mm-root {
    +  color: #333;
    +}
    +
    +.theme-light .mm-node.mm-root .mm-node-content {
    +  color: #fff;
    +  background-color: rgb(0, 170, 255);
    +}
    +
    +.mm-node {
    +  position: absolute;
    +  cursor: pointer;
    +  box-sizing: border-box;
    +}
    +.mm-node .mm-node-content {
    +  padding: 2px 4px;
    +  max-width: 800px;
    +  word-break: break-word;
    +}
    +
    +.mm-node-content > p,
    +.mm-node-content > h1,
    +.mm-node-content > h2,
    +.mm-node-content > h3,
    +.mm-node-content > h4,
    +.mm-node-content > h5,
    +.mm-node-content > h6 {
    +  padding: 0;
    +  margin: 0;
    +}
    +
    +.mm-node.mm-root .mm-node-content {
    +  font-size: 1.6em;
    +  padding: 14px 20px;
    +  border-radius: 0.25rem;
    +  background: white;
    +}
    +
    +.mm-node.mm-node-second .mm-node-content {
    +  padding: 8px 10px;
    +  font-size: 1.2em;
    +}
    +
    +.mm-node.mm-node-select {
    +  border: 2px solid var(--interactive-accent);
    +  border-radius: 0.25rem;
    +}
    +
    +.mm-node-bar {
    +  position: absolute;
    +  height: 12px;
    +  width: 12px;
    +  border-radius: 50%;
    +  padding: 0 3px;
    +  bottom: -8px;
    +  box-sizing: border-box;
    +}
    +
    +.mm-node-bar:hover {
    +  transform: scale(1.2);
    +}
    +
    +.mm-node .mm-node-bar {}
    +
    +.mm-node-right .mm-node-bar,
    +.mm-node.mm-root .mm-node-bar {
    +  right: -12px;
    +}
    +
    +.mm-root .mm-node-bar,
    +.mm-node-second .mm-node-bar {
    +  top: 50%;
    +  margin-top: -5px;
    +  bottom: inherit;
    +}
    +
    +.mm-node-left .mm-node-bar {
    +  left: -12px;
    +}
    +
    +.mm-root .mm-node-bar,
    +.mm-node-leaf .mm-node-bar {
    +  display: none;
    +}
    +
    +.mm-node-collapse .mm-node-bar {
    +  display: block !important;
    +  box-sizing: border-box;
    +  border: 0px;
    +  background-color: #fff !important;
    +  border: 2px solid #ccc;
    +}
    +
    +/* node indicate */
    +.mm-node-layout-indicate {
    +  position: absolute;
    +  left:0;
    +  top:0;
    +  width: 0;
    +  height: 0;
    +  border: 20px solid transparent;
    +  border-bottom: 40px solid  var(--interactive-accent);
    +  transform-origin: center center;
    +  z-index:100;
    +}
    +
    +.mm-node-layout-indicate.mm-arrow-left{
    +    transform: rotate(-90deg)
    +} 
    +.mm-node-layout-indicate.mm-arrow-down{
    +  transform: rotate(180deg)
    +} 
    +.mm-node-layout-indicate.mm-arrow-right{
    +  transform: rotate(-270deg)
    +} 
    +
    +
    +/* edit node style */
    +.mm-node.mm-edit-node{
    +    z-index:50;
    +}
    +.mm-node.mm-edit-node .mm-node-content{
    +    background-color: #333;
    +    color:white;
    +}
    +
    +.theme-light .mm-node.mm-edit-node .mm-node-content{
    +  background-color: white;
    +  color:#333
    +}
    +
    +/* menu */
    +.mm-node-menu{
    +  position:absolute;
    +  background:#333;
    +  border:1px solid #000;
    +  width:56px;
    +  border-radius: 0.25rem;
    +  z-index: 60;
    +}
    +
    +.theme-light .mm-node-menu{
    +    background:white;
    +    border:1px solid #666;
    +}
    +
    +.mm-node-menu span {
    +  vertical-align: middle;
    +  cursor: pointer;
    +  margin:0 5px
    +}
    +
    +.mm-node-menu span svg{
    +   fill:#ccc;
    +}
    +
    +.mm-node-menu span svg:hover{
    +  opacity: 0.8;
    +}
    +
    +.theme-light .mm-node-menu svg{
    +  fill: #333;
    +}
    +
    diff --git a/.obsidian/plugins/rss-reader/data.json b/.obsidian/plugins/rss-reader/data.json
    index 185fb071..ca10ce79 100644
    --- a/.obsidian/plugins/rss-reader/data.json
    +++ b/.obsidian/plugins/rss-reader/data.json
    @@ -2027,6 +2027,446 @@
           "image": null,
           "description": "People's Daily Online",
           "items": [
    +        {
    +          "title": "瞎扯 · 如何正确地吐槽",
    +          "description": "
    \"头图\"/
    \n\n
    \n
    @小队长 :你这话说的,就算全世界国家都提倡吃屎健康,老子也不会吃。
    \n
    \n\n
    \n
    @定序王子 :很明显的造谣,编都不会编,非常低级的那种。 8 亿只是什么概念? 2023 年 ,广东常住人口达 1.27 亿人,算下来平均一人一年只吃 6 只鸡。 改成 300 亿就比较可信了。
    \n
    \n\n
    \n
    @宠物王大拿 :被公司辞退了!! 越想越气!! 然后跑到公司楼下把公司里的小流浪猫打劫走了!!!
    \n
    \n\n
    \n
    @Anjicn :儿子第一次喝可乐,说:就像蜘蛛在舌头上织网。
    \n
    \n\n
    \n
    @摸摸狗头 :有个大胆的想法:每年把珠穆朗玛峰加高 4cm ,是不是就相当于月球就没离开?
    \n
    \n
    \n
    \n\n\n
    \n\n\n\n

    ", + "content": "
    \"头图\"/
    \n\n
    \n
    @小队长 :你这话说的,就算全世界国家都提倡吃屎健康,老子也不会吃。
    \n
    \n\n
    \n
    @定序王子 :很明显的造谣,编都不会编,非常低级的那种。 8 亿只是什么概念? 2023 年 ,广东常住人口达 1.27 亿人,算下来平均一人一年只吃 6 只鸡。 改成 300 亿就比较可信了。
    \n
    \n\n
    \n
    @宠物王大拿 :被公司辞退了!! 越想越气!! 然后跑到公司楼下把公司里的小流浪猫打劫走了!!!
    \n
    \n\n
    \n
    @Anjicn :儿子第一次喝可乐,说:就像蜘蛛在舌头上织网。
    \n
    \n\n
    \n
    @摸摸狗头 :有个大胆的想法:每年把珠穆朗玛峰加高 4cm ,是不是就相当于月球就没离开?
    \n
    \n
    \n
    \n\n\n
    \n\n\n\n

    ", + "category": "", + "link": "https://daily.zhihu.com/story/9782605", + "creator": "", + "pubDate": "Wed, 16 Jul 2025 08:27:03 +0800", + "enclosure": "", + "enclosureType": "", + "image": "", + "id": "", + "language": "", + "folder": "资讯", + "feed": "知乎", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "d3be966f2c44fa32e43c0e624aff446d", + "highlights": [] + }, + { + "title": "非洲有 15 万只鸵鸟,为什么狮子不去吃鸵鸟?", + "description": "
    \"头图\"/
    \n

    看下图,一头狮子被迫逃到树上瑟瑟发抖,底下有什么可怕的动物?

    \n
    \"\"
    \n

    答案是非洲野牛。

    \n

    这一幕发生在肯尼亚马赛马拉国家公园,由挪威摄影师 Olav Thokle 拍摄,经过数小时的对峙,野牛才慢慢离去,本就爬树本领普通的狮子,为了牢牢固定身躯,爪子都疼的厉害。

    \n

    在我们的印象中,非洲野牛是狮子的猎物,虽然它们是食草动物,但战斗力却不容小觑,一头成年非洲野牛体长约为 2.1~3.4 米,平均身高在 1.5 米左右,体重 500~800 公斤。

    \n
    \"\"
    \n

    最致命的莫过于那一对锋利且坚硬的牛角,每年都有大量狮子在捕猎野牛时,被锋利的牛角划伤或刺穿,又或者遭受一招“野牛冲撞”,要知道两头成年野牛碰撞的冲击力,那力量就像被一辆时速 50 公里汽车撞上来。

    \n

    大自然没有医院,所以每年都有大量狮子在狩猎野牛的过程中因伤死亡,然而有个奇怪的现象,那就是非洲明明有约 15 万只鸵鸟,为什么狮子不去猎杀鸵鸟呢?

    \n
    \"\"
    \n

    非洲鸵鸟体长约 180~300 公分,身高 240~280 公分,体重 130-150 公斤,平均寿命长达 60 年,并且没有非洲野牛一样的攻击性“武器”,从图片不难看出,鸵鸟的肉不少。

    \n

    然而在狮子的食谱中,牛科动物占比高达 65%,长颈鹿斑马综合占比 21%,疣猪角马综合占比 11%,其他动物占比 3%,而鸵鸟的占比连 1%都不到,这是为什么?原因主要有以下几点:

    \n
    \"\"
    \n

    1.鸵鸟警惕性高

    \n

    鸵鸟不会飞行,虽然在演化过程中因体型过大,逐渐丧失了飞行能力,但仍然属于鸟纲类,而鸟类的警惕性,从我们身边的麻雀、斑鸠、喜鹊等常见鸟类就能窥一斑而见全豹。

    \n
    \"\"
    \n

    鸵鸟的警惕性远超非洲野牛、长颈鹿、斑马等,即便在喝水时,也会有专门的鸵鸟负责放哨。

    \n

    非洲大草原地势平坦,而非洲鸵鸟体高 180~300 公分,就算平均 2.5 米,如此高度的鸵鸟能轻松获得辽阔的视野,失去了层峦叠嶂的地形,再加上狮子是集体狩猎,但凡狮群靠近,鸵鸟就能轻松发现并逃跑。

    \n
    \"\"
    \n

    2.鸵鸟奔跑速度快

    \n

    物种演化无时无刻都在进行,鸵鸟虽然失去了飞行能力,但动辄 150 公斤的体重压在两条腿上,使得鸵鸟的腿部肌肉极其发达。

    \n

    鸵鸟每一步的距离可达 3~5 公尺,奔跑速度可达 70km/h,想象一下你开着一辆车,脚踩油门到 70km/h 的速度,就能明白鸵鸟的奔跑速度有多快。

    \n
    \"\"
    \n

    再来看狮子的奔跑速度,据靠谱一点的数据显示,狮子奔跑速度可达 80km/h,看似狮子的速度超过鸵鸟,但狮子没有任何优势。

    \n

    鸵鸟占据视野优势,老远就能发现狮群,而狮子虽然速度高达 80km/h,但因体型过大,体力有限,通常只能维持 3 分钟,否则身体会因过热崩溃。

    \n

    而鸵鸟能以 70km/h 的速度持续奔跑 5 分钟以上,有些甚至超过 10 分钟,在耐力方面鸵鸟肯定完胜狮子。

    \n
    \"\"
    \n

    3.鸵鸟的反击

    \n

    狮群在狩猎时,即便猎杀野牛,基本上也会优先选择年迈的野牛或小牛犊下手,并且牛的反应相当迟钝,跟鸵鸟完全不是一个层面。

    \n
    \"\"
    \n

    之所以不敢跟壮年野牛硬碰硬,是因为越是食物链顶端的生物,越懂得趋利避害,但凡被野牛撞断一根骨头或者划伤一个口子,在野外都可能是致命伤。

    \n

    鸵鸟在反击时,会利用粗壮的小腿进行踢打,它们的大腿骨肌肉十分发达,并且腿很长,看下图,鸵鸟的双脚趾结构,能进一步提高攻击力,狮子一旦被鸵鸟踹一脚,也可能升级成致命伤。

    \n
    \"\"
    \n

    综合总结

    \n

    a.狮群还没靠近鸵鸟,就被鸵鸟发现,狩猎行动失败。

    \n

    b.狮群好不容易靠近鸵鸟,但还有一段距离,此时狮群开足马力奔袭,结果鸵鸟撒腿就跑,双方你追我逃,当追捕时间超过 3 分钟,狮群非但浪费了巨大能量,而且还没有收获,狩猎行动非但失败,还憋了一肚子气。

    \n
    \"\"
    \n

    c.狮群艰难的捕捉到一只鸵鸟,结果被鸵鸟踹了一脚,骂骂咧咧的吃着带羽毛的肉,一瘸一拐的回到老巢,没过几天因伤去世。

    \n

    狮子只有在食物极度匮乏,没有多余选择的情况下,才会想到去捕猎鸵鸟,例如生活在纳米布沙漠的狮子,但这支狮群的数量不超过 100 头,而绝大多数狮子都会选择 190-550 公斤区间的猎物,这是在非洲这片土地上长期演化出来的食物选择,必然有一定道理。

    \n
    \n
    \n\n\n\n\n
    \n\n\n\n", + "content": "
    \"头图\"/
    \n

    看下图,一头狮子被迫逃到树上瑟瑟发抖,底下有什么可怕的动物?

    \n
    \"\"
    \n

    答案是非洲野牛。

    \n

    这一幕发生在肯尼亚马赛马拉国家公园,由挪威摄影师 Olav Thokle 拍摄,经过数小时的对峙,野牛才慢慢离去,本就爬树本领普通的狮子,为了牢牢固定身躯,爪子都疼的厉害。

    \n

    在我们的印象中,非洲野牛是狮子的猎物,虽然它们是食草动物,但战斗力却不容小觑,一头成年非洲野牛体长约为 2.1~3.4 米,平均身高在 1.5 米左右,体重 500~800 公斤。

    \n
    \"\"
    \n

    最致命的莫过于那一对锋利且坚硬的牛角,每年都有大量狮子在捕猎野牛时,被锋利的牛角划伤或刺穿,又或者遭受一招“野牛冲撞”,要知道两头成年野牛碰撞的冲击力,那力量就像被一辆时速 50 公里汽车撞上来。

    \n

    大自然没有医院,所以每年都有大量狮子在狩猎野牛的过程中因伤死亡,然而有个奇怪的现象,那就是非洲明明有约 15 万只鸵鸟,为什么狮子不去猎杀鸵鸟呢?

    \n
    \"\"
    \n

    非洲鸵鸟体长约 180~300 公分,身高 240~280 公分,体重 130-150 公斤,平均寿命长达 60 年,并且没有非洲野牛一样的攻击性“武器”,从图片不难看出,鸵鸟的肉不少。

    \n

    然而在狮子的食谱中,牛科动物占比高达 65%,长颈鹿斑马综合占比 21%,疣猪角马综合占比 11%,其他动物占比 3%,而鸵鸟的占比连 1%都不到,这是为什么?原因主要有以下几点:

    \n
    \"\"
    \n

    1.鸵鸟警惕性高

    \n

    鸵鸟不会飞行,虽然在演化过程中因体型过大,逐渐丧失了飞行能力,但仍然属于鸟纲类,而鸟类的警惕性,从我们身边的麻雀、斑鸠、喜鹊等常见鸟类就能窥一斑而见全豹。

    \n
    \"\"
    \n

    鸵鸟的警惕性远超非洲野牛、长颈鹿、斑马等,即便在喝水时,也会有专门的鸵鸟负责放哨。

    \n

    非洲大草原地势平坦,而非洲鸵鸟体高 180~300 公分,就算平均 2.5 米,如此高度的鸵鸟能轻松获得辽阔的视野,失去了层峦叠嶂的地形,再加上狮子是集体狩猎,但凡狮群靠近,鸵鸟就能轻松发现并逃跑。

    \n
    \"\"
    \n

    2.鸵鸟奔跑速度快

    \n

    物种演化无时无刻都在进行,鸵鸟虽然失去了飞行能力,但动辄 150 公斤的体重压在两条腿上,使得鸵鸟的腿部肌肉极其发达。

    \n

    鸵鸟每一步的距离可达 3~5 公尺,奔跑速度可达 70km/h,想象一下你开着一辆车,脚踩油门到 70km/h 的速度,就能明白鸵鸟的奔跑速度有多快。

    \n
    \"\"
    \n

    再来看狮子的奔跑速度,据靠谱一点的数据显示,狮子奔跑速度可达 80km/h,看似狮子的速度超过鸵鸟,但狮子没有任何优势。

    \n

    鸵鸟占据视野优势,老远就能发现狮群,而狮子虽然速度高达 80km/h,但因体型过大,体力有限,通常只能维持 3 分钟,否则身体会因过热崩溃。

    \n

    而鸵鸟能以 70km/h 的速度持续奔跑 5 分钟以上,有些甚至超过 10 分钟,在耐力方面鸵鸟肯定完胜狮子。

    \n
    \"\"
    \n

    3.鸵鸟的反击

    \n

    狮群在狩猎时,即便猎杀野牛,基本上也会优先选择年迈的野牛或小牛犊下手,并且牛的反应相当迟钝,跟鸵鸟完全不是一个层面。

    \n
    \"\"
    \n

    之所以不敢跟壮年野牛硬碰硬,是因为越是食物链顶端的生物,越懂得趋利避害,但凡被野牛撞断一根骨头或者划伤一个口子,在野外都可能是致命伤。

    \n

    鸵鸟在反击时,会利用粗壮的小腿进行踢打,它们的大腿骨肌肉十分发达,并且腿很长,看下图,鸵鸟的双脚趾结构,能进一步提高攻击力,狮子一旦被鸵鸟踹一脚,也可能升级成致命伤。

    \n
    \"\"
    \n

    综合总结

    \n

    a.狮群还没靠近鸵鸟,就被鸵鸟发现,狩猎行动失败。

    \n

    b.狮群好不容易靠近鸵鸟,但还有一段距离,此时狮群开足马力奔袭,结果鸵鸟撒腿就跑,双方你追我逃,当追捕时间超过 3 分钟,狮群非但浪费了巨大能量,而且还没有收获,狩猎行动非但失败,还憋了一肚子气。

    \n
    \"\"
    \n

    c.狮群艰难的捕捉到一只鸵鸟,结果被鸵鸟踹了一脚,骂骂咧咧的吃着带羽毛的肉,一瘸一拐的回到老巢,没过几天因伤去世。

    \n

    狮子只有在食物极度匮乏,没有多余选择的情况下,才会想到去捕猎鸵鸟,例如生活在纳米布沙漠的狮子,但这支狮群的数量不超过 100 头,而绝大多数狮子都会选择 190-550 公斤区间的猎物,这是在非洲这片土地上长期演化出来的食物选择,必然有一定道理。

    \n
    \n
    \n\n\n\n\n
    \n\n\n\n", + "category": "", + "link": "https://daily.zhihu.com/story/9782587", + "creator": "", + "pubDate": "Wed, 16 Jul 2025 08:27:03 +0800", + "enclosure": "", + "enclosureType": "", + "image": "", + "id": "", + "language": "", + "folder": "资讯", + "feed": "知乎", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "432e133ac8c51dd02c10043413734b7d", + "highlights": [] + }, + { + "title": "世界上大多数人似乎普遍存在一种偏见,认为只有哺乳动物才算是真正的「动物」,造成这一现象的原因是什么?", + "description": "
    \"头图\"/
    \n

    题述现象是人类中心主义的体现,主要归因于人自己是哺乳动物,相关信息的传播依赖社会文化和人脑的模式识别与匹配功能。大多数人的观念并不严格符合提问者的描述,但人们确实相当普遍地更关注哺乳动物的生存状态。

    \n
      \n
    • 世界范围内,人们讨论关于生物的话题时,“俺寻思生物就是动物、动物就是四足动物、四足动物就是哺乳动物,充其量再算上鸟”的现象是很常见的。这是进化论的次要证据。
    • \n
    \n

    在日常生活中,你很容易发现,人们普遍认为人的生命比非人动物的生命更宝贵(或曰“高等”),黑猩猩、大猩猩等看起来比较接近人的动物的生命比花鸟虫鱼的生命更宝贵(或曰“高等”),非人动物的生命比植物的生命更宝贵,人的生命比亿万个细菌、病毒的生命更宝贵。

    \n

    这叫做“物种歧视 /Speciesism[1]”:

    \n
      \n
    • 针对一个群体“仅因物种归属不同”而产生一系列价值差别判断、权利差别判断、特殊对待。
    • \n
    \n

    例如:

    \n

    人类物种歧视主义,或曰人类中心主义,指一些人会将非人类排除于人类的道德范围以外,视之为可随意处置的对象。这和道德成员资格的划定有关,其支持者可以提出非人类不能有效地参与人类社会、不能良好地遵守人类的社会规范——但是,人对他人的态度和行为往往也是多重标准的。

    \n

    “可爱动物保护主义”,指一些人会将长得不符合人的审美的动物视为可随意处置的对象,而对猫、狗等“可爱动物”进行法律保护。

    \n
      \n
    • 这既涉及“人用来检测幼体的特征并降低攻击幼体的概率的机制(婴儿图式)对长得在某种程度上像人类幼体的猫狗也有效”,又涉及猫狗长期与人共生、双方的一部分基因已经针对互动进行过筛选(例如人和狗对视时双方的脑释放催产素),不能只归结于当事人双重标准——地球生物圈里的各种共生关系本来就是多重标准的。
    • \n
    \n

    “人类 - 大猩猩物种歧视主义”,指一些人出于人和大猩猩等类人猿的相似性而支持保护大猩猩的权利,胜过保护猫狗的权利,更胜过保护非哺乳类的权利。

    \n
      \n
    • 在世界范围内,动物保护主义者保护哺乳类的积极性明显高于保护无脊椎动物。这或多或少地涉及自然选择塑造的自动判定同类的机制,让人更关注在演化上跟自己相似的生物。
    • \n
    • 所谓“神经系统的复杂程度”,在很大程度上就是“在神经系统方面与人的相似性”“。
    • \n
    \n
    \n
    \n\n\n\n\n
    \n\n\n\n", + "content": "
    \"头图\"/
    \n

    题述现象是人类中心主义的体现,主要归因于人自己是哺乳动物,相关信息的传播依赖社会文化和人脑的模式识别与匹配功能。大多数人的观念并不严格符合提问者的描述,但人们确实相当普遍地更关注哺乳动物的生存状态。

    \n
      \n
    • 世界范围内,人们讨论关于生物的话题时,“俺寻思生物就是动物、动物就是四足动物、四足动物就是哺乳动物,充其量再算上鸟”的现象是很常见的。这是进化论的次要证据。
    • \n
    \n

    在日常生活中,你很容易发现,人们普遍认为人的生命比非人动物的生命更宝贵(或曰“高等”),黑猩猩、大猩猩等看起来比较接近人的动物的生命比花鸟虫鱼的生命更宝贵(或曰“高等”),非人动物的生命比植物的生命更宝贵,人的生命比亿万个细菌、病毒的生命更宝贵。

    \n

    这叫做“物种歧视 /Speciesism[1]”:

    \n
      \n
    • 针对一个群体“仅因物种归属不同”而产生一系列价值差别判断、权利差别判断、特殊对待。
    • \n
    \n

    例如:

    \n

    人类物种歧视主义,或曰人类中心主义,指一些人会将非人类排除于人类的道德范围以外,视之为可随意处置的对象。这和道德成员资格的划定有关,其支持者可以提出非人类不能有效地参与人类社会、不能良好地遵守人类的社会规范——但是,人对他人的态度和行为往往也是多重标准的。

    \n

    “可爱动物保护主义”,指一些人会将长得不符合人的审美的动物视为可随意处置的对象,而对猫、狗等“可爱动物”进行法律保护。

    \n
      \n
    • 这既涉及“人用来检测幼体的特征并降低攻击幼体的概率的机制(婴儿图式)对长得在某种程度上像人类幼体的猫狗也有效”,又涉及猫狗长期与人共生、双方的一部分基因已经针对互动进行过筛选(例如人和狗对视时双方的脑释放催产素),不能只归结于当事人双重标准——地球生物圈里的各种共生关系本来就是多重标准的。
    • \n
    \n

    “人类 - 大猩猩物种歧视主义”,指一些人出于人和大猩猩等类人猿的相似性而支持保护大猩猩的权利,胜过保护猫狗的权利,更胜过保护非哺乳类的权利。

    \n
      \n
    • 在世界范围内,动物保护主义者保护哺乳类的积极性明显高于保护无脊椎动物。这或多或少地涉及自然选择塑造的自动判定同类的机制,让人更关注在演化上跟自己相似的生物。
    • \n
    • 所谓“神经系统的复杂程度”,在很大程度上就是“在神经系统方面与人的相似性”“。
    • \n
    \n
    \n
    \n\n\n\n\n
    \n\n\n\n", + "category": "", + "link": "https://daily.zhihu.com/story/9782594", + "creator": "", + "pubDate": "Wed, 16 Jul 2025 08:27:03 +0800", + "enclosure": "", + "enclosureType": "", + "image": "", + "id": "", + "language": "", + "folder": "资讯", + "feed": "知乎", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "5be57433b8cb4fc19a3834e90903ead3", + "highlights": [] + }, + { + "title": "斑马的斑到底有什么用?", + "description": "
    \"头图\"/
    \n

    斑马的黑白条纹可能是破坏边缘完整性的伪装色,主要作用于吸血昆虫。在野生斑马经常出没的地区,狮子、猎豹等捕食者看起来并未受到斑马黑白条纹的明显影响。

    \n
      \n
    • 在非洲,马蝇、采采蝇等吸血昆虫传播多种流行病。斑马稀疏的毛发不擅长抵御吸血昆虫的叮咬,但是吸血昆虫对斑马的侵袭强度较低。2014 年,Caro 等进行了较大规模的统计,认为斑马的黑白条纹主要用于降低被吸血昆虫叮咬的概率[1]。
    • \n
    \n

    在此基础上,斑马的黑白条纹可能有助于体温调节[2]。

    \n
      \n
    • 参考 2 称,斑马身上黑色毛发的温度可以比白色毛发高 12 至 15 摄氏度,在局部产生有规律的气流。马属动物的汗液含有泡沫蛋白,汗液的表面张力较低,有助于毛发前端的蒸发冷却。观察显示,斑马可以在白色毛发保持倒伏的同时将黑色毛发竖立起来,这大概可以和上述汗液搭配散热。此外,在气温较低时,黑色毛发可能有助于吸热。
    • \n
    \n
    \n

    不限于斑马,破坏轮廓完整性的伪装示意图:

    \n
    \"\"
    By Hugh B. Cott - Adaptive Coloration in Animals; image available at http://camouflage.osu.edu/cott.html, Fair use, https://en.wikipedia.org/w/index.php?curid=35274594
    \n

    2019 年,一项实验显示,在牛身上画出类似斑马的条纹,会降低螫蝇、角蝇等双翅目吸血昆虫叮咬牛的频率。斑马状条纹可能干扰吸血昆虫视觉系统的运动检测,让它们着陆在牛身上的成功率下降[3]。

    \n
    \"\"
    实验用牛
    \n
    \"\"
    CONT:无条纹牛,B and W:黑白条纹牛,B:黑条纹牛
    \n

    吸血昆虫仍被有条纹的牛的视觉特征、气味吸引,可以多次尝试降落,不会简单地放弃叮咬有条纹的牛。条纹在实验中提供的保护效果并不强于看起来更平凡的杀虫剂。

    \n

    读者亦可参考熊猫:

    \n

    熊猫身体和四肢的配色在较远距离上会有效地破坏轮廓完整性、发挥伪装功能,在较近距离上通过背景匹配(黑色皮毛与树干、阴影匹配,白色皮毛与树叶、雪匹配,其余皮毛[4]与岩石、地面匹配)在一定程度上发挥伪装功能[5]。

    \n
    \"\"
    https://www.nature.com/articles/s41598-021-00742-4/figures/1 未经严格筛选的图片
    \n
    \"\"
    https://www.nature.com/articles/s41598-021-00742-4/figures/2
    \n
    \n
    \n\n\n\n\n
    \n\n\n\n", + "content": "
    \"头图\"/
    \n

    斑马的黑白条纹可能是破坏边缘完整性的伪装色,主要作用于吸血昆虫。在野生斑马经常出没的地区,狮子、猎豹等捕食者看起来并未受到斑马黑白条纹的明显影响。

    \n
      \n
    • 在非洲,马蝇、采采蝇等吸血昆虫传播多种流行病。斑马稀疏的毛发不擅长抵御吸血昆虫的叮咬,但是吸血昆虫对斑马的侵袭强度较低。2014 年,Caro 等进行了较大规模的统计,认为斑马的黑白条纹主要用于降低被吸血昆虫叮咬的概率[1]。
    • \n
    \n

    在此基础上,斑马的黑白条纹可能有助于体温调节[2]。

    \n
      \n
    • 参考 2 称,斑马身上黑色毛发的温度可以比白色毛发高 12 至 15 摄氏度,在局部产生有规律的气流。马属动物的汗液含有泡沫蛋白,汗液的表面张力较低,有助于毛发前端的蒸发冷却。观察显示,斑马可以在白色毛发保持倒伏的同时将黑色毛发竖立起来,这大概可以和上述汗液搭配散热。此外,在气温较低时,黑色毛发可能有助于吸热。
    • \n
    \n
    \n

    不限于斑马,破坏轮廓完整性的伪装示意图:

    \n
    \"\"
    By Hugh B. Cott - Adaptive Coloration in Animals; image available at http://camouflage.osu.edu/cott.html, Fair use, https://en.wikipedia.org/w/index.php?curid=35274594
    \n

    2019 年,一项实验显示,在牛身上画出类似斑马的条纹,会降低螫蝇、角蝇等双翅目吸血昆虫叮咬牛的频率。斑马状条纹可能干扰吸血昆虫视觉系统的运动检测,让它们着陆在牛身上的成功率下降[3]。

    \n
    \"\"
    实验用牛
    \n
    \"\"
    CONT:无条纹牛,B and W:黑白条纹牛,B:黑条纹牛
    \n

    吸血昆虫仍被有条纹的牛的视觉特征、气味吸引,可以多次尝试降落,不会简单地放弃叮咬有条纹的牛。条纹在实验中提供的保护效果并不强于看起来更平凡的杀虫剂。

    \n

    读者亦可参考熊猫:

    \n

    熊猫身体和四肢的配色在较远距离上会有效地破坏轮廓完整性、发挥伪装功能,在较近距离上通过背景匹配(黑色皮毛与树干、阴影匹配,白色皮毛与树叶、雪匹配,其余皮毛[4]与岩石、地面匹配)在一定程度上发挥伪装功能[5]。

    \n
    \"\"
    https://www.nature.com/articles/s41598-021-00742-4/figures/1 未经严格筛选的图片
    \n
    \"\"
    https://www.nature.com/articles/s41598-021-00742-4/figures/2
    \n
    \n
    \n\n\n\n\n
    \n\n\n\n", + "category": "", + "link": "https://daily.zhihu.com/story/9782601", + "creator": "", + "pubDate": "Wed, 16 Jul 2025 08:27:03 +0800", + "enclosure": "", + "enclosureType": "", + "image": "", + "id": "", + "language": "", + "folder": "资讯", + "feed": "知乎", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "2c5dce9634a6cbf53698f206807d80b3", + "highlights": [] + }, + { + "title": "瞎扯 · 如何正确地吐槽", + "description": "
    \"头图\"/
    \n\n
    \n
    @南瓜周 :沙僧骂街之前先鞠了一躬,八戒和悟空正骂着呢,看见沙僧鞠躬也跟着鞠躬。
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \"\"\n
     
    \n
     
    \n
    \n
    \n
    \n
    \n
    \n
    \n
     
    \n
    \n
    \n\n
    \n
    @挖坟老狗 :我再说一遍, cos 不知道怎么处理就直接万能公式套上去。
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \"\"\n
     
    \n
     
    \n
    \n
    \n
    \n
    \n
    \n
    \n
     
    \n
    \n
    \n\n
    \n
    @深港双城男 :能喝酒就不要喝水,把水留给不会喝酒的孩子。
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \"\"\n
     
    \n
     
    \n
    \n
    \n
    \n
    \n
    \n
    \n
     
    \n
    \n
    \n\n
    \n
    @过世猪猪 :尖叫的瓶子,优秀到我一点都不记得这饮料是什么味道。
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \"\"\n
     
    \n
     
    \n
    \n
    \n
    \n
    \n
    \n
    \n
     
    \n
    \n
    \n\n
    \n
    @棠风 :大清早的,不知道哪个小孩打错电话, 要跟我请假。 反正我是给他批准了……
    \n
    \n
    \n
    \n\n\n
    \n\n\n\n", + "content": "
    \"头图\"/
    \n\n
    \n
    @南瓜周 :沙僧骂街之前先鞠了一躬,八戒和悟空正骂着呢,看见沙僧鞠躬也跟着鞠躬。
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \"\"\n
     
    \n
     
    \n
    \n
    \n
    \n
    \n
    \n
    \n
     
    \n
    \n
    \n\n
    \n
    @挖坟老狗 :我再说一遍, cos 不知道怎么处理就直接万能公式套上去。
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \"\"\n
     
    \n
     
    \n
    \n
    \n
    \n
    \n
    \n
    \n
     
    \n
    \n
    \n\n
    \n
    @深港双城男 :能喝酒就不要喝水,把水留给不会喝酒的孩子。
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \"\"\n
     
    \n
     
    \n
    \n
    \n
    \n
    \n
    \n
    \n
     
    \n
    \n
    \n\n
    \n
    @过世猪猪 :尖叫的瓶子,优秀到我一点都不记得这饮料是什么味道。
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \"\"\n
     
    \n
     
    \n
    \n
    \n
    \n
    \n
    \n
    \n
     
    \n
    \n
    \n\n
    \n
    @棠风 :大清早的,不知道哪个小孩打错电话, 要跟我请假。 反正我是给他批准了……
    \n
    \n
    \n
    \n\n\n
    \n\n\n\n", + "category": "", + "link": "https://daily.zhihu.com/story/9782579", + "creator": "", + "pubDate": "Tue, 15 Jul 2025 08:27:03 +0800", + "enclosure": "", + "enclosureType": "", + "image": "", + "id": "", + "language": "", + "folder": "资讯", + "feed": "知乎", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "acfc0018a6d1f24a71ab7931b2488200", + "highlights": [] + }, + { + "title": "发明魔芋的前辈是经历了什么?", + "description": "
    \"头图\"/
    \n

    其实没有那么夸张,最重要的就是水煮。

    \n

    我们可以对照土法加工魔芋的步骤来看看祖先的研发过程。

    \n

    1.首先是外观。

    \n

    大型块茎,切开乳白色含有大量淀粉状物质,

    \n

    这对于人类来说基本就是“千方百计想要吃”的食材样子了。

    \n

    剩下的问题就是“怎么吃,好吃么?”

    \n
    \"\"
    \n

    魔芋这个东西有毒,但毒性其实不大。

    \n

    生吃的话,少量会出现恶心、呕吐、腹泻、出汗,对于时常饥荒连草根、树皮都能吃的古代人来说就算是“可以付出的代价了”。

    \n

    而且只要不是剧毒,就可以不断尝试各种处理方法,看看怎么让吃了之后的副作用减弱。

    \n
    提示:如果大量食用生魔芋,严重时也会呼吸中枢完全麻痹而导致死亡。大家不要轻易尝试。
    \n

    2.切碎和浸泡。

    \n

    这步算是处理大部分弱毒性食材的标准方式。

    \n
    \"\"
    \n

    因为植物的毒性很大程度是生物碱,生物碱的分子量通常较小,所以容易溶解于水。

    \n

    比如马铃薯去毒法里面,就有一种水泡法。浸泡 12 小时候可以去马铃薯毒性。

    \n
    发芽的马铃薯最好还是扔掉,不值当冒险,这里只是说理论的去毒方法。
    \n

    而块茎一般含有多糖、淀粉,是比较不容易溶解于水里的,

    \n

    所以浸泡投洗算是古代比较常见的植物脱毒预处理方式。也最简单容易操作。

    \n
    魔芋含有的葡甘聚糖(KGM),也是一种具有特殊理化性质的天然高分子多糖。
    \n

    3.磨碎熬煮。

    \n

    磨碎是为了进一步增加反应速度,加热熬煮则是人类掌握比较成熟的脱毒方式。

    \n
    \"\"
    \n

    像是四季豆、扁豆、金针菇、木薯、蚕豆、黄花菜等,都是必须彻底加热才能吃的食材。

    \n

    这几种植物原理不同,比如豆角是皂素和红细胞凝集素;蚕豆是巢菜碱苷成分及蚕豆嘧啶葡糖苷;木薯是亚麻仁苦苷,金针菇和黄花菜是秋水仙碱,还有一些植物含有较高草酸。

    \n

    而破坏生物碱的方式,古代人类最容易掌握的就是加热。

    \n

    虽然也有不少毒素,比如不少菌类含有的毒素异常稳定,具有耐高温的特性,但这更多是一种“偶然”,因为在自然界中植物不需要面对一百度的高温烹煮。

    \n

    魔芋的毒性主要应该是生物碱糖苷,这类物质比较常见于植物块茎里。

    \n

    比如土豆里含有的龙葵碱,就是种典型的生物碱糖苷,难于被胃肠道吸收,会导致肠胃不适。

    \n

    因为他们对人体的肠胃膜结构油一定破坏作用,还能抑制胆碱酯酶的消化。

    \n

    所以,凉拌土豆丝是焯水后凉拌的,厨房菜鸟切记。

    \n
    \"\"
    \n

    好,我们回到魔芋加工过程。

    \n

    4.点碱水。

    \n

    这一步其实和“去毒”应该没有关系。

    \n

    因为中和生物碱一般是用酸,而加碱显然无助于分解。

    \n

    如果不考虑外形,煮熟后的魔芋糊糊也是可以吃的。但是不好保存。

    \n
    \"\"
    \n

    很可能古人是先吃了很多年的魔芋糊,然后才逐步研究怎么让这个东西凝结,变成类似豆腐的形状,方便保存和携带。

    \n

    魔芋加碱水的主要原因是为了改善其质地和口感。

    \n

    让碱水和魔芋中的谷蛋白酶反应,失去活性,从而凝固魔芋,增加其黏度和弹性。‌

    \n

    农村的这一步是用草木灰(主要成分是碳酸钾(K2CO3))浸泡过滤的水来“点”魔芋。

    \n
    现在食品工艺,大都加入碱性的氢氧化钙来处理。
    显然氢氧化钙更干净、安全,而且是残留量不需限定的加工助剂。
    \n
    \"\"
    \n

    用草木灰来凝固蛋白、脱水,也算是古人掌握的一种常见加工方法。

    \n

    比如用在鸭蛋上,草木灰包裹可以制造松花蛋(蛋白变性作用),把豆腐包裹可以制造出灰培豆腐(脱水作用)。

    \n

    同时,因为草木灰相对卫生(燃烧后相当于消毒),可以调节食物酸碱度让口感更好,还能长期保存食物(吸水让食物表面干燥),所以古人在食材加工中经常会尝试添加。

    \n
    哪怕加错了就是多喝了点碱水,也没有大问题。
    \n

    比如“碱水粽”是用草木灰水浸泡糯米,桑叶凉粉是用草木灰水与桑叶汁制作。在四川,还有用草木灰水和大米制作的“灰水馍馍”。

    \n

    这些显示出我们的祖先做食物时,经常会尝试添加草木灰,看看会有什么变化。

    \n
    \"\"
    \n

    5.成型切分。

    \n

    用碱水点过之后,快速搅拌、冷却,魔芋就会凝结成块状。可以用竹篾划开。

    \n

    就成了块状的土法魔芋了,因为草木灰和过滤不彻底,经常是灰褐色的。

    \n
    \"\"
    草木灰魔芋
    \n

    这里说一下碱水让魔芋成型的化学原理,不感兴趣的可以跳过。

    \n
    魔芋的主要成分是葡甘露聚糖,由葡萄糖和甘露糖按一定比例结合而成的的长链大分子。
    在这个大分子上,除了主链上的葡萄糖和甘露糖之外,还有一些特别的乙酰基。
    正是这些乙酰基的存在,阻止了魔芋多糖分子的“亲密聚合”,所以加工之前的魔芋并不是魔芋块、魔芋丝那样 Q 弹的样子。
    只有加碱性物质才能把乙酰基的阻拦作用“抵消”,让魔芋多糖分子聚集凝固,并且把很多水分锁在其中,最终摇身一变成为魔芋丝、魔芋豆腐等弹滑的状态。
    \n

    古人显然不懂这些深奥原理,但不阻碍他们在实验草木灰水的时候,发现了效果。

    \n

    至此,魔芋就成了我们比较常见的样子,颜色深浅要看熬煮过程的火候和碱水。

    \n

    使用草木灰的会比较灰暗,颜色深,而使用食用碱来做添加剂,魔芋会更透亮,好看。

    \n
    \"\"
    土法碱水魔芋
    \n

    一般在魔芋凉了切块后,食用前还会进行二次煮制。

    \n

    这样可以让口感更有弹性。也防止表面滋生细菌。

    \n

    总结一下,

    \n

    古人应该不是一步到位把魔芋的制作过程发明的。

    \n

    而是发现了可食用的块茎,先凑活生着吃,发现少量毒不死,但吃多了恶心、呕吐、拉肚子。

    \n

    于是试着洗一洗,发现好像好一点,再试试烤熟了、煮熟了吃,发现又好一些。

    \n

    等吃腻了糊糊,试着往里面加点木炭粉、草木灰,乱七八糟厨房附近有的,吃不死人的东西。

    \n

    发现居然变成块了,更容易保存吃好几天。

    \n

    下次试试只加草木灰的过滤水,原来也可以。颜色更好看了。

    \n

    于是,经过漫长的摸索,这种“新型豆腐”的加工方法就算定型了。

    \n

    不得不过,古代人为了吃点好东西,的确是能发动各种研究精神啊。

    \n
    关注也说,阅读更多有趣的思想。
    \n

    当年苏联挖地球为什么挖到 12262 米就停止了?明明不开车为啥还被称为副驾驶?假如忽然火了?怎么接住泼天的富贵。

    \n
    \n
    \n\n\n\n\n
    \n\n\n\n", + "content": "
    \"头图\"/
    \n

    其实没有那么夸张,最重要的就是水煮。

    \n

    我们可以对照土法加工魔芋的步骤来看看祖先的研发过程。

    \n

    1.首先是外观。

    \n

    大型块茎,切开乳白色含有大量淀粉状物质,

    \n

    这对于人类来说基本就是“千方百计想要吃”的食材样子了。

    \n

    剩下的问题就是“怎么吃,好吃么?”

    \n
    \"\"
    \n

    魔芋这个东西有毒,但毒性其实不大。

    \n

    生吃的话,少量会出现恶心、呕吐、腹泻、出汗,对于时常饥荒连草根、树皮都能吃的古代人来说就算是“可以付出的代价了”。

    \n

    而且只要不是剧毒,就可以不断尝试各种处理方法,看看怎么让吃了之后的副作用减弱。

    \n
    提示:如果大量食用生魔芋,严重时也会呼吸中枢完全麻痹而导致死亡。大家不要轻易尝试。
    \n

    2.切碎和浸泡。

    \n

    这步算是处理大部分弱毒性食材的标准方式。

    \n
    \"\"
    \n

    因为植物的毒性很大程度是生物碱,生物碱的分子量通常较小,所以容易溶解于水。

    \n

    比如马铃薯去毒法里面,就有一种水泡法。浸泡 12 小时候可以去马铃薯毒性。

    \n
    发芽的马铃薯最好还是扔掉,不值当冒险,这里只是说理论的去毒方法。
    \n

    而块茎一般含有多糖、淀粉,是比较不容易溶解于水里的,

    \n

    所以浸泡投洗算是古代比较常见的植物脱毒预处理方式。也最简单容易操作。

    \n
    魔芋含有的葡甘聚糖(KGM),也是一种具有特殊理化性质的天然高分子多糖。
    \n

    3.磨碎熬煮。

    \n

    磨碎是为了进一步增加反应速度,加热熬煮则是人类掌握比较成熟的脱毒方式。

    \n
    \"\"
    \n

    像是四季豆、扁豆、金针菇、木薯、蚕豆、黄花菜等,都是必须彻底加热才能吃的食材。

    \n

    这几种植物原理不同,比如豆角是皂素和红细胞凝集素;蚕豆是巢菜碱苷成分及蚕豆嘧啶葡糖苷;木薯是亚麻仁苦苷,金针菇和黄花菜是秋水仙碱,还有一些植物含有较高草酸。

    \n

    而破坏生物碱的方式,古代人类最容易掌握的就是加热。

    \n

    虽然也有不少毒素,比如不少菌类含有的毒素异常稳定,具有耐高温的特性,但这更多是一种“偶然”,因为在自然界中植物不需要面对一百度的高温烹煮。

    \n

    魔芋的毒性主要应该是生物碱糖苷,这类物质比较常见于植物块茎里。

    \n

    比如土豆里含有的龙葵碱,就是种典型的生物碱糖苷,难于被胃肠道吸收,会导致肠胃不适。

    \n

    因为他们对人体的肠胃膜结构油一定破坏作用,还能抑制胆碱酯酶的消化。

    \n

    所以,凉拌土豆丝是焯水后凉拌的,厨房菜鸟切记。

    \n
    \"\"
    \n

    好,我们回到魔芋加工过程。

    \n

    4.点碱水。

    \n

    这一步其实和“去毒”应该没有关系。

    \n

    因为中和生物碱一般是用酸,而加碱显然无助于分解。

    \n

    如果不考虑外形,煮熟后的魔芋糊糊也是可以吃的。但是不好保存。

    \n
    \"\"
    \n

    很可能古人是先吃了很多年的魔芋糊,然后才逐步研究怎么让这个东西凝结,变成类似豆腐的形状,方便保存和携带。

    \n

    魔芋加碱水的主要原因是为了改善其质地和口感。

    \n

    让碱水和魔芋中的谷蛋白酶反应,失去活性,从而凝固魔芋,增加其黏度和弹性。‌

    \n

    农村的这一步是用草木灰(主要成分是碳酸钾(K2CO3))浸泡过滤的水来“点”魔芋。

    \n
    现在食品工艺,大都加入碱性的氢氧化钙来处理。
    显然氢氧化钙更干净、安全,而且是残留量不需限定的加工助剂。
    \n
    \"\"
    \n

    用草木灰来凝固蛋白、脱水,也算是古人掌握的一种常见加工方法。

    \n

    比如用在鸭蛋上,草木灰包裹可以制造松花蛋(蛋白变性作用),把豆腐包裹可以制造出灰培豆腐(脱水作用)。

    \n

    同时,因为草木灰相对卫生(燃烧后相当于消毒),可以调节食物酸碱度让口感更好,还能长期保存食物(吸水让食物表面干燥),所以古人在食材加工中经常会尝试添加。

    \n
    哪怕加错了就是多喝了点碱水,也没有大问题。
    \n

    比如“碱水粽”是用草木灰水浸泡糯米,桑叶凉粉是用草木灰水与桑叶汁制作。在四川,还有用草木灰水和大米制作的“灰水馍馍”。

    \n

    这些显示出我们的祖先做食物时,经常会尝试添加草木灰,看看会有什么变化。

    \n
    \"\"
    \n

    5.成型切分。

    \n

    用碱水点过之后,快速搅拌、冷却,魔芋就会凝结成块状。可以用竹篾划开。

    \n

    就成了块状的土法魔芋了,因为草木灰和过滤不彻底,经常是灰褐色的。

    \n
    \"\"
    草木灰魔芋
    \n

    这里说一下碱水让魔芋成型的化学原理,不感兴趣的可以跳过。

    \n
    魔芋的主要成分是葡甘露聚糖,由葡萄糖和甘露糖按一定比例结合而成的的长链大分子。
    在这个大分子上,除了主链上的葡萄糖和甘露糖之外,还有一些特别的乙酰基。
    正是这些乙酰基的存在,阻止了魔芋多糖分子的“亲密聚合”,所以加工之前的魔芋并不是魔芋块、魔芋丝那样 Q 弹的样子。
    只有加碱性物质才能把乙酰基的阻拦作用“抵消”,让魔芋多糖分子聚集凝固,并且把很多水分锁在其中,最终摇身一变成为魔芋丝、魔芋豆腐等弹滑的状态。
    \n

    古人显然不懂这些深奥原理,但不阻碍他们在实验草木灰水的时候,发现了效果。

    \n

    至此,魔芋就成了我们比较常见的样子,颜色深浅要看熬煮过程的火候和碱水。

    \n

    使用草木灰的会比较灰暗,颜色深,而使用食用碱来做添加剂,魔芋会更透亮,好看。

    \n
    \"\"
    土法碱水魔芋
    \n

    一般在魔芋凉了切块后,食用前还会进行二次煮制。

    \n

    这样可以让口感更有弹性。也防止表面滋生细菌。

    \n

    总结一下,

    \n

    古人应该不是一步到位把魔芋的制作过程发明的。

    \n

    而是发现了可食用的块茎,先凑活生着吃,发现少量毒不死,但吃多了恶心、呕吐、拉肚子。

    \n

    于是试着洗一洗,发现好像好一点,再试试烤熟了、煮熟了吃,发现又好一些。

    \n

    等吃腻了糊糊,试着往里面加点木炭粉、草木灰,乱七八糟厨房附近有的,吃不死人的东西。

    \n

    发现居然变成块了,更容易保存吃好几天。

    \n

    下次试试只加草木灰的过滤水,原来也可以。颜色更好看了。

    \n

    于是,经过漫长的摸索,这种“新型豆腐”的加工方法就算定型了。

    \n

    不得不过,古代人为了吃点好东西,的确是能发动各种研究精神啊。

    \n
    关注也说,阅读更多有趣的思想。
    \n

    当年苏联挖地球为什么挖到 12262 米就停止了?明明不开车为啥还被称为副驾驶?假如忽然火了?怎么接住泼天的富贵。

    \n
    \n
    \n\n\n\n\n
    \n\n\n\n", + "category": "", + "link": "https://daily.zhihu.com/story/9782557", + "creator": "", + "pubDate": "Tue, 15 Jul 2025 08:27:03 +0800", + "enclosure": "", + "enclosureType": "", + "image": "", + "id": "", + "language": "", + "folder": "资讯", + "feed": "知乎", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "e55b066bd5cb0fc524a10242af37695b", + "highlights": [] + }, + { + "title": "少年贪食荔枝后昏迷,确诊为「荔枝病」引发低血糖,为何荔枝是高糖水果会导致低血糖?其他水果也会这样吗?", + "description": "
    \"头图\"/
    \n

    省流:肝脏:视野盲区,还以为是亮氨酸呢,刹不住酶,我们都在用力地活着。

    \n

    嘛……其实这事真的不是新闻说的那样是因为果糖不能用 + 胰岛素分泌过多导致的……

    \n

    果糖被吸收后,经过肝门静脉运输到肝,绝大部分被肝吸收,只有一小部分进入血液循环,因此基本不会刺激胰岛素分泌。

    \n

    那是什么导致了荔枝病呢?答案是——荔枝等无患子目植物中含有的化学防御性物质次甘氨酸 A(Hypoglycin A,准确译名应该是降糖毒素 A)和它的类似物亚甲基环丙基甘氨酸(methylenecyclopropylglycine,MCPG),它俩都是非蛋白质氨基酸,存在于无患子目(尤其是无患子科)植物的种子及未成熟果实中。

    \n
    \"\"
    次甘氨酸 A
    \n
    \"\"
    亚甲基环丙基甘氨酸
    \n

    那这小小的次甘氨酸又是怎么导致低血糖的呢?

    \n

    诶,它本身不导致低血糖,意不意外?

    \n

    虽然它本身确实不导致低血糖,但是肝脏非得作死有什么办法呢?本来它是易溶于水的氨基酸,丢给肾脏扔掉就好了,结果肝脏一没注意,给看成亮氨酸转氨了,转氨之后还给塞进支链酮酸脱氢酶里脱氢了,结果把无害的次甘氨酸 A 硬生生变成了有毒的亚甲基环丙基乙酸(Methylene cyclopropyl acetic acid,MCPA)。

    \n
    \"\"
    亚甲基环丙基乙酸
    \n

    (肝脏:视野盲区,还以为是亮氨酸呢,刹不住酶,我们都在用力地活着。)

    \n
    \"\"
    支链酮酸脱氢酶复合物(异三聚体)由三个不同的酶组成,分别是α- 酮酸脱氢酶(E1)、二氢硫酰基转酰基酶(E2)和二氢脂肪酰胺脱氢酶(E3)
    \n
    \"\"
    二氢脂肪酰胺脱氢酶从各个方向观察的构象
    \n

    可以注意到,由支链酮酸脱氢酶复合物中出来的酸已经是与辅酶 A 结合的酰基辅酶 A形态了,而正是这个无法进一步代谢的亚甲基环丙基乙酰辅酶 A导致了后续的一系列酶出了问题,最终导致了低血糖的发生。

    \n

    那么话讲到这里就有同学要问了:老师老师,这个东西到底是怎么导致酶出问题的啊?

    \n

    那我问你,糖异生要不要调控?如果要调控你说这样的话啊?那我问你,那你糖异生怎么调控那我问你……

    \n

    咳咳,不搞抽象了,糖异生的调控主要发生在丙酮酸羧化酶、磷酸烯醇式丙酮酸(PEP)羧激酶、1,6- 二磷酸果糖(F-1,6-BP)磷酸酶这三种酶中,其中丙酮酸羧化酶受到乙酰辅酶 A的正向调控和ADP的负向调控,PEP 羧激酶受ADP的负向调控,F-1,6-BP 磷酸酶受AMP、F-2,6-BP的负向调控和柠檬酸的正向调控。

    \n

    现在问题来了,在进行糖异生的情况下,乙酰辅酶 A 肯定不会是从糖中来,肯定是脂肪酸代谢的结果,但是脂肪酸要形成乙酰辅酶 A 就要进行β氧化,要进行β氧化就需要辅酶 A……诶等等,我辅酶 A 呢?丸辣,辅酶 A 全被变成亚甲基环丙基乙酰辅酶 A了!

    \n

    于是乎,糖异生就被一颗小小的荔枝搞瘫痪了。

    \n

    那么又有同学要问了:老师,用不了葡萄糖我用酮体还不行吗?酮体一样可以穿过血脑屏障啊?

    \n

    问得好,那我问你:酮体是由什么合成的?

    \n
    \"\"
    乙酰乙酸,一看就知道是两个乙酰辅酶 A 缩合而来的。
    \n
    \"\"
    丙酮,由乙酰乙酸自发或酶催化脱羧而来
    \n
    \"\"
    β- 羟基丁酸,由乙酰乙酸还原而来
    \n

    酮体都是由乙酰辅酶 A 直接合成为乙酰乙酰辅酶 A,然后形成 HMG-CoA(β- 羟基 -β- 甲基戊二酸单酰辅酶 A),最后再形成乙酰乙酸这一过程得到的,那么如果没有乙酰辅酶 A,拿什么去合成酮体呢?

    \n

    糖指望不上,酮体也合成不了,人不就低血糖倒了嘛,很正常。

    \n

    以上。

    \n
    \n

    这玩意有肝毒性,不能用来治糖尿病!!!

    \n

    这玩意也会抑制脂肪酸氧化(脂肪酸氧化需要先与辅酶 A 结合为脂酰辅酶 A),不能用来减肥!!!

    \n
    \n
    \n\n\n\n\n
    \n\n\n\n", + "content": "
    \"头图\"/
    \n

    省流:肝脏:视野盲区,还以为是亮氨酸呢,刹不住酶,我们都在用力地活着。

    \n

    嘛……其实这事真的不是新闻说的那样是因为果糖不能用 + 胰岛素分泌过多导致的……

    \n

    果糖被吸收后,经过肝门静脉运输到肝,绝大部分被肝吸收,只有一小部分进入血液循环,因此基本不会刺激胰岛素分泌。

    \n

    那是什么导致了荔枝病呢?答案是——荔枝等无患子目植物中含有的化学防御性物质次甘氨酸 A(Hypoglycin A,准确译名应该是降糖毒素 A)和它的类似物亚甲基环丙基甘氨酸(methylenecyclopropylglycine,MCPG),它俩都是非蛋白质氨基酸,存在于无患子目(尤其是无患子科)植物的种子及未成熟果实中。

    \n
    \"\"
    次甘氨酸 A
    \n
    \"\"
    亚甲基环丙基甘氨酸
    \n

    那这小小的次甘氨酸又是怎么导致低血糖的呢?

    \n

    诶,它本身不导致低血糖,意不意外?

    \n

    虽然它本身确实不导致低血糖,但是肝脏非得作死有什么办法呢?本来它是易溶于水的氨基酸,丢给肾脏扔掉就好了,结果肝脏一没注意,给看成亮氨酸转氨了,转氨之后还给塞进支链酮酸脱氢酶里脱氢了,结果把无害的次甘氨酸 A 硬生生变成了有毒的亚甲基环丙基乙酸(Methylene cyclopropyl acetic acid,MCPA)。

    \n
    \"\"
    亚甲基环丙基乙酸
    \n

    (肝脏:视野盲区,还以为是亮氨酸呢,刹不住酶,我们都在用力地活着。)

    \n
    \"\"
    支链酮酸脱氢酶复合物(异三聚体)由三个不同的酶组成,分别是α- 酮酸脱氢酶(E1)、二氢硫酰基转酰基酶(E2)和二氢脂肪酰胺脱氢酶(E3)
    \n
    \"\"
    二氢脂肪酰胺脱氢酶从各个方向观察的构象
    \n

    可以注意到,由支链酮酸脱氢酶复合物中出来的酸已经是与辅酶 A 结合的酰基辅酶 A形态了,而正是这个无法进一步代谢的亚甲基环丙基乙酰辅酶 A导致了后续的一系列酶出了问题,最终导致了低血糖的发生。

    \n

    那么话讲到这里就有同学要问了:老师老师,这个东西到底是怎么导致酶出问题的啊?

    \n

    那我问你,糖异生要不要调控?如果要调控你说这样的话啊?那我问你,那你糖异生怎么调控那我问你……

    \n

    咳咳,不搞抽象了,糖异生的调控主要发生在丙酮酸羧化酶、磷酸烯醇式丙酮酸(PEP)羧激酶、1,6- 二磷酸果糖(F-1,6-BP)磷酸酶这三种酶中,其中丙酮酸羧化酶受到乙酰辅酶 A的正向调控和ADP的负向调控,PEP 羧激酶受ADP的负向调控,F-1,6-BP 磷酸酶受AMP、F-2,6-BP的负向调控和柠檬酸的正向调控。

    \n

    现在问题来了,在进行糖异生的情况下,乙酰辅酶 A 肯定不会是从糖中来,肯定是脂肪酸代谢的结果,但是脂肪酸要形成乙酰辅酶 A 就要进行β氧化,要进行β氧化就需要辅酶 A……诶等等,我辅酶 A 呢?丸辣,辅酶 A 全被变成亚甲基环丙基乙酰辅酶 A了!

    \n

    于是乎,糖异生就被一颗小小的荔枝搞瘫痪了。

    \n

    那么又有同学要问了:老师,用不了葡萄糖我用酮体还不行吗?酮体一样可以穿过血脑屏障啊?

    \n

    问得好,那我问你:酮体是由什么合成的?

    \n
    \"\"
    乙酰乙酸,一看就知道是两个乙酰辅酶 A 缩合而来的。
    \n
    \"\"
    丙酮,由乙酰乙酸自发或酶催化脱羧而来
    \n
    \"\"
    β- 羟基丁酸,由乙酰乙酸还原而来
    \n

    酮体都是由乙酰辅酶 A 直接合成为乙酰乙酰辅酶 A,然后形成 HMG-CoA(β- 羟基 -β- 甲基戊二酸单酰辅酶 A),最后再形成乙酰乙酸这一过程得到的,那么如果没有乙酰辅酶 A,拿什么去合成酮体呢?

    \n

    糖指望不上,酮体也合成不了,人不就低血糖倒了嘛,很正常。

    \n

    以上。

    \n
    \n

    这玩意有肝毒性,不能用来治糖尿病!!!

    \n

    这玩意也会抑制脂肪酸氧化(脂肪酸氧化需要先与辅酶 A 结合为脂酰辅酶 A),不能用来减肥!!!

    \n
    \n
    \n\n\n\n\n
    \n\n\n\n", + "category": "", + "link": "https://daily.zhihu.com/story/9782563", + "creator": "", + "pubDate": "Tue, 15 Jul 2025 08:27:03 +0800", + "enclosure": "", + "enclosureType": "", + "image": "", + "id": "", + "language": "", + "folder": "资讯", + "feed": "知乎", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "6b64eed1da2738771dce46122e27fcd1", + "highlights": [] + }, + { + "title": "孩子因基因突变出生几个月后眼睛变成蓝色,如何从医学角度解释这种现象?哪些因素会导致基因突变?", + "description": "
    \"头图\"/
    \n

    目前当事人已经说明,孩子的蓝眼是由瓦登伯格综合征(Waardenburg Syndrome)引起的。

    \n

    但仍然有网友玩梗说到”“这小孩怕不是混血”的玩笑式推测,如果单看结果,这种推测符合来自对“蓝眼=欧美血统”的印象。

    \n

    不过从医学角度来看,蓝色眼睛并非某一人种的专属特征,也不仅仅是基因突变带来的病理表现。

    \n

    有些宝宝一开始眼睛偏灰偏蓝,随着黑色素增加,会慢慢变深;而也有少数孩子,黑色素合成减少,眼睛反而变浅、变蓝。

    \n

    实际上,蓝眼睛的形成机制要复杂得多。除了特定基因突变导致的病理性浅色虹膜,更多时候,眼睛颜色取决于父母双方遗传给孩子的多个基因组合——这些基因共同决定了色素细胞的数量、活性以及色素的种类和沉积程度。

    \n

    生理性方面,可以以这个顺序来理解:虹膜→肤色 / 血统→黑色素→基因

    \n

    我们来先说下生理的蓝眼睛是如何形成的。新生儿的眼睛颜色和肤色都受到黑色素含量的影响。总体上,浅肤色人群(如白人)新生儿更可能出生时眼睛呈蓝色或灰色,而深肤色人群(如亚洲人、非洲人)则多出生时即为棕色或深色眼睛。

    \n

    一种观点认为,人类眼睛颜色的地理分布可能与光照强度有关。在阳光充足的赤道附近,人群普遍虹膜色素较深,可能有助于阻挡部分紫外线;而在高纬度地区,虹膜颜色较浅,或许有利于增加光线透过率。不过,这仍属于演化假说,目前尚无确凿证据说明人眼色进化是光照压力直接导致的。

    \n

    目前普遍认可虹膜的颜色主要由黑色素含量决定。当黑色素含量高时眼睛呈现棕色,含量低时则呈现蓝色或绿色,不同类型的黑色素以及色素体的密度和结构会影响最终颜色。而再深层次探讨,黑色素又由基因决定,浅肤色人群(如白人)主要是由于OCA2 和 HERC2 基因的变异。

    \n
    \"\"
    \n

    OCA2 编码 P 蛋白,HERC2 某些调控区变异可抑制 OCA2 表达,从而减少黑色素,导致蓝眼形成。简单的来说,就是携带 HERC2 基因的婴儿眼睛呈蓝色,并且倾向于保持蓝色;而携带 OCA2 基因的婴儿眼睛则呈绿色或棕色。

    \n

    Punnett Square(庞尼特方格 / 庞氏表)来预测宝宝的眼睛颜色是很多人常见的做法,但在现代遗传学视角下,这种方法只能作为一个非常初步、简化的工具,实际上没有万无一失的方法。

    \n
    \"\"
    \n

    比如以上这个眼睛颜色预测图(Punnett square )虽然第一组表明两个蓝眼睛的父母极生出棕色眼睛的孩子的概率很低,但如果父母双方都携带棕色眼睛的隐性 DNA 变异,他们的孩子仍然有可能遗传棕色眼睛,毕竟父母的眼睛颜色(表型)不等于他们的基因组合(基因型),在多基因性状中,还有隐藏的等位基因,此外还存在基因互作、调控表达、表观遗传等复杂机制。

    \n

    换句话说,孩子的眼睛颜色,不只是“妈妈是黑眼,爸爸是棕眼”这么简单的加法题,而是涉及复杂的遗传表达和调控机制,不可能完全准确预测。

    \n

    而在某些特殊情况下,比如像本新闻中的瓦登伯格综合征[1],则是由于调控色素合成的关键基因发生突变,导致虹膜黑色素缺失或显著减少,进而呈现出显著的浅蓝或异色双眼。这类改变,虽然稀有,但确实存在于亚洲人群中。

    \n

    瓦登伯格综合征并不是一个生理情况下的蓝眼睛,而是一种罕见的遗传性疾病,主要由基因突变引起,常见的相关基因包括 PAX3、MITF、SOX10 等。

    \n

    这些基因在胚胎发育过程中起着关键作用,当这些基因发生突变时,会影响黑色素细胞的正常发育和功能,产生包括、但不限于——色素沉着异常、听力系统发育障碍等等。当虹膜中黑色素含量减少,光线在虹膜表面发生散射,从而呈现出蓝色。也就是说,瓦登伯格综合征的蓝眼睛是疾病的表现形式之一。

    \n

    那还有没有其他变成蓝眼睛的可能原因呢?当然也有可能。

    \n

    白化病患者由于 OCA2 严重突变导致全身黑色素合成缺陷,虹膜淡蓝、灰色甚至透出眼底血管的颜色。由于患者虹膜因色素缺失,透光性增强,眼睛会对强光异常敏感,所以我们通常建议需佩戴防紫外线眼镜保护眼睛,这也是大家不常注意到他们虹膜的原因。

    \n

    还有一些欧美人的婴儿在出生时眼睛是蓝灰色(因为彩虹膜内的黑色素尚未完全生成或分布,其结构容易让光线散射,造成“蓝色外观”),等随着孩子慢慢长大(一般在出生后 6~12 个月内),随着黑色素合成的进行,眼睛会逐步变为棕色、绿色或保持蓝色。这个就和突变没什么关系了。

    \n

    此外也不能排除环境因素和染色体异常。怀孕期间如果母亲接触到放射线、有害化学物质、某些药物或病毒感染,也可能干扰胎儿 DNA 结构,引起突变。所以这也是为什么产科的医生非常强调孕妇进行遗传咨询和相关检测的原因。

    \n
    \n
    \n\n\n\n\n
    \n\n\n\n", + "content": "
    \"头图\"/
    \n

    目前当事人已经说明,孩子的蓝眼是由瓦登伯格综合征(Waardenburg Syndrome)引起的。

    \n

    但仍然有网友玩梗说到”“这小孩怕不是混血”的玩笑式推测,如果单看结果,这种推测符合来自对“蓝眼=欧美血统”的印象。

    \n

    不过从医学角度来看,蓝色眼睛并非某一人种的专属特征,也不仅仅是基因突变带来的病理表现。

    \n

    有些宝宝一开始眼睛偏灰偏蓝,随着黑色素增加,会慢慢变深;而也有少数孩子,黑色素合成减少,眼睛反而变浅、变蓝。

    \n

    实际上,蓝眼睛的形成机制要复杂得多。除了特定基因突变导致的病理性浅色虹膜,更多时候,眼睛颜色取决于父母双方遗传给孩子的多个基因组合——这些基因共同决定了色素细胞的数量、活性以及色素的种类和沉积程度。

    \n

    生理性方面,可以以这个顺序来理解:虹膜→肤色 / 血统→黑色素→基因

    \n

    我们来先说下生理的蓝眼睛是如何形成的。新生儿的眼睛颜色和肤色都受到黑色素含量的影响。总体上,浅肤色人群(如白人)新生儿更可能出生时眼睛呈蓝色或灰色,而深肤色人群(如亚洲人、非洲人)则多出生时即为棕色或深色眼睛。

    \n

    一种观点认为,人类眼睛颜色的地理分布可能与光照强度有关。在阳光充足的赤道附近,人群普遍虹膜色素较深,可能有助于阻挡部分紫外线;而在高纬度地区,虹膜颜色较浅,或许有利于增加光线透过率。不过,这仍属于演化假说,目前尚无确凿证据说明人眼色进化是光照压力直接导致的。

    \n

    目前普遍认可虹膜的颜色主要由黑色素含量决定。当黑色素含量高时眼睛呈现棕色,含量低时则呈现蓝色或绿色,不同类型的黑色素以及色素体的密度和结构会影响最终颜色。而再深层次探讨,黑色素又由基因决定,浅肤色人群(如白人)主要是由于OCA2 和 HERC2 基因的变异。

    \n
    \"\"
    \n

    OCA2 编码 P 蛋白,HERC2 某些调控区变异可抑制 OCA2 表达,从而减少黑色素,导致蓝眼形成。简单的来说,就是携带 HERC2 基因的婴儿眼睛呈蓝色,并且倾向于保持蓝色;而携带 OCA2 基因的婴儿眼睛则呈绿色或棕色。

    \n

    Punnett Square(庞尼特方格 / 庞氏表)来预测宝宝的眼睛颜色是很多人常见的做法,但在现代遗传学视角下,这种方法只能作为一个非常初步、简化的工具,实际上没有万无一失的方法。

    \n
    \"\"
    \n

    比如以上这个眼睛颜色预测图(Punnett square )虽然第一组表明两个蓝眼睛的父母极生出棕色眼睛的孩子的概率很低,但如果父母双方都携带棕色眼睛的隐性 DNA 变异,他们的孩子仍然有可能遗传棕色眼睛,毕竟父母的眼睛颜色(表型)不等于他们的基因组合(基因型),在多基因性状中,还有隐藏的等位基因,此外还存在基因互作、调控表达、表观遗传等复杂机制。

    \n

    换句话说,孩子的眼睛颜色,不只是“妈妈是黑眼,爸爸是棕眼”这么简单的加法题,而是涉及复杂的遗传表达和调控机制,不可能完全准确预测。

    \n

    而在某些特殊情况下,比如像本新闻中的瓦登伯格综合征[1],则是由于调控色素合成的关键基因发生突变,导致虹膜黑色素缺失或显著减少,进而呈现出显著的浅蓝或异色双眼。这类改变,虽然稀有,但确实存在于亚洲人群中。

    \n

    瓦登伯格综合征并不是一个生理情况下的蓝眼睛,而是一种罕见的遗传性疾病,主要由基因突变引起,常见的相关基因包括 PAX3、MITF、SOX10 等。

    \n

    这些基因在胚胎发育过程中起着关键作用,当这些基因发生突变时,会影响黑色素细胞的正常发育和功能,产生包括、但不限于——色素沉着异常、听力系统发育障碍等等。当虹膜中黑色素含量减少,光线在虹膜表面发生散射,从而呈现出蓝色。也就是说,瓦登伯格综合征的蓝眼睛是疾病的表现形式之一。

    \n

    那还有没有其他变成蓝眼睛的可能原因呢?当然也有可能。

    \n

    白化病患者由于 OCA2 严重突变导致全身黑色素合成缺陷,虹膜淡蓝、灰色甚至透出眼底血管的颜色。由于患者虹膜因色素缺失,透光性增强,眼睛会对强光异常敏感,所以我们通常建议需佩戴防紫外线眼镜保护眼睛,这也是大家不常注意到他们虹膜的原因。

    \n

    还有一些欧美人的婴儿在出生时眼睛是蓝灰色(因为彩虹膜内的黑色素尚未完全生成或分布,其结构容易让光线散射,造成“蓝色外观”),等随着孩子慢慢长大(一般在出生后 6~12 个月内),随着黑色素合成的进行,眼睛会逐步变为棕色、绿色或保持蓝色。这个就和突变没什么关系了。

    \n

    此外也不能排除环境因素和染色体异常。怀孕期间如果母亲接触到放射线、有害化学物质、某些药物或病毒感染,也可能干扰胎儿 DNA 结构,引起突变。所以这也是为什么产科的医生非常强调孕妇进行遗传咨询和相关检测的原因。

    \n
    \n
    \n\n\n\n\n
    \n\n\n\n", + "category": "", + "link": "https://daily.zhihu.com/story/9782571", + "creator": "", + "pubDate": "Tue, 15 Jul 2025 08:27:03 +0800", + "enclosure": "", + "enclosureType": "", + "image": "", + "id": "", + "language": "", + "folder": "资讯", + "feed": "知乎", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "2232eb02b1f04636ab86673493880117", + "highlights": [] + }, + { + "title": "瞎扯 · 如何正确地吐槽", + "description": "
    \"头图\"/
    \n\n
    \n
    @执笔顾长安 :奶酪体,荧光笔,每次考试必垫底; 狗爬体,借的笔,一问成绩六百几。
    \n
    \n\n
    \n
    @将浊 :不知道啊,学校里说南京打江苏,我就去了。
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \"\"\n
     
    \n
     
    \n
    \n
    \n
    \n
    \n
    \n
    \n
     
    \n
    \n
    \n\n
    \n
    @知知 :小时候想养猫,费尽千辛万苦说服家长。 当时给出的理由是:「猫长大了可以抓老鼠。」 结果猫长大了……他不会抓老鼠…… 有次听到奶奶和别人聊天说,这猫不会抓老鼠,干脆哪天送走得了。 给我急得,自己抓了三只老鼠,用绳子把它们勒死,再送到隔壁猫嘴下求(逼)他啃几口,再扔自己家猫窝里 第二天给我奶奶惊呆了,逢人就说 不抓而已,一抓就是三只……
    \n
    \n\n
    \n
    @柠檬螃小蟹 :初看此图当笑话,结果我们公司就是这么干的!
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \"\"\n
     
    \n
     
    \n
    \n
    \n
    \n
    \n
    \n
    \n
     
    \n
    \n
    \n\n
    \n
    @煮自己的唐僧 :你见过骨科手术嘛。 这么说吧。 骨科本质就是比较注重卫生的木匠。
    \n
    \n
    \n
    \n\n\n
    \n\n\n\n", + "content": "
    \"头图\"/
    \n\n
    \n
    @执笔顾长安 :奶酪体,荧光笔,每次考试必垫底; 狗爬体,借的笔,一问成绩六百几。
    \n
    \n\n
    \n
    @将浊 :不知道啊,学校里说南京打江苏,我就去了。
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \"\"\n
     
    \n
     
    \n
    \n
    \n
    \n
    \n
    \n
    \n
     
    \n
    \n
    \n\n
    \n
    @知知 :小时候想养猫,费尽千辛万苦说服家长。 当时给出的理由是:「猫长大了可以抓老鼠。」 结果猫长大了……他不会抓老鼠…… 有次听到奶奶和别人聊天说,这猫不会抓老鼠,干脆哪天送走得了。 给我急得,自己抓了三只老鼠,用绳子把它们勒死,再送到隔壁猫嘴下求(逼)他啃几口,再扔自己家猫窝里 第二天给我奶奶惊呆了,逢人就说 不抓而已,一抓就是三只……
    \n
    \n\n
    \n
    @柠檬螃小蟹 :初看此图当笑话,结果我们公司就是这么干的!
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \"\"\n
     
    \n
     
    \n
    \n
    \n
    \n
    \n
    \n
    \n
     
    \n
    \n
    \n\n
    \n
    @煮自己的唐僧 :你见过骨科手术嘛。 这么说吧。 骨科本质就是比较注重卫生的木匠。
    \n
    \n
    \n
    \n\n\n
    \n\n\n\n", + "category": "", + "link": "https://daily.zhihu.com/story/9782554", + "creator": "", + "pubDate": "Mon, 14 Jul 2025 08:27:04 +0800", + "enclosure": "", + "enclosureType": "", + "image": "", + "id": "", + "language": "", + "folder": "资讯", + "feed": "知乎", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "e31c61c2e82cba6d6292e51dd346799d", + "highlights": [] + }, + { + "title": "父母会嫉妒自己的子女吗?为什么?", + "description": "
    \"头图\"/
    \n
    “你也得吃点苦,才知道生活不容易。”
    “我小时候哪有你这待遇?”
    “我妈也从来不管我,我不也长大了?”
    “这有什么的,我也是这么被养大的,我不也好好吗?”
    \n

    心理咨询中,父母可能会这样说。但真的是这样吗,你真的“好好的”吗?这听起来像是在自证,实际上却往往是对伤痛的否认与传递。

    \n

    这些说辞的背后,是父母不愿被看见的伤痛,因为从未被真正处理过,就只能以另一种形式延续下去。心理创伤不会因为扛过来了就自动痊愈,反而可能成为父母控制孩子、否定孩子、阻碍孩子发展的潜在动力。

    \n

    而那些看起来毫无道理的控制与打压的行为,背后往往藏着人性中最复杂的情绪之一——父母对孩子的嫉妒。

    \n

    01 隐藏在爱背后的嫉妒

    \n

    “你还真会享受。”

    \n

    听起来好像是父母在感叹孩子的美好生活,但可能隐藏着嫉妒的情绪。更准确地说,是一种混杂了悲伤、羡慕、愤怒、失落、无力的复杂感受。

    \n

    当一个孩子有了父母从未拥有过的幸福、自由和选择权,这对父母来说,可能不只会带来欣慰的感觉,还可能是一种提醒:我从未拥有过这些,我失去了我的幸福,而你有了。

    \n

    就像一个小时候饿过很久的人,即使现在已经不用饿肚子了,但看到别的小孩丰盛地吃着饭,会不由自主地泛起不满:你咋这么幸运呢,凭什么?孩子的快乐,在一些父母眼中,是不公平的证据。

    \n

    他们感到自己的苦无人知晓,也会怀疑:“孩子是不是就此要离开我了?”他们焦虑,随着孩子越发独立自主和成功,自己可能就再也无法掌控这个孩子了。

    \n

    于是,孩子想走向光亮的一步,就被一句“我看也不过如此。”泼了冷水。孩子想分享自己的幸福,就换来一句“你可真会享福,我可享不到这些喽”的阴阳怪气。

    \n

    这种情绪的核心,不是不够爱孩子,而是创伤太深。

    \n

    02 “你比我幸福,那我算什么?”

    \n

    在一些家庭中,父母对子女的态度夹杂着微妙的竞争色彩。

    \n

    有时是回避或拒绝帮助:

    \n
    “我小时候也没人帮我。”
    \n

    有时是明目张胆的控制:

    \n
    “你听我的就不会错。”
    \n

    有时则是更隐秘的拉扯:

    \n
    “你以为你现在就比我强了吗?”
    \n

    他们并不是在计较一顿饭或者一句赞扬。

    \n

    他们在意的可能是:你是否正在活出我从未拥有过的人生?这会让一些父母觉得自己失败、无力、被超越,而这一切,恰好都是他们最害怕面对的情绪。

    \n

    最常见的解决方式不是面对自己的伤痛和失败,因为这太痛苦了,更轻松的方法是把孩子“拉下来”,用否定、指责、打压的方式,恢复自己对孩子的掌控感。

    \n

    潜意识中,如果孩子永远不超过他们、依赖他们,他们会更好受。

    \n

    03 孩子不是附属品,更不是补偿

    \n

    一位母亲说:“孩子拥有了我没拥有过的一切,就像是我的幸福被抢走了一样。

    \n

    这并不是她不爱孩子,而是她内心那个委屈的小孩从未有机会疗愈。

    \n

    自体心理学中称之为“自体虚弱”,他们从小缺乏被理解、被肯定的经验,内心长时间处于压抑和否认的状态,所以只能通过孩子的依赖,来补偿自己的匮乏。当孩子不再需要他们时,他们就会感到无比痛苦、无价值,甚至失控。

    \n

    于是,一些父母会将孩子当作自己的延伸,必须听话、忠诚、感恩。他们很难接受孩子的分化、独立,更不能接受孩子“不属于自己”。

    \n

    但孩子是独立的个体,他们不是父母未竟梦想的载体,也不是父母人生的安慰奖。

    \n

    04 “我爸妈见不得我好”,这不是错觉

    \n

    “我爸妈见不得我开心,每次我有点好事,他们就要来挑刺。”

    \n

    “有一次我正在超级开心的疯玩儿,妈妈突然过来揪住我说:我小时候从来不会像你这样胡闹。”

    \n

    这些声音反映了孩子真实的情感创伤:自己最初的欢欣和成就,常常换来的不是父母的共鸣和欣慰,而是冷漠与怀疑,甚至打压。

    \n

    于是这些孩子开始学会报喜不报忧、降低存在感,学会为了换取爱而牺牲自己的一部分。久而久之,成就带来愧疚,幸福也会带来不安。

    \n

    这并不正常,这是因为你习惯了隐藏,害怕真实的自己会“太刺眼”,引来不满或攻击。

    \n

    05 “背叛”父母,是一种必要的成长

    \n

    如果你成长在这样的环境中,请先告诉自己一件事:你没有疯,你没错。

    \n

    你所感受到的那些嫉妒、控制、冷漠、否定,是真实存在的情感,它们对你产生了影响,而这不是你的责任。你不需要强迫自己原谅和理解,不需要合理化那些伤害。你首先需要的是:看见自己,接纳自己。

    \n

    看见父母也是受伤的个体,但不代表你要继续承受他们的伤痛。看见那些控制背后的无力和痛苦,但不代表你要牺牲自己来安抚他们的脆弱。看见那句“我小时候也这样”,其实不是教育方式的证明,而是创伤的延续。

    \n

    改变,不是反抗父母,而是开始选择为自己而活。当你试图做回自己,找到自己时,你可能会感到一种对父母的背叛感,但这不是对父母的抛弃,而是对自己的诚实,不必愧疚。

    \n

    你会开始设定边界,明确告诉自己哪些是你愿意接受的,哪些不是。你会不再急于讨好,不再急于证明,不再把自己的生活建立在“获得他们认可”的基础上。

    \n

    学着为自己的情绪负责,不再无条件承接他们的情绪。当遭遇贬低、抱怨或嘲讽时,区分“这是他们的课题”而非你的缺陷,不再自责、压抑、或陷入争辩的漩涡。

    \n

    你会发现,当你真的不再执着于父母的爱与认可,你反而更有能力去理解和拥抱他们,也更能看到他们曾经的局限。

    \n

    因为你终于从他们的影子里,走出来了。

    \n

    06 孩子是镜子,让我们看见自己

    \n

    对于父母来说,孩子常常像一面镜子,照见我们内心深处那些未曾被满足的愿望。看到孩子无拘无束地表达自己、追求喜欢的事物,我们有时会泛起复杂的情绪。那不是恨,而是对自己某些被压抑太久的渴望的隐隐心疼。

    \n

    孩子的自由,是我未曾拥有过的自由。孩子的快乐,是我曾被剥夺过的快乐。孩子的强大,是我渴望又无法实现的强大。

    \n

    但这面镜子并不是来指责的,而是来提醒的。孩子不仅带着我们向前走,也让我们回头看,他们唤醒了我们未竟的部分。当我们愿意看见自己,承认那份遗憾,也许就能不再被这些情绪带着走。

    \n

    你眼中看似比你幸福一百倍的孩子,也必然有他们自己需要去面对的痛苦和缺憾。在苦难面前,父母和孩子都是无辜的、需要被爱的人。只有当你愿意面对那个不完美的自己,接纳自己缺憾的人生,才能真正停止对孩子的控制和投射。

    \n

    而我们自己,也才能开始活出属于自己的人生。

    \n

    * 相关阅读推荐

    \n

    有一种心理叫「不允许自己快乐」,这到底是种什么心态? - 知乎

    \n

    有一种心理叫「不允许自己快乐」,这到底是种什么心态?

    \n
    \"\"
    \n

    文 / 红树林心理咨询中心 吴凡

    \n
    \n
    \n\n\n\n\n
    \n\n\n\n

    ", + "content": "
    \"头图\"/
    \n
    “你也得吃点苦,才知道生活不容易。”
    “我小时候哪有你这待遇?”
    “我妈也从来不管我,我不也长大了?”
    “这有什么的,我也是这么被养大的,我不也好好吗?”
    \n

    心理咨询中,父母可能会这样说。但真的是这样吗,你真的“好好的”吗?这听起来像是在自证,实际上却往往是对伤痛的否认与传递。

    \n

    这些说辞的背后,是父母不愿被看见的伤痛,因为从未被真正处理过,就只能以另一种形式延续下去。心理创伤不会因为扛过来了就自动痊愈,反而可能成为父母控制孩子、否定孩子、阻碍孩子发展的潜在动力。

    \n

    而那些看起来毫无道理的控制与打压的行为,背后往往藏着人性中最复杂的情绪之一——父母对孩子的嫉妒。

    \n

    01 隐藏在爱背后的嫉妒

    \n

    “你还真会享受。”

    \n

    听起来好像是父母在感叹孩子的美好生活,但可能隐藏着嫉妒的情绪。更准确地说,是一种混杂了悲伤、羡慕、愤怒、失落、无力的复杂感受。

    \n

    当一个孩子有了父母从未拥有过的幸福、自由和选择权,这对父母来说,可能不只会带来欣慰的感觉,还可能是一种提醒:我从未拥有过这些,我失去了我的幸福,而你有了。

    \n

    就像一个小时候饿过很久的人,即使现在已经不用饿肚子了,但看到别的小孩丰盛地吃着饭,会不由自主地泛起不满:你咋这么幸运呢,凭什么?孩子的快乐,在一些父母眼中,是不公平的证据。

    \n

    他们感到自己的苦无人知晓,也会怀疑:“孩子是不是就此要离开我了?”他们焦虑,随着孩子越发独立自主和成功,自己可能就再也无法掌控这个孩子了。

    \n

    于是,孩子想走向光亮的一步,就被一句“我看也不过如此。”泼了冷水。孩子想分享自己的幸福,就换来一句“你可真会享福,我可享不到这些喽”的阴阳怪气。

    \n

    这种情绪的核心,不是不够爱孩子,而是创伤太深。

    \n

    02 “你比我幸福,那我算什么?”

    \n

    在一些家庭中,父母对子女的态度夹杂着微妙的竞争色彩。

    \n

    有时是回避或拒绝帮助:

    \n
    “我小时候也没人帮我。”
    \n

    有时是明目张胆的控制:

    \n
    “你听我的就不会错。”
    \n

    有时则是更隐秘的拉扯:

    \n
    “你以为你现在就比我强了吗?”
    \n

    他们并不是在计较一顿饭或者一句赞扬。

    \n

    他们在意的可能是:你是否正在活出我从未拥有过的人生?这会让一些父母觉得自己失败、无力、被超越,而这一切,恰好都是他们最害怕面对的情绪。

    \n

    最常见的解决方式不是面对自己的伤痛和失败,因为这太痛苦了,更轻松的方法是把孩子“拉下来”,用否定、指责、打压的方式,恢复自己对孩子的掌控感。

    \n

    潜意识中,如果孩子永远不超过他们、依赖他们,他们会更好受。

    \n

    03 孩子不是附属品,更不是补偿

    \n

    一位母亲说:“孩子拥有了我没拥有过的一切,就像是我的幸福被抢走了一样。

    \n

    这并不是她不爱孩子,而是她内心那个委屈的小孩从未有机会疗愈。

    \n

    自体心理学中称之为“自体虚弱”,他们从小缺乏被理解、被肯定的经验,内心长时间处于压抑和否认的状态,所以只能通过孩子的依赖,来补偿自己的匮乏。当孩子不再需要他们时,他们就会感到无比痛苦、无价值,甚至失控。

    \n

    于是,一些父母会将孩子当作自己的延伸,必须听话、忠诚、感恩。他们很难接受孩子的分化、独立,更不能接受孩子“不属于自己”。

    \n

    但孩子是独立的个体,他们不是父母未竟梦想的载体,也不是父母人生的安慰奖。

    \n

    04 “我爸妈见不得我好”,这不是错觉

    \n

    “我爸妈见不得我开心,每次我有点好事,他们就要来挑刺。”

    \n

    “有一次我正在超级开心的疯玩儿,妈妈突然过来揪住我说:我小时候从来不会像你这样胡闹。”

    \n

    这些声音反映了孩子真实的情感创伤:自己最初的欢欣和成就,常常换来的不是父母的共鸣和欣慰,而是冷漠与怀疑,甚至打压。

    \n

    于是这些孩子开始学会报喜不报忧、降低存在感,学会为了换取爱而牺牲自己的一部分。久而久之,成就带来愧疚,幸福也会带来不安。

    \n

    这并不正常,这是因为你习惯了隐藏,害怕真实的自己会“太刺眼”,引来不满或攻击。

    \n

    05 “背叛”父母,是一种必要的成长

    \n

    如果你成长在这样的环境中,请先告诉自己一件事:你没有疯,你没错。

    \n

    你所感受到的那些嫉妒、控制、冷漠、否定,是真实存在的情感,它们对你产生了影响,而这不是你的责任。你不需要强迫自己原谅和理解,不需要合理化那些伤害。你首先需要的是:看见自己,接纳自己。

    \n

    看见父母也是受伤的个体,但不代表你要继续承受他们的伤痛。看见那些控制背后的无力和痛苦,但不代表你要牺牲自己来安抚他们的脆弱。看见那句“我小时候也这样”,其实不是教育方式的证明,而是创伤的延续。

    \n

    改变,不是反抗父母,而是开始选择为自己而活。当你试图做回自己,找到自己时,你可能会感到一种对父母的背叛感,但这不是对父母的抛弃,而是对自己的诚实,不必愧疚。

    \n

    你会开始设定边界,明确告诉自己哪些是你愿意接受的,哪些不是。你会不再急于讨好,不再急于证明,不再把自己的生活建立在“获得他们认可”的基础上。

    \n

    学着为自己的情绪负责,不再无条件承接他们的情绪。当遭遇贬低、抱怨或嘲讽时,区分“这是他们的课题”而非你的缺陷,不再自责、压抑、或陷入争辩的漩涡。

    \n

    你会发现,当你真的不再执着于父母的爱与认可,你反而更有能力去理解和拥抱他们,也更能看到他们曾经的局限。

    \n

    因为你终于从他们的影子里,走出来了。

    \n

    06 孩子是镜子,让我们看见自己

    \n

    对于父母来说,孩子常常像一面镜子,照见我们内心深处那些未曾被满足的愿望。看到孩子无拘无束地表达自己、追求喜欢的事物,我们有时会泛起复杂的情绪。那不是恨,而是对自己某些被压抑太久的渴望的隐隐心疼。

    \n

    孩子的自由,是我未曾拥有过的自由。孩子的快乐,是我曾被剥夺过的快乐。孩子的强大,是我渴望又无法实现的强大。

    \n

    但这面镜子并不是来指责的,而是来提醒的。孩子不仅带着我们向前走,也让我们回头看,他们唤醒了我们未竟的部分。当我们愿意看见自己,承认那份遗憾,也许就能不再被这些情绪带着走。

    \n

    你眼中看似比你幸福一百倍的孩子,也必然有他们自己需要去面对的痛苦和缺憾。在苦难面前,父母和孩子都是无辜的、需要被爱的人。只有当你愿意面对那个不完美的自己,接纳自己缺憾的人生,才能真正停止对孩子的控制和投射。

    \n

    而我们自己,也才能开始活出属于自己的人生。

    \n

    * 相关阅读推荐

    \n

    有一种心理叫「不允许自己快乐」,这到底是种什么心态? - 知乎

    \n

    有一种心理叫「不允许自己快乐」,这到底是种什么心态?

    \n
    \"\"
    \n

    文 / 红树林心理咨询中心 吴凡

    \n
    \n
    \n\n\n\n\n
    \n\n\n\n

    ", + "category": "", + "link": "https://daily.zhihu.com/story/9782536", + "creator": "", + "pubDate": "Mon, 14 Jul 2025 08:27:04 +0800", + "enclosure": "", + "enclosureType": "", + "image": "", + "id": "", + "language": "", + "folder": "资讯", + "feed": "知乎", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "e1cdb642d7fa16d2ca26c309c22c9f18", + "highlights": [] + }, + { + "title": "如果一个行星有两个或者多个大型封闭水域,那么这些水域到底算是海洋还是湖?", + "description": "
    \"头图\"/
    \n

    谢邀,私以为现代地球科学(这是一门学科名称)当中海洋和湖的定义适用范围还是比较限定于地球这一独特的环境,所以这个问题确实挺值得讨论的,我认为异星的海和湖或许需要因“异”制宜来看待,不能按地球的湖海原本定义生搬硬套。那些广袤无垠,站在岸边无法一眼看到对岸的巨型水体,称之为海又何妨呢。

    \n
    \"\"
    NASA 拍摄的地球照片,比起“陆地”覆盖地球表面积的分量,或许我们这颗星球应该叫做“海球”。
    \n

    众所周知,地球上各大洋是联通的,无比宽广的水体包围着作为少数的陆地,海洋占据了约 71%的地球表面积;正是因为海洋无比广大,而且还相互连通,连续水体的大小压倒性的压制住了其他次要分类条件,故我们可以把这个连续的水体称之为海,通过水体的连续性来划分海洋和内陆湖泊。

    \n
    \"\"
    为了展现海洋连贯性而创作的 Spilhaus 投影,网图侵删。
    \n

    当然,里海也被称之为海,不过就因为是否与大洋相连续这一问题,他被划分到了内陆咸水湖当中。里海他曾经也是和包裹陆地的那个巨大连续水体相连的,后面因为地质变化,特提斯海的遗存与大洋之间的联系被切断,就变成了帕拉特提斯湖;帕拉特提斯湖曾是世界上最大湖,不过后续的地质变化也让帕拉特提斯湖其中一部分寻回了和大海的联系,形成了现在的黑海,而另一个大的部分就形成了现在的里海。

    \n
    \"\"
    帕拉特提斯湖巨湖,网图侵删。
    \n

    因为里海曾经作为大洋的一部分,其环境生态和海洋十分相似,水质也是类似海洋的咸水,甚至还有大量海栖生物生活。如果忽略与大洋之间的连续这一条件,里海有着大量与其他海洋的相似性,里海当然是可以被归纳成海,这也是其名称当中保留了“海”的一个原因。

    \n
    \"\"
    里海海豹,截取自 ICUN 的网站,该物种现在因为生存环境的恶化陷入濒危。
    \n

    要注意的是,这里的连续性应不止是“空间”上的连续,海水成分和咸度与密度等性状也存在一定的连续性。虽然有时候海水当中也会存在密度骤变的“负密度梯度跃变层”这样的特殊的“海底断崖”(会让潜艇发生事故)以及不同海域之间可能也会存在一定的分异,但是一般而言海水的密度、成分、咸度等还是变化幅度较小呈现一定连续性的,这些特征都和淡水河湖等水体存在不小的差异。

    \n
    \"\"
    Landsat 5 卫星在 2010 年拍摄的长江入海口遥感图像(LT51180382010313BJC00),色彩是合成的假彩色有所失真,但是还是可以看到长江和东海存在明显的过渡带与差异。
    \n

    虽然海洋和河流的水域切分还是很难明确画出一个明确界限的,且因为潮汐洋流等原因,江河和海洋的过渡带还是动态变化的,甚至海洋还会发生海水倒灌侵占河流(如海河据称就是因为在现代造闸之前经常被大量海水侵入导致河水呈现出咸苦等特征而得名的),但明确江河与海的界限可能最后还可能更接近水力学与分形的问题,与题关系不大不在展开。

    \n

    扯远了,回归正题,前述的基于地球上海洋连续性的判定方式,抑或是将星球上最大的连续水体称之为海的行为,在异星球,恐怕就失去了意义;地球的海洋肯定无法超距连接到异星球的水体,而其他星球上有着占据表面超一半的水体的情况也可能不多见,题设条件显然也不满足这样的条件。

    \n

    在探索外层空间的过程中,外星球“地理”恐怕还是得用一些不局限于地球的方式来进行判定。不过这方面的建设我也不是非常了解,但是可以知道现在的学者们在地球外将很多水体归类成海洋,哪怕这些“海洋”和地球海洋特征大相径庭。

    \n

    木卫二大概是一个与地球海洋相近的例子(虽然是和题设不同),其冰下可能存在一个能包裹全球的巨型连续水体,而且可能是含氯化钠的咸水。如果学者们的拟构的木卫结构为真,在水体的覆盖面积和连续性上,木卫二的冰下巨型水体那可是当之无愧的海洋。

    \n
    \"\"
    木卫二“欧罗巴”的构造推测,可能其表层的冰壳下存在一个非常深邃包裹全纬线的海洋。(网图侵删)
    \n

    当然木卫二的“海洋”是在冰下,在环境上与地球海洋还是大相径庭,这也不影响学者们将其归纳成海洋,顺带一说有不少学者期待能在上面找到生命迹象。这样的“海洋”不止会出现在在木卫二,土卫二、木卫三和木卫四都可能存在。

    \n
    \"\"
    木卫二的冰下海洋结构示意图,网图侵删。
    \n

    书归正题,回到题设当中来看,“如果一个行星几个水体都很广阔且互不连通呢,或者不连通的水体有大有小,该怎么定义湖和海”,这样能占据一半星球表面积具有压倒性占比的连续水体应并不存在,所以地球的经验就很难套用过来了。

    \n

    在这种的特殊限定条件下,缺失了能压倒性压制其他条件的那个最重要的条件(海洋的面积),仔细分类海和湖可能意义并不会非常大,或许,这将会变成一种归纳的问题与命名学的问题。

    \n

    没有那个压倒性的条件,我们可以根据水是否含有氯化钠、氯化钠的含量、水能否直接给人饮用、是否可以放养海栖生物、密度是否大于人体等众多条件分门别类的分出一大堆水体种类,分类为“湖”“海”“亚湖”“巨湖”“微洋”“淡海”“海海”等一系列乱七八糟的种类,而且这样的种类可能除了特定研究并无太大的意义。

    \n

    有人说从有无“内外流”河来区分,但是外流本身就是基于已经确定了海洋后以更大面积的海洋为外所定下的概念,基于内外流来区分海洋只会是循环论证。缺少了面积断崖式领先的水体,哪会有“内外”之分呢?甚至通过 Spilhaus 投影来理解地球,我们甚至能认为海洋被陆地包裹着,都不需要题设的补充条件把太平洋围起来。而且地球 46 亿年的海陆变迁也非常巨大,往昔的特提斯海今天变成了特提斯成山带(青藏高原就是这个成山带的重要现今组成部分)和诸多海迹湖,哪怕地球的海洋也不是一成不变的。

    \n
    \"\"
    古特提斯洋,在“盘古大陆”(Pangea,直译泛大陆)时期作为被大陆半包围的海洋而存在,因地质运动昔日的海洋变成了今天的高山峻岭和一些海迹湖。(网图侵删)
    \n

    而从命名学的方式来看,我认为只要是广袤无垠一眼无法看到对岸的巨型水体,都能可以叫成海,而这种方式其实自古就存在,甚至不少语言当中湖海曾经用同一个词就能表示,抑或是能互相混用取代;汉语当中也有很多把湖泊命名为海的地名留存,我还曾经在昆明滇池(亦名滇海)畔看过“海鸥”(红嘴鸥)。

    \n

    这些只是我的浅薄之见,或许以后会出现什么划定异星湖海的必要吧,但我觉得题设当中出现的多个不连续的水体都叫海其实并没有问题,不需要在意异星的湖海之分,真要分可能还是大小和成分咸淡比较重要吧。而且作为地名而言,一个 XX 海在标志地点和位置之外,他究竟是不是海,重要吗?16 世纪时就有学者给月球朝向地球的面起了一堆湖海的名字,并且这些名字在今天仍然在使用。

    \n
    \"\"
    月球表面有大量以“海”命名的地理单元,被统称为“月海”。(网图侵删)
    \n

    说句题外话,土卫六和金星表面存在不少流动液体,其表面和内部的流动液体被不少学者称为“甲烷海”(或许成分还有其他有机物),也就是说是否可以认为主要成分是否是“H2O”也不干扰分类湖海呢?你的下一个海,又何必是“海”?

    \n
    \n
    \n\n\n\n\n
    \n\n\n\n", + "content": "
    \"头图\"/
    \n

    谢邀,私以为现代地球科学(这是一门学科名称)当中海洋和湖的定义适用范围还是比较限定于地球这一独特的环境,所以这个问题确实挺值得讨论的,我认为异星的海和湖或许需要因“异”制宜来看待,不能按地球的湖海原本定义生搬硬套。那些广袤无垠,站在岸边无法一眼看到对岸的巨型水体,称之为海又何妨呢。

    \n
    \"\"
    NASA 拍摄的地球照片,比起“陆地”覆盖地球表面积的分量,或许我们这颗星球应该叫做“海球”。
    \n

    众所周知,地球上各大洋是联通的,无比宽广的水体包围着作为少数的陆地,海洋占据了约 71%的地球表面积;正是因为海洋无比广大,而且还相互连通,连续水体的大小压倒性的压制住了其他次要分类条件,故我们可以把这个连续的水体称之为海,通过水体的连续性来划分海洋和内陆湖泊。

    \n
    \"\"
    为了展现海洋连贯性而创作的 Spilhaus 投影,网图侵删。
    \n

    当然,里海也被称之为海,不过就因为是否与大洋相连续这一问题,他被划分到了内陆咸水湖当中。里海他曾经也是和包裹陆地的那个巨大连续水体相连的,后面因为地质变化,特提斯海的遗存与大洋之间的联系被切断,就变成了帕拉特提斯湖;帕拉特提斯湖曾是世界上最大湖,不过后续的地质变化也让帕拉特提斯湖其中一部分寻回了和大海的联系,形成了现在的黑海,而另一个大的部分就形成了现在的里海。

    \n
    \"\"
    帕拉特提斯湖巨湖,网图侵删。
    \n

    因为里海曾经作为大洋的一部分,其环境生态和海洋十分相似,水质也是类似海洋的咸水,甚至还有大量海栖生物生活。如果忽略与大洋之间的连续这一条件,里海有着大量与其他海洋的相似性,里海当然是可以被归纳成海,这也是其名称当中保留了“海”的一个原因。

    \n
    \"\"
    里海海豹,截取自 ICUN 的网站,该物种现在因为生存环境的恶化陷入濒危。
    \n

    要注意的是,这里的连续性应不止是“空间”上的连续,海水成分和咸度与密度等性状也存在一定的连续性。虽然有时候海水当中也会存在密度骤变的“负密度梯度跃变层”这样的特殊的“海底断崖”(会让潜艇发生事故)以及不同海域之间可能也会存在一定的分异,但是一般而言海水的密度、成分、咸度等还是变化幅度较小呈现一定连续性的,这些特征都和淡水河湖等水体存在不小的差异。

    \n
    \"\"
    Landsat 5 卫星在 2010 年拍摄的长江入海口遥感图像(LT51180382010313BJC00),色彩是合成的假彩色有所失真,但是还是可以看到长江和东海存在明显的过渡带与差异。
    \n

    虽然海洋和河流的水域切分还是很难明确画出一个明确界限的,且因为潮汐洋流等原因,江河和海洋的过渡带还是动态变化的,甚至海洋还会发生海水倒灌侵占河流(如海河据称就是因为在现代造闸之前经常被大量海水侵入导致河水呈现出咸苦等特征而得名的),但明确江河与海的界限可能最后还可能更接近水力学与分形的问题,与题关系不大不在展开。

    \n

    扯远了,回归正题,前述的基于地球上海洋连续性的判定方式,抑或是将星球上最大的连续水体称之为海的行为,在异星球,恐怕就失去了意义;地球的海洋肯定无法超距连接到异星球的水体,而其他星球上有着占据表面超一半的水体的情况也可能不多见,题设条件显然也不满足这样的条件。

    \n

    在探索外层空间的过程中,外星球“地理”恐怕还是得用一些不局限于地球的方式来进行判定。不过这方面的建设我也不是非常了解,但是可以知道现在的学者们在地球外将很多水体归类成海洋,哪怕这些“海洋”和地球海洋特征大相径庭。

    \n

    木卫二大概是一个与地球海洋相近的例子(虽然是和题设不同),其冰下可能存在一个能包裹全球的巨型连续水体,而且可能是含氯化钠的咸水。如果学者们的拟构的木卫结构为真,在水体的覆盖面积和连续性上,木卫二的冰下巨型水体那可是当之无愧的海洋。

    \n
    \"\"
    木卫二“欧罗巴”的构造推测,可能其表层的冰壳下存在一个非常深邃包裹全纬线的海洋。(网图侵删)
    \n

    当然木卫二的“海洋”是在冰下,在环境上与地球海洋还是大相径庭,这也不影响学者们将其归纳成海洋,顺带一说有不少学者期待能在上面找到生命迹象。这样的“海洋”不止会出现在在木卫二,土卫二、木卫三和木卫四都可能存在。

    \n
    \"\"
    木卫二的冰下海洋结构示意图,网图侵删。
    \n

    书归正题,回到题设当中来看,“如果一个行星几个水体都很广阔且互不连通呢,或者不连通的水体有大有小,该怎么定义湖和海”,这样能占据一半星球表面积具有压倒性占比的连续水体应并不存在,所以地球的经验就很难套用过来了。

    \n

    在这种的特殊限定条件下,缺失了能压倒性压制其他条件的那个最重要的条件(海洋的面积),仔细分类海和湖可能意义并不会非常大,或许,这将会变成一种归纳的问题与命名学的问题。

    \n

    没有那个压倒性的条件,我们可以根据水是否含有氯化钠、氯化钠的含量、水能否直接给人饮用、是否可以放养海栖生物、密度是否大于人体等众多条件分门别类的分出一大堆水体种类,分类为“湖”“海”“亚湖”“巨湖”“微洋”“淡海”“海海”等一系列乱七八糟的种类,而且这样的种类可能除了特定研究并无太大的意义。

    \n

    有人说从有无“内外流”河来区分,但是外流本身就是基于已经确定了海洋后以更大面积的海洋为外所定下的概念,基于内外流来区分海洋只会是循环论证。缺少了面积断崖式领先的水体,哪会有“内外”之分呢?甚至通过 Spilhaus 投影来理解地球,我们甚至能认为海洋被陆地包裹着,都不需要题设的补充条件把太平洋围起来。而且地球 46 亿年的海陆变迁也非常巨大,往昔的特提斯海今天变成了特提斯成山带(青藏高原就是这个成山带的重要现今组成部分)和诸多海迹湖,哪怕地球的海洋也不是一成不变的。

    \n
    \"\"
    古特提斯洋,在“盘古大陆”(Pangea,直译泛大陆)时期作为被大陆半包围的海洋而存在,因地质运动昔日的海洋变成了今天的高山峻岭和一些海迹湖。(网图侵删)
    \n

    而从命名学的方式来看,我认为只要是广袤无垠一眼无法看到对岸的巨型水体,都能可以叫成海,而这种方式其实自古就存在,甚至不少语言当中湖海曾经用同一个词就能表示,抑或是能互相混用取代;汉语当中也有很多把湖泊命名为海的地名留存,我还曾经在昆明滇池(亦名滇海)畔看过“海鸥”(红嘴鸥)。

    \n

    这些只是我的浅薄之见,或许以后会出现什么划定异星湖海的必要吧,但我觉得题设当中出现的多个不连续的水体都叫海其实并没有问题,不需要在意异星的湖海之分,真要分可能还是大小和成分咸淡比较重要吧。而且作为地名而言,一个 XX 海在标志地点和位置之外,他究竟是不是海,重要吗?16 世纪时就有学者给月球朝向地球的面起了一堆湖海的名字,并且这些名字在今天仍然在使用。

    \n
    \"\"
    月球表面有大量以“海”命名的地理单元,被统称为“月海”。(网图侵删)
    \n

    说句题外话,土卫六和金星表面存在不少流动液体,其表面和内部的流动液体被不少学者称为“甲烷海”(或许成分还有其他有机物),也就是说是否可以认为主要成分是否是“H2O”也不干扰分类湖海呢?你的下一个海,又何必是“海”?

    \n
    \n
    \n\n\n\n\n
    \n\n\n\n", + "category": "", + "link": "https://daily.zhihu.com/story/9782544", + "creator": "", + "pubDate": "Mon, 14 Jul 2025 08:27:04 +0800", + "enclosure": "", + "enclosureType": "", + "image": "", + "id": "", + "language": "", + "folder": "资讯", + "feed": "知乎", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "a97ef9968a92ae187b5b89a1814fc818", + "highlights": [] + }, + { + "title": "孩子只在父母面前「情绪失控」,在外却近乎「完美」,正常吗?该如何引导?", + "description": "
    \"头图\"/
    \n

    弗洛伊德认为:“攻击性如同是一个人的生命力,如果一个人不能活出自己的攻击性,就会出现心理问题。”

    \n

    即使环境很糟糕,只要一个人能够正常表达攻击性,就能够保护自我不受外界蹂躏。

    \n

    心理学研究表明:孩子在很小的时候会强烈地依恋父母或其他养育者,当孩子接触外部世界时,失去这种依恋会使他产生一种不安全感,从而使孩子胆小。

    \n

    但是如果孩子上学以后对外界表现出过分“完美”“懂事”“胆怯”,很大可能就是家庭教育的问题。

    \n

    我邻居家的孩子就是这样,在家里,脾气大得惊人。和妈妈顶嘴,对弟弟妹妹呼来喝去,翻脸比翻书还快。

    \n

    爸妈一说他一句,他能当场怼回几句,搞得全家都不安生。

    \n

    可一旦出了门,到了学校或者外人面前,立马变脸。

    \n

    不说话,胆小,做事缩手缩脚,老师点个名都吓一跳,像换了个人。

    \n

    说白了,孩子在家里发横,是因为他知道自己“横得起”;在外面发怂,是因为他不知道能不能“撑得住”。

    \n

    而这种反差,很多时候不是性格问题,是家庭结构的问题。

    \n

    家长要反思自己给孩子是否创造了足够安全的生活环境。例如:夫妻之间的吵架、对孩子言语的伤害或者是过度保护孩子,都是有可能让孩子出现这一表现的原因。

    \n

    如果想要改变,从这五点做起吧!

    \n

    一、不过分溺爱,做有原则有底线的父母

    \n

    “爱孩子”和“立规矩”,从来不是一道单选题。

    \n

    明确规则,不被孩子牵着鼻子走,不能因为孩子哭闹,就主动打破规则的执行,和善而坚定地对孩子说“不”。让孩子明白,边界感的所在。

    \n
    \"\"
    在孩子的成长过程中,要让他知道:除了爱之外,还要有敬畏
    \n

    二、每天花 15 分钟的时间和孩子聊天,问问他,学校都发生了什么。

    \n

    三、每周抽时间给孩子拍一段视频,让他听自己的声音,看自己的表现,发现问题及时调整。

    \n
    \"\"
    \n

    四、周末邀请孩子的好朋友到家里来玩,给孩子创造多种社交机会和条件,提升孩子的社交能力。

    \n

    五、在孩子愿意的前提下,给孩子创造一些上台表演的机会,通过正向的实践提升孩子的自信心。

    \n
    \"\"
    提升孩子自信心的 5 句话
    \n

    亲测,这几个方法对提高孩子自信心,让孩子变得有原则有担当非常有帮助。

    \n
    \n
    \n\n\n\n\n
    \n\n\n\n", + "content": "
    \"头图\"/
    \n

    弗洛伊德认为:“攻击性如同是一个人的生命力,如果一个人不能活出自己的攻击性,就会出现心理问题。”

    \n

    即使环境很糟糕,只要一个人能够正常表达攻击性,就能够保护自我不受外界蹂躏。

    \n

    心理学研究表明:孩子在很小的时候会强烈地依恋父母或其他养育者,当孩子接触外部世界时,失去这种依恋会使他产生一种不安全感,从而使孩子胆小。

    \n

    但是如果孩子上学以后对外界表现出过分“完美”“懂事”“胆怯”,很大可能就是家庭教育的问题。

    \n

    我邻居家的孩子就是这样,在家里,脾气大得惊人。和妈妈顶嘴,对弟弟妹妹呼来喝去,翻脸比翻书还快。

    \n

    爸妈一说他一句,他能当场怼回几句,搞得全家都不安生。

    \n

    可一旦出了门,到了学校或者外人面前,立马变脸。

    \n

    不说话,胆小,做事缩手缩脚,老师点个名都吓一跳,像换了个人。

    \n

    说白了,孩子在家里发横,是因为他知道自己“横得起”;在外面发怂,是因为他不知道能不能“撑得住”。

    \n

    而这种反差,很多时候不是性格问题,是家庭结构的问题。

    \n

    家长要反思自己给孩子是否创造了足够安全的生活环境。例如:夫妻之间的吵架、对孩子言语的伤害或者是过度保护孩子,都是有可能让孩子出现这一表现的原因。

    \n

    如果想要改变,从这五点做起吧!

    \n

    一、不过分溺爱,做有原则有底线的父母

    \n

    “爱孩子”和“立规矩”,从来不是一道单选题。

    \n

    明确规则,不被孩子牵着鼻子走,不能因为孩子哭闹,就主动打破规则的执行,和善而坚定地对孩子说“不”。让孩子明白,边界感的所在。

    \n
    \"\"
    在孩子的成长过程中,要让他知道:除了爱之外,还要有敬畏
    \n

    二、每天花 15 分钟的时间和孩子聊天,问问他,学校都发生了什么。

    \n

    三、每周抽时间给孩子拍一段视频,让他听自己的声音,看自己的表现,发现问题及时调整。

    \n
    \"\"
    \n

    四、周末邀请孩子的好朋友到家里来玩,给孩子创造多种社交机会和条件,提升孩子的社交能力。

    \n

    五、在孩子愿意的前提下,给孩子创造一些上台表演的机会,通过正向的实践提升孩子的自信心。

    \n
    \"\"
    提升孩子自信心的 5 句话
    \n

    亲测,这几个方法对提高孩子自信心,让孩子变得有原则有担当非常有帮助。

    \n
    \n
    \n\n\n\n\n
    \n\n\n\n", + "category": "", + "link": "https://daily.zhihu.com/story/9782552", + "creator": "", + "pubDate": "Mon, 14 Jul 2025 08:27:04 +0800", + "enclosure": "", + "enclosureType": "", + "image": "", + "id": "", + "language": "", + "folder": "资讯", + "feed": "知乎", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "54d622541f4f473a8dd14f6e4d7644c7", + "highlights": [] + }, + { + "title": "当回避型依恋开始回避你时她(他)的内心活动是怎样的?", + "description": "
    \"头图\"/
    \n

    众多回答都告诉题主了,回避型依恋的特征不愿意和爱人亲密。这种不愿意体现在高度的亲密回避,以及极低的被弃焦虑,而这两个点也正是 AAS(成人依恋量表)中的两个维度。

    \n

    作为咨询师,我会通过认知行为理论和客体关系理论来讲一下这两点到底意味着什么。

    \n

    首先先从认知行为模式(Cognitive Behavior Therapy)的角度来说一下。亲密回避很好理解,就是避免与你有“过度”的亲密接触。因为这些亲密接触,都会给 TA 一种不安感。

    \n

    一、回避亲密的根源在于自我否定

    \n

    这种不安是来自于童年婴儿时期的,TA 的父母因为照顾得并不好,所以让 TA 内心深处一直对自己说:

    \n

    我不够好,没有人会来帮助我。

    \n

    慢慢随着他长大,这个内心想法开始逐渐演化成了一种很自然的想法,或者说是生活规则。由于没有人来帮助我。所以,万事不能靠别人,只能靠自己。最终演化为:

    \n

    别人介入一定会坏事,只有自己是能信任的人

    \n

    所以通过这个思考回路,TA 也会觉得在感情中也不能靠别人。也就是说,在亲密关系中,TA 依然认为相信别人,或者“过度”依赖别人,会导致“坏事”。所以为了避免这种情况,TA 就会刻意的保持一种若即若离的状态。

    \n
      \n
    1. 内心深处:我不够好,没人会帮助我
    2. \n
    3. 行为习惯:别人不可相信,只能相信自己
    4. \n
    5. 情绪感受:一旦亲密就会感觉到不安全
    6. \n
    \n
    \"\"
    \n

    TA 之所以会表现出不耐烦以及烦躁,甚至故意做出故意疏远你的行为。其实就是因为 TA 一直都是靠自己生活的,从小到大这已经深深的刻进了 TA 的行为模式之中。所以一旦你和 TA 靠的太近,就会让 TA 陷入一种非常陌生的环境之中。

    \n

    当我们进入一个完全陌生的环境中时,也会全身肌肉紧绷,随时准备逃跑。而 TA 在亲密关系中,也就相当于进入了一个 TA 完全不适应的环境之中,让 TA 产生了应激反应:

    \n

    本能的排斥并且想要回避你。

    \n

    但是需要强调的是,TA 的这种想法和逻辑,都是埋藏在潜意识层面一下的。大部分人看到蜘蛛就心里发毛,这是因为天生的经验在告诉我们蜘蛛很危险。TA 的回避也是一样的,TA 一但和你亲密就觉得难受,不过 TA 并不能知道为什么难受。所以你如果问 TA,得到的答案可能会很模棱两可:

    \n
      \n
    • “我就是觉得两个人一起久了太腻了”
    • \n
    • “我喜欢一个人”
    • \n
    • “我也不知道为什么,就是觉得不舒服”
    • \n
    \n

    TA 可以丰富的向你描述 TA 的感受,但是真要让 TA 说出一个所以然来,是很困难的。因为自动想法部分,多是停留在意识层面之下,也就是说这一套机制是不受意识的自我检查的。

    \n

    也就是说,这个想法先于自我判断对错的心理行为,TA 永远都不会知道自己为什么要这样,也永远不会思考这样到底对不对。

    \n

    另外一个特征是很低的被弃焦虑。

    \n

    这也是回避型依恋相较于恐惧型与痴迷型来说,有着更好的稳定性的表现。低被弃焦虑,可以简单的理解为不怕你主动提分手,TA 比另外两种不安全依恋更容易接受这个结果。因为如此,回避型依恋的人相对而言会作的更少一点。

    \n

    然而你并不是完全的安全型依恋类型,多多少少你也会在亲密关系中感觉到不安全,有着一定程度的被弃焦虑,进而找 TA 寻求安全感。这个时候攻守易主,你成了所求的一方,TA 反而成了诉求少的一方。所以有些时候,TA 甚至会反过来觉得你很作,让 TA 感觉到很累很烦。

    \n

    比如在你寻求亲密行为的时候,很可能整个行为只是非常正常的一次亲密互动,但是依然会被他视作是一种过度的诉求,是一种作。面对这种“作”,无论他外在怎么做的,但 TA 的内心一定是排斥的,而且排斥的有理有据。

    \n

    但是,需要强调的是:不怕被你分手,不意味着希望被你分手。

    \n

    前面也说过,回避型依恋的 TA,在面对人生的所有事件时,都会有自己一个人单干的习惯。这是因为 TA 的内心想法:认为自己不够好,所有人都不会帮 TA,你也不会帮。这延伸在亲密关系中,就会产生出一种结论:自己不够好,被分手也是正常的。

    \n

    所以 TA 几乎上是认定了,你与 TA 的这段关系是不长久的。在这个思维的基础上,TA 才不怕分手,因为这已经是 TA 所能预见的最有可能的结局了。

    \n
      \n
    • 你和 TA 一起旅行或者出去玩
    • \n
    \n

    TA 外在的想法:每天都要和腻在一起,没有自己的空间,感觉好难受。

    \n

    TA 内在的潜意识:我付出和牺牲自己的生活,也很难让关系更好。

    \n
      \n
    • 你每天都关心 TA 的生活
    • \n
    \n

    TA 外在的想法:每天都要和你分享自己的生活,就像报告一样·,让人很烦。

    \n

    TA 内在的潜意识:我自己的生活节奏被打乱了,我没有办法掌控这段感情了。

    \n
      \n
    • 你希望 TA 能对你更上心一点,再多关心一下自己
    • \n
    \n

    TA 外在的想法:我自己一个人挺舒服,现在还要多照顾一个人,好麻烦啊。

    \n

    TA 内在的潜意识:我没有能力为别人的生活责任。

    \n

    你可以发现回避型依恋的人是很悲观的,这认知行为理论中,称为糟糕至极(Awflizing)。TA 会把你们这段感情看的很悲观,比如说 TA 的内心深处可能有这些想法:

    \n
      \n
    • “你肯定很难和我感同身受,根本不可能理解我”
    • \n
    • “我真的很难和你说一些内心的话,我说不出口”
    • \n
    • “如果我向你示弱了,你肯定就会伤害我,背叛我”
    • \n
    \n

    但是 TA 想不想和你亲近呢?——答案是肯定的,TA 当然也不想被车子撞到,也不想自己在亲密关系中永远都是悲剧的男 / 女主角。但是经验告诉 TA,不能多想,做好自己就行了。

    \n

    所以回避型依恋的人都处于一个很矛盾的境地中,TA 一方面因为想要摆脱悲剧色彩而极度想要亲密关系的帮助,但是一方又对亲密关系不抱期望。

    \n

    换句话说,TA 期望的是你,但是又对自己失望。而恰巧亲密关系又需要两个人共同承担责任,面对问题,所以回避型依恋的人往往会对亲密关系持有极度正面以及极度负面的两种态度。

    \n

    现在你应该知道了,回避型依恋的人在亲密关系中是有着大量的不安全感,甚至说带有很大程度的自我否定感。这种感觉渗透到了他们生活中的方方面面。TA 不想和你去旅游,不想更多的关心你,其实是因为 TA 潜意识觉得自己付不起这个责任,觉得自己和你的感情很难有一个完美的结果。

    \n

    二、对关系的焦虑源于对死亡的恐惧

    \n

    那这些潜意识又是从什么地方产生的呢?其实就是她的童年成长。客体关系理论(Object-Relations Theory)中讲,我们与外在客体的关系模式,如果一旦定型,那么我们之后对待别人,对待世界的方式都会遵循这种模式。

    \n

    简单来说,就是爸爸妈妈怎么对的你,你以后就会怎么对别人。回避型依恋的人,在童年早期很可能并没有受到父母很好地照顾。所以认识自我和别人时,可能会出现问题。

    \n

    温尼科特说:“每个婴儿都是在自己母亲眼眸中所映射的自己的影像中,找到自己的存在”。

    \n

    所以,回避型依恋的存在感和安全感,都是很缺失的。这种缺失在于重要养育人对自己照顾的疏忽,这种重大的早期经验带给了 TA 非常不安全的感觉。这也是迫使 TA 认为别人不可靠,只能靠自己的原因。

    \n

    所以说,在心理动力学的层面上,回避型依恋并不简简单单是一个性格的问题,也不是你给与一些安全感,或者照顾一下 TA 的感受,就能解决的问题。这个问题的根本,很可能是涉及到 TA 潜意识中对死亡的恐惧。

    \n

    当你要求 TA 每天报告自己的生活清单时、要求 TA 必须每天陪你多久多久时,TA 内心深处好像就回到了小时候,无法掌控自己生活的婴儿时期。让他重新再一次感受到了作为婴儿的无力感。

    \n

    当然,我们知道你并不是这个意思,甚至 TA 的意识层面也知道你不是这个意思。但是这并不影响 TA 的潜意识这样认为,而且 TA 的潜意识甚至会再度感觉到童年时期那种不被照顾,不被关心的状态。

    \n

    简单地说:作为一个回避型依恋的人,TA 已经成长为了一个事事只靠自己的独立个体。而亲密关系在一定程度上会让 TA 重新回到一个共有的关系之中,这种关系的变化让 TA 又一次感觉自己失去独立感,从而带来的就是无法掌握自己生活的恐惧感。

    \n

    这些最底层的恐惧感,甚至让 TA 即使想和你卿卿我我,但也没有办法违抗最本能的恐惧感。对于 TA 来说,“过度”的亲密就代表着不独立,就代表着没有独立活下去的可能。这些恐惧感的背后,其实是一次次童年创伤以及成长经历所累计起来的一种创伤后应激反应(PTSD)。

    \n

    三、回避型依恋要慢慢感化

    \n

    亲密关系中两个人甚至可以说是重温了童年时期父母的爱,但同时还有童年时期父母的伤害。你可以把 TA 的回避看做是在回避童年早期经验的伤害,TA 只是在潜意识中把你当做了 TA 的父母了。

    \n

    所以急于解决 TA 的回避行为,只会让 TA 感觉到更没有安全感。你可以试着相信 TA,接纳 TA。很多时候 TA 在怀疑你,伤害你的时候,其实也在怀疑自己,伤害自己。你可以表现出没有攻击性的一面,让 TA 慢慢能够主动地和你更加亲近。

    \n

    回避型在回避你时,其实是因为不相信你能够疗愈与自己,即使是能够疗愈自己,也不见得能够认同自己的孤独。当然,这些都是 TA 潜意识层面的恐惧和顾虑,TA 在具体生活中的表现就是喜欢一个人,不喜欢腻在一起。

    \n

    我有很多情侣的案例,两个人三天两头就要吵架。两个人对于吵架这个事情都已经很累了,很厌烦了,但为什么还要去做呢?其实很简单,当你对一个事情很烦很讨厌,但依然要继续做时,那说明这个事情只能是非做不可的。

    \n

    两个人通过吵架,通过矛盾来一次又一次的重新体验童年早期经验的创伤,最后又通过和好理解来达到童年创伤的疗愈。回避也是一样的道理,只是具体的情况与其他的类型稍有不同:别人都是爆发冲突,TA 是害怕冲突而躲避冲突,或者说这种回避其实就是 TA 所经历的创伤。

    \n

    所以当你下次再感觉到 TA 在回避的时候,你可以认为 TA 的内心现在为了寻求一些 TA 自己认为的安全空间而在疯狂的做着努力。而 TA 的潜意识深处,其实是希望你能帮助 TA 脱离这个苦海的,只是在脱离苦海的前一步是进入苦海。

    \n

    温尼科特还说过,他认为真正完美的亲密关系是:窝在爱人怀里的孤独(Dare to be lonely in someone else‘s arms)。

    \n

    Dare代表亲密关系是需要勇气的,Arms代表亲密关系是疗愈和信任,同时最重要的Lonely说明亲密关系中的个体,再如何亲密其内心——依然是孤独的。

    \n

    不要害怕 TA 这么回避你,你要知道的是:TA 正在尝试找到能够让你满意也能够让自己满意的关系距离。

    \n

    太远了你没有安全感,太近了他没有安全感,但 TA 相信会有那么一个距离能够同时满足你和 TA 两个人,所以 TA 也在为此努力之中。如果 TA 都如此相信,那么你又有什么理由不去相信呢。

    \n
    \n

    我是 @远叔 国家认证心理咨询师,成都精神分析协会会员、全网粉丝累计超 80w,从业 10 年,4000+ 咨询案例。 回避型创伤修复、焦虑型依恋成长、原生家庭疗愈、社交障碍解决、失恋疗愈........ 与其自己纠结,不如与我并肩,欢迎来到主角的世界→咨询请联系(vx:jyxfd001)

    \n
    \n

    关于回避型,我写过很多回答:

    \n

    01 回避型依恋者怎么谈恋爱?当喜欢的人也喜欢自己,想要逃跑?

    \n

    02 怎么追回避型依恋的女生?

    \n

    03 回避型依恋的人适合和什么人在一起?

    \n

    04 回避型依恋者在亲密关系中的需求是什么?他们希望伴侣给予他们什么?

    \n

    05 当回避型依恋开始回避你时她(他)的内心活动是怎样的?

    \n

    06 怎样摆脱焦虑型依恋?

    \n

    07 回避型的人为什么那么冷漠?

    \n

    08 回避型人格的人会真心爱上一个人吗?

    \n

    09 如何与回避型依恋的人谈恋爱?

    \n
    \n
    \n\n\n\n\n
    \n\n\n\n", + "content": "
    \"头图\"/
    \n

    众多回答都告诉题主了,回避型依恋的特征不愿意和爱人亲密。这种不愿意体现在高度的亲密回避,以及极低的被弃焦虑,而这两个点也正是 AAS(成人依恋量表)中的两个维度。

    \n

    作为咨询师,我会通过认知行为理论和客体关系理论来讲一下这两点到底意味着什么。

    \n

    首先先从认知行为模式(Cognitive Behavior Therapy)的角度来说一下。亲密回避很好理解,就是避免与你有“过度”的亲密接触。因为这些亲密接触,都会给 TA 一种不安感。

    \n

    一、回避亲密的根源在于自我否定

    \n

    这种不安是来自于童年婴儿时期的,TA 的父母因为照顾得并不好,所以让 TA 内心深处一直对自己说:

    \n

    我不够好,没有人会来帮助我。

    \n

    慢慢随着他长大,这个内心想法开始逐渐演化成了一种很自然的想法,或者说是生活规则。由于没有人来帮助我。所以,万事不能靠别人,只能靠自己。最终演化为:

    \n

    别人介入一定会坏事,只有自己是能信任的人

    \n

    所以通过这个思考回路,TA 也会觉得在感情中也不能靠别人。也就是说,在亲密关系中,TA 依然认为相信别人,或者“过度”依赖别人,会导致“坏事”。所以为了避免这种情况,TA 就会刻意的保持一种若即若离的状态。

    \n
      \n
    1. 内心深处:我不够好,没人会帮助我
    2. \n
    3. 行为习惯:别人不可相信,只能相信自己
    4. \n
    5. 情绪感受:一旦亲密就会感觉到不安全
    6. \n
    \n
    \"\"
    \n

    TA 之所以会表现出不耐烦以及烦躁,甚至故意做出故意疏远你的行为。其实就是因为 TA 一直都是靠自己生活的,从小到大这已经深深的刻进了 TA 的行为模式之中。所以一旦你和 TA 靠的太近,就会让 TA 陷入一种非常陌生的环境之中。

    \n

    当我们进入一个完全陌生的环境中时,也会全身肌肉紧绷,随时准备逃跑。而 TA 在亲密关系中,也就相当于进入了一个 TA 完全不适应的环境之中,让 TA 产生了应激反应:

    \n

    本能的排斥并且想要回避你。

    \n

    但是需要强调的是,TA 的这种想法和逻辑,都是埋藏在潜意识层面一下的。大部分人看到蜘蛛就心里发毛,这是因为天生的经验在告诉我们蜘蛛很危险。TA 的回避也是一样的,TA 一但和你亲密就觉得难受,不过 TA 并不能知道为什么难受。所以你如果问 TA,得到的答案可能会很模棱两可:

    \n
      \n
    • “我就是觉得两个人一起久了太腻了”
    • \n
    • “我喜欢一个人”
    • \n
    • “我也不知道为什么,就是觉得不舒服”
    • \n
    \n

    TA 可以丰富的向你描述 TA 的感受,但是真要让 TA 说出一个所以然来,是很困难的。因为自动想法部分,多是停留在意识层面之下,也就是说这一套机制是不受意识的自我检查的。

    \n

    也就是说,这个想法先于自我判断对错的心理行为,TA 永远都不会知道自己为什么要这样,也永远不会思考这样到底对不对。

    \n

    另外一个特征是很低的被弃焦虑。

    \n

    这也是回避型依恋相较于恐惧型与痴迷型来说,有着更好的稳定性的表现。低被弃焦虑,可以简单的理解为不怕你主动提分手,TA 比另外两种不安全依恋更容易接受这个结果。因为如此,回避型依恋的人相对而言会作的更少一点。

    \n

    然而你并不是完全的安全型依恋类型,多多少少你也会在亲密关系中感觉到不安全,有着一定程度的被弃焦虑,进而找 TA 寻求安全感。这个时候攻守易主,你成了所求的一方,TA 反而成了诉求少的一方。所以有些时候,TA 甚至会反过来觉得你很作,让 TA 感觉到很累很烦。

    \n

    比如在你寻求亲密行为的时候,很可能整个行为只是非常正常的一次亲密互动,但是依然会被他视作是一种过度的诉求,是一种作。面对这种“作”,无论他外在怎么做的,但 TA 的内心一定是排斥的,而且排斥的有理有据。

    \n

    但是,需要强调的是:不怕被你分手,不意味着希望被你分手。

    \n

    前面也说过,回避型依恋的 TA,在面对人生的所有事件时,都会有自己一个人单干的习惯。这是因为 TA 的内心想法:认为自己不够好,所有人都不会帮 TA,你也不会帮。这延伸在亲密关系中,就会产生出一种结论:自己不够好,被分手也是正常的。

    \n

    所以 TA 几乎上是认定了,你与 TA 的这段关系是不长久的。在这个思维的基础上,TA 才不怕分手,因为这已经是 TA 所能预见的最有可能的结局了。

    \n
      \n
    • 你和 TA 一起旅行或者出去玩
    • \n
    \n

    TA 外在的想法:每天都要和腻在一起,没有自己的空间,感觉好难受。

    \n

    TA 内在的潜意识:我付出和牺牲自己的生活,也很难让关系更好。

    \n
      \n
    • 你每天都关心 TA 的生活
    • \n
    \n

    TA 外在的想法:每天都要和你分享自己的生活,就像报告一样·,让人很烦。

    \n

    TA 内在的潜意识:我自己的生活节奏被打乱了,我没有办法掌控这段感情了。

    \n
      \n
    • 你希望 TA 能对你更上心一点,再多关心一下自己
    • \n
    \n

    TA 外在的想法:我自己一个人挺舒服,现在还要多照顾一个人,好麻烦啊。

    \n

    TA 内在的潜意识:我没有能力为别人的生活责任。

    \n

    你可以发现回避型依恋的人是很悲观的,这认知行为理论中,称为糟糕至极(Awflizing)。TA 会把你们这段感情看的很悲观,比如说 TA 的内心深处可能有这些想法:

    \n
      \n
    • “你肯定很难和我感同身受,根本不可能理解我”
    • \n
    • “我真的很难和你说一些内心的话,我说不出口”
    • \n
    • “如果我向你示弱了,你肯定就会伤害我,背叛我”
    • \n
    \n

    但是 TA 想不想和你亲近呢?——答案是肯定的,TA 当然也不想被车子撞到,也不想自己在亲密关系中永远都是悲剧的男 / 女主角。但是经验告诉 TA,不能多想,做好自己就行了。

    \n

    所以回避型依恋的人都处于一个很矛盾的境地中,TA 一方面因为想要摆脱悲剧色彩而极度想要亲密关系的帮助,但是一方又对亲密关系不抱期望。

    \n

    换句话说,TA 期望的是你,但是又对自己失望。而恰巧亲密关系又需要两个人共同承担责任,面对问题,所以回避型依恋的人往往会对亲密关系持有极度正面以及极度负面的两种态度。

    \n

    现在你应该知道了,回避型依恋的人在亲密关系中是有着大量的不安全感,甚至说带有很大程度的自我否定感。这种感觉渗透到了他们生活中的方方面面。TA 不想和你去旅游,不想更多的关心你,其实是因为 TA 潜意识觉得自己付不起这个责任,觉得自己和你的感情很难有一个完美的结果。

    \n

    二、对关系的焦虑源于对死亡的恐惧

    \n

    那这些潜意识又是从什么地方产生的呢?其实就是她的童年成长。客体关系理论(Object-Relations Theory)中讲,我们与外在客体的关系模式,如果一旦定型,那么我们之后对待别人,对待世界的方式都会遵循这种模式。

    \n

    简单来说,就是爸爸妈妈怎么对的你,你以后就会怎么对别人。回避型依恋的人,在童年早期很可能并没有受到父母很好地照顾。所以认识自我和别人时,可能会出现问题。

    \n

    温尼科特说:“每个婴儿都是在自己母亲眼眸中所映射的自己的影像中,找到自己的存在”。

    \n

    所以,回避型依恋的存在感和安全感,都是很缺失的。这种缺失在于重要养育人对自己照顾的疏忽,这种重大的早期经验带给了 TA 非常不安全的感觉。这也是迫使 TA 认为别人不可靠,只能靠自己的原因。

    \n

    所以说,在心理动力学的层面上,回避型依恋并不简简单单是一个性格的问题,也不是你给与一些安全感,或者照顾一下 TA 的感受,就能解决的问题。这个问题的根本,很可能是涉及到 TA 潜意识中对死亡的恐惧。

    \n

    当你要求 TA 每天报告自己的生活清单时、要求 TA 必须每天陪你多久多久时,TA 内心深处好像就回到了小时候,无法掌控自己生活的婴儿时期。让他重新再一次感受到了作为婴儿的无力感。

    \n

    当然,我们知道你并不是这个意思,甚至 TA 的意识层面也知道你不是这个意思。但是这并不影响 TA 的潜意识这样认为,而且 TA 的潜意识甚至会再度感觉到童年时期那种不被照顾,不被关心的状态。

    \n

    简单地说:作为一个回避型依恋的人,TA 已经成长为了一个事事只靠自己的独立个体。而亲密关系在一定程度上会让 TA 重新回到一个共有的关系之中,这种关系的变化让 TA 又一次感觉自己失去独立感,从而带来的就是无法掌握自己生活的恐惧感。

    \n

    这些最底层的恐惧感,甚至让 TA 即使想和你卿卿我我,但也没有办法违抗最本能的恐惧感。对于 TA 来说,“过度”的亲密就代表着不独立,就代表着没有独立活下去的可能。这些恐惧感的背后,其实是一次次童年创伤以及成长经历所累计起来的一种创伤后应激反应(PTSD)。

    \n

    三、回避型依恋要慢慢感化

    \n

    亲密关系中两个人甚至可以说是重温了童年时期父母的爱,但同时还有童年时期父母的伤害。你可以把 TA 的回避看做是在回避童年早期经验的伤害,TA 只是在潜意识中把你当做了 TA 的父母了。

    \n

    所以急于解决 TA 的回避行为,只会让 TA 感觉到更没有安全感。你可以试着相信 TA,接纳 TA。很多时候 TA 在怀疑你,伤害你的时候,其实也在怀疑自己,伤害自己。你可以表现出没有攻击性的一面,让 TA 慢慢能够主动地和你更加亲近。

    \n

    回避型在回避你时,其实是因为不相信你能够疗愈与自己,即使是能够疗愈自己,也不见得能够认同自己的孤独。当然,这些都是 TA 潜意识层面的恐惧和顾虑,TA 在具体生活中的表现就是喜欢一个人,不喜欢腻在一起。

    \n

    我有很多情侣的案例,两个人三天两头就要吵架。两个人对于吵架这个事情都已经很累了,很厌烦了,但为什么还要去做呢?其实很简单,当你对一个事情很烦很讨厌,但依然要继续做时,那说明这个事情只能是非做不可的。

    \n

    两个人通过吵架,通过矛盾来一次又一次的重新体验童年早期经验的创伤,最后又通过和好理解来达到童年创伤的疗愈。回避也是一样的道理,只是具体的情况与其他的类型稍有不同:别人都是爆发冲突,TA 是害怕冲突而躲避冲突,或者说这种回避其实就是 TA 所经历的创伤。

    \n

    所以当你下次再感觉到 TA 在回避的时候,你可以认为 TA 的内心现在为了寻求一些 TA 自己认为的安全空间而在疯狂的做着努力。而 TA 的潜意识深处,其实是希望你能帮助 TA 脱离这个苦海的,只是在脱离苦海的前一步是进入苦海。

    \n

    温尼科特还说过,他认为真正完美的亲密关系是:窝在爱人怀里的孤独(Dare to be lonely in someone else‘s arms)。

    \n

    Dare代表亲密关系是需要勇气的,Arms代表亲密关系是疗愈和信任,同时最重要的Lonely说明亲密关系中的个体,再如何亲密其内心——依然是孤独的。

    \n

    不要害怕 TA 这么回避你,你要知道的是:TA 正在尝试找到能够让你满意也能够让自己满意的关系距离。

    \n

    太远了你没有安全感,太近了他没有安全感,但 TA 相信会有那么一个距离能够同时满足你和 TA 两个人,所以 TA 也在为此努力之中。如果 TA 都如此相信,那么你又有什么理由不去相信呢。

    \n
    \n

    我是 @远叔 国家认证心理咨询师,成都精神分析协会会员、全网粉丝累计超 80w,从业 10 年,4000+ 咨询案例。 回避型创伤修复、焦虑型依恋成长、原生家庭疗愈、社交障碍解决、失恋疗愈........ 与其自己纠结,不如与我并肩,欢迎来到主角的世界→咨询请联系(vx:jyxfd001)

    \n
    \n

    关于回避型,我写过很多回答:

    \n

    01 回避型依恋者怎么谈恋爱?当喜欢的人也喜欢自己,想要逃跑?

    \n

    02 怎么追回避型依恋的女生?

    \n

    03 回避型依恋的人适合和什么人在一起?

    \n

    04 回避型依恋者在亲密关系中的需求是什么?他们希望伴侣给予他们什么?

    \n

    05 当回避型依恋开始回避你时她(他)的内心活动是怎样的?

    \n

    06 怎样摆脱焦虑型依恋?

    \n

    07 回避型的人为什么那么冷漠?

    \n

    08 回避型人格的人会真心爱上一个人吗?

    \n

    09 如何与回避型依恋的人谈恋爱?

    \n
    \n
    \n\n\n\n\n
    \n\n\n\n", + "category": "", + "link": "https://daily.zhihu.com/story/9782524", + "creator": "", + "pubDate": "Sun, 13 Jul 2025 08:27:02 +0800", + "enclosure": "", + "enclosureType": "", + "image": "", + "id": "", + "language": "", + "folder": "资讯", + "feed": "知乎", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "052d9ff428f7bbcde846d2cd16d98e16", + "highlights": [] + }, + { + "title": "为什么没熟透的香蕉总是皮厚肉少,而熟透的香蕉总是薄皮肉多?", + "description": "
    \"头图\"/
    \n

    让我们看下香蕉君の成熟之路。

    \n

    首先,我们都知道,香蕉苹果猕猴桃芒果等属于典型的呼吸跃变型水果。

    \n

    就是在绿熟期即可摘下,放一段时间自己会成熟。

    \n

    与之对应的就是非呼吸跃变型水果,比如樱桃草莓葡萄荔枝,

    \n

    出道即巅峰,多放一天坏一天。

    \n
    \"\"
    \n

    而促使呼吸跃变型水果成熟启动的主要信号分子大家都知道——乙烯[1]。

    \n

    乙烯的合成始于甲硫氨酸,

    \n

    想合成乙烯需要经过三次转化。

    \n
    首先是在 S - 腺苷甲硫氨酸合成酶作用下转化为 S - 腺苷甲硫氨酸(SAM),
    然后在 ACC 合成酶(ACS)催化下生成 1 - 氨基环丙烷 - 1 - 羧酸(ACC),
    最后在 ACC 氧化酶(ACO)作用下形成乙烯。
    \n
    \"\"
    乙烯生物合成与信号传导通路图 b 为信号传导通路图
    \n

    这个过程随着温度上升呼吸作用的加强而飙升,

    \n

    最终到达顶峰后迅速下降,被称为跃变期。[2]

    \n
    \"\"
    香蕉成熟度里乙烯和呼吸强度的速率,可以看到到了后期断崖式上升,下降
    \n

    而在这之前,香蕉刚被摘下来时还是只青涩的蕉,

    \n

    注意啦,

    \n

    青涩不是形容词,而是叙述词。

    \n

    此时果肉里 70%-80%是淀粉,结构紧密不说,

    \n

    这类淀粉人的肠胃很难消化吸收,可以说是又硬又难吃。

    \n

    同时,果肉里还含有不少单宁(鞣酸)和果胶,

    \n

    有小伙伴讲了:

    \n

    单宁我知道,干红葡萄酒里有,喝嘴里会产生褶皱感。

    \n

    没错,单宁具有强收敛作用,

    \n

    不光会让嘴巴产生褶皱感,还可能影响肠道功能,

    \n

    咱常吃的生柿子就含有不少,

    \n

    简单的说,吃起来不光涩,吃多了还容易便秘。

    \n
    所以生香蕉就压根不能吃,更不能减肥。
    \n

    香蕉皮就更别说了,果皮塞满了叶绿素 a 和 b,整个看起来是绿油油的,

    \n

    同时细胞壁由果胶基质 - 纤维素 - 半纤维素构成坚挺的网络,导致香蕉皮此时很硬,甚至很难扒开。

    \n

    这个阶段被称为青熟期。

    \n

    香蕉没法吃,但是已经满足收获的条件了。

    \n

    收获好的香蕉被运往全国各地,

    \n
    此时一段焦灼的小调响起,又到了奥德彪拉香蕉的时间。
    \n
    \"\"
    \n

    正常来说香蕉在树上会受到乙烯的刺激,触发自我催化。

    \n

    ACC 合成酶(ACS)和 ACC 氧化酶(ACO)基因表达上调,

    \n

    使乙烯爆发式增长,激活 ERF 转录因子调控成熟基因,

    \n

    此时,呼吸速率显著提高。

    \n

    乙烯激活了香蕉内部的淀粉酶、糖苷酶,淀粉水解为可溶性糖,果肉变软变甜。[3]

    \n

    香蕉表皮的叶绿素 a 和 b 被叶绿素酶降解,导致绿色褪去,

    \n
    \"\"
    成熟香蕉果皮力度叶绿素和可溶性固形物含量,可以看到叶绿色消失,而可溶性固形物大大增加。
    \n

    类胡萝卜素合成开始增加,类黄酮含量上升,使果皮被显现出黄色,

    \n

    部分品种还会积累花青素,出现红香蕉。[4]

    \n
    \"\"
    红香蕉长这样,变红是因为花青素。
    \n

    再之后,,,,,

    \n

    多聚半乳糖醛酸酶 (polygalaturonase,PG)催化果胶分子中的 α-1,4- 半乳糖苷键裂解,

    \n

    使得果胶降解,香蕉软了。

    \n

    α-L- 阿拉伯呋喃糖苷酶(α-L-Arabinofuranosidase, α-L-Af)降解细胞壁多糖中的阿拉伯糖残基,

    \n

    导致果胶溶解和细胞壁结构松散,香蕉更软了。[5]

    \n

    纤维素酶(Cellulase,CEL)和β-D- 木聚糖苷酶活性增强,干掉香蕉皮细胞壁中的部分骨架结构,

    \n

    使纤维素半纤维素降解,香蕉更更软了。

    \n

    香蕉皮中还积累了部分抗性淀粉,也也被降解成可溶性糖,用于给果皮代谢供能。

    \n

    更更更软了,所以,香蕉皮软了,也薄了。

    \n

    但是没变强。

    \n

    正常来说,这个过程随着气温升高而加快,

    \n
    \"\"
    香蕉在 20°C 和 30°C 里各种酶的变化,可以看到温度越高曲线越高
    \n

    但是过了 30°C 后,高温会抑制叶绿素酶的发挥,

    \n

    导致出现香蕉果肉熟了,果皮还是绿色的情况。[6]

    \n
    \"\"
    香蕉在 20°C 和 30°C 果实硬度和果皮色度变化,可以明显看到 30°C 果实软了,但是果皮颜色还是绿色。
    \n

    由于香蕉摘下后自然产出的乙烯含量明显降低,

    \n

    所以会在袋子里放入含有乙烯利(2- 氯乙基磷酸)的纸片。

    \n

    在运输过程中缓慢产出乙烯,模拟这一过程。

    \n
    \"\"
    常见乙烯利卡片,类似一张小纸片。
    \n

    而除了乙烯,呼吸作用也是促使香蕉后熟的重要原因。

    \n

    所以提高环境中的二氧化碳浓度(大概在 4% - 6% ),

    \n

    降低氧气浓度(大约 2% - 5% )会显著提高香蕉的保鲜期。

    \n

    这就是气调储藏的原理。

    \n

    气调包装听着挺高大上,咱也挺常见,香蕉箱里那个透明袋就是气调袋。

    \n
    \"\"
    香蕉气调袋
    \n

    当然了,万事皆有度,

    \n

    人工乙烯过度催熟,

    \n

    除了会因催熟不均导致部分单宁等物质未完全转化,使一些不是特别熟的香蕉发涩。

    \n

    还会导致香蕉后熟过快,

    \n

    而香蕉成熟的过程中代谢前体分解的过程会产生挥发性酯类化合物,是香蕉香味的来源。

    \n

    后熟过快会使得这些酯类化合物产出过少,失去香味。

    \n

    这就是为啥感觉有的香蕉熟了特别特别香,而有些香蕉却没味的原因。

    \n
    \"\"
    香蕉从青熟到成熟后的过程。
    \n

    那么,现在。

    \n

    大声告诉我,香蕉君变软变黄的原因是哈?!!!!

    \n

    是乙烯吗?

    \n

    不,是哲♂学!!!!!!

    \n
    \"\"
    \n
    \n
    \n\n\n\n\n
    \n\n\n\n", + "content": "
    \"头图\"/
    \n

    让我们看下香蕉君の成熟之路。

    \n

    首先,我们都知道,香蕉苹果猕猴桃芒果等属于典型的呼吸跃变型水果。

    \n

    就是在绿熟期即可摘下,放一段时间自己会成熟。

    \n

    与之对应的就是非呼吸跃变型水果,比如樱桃草莓葡萄荔枝,

    \n

    出道即巅峰,多放一天坏一天。

    \n
    \"\"
    \n

    而促使呼吸跃变型水果成熟启动的主要信号分子大家都知道——乙烯[1]。

    \n

    乙烯的合成始于甲硫氨酸,

    \n

    想合成乙烯需要经过三次转化。

    \n
    首先是在 S - 腺苷甲硫氨酸合成酶作用下转化为 S - 腺苷甲硫氨酸(SAM),
    然后在 ACC 合成酶(ACS)催化下生成 1 - 氨基环丙烷 - 1 - 羧酸(ACC),
    最后在 ACC 氧化酶(ACO)作用下形成乙烯。
    \n
    \"\"
    乙烯生物合成与信号传导通路图 b 为信号传导通路图
    \n

    这个过程随着温度上升呼吸作用的加强而飙升,

    \n

    最终到达顶峰后迅速下降,被称为跃变期。[2]

    \n
    \"\"
    香蕉成熟度里乙烯和呼吸强度的速率,可以看到到了后期断崖式上升,下降
    \n

    而在这之前,香蕉刚被摘下来时还是只青涩的蕉,

    \n

    注意啦,

    \n

    青涩不是形容词,而是叙述词。

    \n

    此时果肉里 70%-80%是淀粉,结构紧密不说,

    \n

    这类淀粉人的肠胃很难消化吸收,可以说是又硬又难吃。

    \n

    同时,果肉里还含有不少单宁(鞣酸)和果胶,

    \n

    有小伙伴讲了:

    \n

    单宁我知道,干红葡萄酒里有,喝嘴里会产生褶皱感。

    \n

    没错,单宁具有强收敛作用,

    \n

    不光会让嘴巴产生褶皱感,还可能影响肠道功能,

    \n

    咱常吃的生柿子就含有不少,

    \n

    简单的说,吃起来不光涩,吃多了还容易便秘。

    \n
    所以生香蕉就压根不能吃,更不能减肥。
    \n

    香蕉皮就更别说了,果皮塞满了叶绿素 a 和 b,整个看起来是绿油油的,

    \n

    同时细胞壁由果胶基质 - 纤维素 - 半纤维素构成坚挺的网络,导致香蕉皮此时很硬,甚至很难扒开。

    \n

    这个阶段被称为青熟期。

    \n

    香蕉没法吃,但是已经满足收获的条件了。

    \n

    收获好的香蕉被运往全国各地,

    \n
    此时一段焦灼的小调响起,又到了奥德彪拉香蕉的时间。
    \n
    \"\"
    \n

    正常来说香蕉在树上会受到乙烯的刺激,触发自我催化。

    \n

    ACC 合成酶(ACS)和 ACC 氧化酶(ACO)基因表达上调,

    \n

    使乙烯爆发式增长,激活 ERF 转录因子调控成熟基因,

    \n

    此时,呼吸速率显著提高。

    \n

    乙烯激活了香蕉内部的淀粉酶、糖苷酶,淀粉水解为可溶性糖,果肉变软变甜。[3]

    \n

    香蕉表皮的叶绿素 a 和 b 被叶绿素酶降解,导致绿色褪去,

    \n
    \"\"
    成熟香蕉果皮力度叶绿素和可溶性固形物含量,可以看到叶绿色消失,而可溶性固形物大大增加。
    \n

    类胡萝卜素合成开始增加,类黄酮含量上升,使果皮被显现出黄色,

    \n

    部分品种还会积累花青素,出现红香蕉。[4]

    \n
    \"\"
    红香蕉长这样,变红是因为花青素。
    \n

    再之后,,,,,

    \n

    多聚半乳糖醛酸酶 (polygalaturonase,PG)催化果胶分子中的 α-1,4- 半乳糖苷键裂解,

    \n

    使得果胶降解,香蕉软了。

    \n

    α-L- 阿拉伯呋喃糖苷酶(α-L-Arabinofuranosidase, α-L-Af)降解细胞壁多糖中的阿拉伯糖残基,

    \n

    导致果胶溶解和细胞壁结构松散,香蕉更软了。[5]

    \n

    纤维素酶(Cellulase,CEL)和β-D- 木聚糖苷酶活性增强,干掉香蕉皮细胞壁中的部分骨架结构,

    \n

    使纤维素半纤维素降解,香蕉更更软了。

    \n

    香蕉皮中还积累了部分抗性淀粉,也也被降解成可溶性糖,用于给果皮代谢供能。

    \n

    更更更软了,所以,香蕉皮软了,也薄了。

    \n

    但是没变强。

    \n

    正常来说,这个过程随着气温升高而加快,

    \n
    \"\"
    香蕉在 20°C 和 30°C 里各种酶的变化,可以看到温度越高曲线越高
    \n

    但是过了 30°C 后,高温会抑制叶绿素酶的发挥,

    \n

    导致出现香蕉果肉熟了,果皮还是绿色的情况。[6]

    \n
    \"\"
    香蕉在 20°C 和 30°C 果实硬度和果皮色度变化,可以明显看到 30°C 果实软了,但是果皮颜色还是绿色。
    \n

    由于香蕉摘下后自然产出的乙烯含量明显降低,

    \n

    所以会在袋子里放入含有乙烯利(2- 氯乙基磷酸)的纸片。

    \n

    在运输过程中缓慢产出乙烯,模拟这一过程。

    \n
    \"\"
    常见乙烯利卡片,类似一张小纸片。
    \n

    而除了乙烯,呼吸作用也是促使香蕉后熟的重要原因。

    \n

    所以提高环境中的二氧化碳浓度(大概在 4% - 6% ),

    \n

    降低氧气浓度(大约 2% - 5% )会显著提高香蕉的保鲜期。

    \n

    这就是气调储藏的原理。

    \n

    气调包装听着挺高大上,咱也挺常见,香蕉箱里那个透明袋就是气调袋。

    \n
    \"\"
    香蕉气调袋
    \n

    当然了,万事皆有度,

    \n

    人工乙烯过度催熟,

    \n

    除了会因催熟不均导致部分单宁等物质未完全转化,使一些不是特别熟的香蕉发涩。

    \n

    还会导致香蕉后熟过快,

    \n

    而香蕉成熟的过程中代谢前体分解的过程会产生挥发性酯类化合物,是香蕉香味的来源。

    \n

    后熟过快会使得这些酯类化合物产出过少,失去香味。

    \n

    这就是为啥感觉有的香蕉熟了特别特别香,而有些香蕉却没味的原因。

    \n
    \"\"
    香蕉从青熟到成熟后的过程。
    \n

    那么,现在。

    \n

    大声告诉我,香蕉君变软变黄的原因是哈?!!!!

    \n

    是乙烯吗?

    \n

    不,是哲♂学!!!!!!

    \n
    \"\"
    \n
    \n
    \n\n\n\n\n
    \n\n\n\n", + "category": "", + "link": "https://daily.zhihu.com/story/9782525", + "creator": "", + "pubDate": "Sun, 13 Jul 2025 08:27:02 +0800", + "enclosure": "", + "enclosureType": "", + "image": "", + "id": "", + "language": "", + "folder": "资讯", + "feed": "知乎", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "effd0addb60e241f3b0f566c8634b7ee", + "highlights": [] + }, + { + "title": "为什么热带鱼的颜色五彩缤纷,其它地带的就没有那么鲜艳呢", + "description": "
    \"头图\"/
    \n

    动物行为学的祖师爷康拉德·洛伦茨在他出版于 1963 年的《论攻击》一书里,也对热带鱼的颜色之鲜艳多变惊叹不已:

    \n
    \"\"
    \n
    \"\"
    \n

    所以这些鱼为什么有如此鲜艳的颜色呢?在潜水中,洛伦茨发现,这些鲜明的鱼有三个特性:

    \n

    1、它们定居在固定的地盘。

    \n

    2、它们用凶猛的攻击保护领地。

    \n

    3、除了捕食者猎食以外,它们只攻击同类的鱼。

    \n

    在实验室里,也出现了同样的现象,一个水族箱不能容下两条色彩鲜艳的鱼,否则会自相残杀到一条死去为止。

    \n

    为了进一步研究,洛伦茨和他的研究伙伴 Doris Zumpe 订做了一个两立方米的特大水缸,这样这些鱼都可以得到足够的空间建立自己的地盘。他们放进了 25 种小鱼,每种四条。Zumpe 经过统计发现,虽然鱼缸里同类的鱼只占很少的比例,每种鱼同类和异类之间咬架的频次之比为 85:15。

    \n

    所以答案就很明确了,鲜艳的色彩是同类互相争夺地盘时发出的信号,表示“别惹我!”。色彩越鲜明,同类越能清楚地感知到这一信息。

    \n
    \"\"
    \n

    那么为什么是热带的鱼而不是别的地方的鱼呢?下面是猫盟编辑的推测,未必全可信,但我想不会特别离题的。

    \n

    生物拥有两条基本的化学性质:

    \n

    1,所有生物控制生化活动的酶都对温度敏感,所以超出或者低于它们的最适温度,生命活性都会大打折扣。

    \n

    2,所有生物的生化活动都离不开水。

    \n

    另外还要加上第三条,大部分食物链是从绿色植物和藻类(光合作用)开始的,所以大部分生物离不开阳光。

    \n

    因此生命在什么样的地方最繁盛?集齐适宜的水、热、光三大因素的地方。所以庄稼在夏天生长而不是在冬天。

    \n

    热带的浅海就是具有丰富的水、热、光资源的地方。结果是这里的生物量和生物多样性都非常高,这是一个生命非常拥挤,或者说“卷”的地方。

    \n

    “卷”的环境有什么特征?

    \n

    你遭受到的压力大多是来自其他人,而不是来自物理环境的。

    \n

    同理,在一个生物密度非常高,生物之间的互动非常多的环境里,生物受到的自然选择大多是来自其他生物。

    \n

    于是在这种环境下,生物演化出大量的信号发给其他生物,颜色就是常用的一种发信号的方式。除了表示“从我的地盘滚开!”的颜色,还有表示“我有毒”的警告色。顺便一提,这些信号不一定意味着相互仇视,比如雄性动物可能有鲜艳颜色来吸引雌性,会给大鱼抓寄生虫的清洁鱼和清洁虾,也有鲜艳的条纹作为“医生制服”。

    \n

    类似的演化逻辑也适用于热带雨林。

    \n
    \"\"
    \n
    \"\"
    \n
    \"\"
    \n
    \"\"
    \n
    \"\"
    猫盟在云南考察中拍下的一些色彩鲜艳的动植物。
    \n

    反过来在缺乏某一条件的环境,比如冰原、荒漠,动物的颜色就要单调得多。

    \n

    而光照条件差的环境比如深海,不仅生物的数量少,而且视觉信号也很难发出,也就是“深海里黑咕隆咚,大家谁也看不见谁,随便长长就得了”。

    \n

    但是要说明这个原则不是绝对的。比如以色彩鲜艳著称的鸡形目鸟,很多都不是生活在热带地区。比如六盘山的气候属于温带半湿润大陆性气候,我们在这里拍到了很多的红腹锦鸡。

    \n
    \"\"
    \n
    \n
    \n\n\n\n\n
    \n\n\n\n", + "content": "
    \"头图\"/
    \n

    动物行为学的祖师爷康拉德·洛伦茨在他出版于 1963 年的《论攻击》一书里,也对热带鱼的颜色之鲜艳多变惊叹不已:

    \n
    \"\"
    \n
    \"\"
    \n

    所以这些鱼为什么有如此鲜艳的颜色呢?在潜水中,洛伦茨发现,这些鲜明的鱼有三个特性:

    \n

    1、它们定居在固定的地盘。

    \n

    2、它们用凶猛的攻击保护领地。

    \n

    3、除了捕食者猎食以外,它们只攻击同类的鱼。

    \n

    在实验室里,也出现了同样的现象,一个水族箱不能容下两条色彩鲜艳的鱼,否则会自相残杀到一条死去为止。

    \n

    为了进一步研究,洛伦茨和他的研究伙伴 Doris Zumpe 订做了一个两立方米的特大水缸,这样这些鱼都可以得到足够的空间建立自己的地盘。他们放进了 25 种小鱼,每种四条。Zumpe 经过统计发现,虽然鱼缸里同类的鱼只占很少的比例,每种鱼同类和异类之间咬架的频次之比为 85:15。

    \n

    所以答案就很明确了,鲜艳的色彩是同类互相争夺地盘时发出的信号,表示“别惹我!”。色彩越鲜明,同类越能清楚地感知到这一信息。

    \n
    \"\"
    \n

    那么为什么是热带的鱼而不是别的地方的鱼呢?下面是猫盟编辑的推测,未必全可信,但我想不会特别离题的。

    \n

    生物拥有两条基本的化学性质:

    \n

    1,所有生物控制生化活动的酶都对温度敏感,所以超出或者低于它们的最适温度,生命活性都会大打折扣。

    \n

    2,所有生物的生化活动都离不开水。

    \n

    另外还要加上第三条,大部分食物链是从绿色植物和藻类(光合作用)开始的,所以大部分生物离不开阳光。

    \n

    因此生命在什么样的地方最繁盛?集齐适宜的水、热、光三大因素的地方。所以庄稼在夏天生长而不是在冬天。

    \n

    热带的浅海就是具有丰富的水、热、光资源的地方。结果是这里的生物量和生物多样性都非常高,这是一个生命非常拥挤,或者说“卷”的地方。

    \n

    “卷”的环境有什么特征?

    \n

    你遭受到的压力大多是来自其他人,而不是来自物理环境的。

    \n

    同理,在一个生物密度非常高,生物之间的互动非常多的环境里,生物受到的自然选择大多是来自其他生物。

    \n

    于是在这种环境下,生物演化出大量的信号发给其他生物,颜色就是常用的一种发信号的方式。除了表示“从我的地盘滚开!”的颜色,还有表示“我有毒”的警告色。顺便一提,这些信号不一定意味着相互仇视,比如雄性动物可能有鲜艳颜色来吸引雌性,会给大鱼抓寄生虫的清洁鱼和清洁虾,也有鲜艳的条纹作为“医生制服”。

    \n

    类似的演化逻辑也适用于热带雨林。

    \n
    \"\"
    \n
    \"\"
    \n
    \"\"
    \n
    \"\"
    \n
    \"\"
    猫盟在云南考察中拍下的一些色彩鲜艳的动植物。
    \n

    反过来在缺乏某一条件的环境,比如冰原、荒漠,动物的颜色就要单调得多。

    \n

    而光照条件差的环境比如深海,不仅生物的数量少,而且视觉信号也很难发出,也就是“深海里黑咕隆咚,大家谁也看不见谁,随便长长就得了”。

    \n

    但是要说明这个原则不是绝对的。比如以色彩鲜艳著称的鸡形目鸟,很多都不是生活在热带地区。比如六盘山的气候属于温带半湿润大陆性气候,我们在这里拍到了很多的红腹锦鸡。

    \n
    \"\"
    \n
    \n
    \n\n\n\n\n
    \n\n\n\n", + "category": "", + "link": "https://daily.zhihu.com/story/9782530", + "creator": "", + "pubDate": "Sun, 13 Jul 2025 08:27:02 +0800", + "enclosure": "", + "enclosureType": "", + "image": "", + "id": "", + "language": "", + "folder": "资讯", + "feed": "知乎", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "565a00b28612b35d0e8b652eefd57d4e", + "highlights": [] + }, + { + "title": "为什么有时候无法控制自己的情绪?", + "description": "
    \"头图\"/
    \n

    由于天性敏感,成长至今我一直都在与强烈的情绪做对抗。管理情绪是我无时无刻都在做的事情,因为我相信:“ 驾驭情绪 = 驾驭人生。”

    \n

    如果一个人无法管理自己的情绪,任由情绪控制自己,不仅会伤到身边的人,更会伤到自己。无法控制情绪,有时候比生活无法自理后果更严重。

    \n

    当你意识到这个问题的存在,就已经是一件非常好的事情。以下是这些年我整理的一套对我自己适用的方法:

    \n

    1. 认识情绪 - 情绪圆盘

    \n

    应该有许多人和我一样,小时候并没有人教过我们情绪的种类。我在这几年养育自己的孩子时,因为他不会说话,所以我必须观察他的表情和动作,再给他的情绪进行命名,才知道如何去处理他的情绪。

    \n

    也就是在这个时候,我意识到我根本就不具备 “ 开心 ” “ 伤心 ” “ 生气 ” 等基础情绪词汇以外的词汇。这也就导致我在最初处理我孩子的情绪时,只能手忙脚乱的什么都给他,直到给到他要的那个东西为止。

    \n

    联想到我小时候,一件事情刺激了我的情绪时,我的大脑死机时只会会发生 “冻结 ” “ 攻击 ” “ 逃跑 ” 三种原始状态。为了高效识别我那还不会说话的孩子的需要,我第一次在人生中向自己提问,到底情绪有多少种?

    \n

    在无意间,我看到了这个:

    \n
    \"\"
    \n

    这张图叫作情绪圆盘(Emotion Wheel),又叫情绪之轮、情感轮盘、情绪图谱,最早由美国心理学家 Robert Plutchik 提出。他认为情绪像颜色一样有核心原色,也有混合、层级与强度。

    \n

    情绪圆盘的目的是帮助我们从模糊的 “我不太舒服” “我很烦”中,精准识别出情绪的种类、强度与成因,从而提升自我理解、沟通能力和情绪调节力。

    \n

    最基本的感受,Plutchik 定义为 8 种“情绪原色”:喜悦、信任、恐惧、惊讶、悲伤、厌恶、愤怒、期待。此外,还有衍生情绪与细化表达让情绪的定义更加丰富多彩。

    \n

    词汇量的限制让我们无法识别与命名自己的情绪,就导致我们无法控制情绪。所以我们首先需要去学会识别,在有情绪的那一秒,先问问自己:“ 你感受到了什么?” 才能逐渐去控制和驾驭自己的情绪。

    \n

    2. “ 你现在是什么感受?”

    \n

    这个问题,是无法控制情绪者直接跳过却最关键的问题。因为在我们的教育中,我们常常将被动的接受他人的感受视为一种礼仪与宽容。而我们常常忽略自己的感受,并将它压制在心底。一直到我们爆发为止。

    \n

    在我第一次教我孩子什么是情绪时,发现光说没用,于是我给他买了一个情绪小镜子。

    \n
    \"\"
    \n

    每当我感受到孩子的不同情绪时,我则会尽可能的用丰富的词汇帮他定义他此刻的感受。并且陪着他一起找到他真正想要做却自己做不到的事情。

    \n

    我的问题通常是三个:

    \n
      \n
    • “ 你怎么啦?”
    • \n
    • “ 为什么会这样?”
    • \n
    • “ 可以不可以指给我看你要什么?”
    • \n
    \n

    同理,每个人对待自己,应该好好问问自己心里的小孩为什么会愤怒、伤心、痛苦,除了闷在心里或者爆发,我们有没有别的方式去表达?

    \n

    情绪是我们与世界互动最直接的语言。当你能精准地说出情绪,就拥有了更强的 自我掌控力、沟通能力与人际穿透力。

    \n

    所以常常要问自己:“ 你怎么啦?”

    \n

    3. 未被处理的心理创伤

    \n

    弗洛伊德说:“ 人会反复寻找类似早年创伤场景来“重新演出”,试图这一次赢回掌控感、修复当年的无力。”

    \n

    有时候我们的情绪无法控制,也是因为小时候没有治愈与闭环的一个心理创伤的重演。我们选择的人,常常又一次让我们失望。这就叫作 “ 重演强迫(Repetition Compulsion)”

    \n

    你的反应不是“过度”,而是你小时候的那部分受伤的你,在今天被某句话、某个动作,激活了旧有痛点。于是在心理学上,我们使用 “ 内在小孩(Inner Child)疗法” 去识别当年我们的伤,并以成年人的方式完成闭环。

    \n

    4. 情绪管理实操4.1. 呼吸法

    \n

    很长时间以来,由于有正念练习的习惯,我控制自己强烈情绪的最常用方式就是深呼吸。快速激活副交感神经的方式,深而缓的吸气和呼气,可以激活迷走神经,让心率减慢从而平静情绪。除此之外可以调节血氧与二氧化碳平衡、让注意力回到身体。

    \n

    我的钢琴老师教给我的呼吸方式是在脑子里数数,每一秒一个数字。吸 6 下,停顿,呼 4 下,停顿。吸气时,肚子鼓起来,呼气时,肚子扁下去(腹式呼吸)。

    \n

    4.2. 5-4-3-2-1 情绪锚定法

    \n

    这种适用于激烈情绪来临,并且你已经快失控的时候。也可以用于焦虑症患者濒死感爆发、恐慌发作、失眠等等情景。

    \n
      \n
    • 看
    • \n
    \n

    环顾四周,说出你能看见的 5 个东西,详细描述外观

    \n
      \n
    • 碰
    • \n
    \n

    说出你现在能碰到的 4 个东西,详细描述质感

    \n
      \n
    • 听
    • \n
    \n

    并说出你现在能听到的 3 种声音,并描述它们的来源

    \n
      \n
    • 闻
    • \n
    \n

    说一说你现在能闻到的两种味道,并描述这种味道的来源

    \n
      \n
    • 尝
    • \n
    \n

    感受一下你嘴巴里的味道

    \n

    最后,慢慢吐气,反复一轮。

    \n

    4.3. 情绪小游戏

    \n

    这是我在一本叫《小学生心理手册》上看到的,由于我是教育学专业,课程中很大一部分占比是心理学。然而我觉得专业心理学知识其实并不适合每个人看。因为心理学是一个庞大的学科,里面包含了神经学、生物学、医学等交叉领域。很容易把自己看出问题,觉得自己有病。

    \n

    这本书是我在给孩子做情绪引导的时候真正会用的一本书,帮助他说出他想说的话。也在这个过程中,我找到了很多我小时候感觉受伤却不知道为什么的成因。这本书名字虽然很不怎么样,但是它其实同样适用于成年人,可以重新治愈一次内心小小的自己。

    \n
    \"\"
    \n

    这里面我最喜欢的游戏例如:

    \n
      \n
    • 写出能立即让自己开心的几件事情,例如:吃个冰激凌、吃个火锅、买一件新衣服等等
    • \n
    • 写出让自己恐惧的事情,
    • \n
    • 将自己现在的情绪全部都剪成小纸片,塞到一个瓶子里
    • \n
    \n

    时间久了你会发现,原来有情绪,并不是那么可怕的事情,甚至是有趣的。只是我们也许从小没有真正的经历过系统性的情绪管理训练,导致我们一直都觉得自己:

    \n

    有情绪 = 不稳定 = 有病。

    \n

    情绪的输出是必要的,也是健康的。我们要告诉自己,我们没有病,我们只是却不知道怎么表达自己的情绪。

    \n

    5. 允许自己有情绪才能更好的认识自己

    \n

    一个没有情绪的人,要么生病了,要么死了。真正的情绪稳定,并不是不发脾气压抑自己,而是能够直面情绪的存在与发生,并且接受它存在的合理性。

    \n

    即使是焦虑和恐惧,也都是原始社会中让人活命的必要情绪,否则你将识别不出危险。所以情绪的存在是必要的。而我们不能以绝对的方式去 “攻击” 或 “逃跑”, 而是要学会在不伤害他人的情况下,表明自己的情绪。

    \n

    记住,你的情绪,决定了你的命运。

    \n

    最后,请好好治愈你自己吧!愿每个像我一样的人,最终找到一个驾驭自己情绪的方式,快乐开心的生活。

    \n
    \n
    \n\n\n\n\n
    \n\n\n\n

    ", + "content": "
    \"头图\"/
    \n

    由于天性敏感,成长至今我一直都在与强烈的情绪做对抗。管理情绪是我无时无刻都在做的事情,因为我相信:“ 驾驭情绪 = 驾驭人生。”

    \n

    如果一个人无法管理自己的情绪,任由情绪控制自己,不仅会伤到身边的人,更会伤到自己。无法控制情绪,有时候比生活无法自理后果更严重。

    \n

    当你意识到这个问题的存在,就已经是一件非常好的事情。以下是这些年我整理的一套对我自己适用的方法:

    \n

    1. 认识情绪 - 情绪圆盘

    \n

    应该有许多人和我一样,小时候并没有人教过我们情绪的种类。我在这几年养育自己的孩子时,因为他不会说话,所以我必须观察他的表情和动作,再给他的情绪进行命名,才知道如何去处理他的情绪。

    \n

    也就是在这个时候,我意识到我根本就不具备 “ 开心 ” “ 伤心 ” “ 生气 ” 等基础情绪词汇以外的词汇。这也就导致我在最初处理我孩子的情绪时,只能手忙脚乱的什么都给他,直到给到他要的那个东西为止。

    \n

    联想到我小时候,一件事情刺激了我的情绪时,我的大脑死机时只会会发生 “冻结 ” “ 攻击 ” “ 逃跑 ” 三种原始状态。为了高效识别我那还不会说话的孩子的需要,我第一次在人生中向自己提问,到底情绪有多少种?

    \n

    在无意间,我看到了这个:

    \n
    \"\"
    \n

    这张图叫作情绪圆盘(Emotion Wheel),又叫情绪之轮、情感轮盘、情绪图谱,最早由美国心理学家 Robert Plutchik 提出。他认为情绪像颜色一样有核心原色,也有混合、层级与强度。

    \n

    情绪圆盘的目的是帮助我们从模糊的 “我不太舒服” “我很烦”中,精准识别出情绪的种类、强度与成因,从而提升自我理解、沟通能力和情绪调节力。

    \n

    最基本的感受,Plutchik 定义为 8 种“情绪原色”:喜悦、信任、恐惧、惊讶、悲伤、厌恶、愤怒、期待。此外,还有衍生情绪与细化表达让情绪的定义更加丰富多彩。

    \n

    词汇量的限制让我们无法识别与命名自己的情绪,就导致我们无法控制情绪。所以我们首先需要去学会识别,在有情绪的那一秒,先问问自己:“ 你感受到了什么?” 才能逐渐去控制和驾驭自己的情绪。

    \n

    2. “ 你现在是什么感受?”

    \n

    这个问题,是无法控制情绪者直接跳过却最关键的问题。因为在我们的教育中,我们常常将被动的接受他人的感受视为一种礼仪与宽容。而我们常常忽略自己的感受,并将它压制在心底。一直到我们爆发为止。

    \n

    在我第一次教我孩子什么是情绪时,发现光说没用,于是我给他买了一个情绪小镜子。

    \n
    \"\"
    \n

    每当我感受到孩子的不同情绪时,我则会尽可能的用丰富的词汇帮他定义他此刻的感受。并且陪着他一起找到他真正想要做却自己做不到的事情。

    \n

    我的问题通常是三个:

    \n
      \n
    • “ 你怎么啦?”
    • \n
    • “ 为什么会这样?”
    • \n
    • “ 可以不可以指给我看你要什么?”
    • \n
    \n

    同理,每个人对待自己,应该好好问问自己心里的小孩为什么会愤怒、伤心、痛苦,除了闷在心里或者爆发,我们有没有别的方式去表达?

    \n

    情绪是我们与世界互动最直接的语言。当你能精准地说出情绪,就拥有了更强的 自我掌控力、沟通能力与人际穿透力。

    \n

    所以常常要问自己:“ 你怎么啦?”

    \n

    3. 未被处理的心理创伤

    \n

    弗洛伊德说:“ 人会反复寻找类似早年创伤场景来“重新演出”,试图这一次赢回掌控感、修复当年的无力。”

    \n

    有时候我们的情绪无法控制,也是因为小时候没有治愈与闭环的一个心理创伤的重演。我们选择的人,常常又一次让我们失望。这就叫作 “ 重演强迫(Repetition Compulsion)”

    \n

    你的反应不是“过度”,而是你小时候的那部分受伤的你,在今天被某句话、某个动作,激活了旧有痛点。于是在心理学上,我们使用 “ 内在小孩(Inner Child)疗法” 去识别当年我们的伤,并以成年人的方式完成闭环。

    \n

    4. 情绪管理实操4.1. 呼吸法

    \n

    很长时间以来,由于有正念练习的习惯,我控制自己强烈情绪的最常用方式就是深呼吸。快速激活副交感神经的方式,深而缓的吸气和呼气,可以激活迷走神经,让心率减慢从而平静情绪。除此之外可以调节血氧与二氧化碳平衡、让注意力回到身体。

    \n

    我的钢琴老师教给我的呼吸方式是在脑子里数数,每一秒一个数字。吸 6 下,停顿,呼 4 下,停顿。吸气时,肚子鼓起来,呼气时,肚子扁下去(腹式呼吸)。

    \n

    4.2. 5-4-3-2-1 情绪锚定法

    \n

    这种适用于激烈情绪来临,并且你已经快失控的时候。也可以用于焦虑症患者濒死感爆发、恐慌发作、失眠等等情景。

    \n
      \n
    • 看
    • \n
    \n

    环顾四周,说出你能看见的 5 个东西,详细描述外观

    \n
      \n
    • 碰
    • \n
    \n

    说出你现在能碰到的 4 个东西,详细描述质感

    \n
      \n
    • 听
    • \n
    \n

    并说出你现在能听到的 3 种声音,并描述它们的来源

    \n
      \n
    • 闻
    • \n
    \n

    说一说你现在能闻到的两种味道,并描述这种味道的来源

    \n
      \n
    • 尝
    • \n
    \n

    感受一下你嘴巴里的味道

    \n

    最后,慢慢吐气,反复一轮。

    \n

    4.3. 情绪小游戏

    \n

    这是我在一本叫《小学生心理手册》上看到的,由于我是教育学专业,课程中很大一部分占比是心理学。然而我觉得专业心理学知识其实并不适合每个人看。因为心理学是一个庞大的学科,里面包含了神经学、生物学、医学等交叉领域。很容易把自己看出问题,觉得自己有病。

    \n

    这本书是我在给孩子做情绪引导的时候真正会用的一本书,帮助他说出他想说的话。也在这个过程中,我找到了很多我小时候感觉受伤却不知道为什么的成因。这本书名字虽然很不怎么样,但是它其实同样适用于成年人,可以重新治愈一次内心小小的自己。

    \n
    \"\"
    \n

    这里面我最喜欢的游戏例如:

    \n
      \n
    • 写出能立即让自己开心的几件事情,例如:吃个冰激凌、吃个火锅、买一件新衣服等等
    • \n
    • 写出让自己恐惧的事情,
    • \n
    • 将自己现在的情绪全部都剪成小纸片,塞到一个瓶子里
    • \n
    \n

    时间久了你会发现,原来有情绪,并不是那么可怕的事情,甚至是有趣的。只是我们也许从小没有真正的经历过系统性的情绪管理训练,导致我们一直都觉得自己:

    \n

    有情绪 = 不稳定 = 有病。

    \n

    情绪的输出是必要的,也是健康的。我们要告诉自己,我们没有病,我们只是却不知道怎么表达自己的情绪。

    \n

    5. 允许自己有情绪才能更好的认识自己

    \n

    一个没有情绪的人,要么生病了,要么死了。真正的情绪稳定,并不是不发脾气压抑自己,而是能够直面情绪的存在与发生,并且接受它存在的合理性。

    \n

    即使是焦虑和恐惧,也都是原始社会中让人活命的必要情绪,否则你将识别不出危险。所以情绪的存在是必要的。而我们不能以绝对的方式去 “攻击” 或 “逃跑”, 而是要学会在不伤害他人的情况下,表明自己的情绪。

    \n

    记住,你的情绪,决定了你的命运。

    \n

    最后,请好好治愈你自己吧!愿每个像我一样的人,最终找到一个驾驭自己情绪的方式,快乐开心的生活。

    \n
    \n
    \n\n\n\n\n
    \n\n\n\n

    ", + "category": "", + "link": "https://daily.zhihu.com/story/9782531", + "creator": "", + "pubDate": "Sun, 13 Jul 2025 08:27:02 +0800", + "enclosure": "", + "enclosureType": "", + "image": "", + "id": "", + "language": "", + "folder": "资讯", + "feed": "知乎", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "a6edf8b3267afbd3606be90cf9096317", + "highlights": [] + }, + { + "title": "「祛魅」的本质是什么?不断暗示自己应该对理想化的感情「祛魅」、接受现实的不完美,这是正确的吗?", + "description": "
    \"头图\"/
    \n

    你就记着一个原则,凡是类似「祛魅」、「异化」、「物化」、「解构」等等,这种看上去很抽象很厉害的词儿,你都不能用二极管式的对错思维来进行判断。

    \n

    这是因为当我们将这些词汇从它们原来所存在的上下文抽取出来,用于形容我们具体的生活现象时,它们往往已经不再是其本意。

    \n

    所以讨论这些词本身的对错并没有太大的意义,这全看你怎么去认知它,怎么去构建它。

    \n

    而我,则更倾向用重新解释的方式,让这些词真正能帮到你,而不是成为一个单纯用来被肯定或否认的对象。

    \n
    \n

    比如,这个问题中问到的「祛魅」。

    \n

    它本来指的是现代社会对于宗教进行理性化和文化合理化解释的现象,后来被马克思·韦伯拿来描述现代社会理性化进程中,神秘、神圣的东西逐渐被科学和效率取代的现象,这本身是个宏观层面、社会层面的,很大的词汇。

    \n

    但好像就在一夜之间,所有人都开始念叨着要「祛魅」——对爱情祛魅,对工作祛魅,对人生的意义祛魅……

    \n

    仿佛只要把什么东西从神坛上拽下来,踩上两脚,吐口唾沫,自己就能立刻获得某种超然的清醒。

    \n

    「爱情?别傻了,都是荷尔蒙和多巴胺的骗局!」

    \n

    「理想?醒醒吧,996 和还不完的房贷才是你的宿命!」

    \n

    「婚姻?不过是经济共同体的契约罢了。」

    \n

    你看,只要善于祛魅,就能把自己活成一个清醒的悲观主义者,提前把所有美好的可能统统踩碎,然后骄傲的宣布:「看吧,我早知道会这样!」

    \n

    但问题是——你这是「祛魅」了吗?

    \n

    还是只是换了一种方式继续自我欺骗?

    \n
    \n

    我跟你讲,真落地到操作层面,比如说心理学中,最接近「日常祛魅」的词汇,叫做「防御机制」。

    \n

    想象一下,一个孩子第一次被朋友背叛,他可能会哭,会愤怒,会不解。

    \n

    但是到了第三次、第四次,他开始学会冷笑:「呵,人性本恶,我早该知道……」

    \n

    这不叫清醒,这叫麻木!

    \n

    祛魅者们的逻辑很简单:只要我先否定一切,就没人能伤害我。

    \n

    他们害怕失望,所以就干脆先让自己对一切不抱希望;

    \n

    他们害怕幻灭,所以就干脆先亲手打碎所有的幻想。

    \n

    你问我,这样的祛魅是正确的吗?

    \n

    要我说,这哪里是祛魅,不过是一场更高级的自我安慰罢了。

    \n
    \n

    所以我想斗胆重新定义一下祛魅这个词汇,

    \n

    我认为真正合理的祛魅,不是否认,而是重构。

    \n

    不是让你变成一个冷血的现实主义者,而是让你学会在幻灭之后,依然能建立起自己的信仰。

    \n

    爱情祛魅?不是让你相信「爱情不存在」,而是让你明白,爱情不是童话里的完美故事,是两个不完美的人,在泥潭里互相拉扯着向前走。

    \n

    理想祛魅?不是让你放弃追求,而是让你接受,理想不是一蹴而就的奇迹,而是你日复一日的枯燥的坚持。

    \n

    人生祛魅?不是让你变成行尸走肉,而是让你在看清生活的荒诞之后,依然能找到属于自己的那点微光。

    \n

    祛魅不是让你放弃相信,而是让你学会更坚韧的相信。

    \n

    所以,别再去纠结「祛魅」是对还是错了。

    \n

    也别在自欺欺人说「我已经祛魅了」。

    \n

    真正的祛魅,是让你学会在废墟之上,重新点燃自己的火把。

    \n
    \n
    \n\n\n\n\n
    \n\n\n\n

    ", + "content": "
    \"头图\"/
    \n

    你就记着一个原则,凡是类似「祛魅」、「异化」、「物化」、「解构」等等,这种看上去很抽象很厉害的词儿,你都不能用二极管式的对错思维来进行判断。

    \n

    这是因为当我们将这些词汇从它们原来所存在的上下文抽取出来,用于形容我们具体的生活现象时,它们往往已经不再是其本意。

    \n

    所以讨论这些词本身的对错并没有太大的意义,这全看你怎么去认知它,怎么去构建它。

    \n

    而我,则更倾向用重新解释的方式,让这些词真正能帮到你,而不是成为一个单纯用来被肯定或否认的对象。

    \n
    \n

    比如,这个问题中问到的「祛魅」。

    \n

    它本来指的是现代社会对于宗教进行理性化和文化合理化解释的现象,后来被马克思·韦伯拿来描述现代社会理性化进程中,神秘、神圣的东西逐渐被科学和效率取代的现象,这本身是个宏观层面、社会层面的,很大的词汇。

    \n

    但好像就在一夜之间,所有人都开始念叨着要「祛魅」——对爱情祛魅,对工作祛魅,对人生的意义祛魅……

    \n

    仿佛只要把什么东西从神坛上拽下来,踩上两脚,吐口唾沫,自己就能立刻获得某种超然的清醒。

    \n

    「爱情?别傻了,都是荷尔蒙和多巴胺的骗局!」

    \n

    「理想?醒醒吧,996 和还不完的房贷才是你的宿命!」

    \n

    「婚姻?不过是经济共同体的契约罢了。」

    \n

    你看,只要善于祛魅,就能把自己活成一个清醒的悲观主义者,提前把所有美好的可能统统踩碎,然后骄傲的宣布:「看吧,我早知道会这样!」

    \n

    但问题是——你这是「祛魅」了吗?

    \n

    还是只是换了一种方式继续自我欺骗?

    \n
    \n

    我跟你讲,真落地到操作层面,比如说心理学中,最接近「日常祛魅」的词汇,叫做「防御机制」。

    \n

    想象一下,一个孩子第一次被朋友背叛,他可能会哭,会愤怒,会不解。

    \n

    但是到了第三次、第四次,他开始学会冷笑:「呵,人性本恶,我早该知道……」

    \n

    这不叫清醒,这叫麻木!

    \n

    祛魅者们的逻辑很简单:只要我先否定一切,就没人能伤害我。

    \n

    他们害怕失望,所以就干脆先让自己对一切不抱希望;

    \n

    他们害怕幻灭,所以就干脆先亲手打碎所有的幻想。

    \n

    你问我,这样的祛魅是正确的吗?

    \n

    要我说,这哪里是祛魅,不过是一场更高级的自我安慰罢了。

    \n
    \n

    所以我想斗胆重新定义一下祛魅这个词汇,

    \n

    我认为真正合理的祛魅,不是否认,而是重构。

    \n

    不是让你变成一个冷血的现实主义者,而是让你学会在幻灭之后,依然能建立起自己的信仰。

    \n

    爱情祛魅?不是让你相信「爱情不存在」,而是让你明白,爱情不是童话里的完美故事,是两个不完美的人,在泥潭里互相拉扯着向前走。

    \n

    理想祛魅?不是让你放弃追求,而是让你接受,理想不是一蹴而就的奇迹,而是你日复一日的枯燥的坚持。

    \n

    人生祛魅?不是让你变成行尸走肉,而是让你在看清生活的荒诞之后,依然能找到属于自己的那点微光。

    \n

    祛魅不是让你放弃相信,而是让你学会更坚韧的相信。

    \n

    所以,别再去纠结「祛魅」是对还是错了。

    \n

    也别在自欺欺人说「我已经祛魅了」。

    \n

    真正的祛魅,是让你学会在废墟之上,重新点燃自己的火把。

    \n
    \n
    \n\n\n\n\n
    \n\n\n\n

    ", + "category": "", + "link": "https://daily.zhihu.com/story/9782493", + "creator": "", + "pubDate": "Sat, 12 Jul 2025 08:27:03 +0800", + "enclosure": "", + "enclosureType": "", + "image": "", + "id": "", + "language": "", + "folder": "资讯", + "feed": "知乎", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "987540095f46ac857587ce0bc25728dd", + "highlights": [] + }, + { + "title": "「卷又卷不动,躺又躺不平」?总是因为「放松」而感到自责,这是种怎样的心理?如何调节?", + "description": "
    \"头图\"/
    \n

    「卷又卷不动,躺又躺不平」,以及因此为「放松」感到自责的心理,恰恰是我在《倾尽全力去「敷衍」》一文中试图剖析的困境。这是一种被困在「得过且过者」和「拼命努力者」这两种无效模式之间的状态,其核心在于人们混淆了外部强加的「质量标尺」与自身真实的「偏好曲线」,并因此产生了不必要的内疚感。

    \n

    让我来解释一下这种心理:

    \n
      \n
    1. 内化了「应该拼命」的社会规训: 社会、家长、老师从小就向我们灌输「你应该尽力做到最好」、「努力奋斗是一种美德」的观念。这种观念让你觉得,任何时候,只要没有在某个(往往是他人定义的)「质量标尺」上向右推进(即追求更高质量、更多产出),你就是在犯错,就是「不够好」。于是,「放松」这种在质量标尺上看起来是「停滞」甚至「倒退」的行为,自然会引发自责。你觉得自己在「浪费时间」,没有「尽力」。

    2. \n
    3. 对「卷」的无效性有所察觉,但又无法彻底「躺平」: 「卷又卷不动」意味着你可能已经意识到,像个「拼命努力者」一样,不问目标、不计成本地在某个质量标尺上盲目追求极致,往往是「做无用功」,投入巨大却可能偏离了你真正的需求,或者让你精疲力尽,难以为继。 然而,「躺又躺不平」则是因为你内心深处依然被那种「应该拼命」的规训所束缚,或者你无法完全抛弃所有目标。你害怕彻底的「得过且过」会让你一事无成,或者受到他人的负面评价。这种状态下,即使你尝试放松,内心那个「应该努力」的声音也不会停止,于是自责感便油然而生。

    4. \n
    5. 目标模糊,导致评价标准混乱: 当你因为放松而自责时,很可能你并没有清晰地定义自己「真正想要实现的目标」以及达成该目标所需的「质量目标」。你只是笼统地觉得「应该做得更多更好」。没有明确的目标,你就无法判断当前的「放松」是必要的休整(为了更高效地达成目标),还是真正的懈怠(偏离了目标)。
    6. \n
    \n

    如何调节这种心理?关键在于采取我所说的「第三条路」——倾尽全力去「敷衍」:

    \n
      \n
    1. 明确你的真实目标: 首先,停下来,认真思考:对于你当前面对的任务或人生阶段,你真正想要的是什么?是为了拿到一个特定的证书?是为了学到某项具体技能?是为了维持一个舒适的生活水平?还是为了某个更宏大的理想?不要被社会或他人告诉你「应该」想要什么。

    2. \n
    3. 根据真实目标,设定清晰的「质量目标」: 一旦明确了真实目标,再据此设定一个恰到好处的质量目标。这个目标应该是「精确命中」,不多也不少。例如,如果你的目标只是通过一门考试,那么你的质量目标就是及格线,而不是完美无缺的满分。如果你的目标是学习知识,那么分数可能就不是最重要的,重要的是你是否吸收了你需要的信息。

    4. \n
    5. 以「最小代价」精确命中你的质量目标: 将你的精力和聪明才智用于「如何以最小的努力(时间、精力、资源)精确达成你设定的质量目标」。这本身就是一个有趣的优化问题。这包括了高效工作,也包括了必要的休息和放松,因为持续的疲劳会降低效率,反而增加达成目标的总代价。

    6. \n
    7. 重新定义「放松」的意义: 如果「放松」和「休息」是你为了以最小代价、可持续地达成你的真实目标所必需的环节,那么它就不是「浪费」,而是高效策略的一部分。就像一台机器需要保养才能更好地运转一样。这种情况下,因为必要的放松而自责是毫无道理的。

    8. \n
    9. 将完美主义导向「过程」而非「结果」: 如果你是个完美主义者,不要强迫自己去容忍低质量的产出(如果那确实低于你为自己设定的合理目标)。但是,你可以将你的完美主义倾向导向「以最小努力达成目标」这个过程。追求策略的完美、执行的完美、对沉没成本的果断放弃、以及对何时「恰到好处」的精准判断。

    10. \n
    11. 为「做无用功」而内疚,而非为「未达他人标准」而内疚: 如果你非要内疚,那么请为你「未能高效达成自己的目标」(比如投入了过多精力却收效甚微,或者因为不必要的拖延而错失良机)而内疚,而不是为没有将某个随意的、他人设定的质量标准当作自己的真实偏好而内疚。
    12. \n
    \n

    当你能清晰地知道自己要去哪里(真实目标),需要做到什么程度(质量目标),并且专注于如何最高效地到达那里时,「放松」就不再是让你自责的洪水猛兽,而是你智慧策略的一部分。你会发现,「卷」可以是有方向、有效率的,「躺」也可以是策略性的休整,而不是彻底的放弃。这样,你就能从「卷又卷不动,躺又躺不平」的困境中解脱出来。

    \n
    以上内容为 gemini-2.5-pro 生成的序言
    以下内容摘自 @Thoughts Memo 汉化组的译文《倾尽全力去「敷衍」》
    \n

    我经常接触许多有效利他主义者(EA)。他们中的许多人,其主要驱动力往往类似于内疚感(因自身坐拥优渥资源与机遇而他人仍在受苦)或羞愧感(因觉得自己做得不够)。说实话,我许多非 EA 圈的朋友,其主要动力也源于内疚或羞愧。

    \n

    我无意指摘这种由内疚 / 羞愧驱动的动机:我个人遵循这样一条原则,当有人为了让世界变得更美好而付出巨大努力或投入大量资金时,我会极力避免去评判他们的动机。内疚感和羞愧感或许是将人们从自满状态中警醒过来的有效工具。然而,我担心从长远来看,内疚和羞愧并非健康的动力来源。在我许多朋友身上,内疚和羞愧往往会引发执行力缺失(akrasia)、降低效率,并消耗积极性。因此,在未来几周,我将撰写系列文章,探讨如何摆脱内疚 / 羞愧驱动的动机,并代之以更强大的动力。

    \n

    1

    \n

    假设你是一名大学生,需要交一篇论文。论文的质量取决于你投入的精力。我们假定你对这个项目相当了解:只需适度努力便能拿到 A,而若投入巨大精力,你能写出远超普通 A 级水准的佳作。

    \n
    \"\"
    img
    \n

    教育环境总是潜移默化地试图让学生相信,他们的偏好应当永远指向质量标尺的右端。家长和老师会说「你应该尽力做到最好」之类的话,并对那些不奋力追求更高质量的人大加指责或令其羞愧。

    \n

    面对这种规训,人们通常有两种反应。第一类人(「得过且过者」)不认同质量越高越符合个人偏好这种看法。他们不在乎课程,不停抱怨必须做的那些毫无意义的无用功,马马虎虎地应付作业,交上去的东西要么勉强及格,要么干脆不及格。「得过且过者」往往对强迫他们写论文的权威感到不满。

    \n

    第二类人(「拼命努力者」)则认同质量越高越符合个人偏好这一前提,在质量标尺上不断追求更高。「拼命努力者」涵盖了各种能力水平的人:有的拼尽全力只为拿到 C,有的则以能创作出杰作为傲。有些人试图讨好老师,另一些人则是完美主义者,绝不允许自己交出任何未尽全力的作品。他们中有些是一丝不苟的人,即便拿了 A 仍感内疚,因为他们知道自己本可以做得更好,也认为本应做得更好。他们中有的谦逊,有的爱炫耀,但无一例外都在向右推进。

    \n

    社会花费了大量时间规训我们,让我们认为「拼命努力者」优于「得过且过者」。努力奋斗是一种美德。「得过且过者」没抓住教育的意义;他们待在学校干嘛呢?「拼命努力者」前途无量,「得过且过者」则一事无成。

    \n

    但实际上,这两类人的做法都有问题。

    \n

    如果你想变得真正高效,请记住你的奋斗目标是什么。

    \n

    而且,剧透一下,你的目标并非「写一篇高质量的论文」。那会是一个相当愚蠢的奋斗目标。

    \n

    你选修这门课的目标究竟是什么?也许是出于社会压力(父母要求的)、社会惯性(别人都上大学)以及一种模糊的信念——认为这是通往好工作和舒适生活的必经之路。又或许,你想要好成绩,以便将来获得大量金钱和权力去「屠龙」(实现宏伟目标)。再或许,你纯粹是出于对知识的渴求。但无论原因为何,这个原因都会在质量标尺上确定一个具体的目标点。因此,你的目标应该是精确命中那个质量目标——既不超出,也不低于。

    \n

    你的偏好并非「在质量标尺上越往右越好」。你的偏好是以最小的代价(努力)精确命中那个质量目标。

    \n
    \"\"
    img
    \n

    如果你只想通过这门课,那就用最少的精力去通过。其他的努力都是在做无用功。

    \n

    如果你想拿高分,那就用最少的精力去拿高分。其他的努力都是在做无用功。

    \n

    如果你想尽可能透彻地学习知识内容,那就从作业中汲取所有知识养分,不必纠结于分数。其他的努力都是在做无用功。

    \n

    如果你想同时达成多个目标:好成绩(作为能力信号)、赢得尊重(出于社交需求)、获取知识(为了种种益处),那么就精准定位那个能让你获得好成绩、给老师留下好印象、并学到知识的最低质量目标,并尽可能高效地达成它。任何超出的部分都是在做无用功。

    \n

    你的质量目标可能远低于 F 线(不及格)——比如,你早已通过该课程,这份作业无关紧要。你的质量目标也可能远高于 A 线——比如,你是为了学习知识内容,而分数膨胀意味着拿 A 比以信息量最大的方式完成作业要容易得多。但无论如何,你的真实目标总会对应一个质量目标。

    \n

    「得过且过者」和「拼命努力者」的目标都偏离了方向。「得过且过者」嘲笑「拼命努力者」,认为他们将人为设定的质量标准奉为圭臬,在过度努力上浪费精力。「拼命努力者」则嘲笑「得过且过者」,认为他们身在课堂却拒绝学习。而双方的嘲笑都有道理!因为他们都在做无用功。

    \n

    「得过且过者」未能全力以赴,因为他们意识到那条质量标尺并非他们的偏好曲线。「拼命努力者」则将全部力量投入错误的目标,试图在标尺上走得越远越好,将精力浪费在一场并不属于他们的战斗中。所以,请选择第三条路:记住你的奋斗目标。永远全力以赴,只为尽快达成你的质量目标。

    \n

    倾尽全力去「敷衍」一切。

    \n

    (我的老师们过去常说,只要我肯努力,定能成就一番事业。我则回应他们,如果想让我更努力,他们得先发明出比 A 更高的分数等级才行。)

    \n

    2

    \n

    一个常见的反对意见在此出现:

    \n
    有些事情太重要了,不能「敷衍了事」。有些事情就值得你全力以赴去争取。敷衍家庭作业是一回事,敷衍地救人命则是完全不同的另一回事。有时候,你确实想在质量标尺上走得越远越好。
    \n

    这种说法对也不对,因为它混淆了一些概念。对于任何项目,目标始终不应高于预设的质量目标,并且永远要追求以最小的代价(精力、资源)达成目标。无论你是在写学期论文、从火场救人,还是在创建跨星系的人类文明——目标永远是以最小代价达到某个质量目标。有效资源是稀缺的。

    \n

    话虽如此,质量目标可以设得非常非常高。事实上,质量目标有时甚至高不可攀。通常,我们根本没有能力达到我们设定的质量目标,在这些情况下,我们确实想要沿着偏好曲线尽可能地向右推进。

    \n
    \"\"
    img
    \n

    当你从事的工作相对于你的技能水平而言非常困难时,或者在竞争激烈的环境中,又或者当你在发出你「能吃苦耐劳」的信号时,这种情况会自然发生。但别搞混了。即使你是为热爱而写作,最终也必须停止修改,宣告完成。即使你正在从燃烧的建筑中救人,最终也会停止为确保此人存活而投入更多精力,转而将精力用于拯救其他濒危者。即使你在建设一个跨星系的文明,也需要在建设文明的投入与享受文明生活的投入之间做出权衡。

    \n

    对于某些目标,你可能无法达到预设的质量要求,在这些情况下,你会不断向右推进(尽力而为)。但是,太多人会下意识地认为,当权威人士划定一条质量标准线时,他们就「理应」尽可能地向右靠拢。他们认为自己「应该」在意质量本身。这是很傻的:现实世界的问题并非要生产出最高质量的产品。在各行各业,目标都是以最小的代价达到某个质量目标。

    \n

    当然,这只是对现实的一个模糊且不准确的描述。时间、精力、能量、注意力和质量之间的相对成本通常是不断变化的,并随着信息和环境的改变而改变。关键在于能够区分开环境所暗示的质量标尺与你真实的偏好曲线。

    \n
    \n

    需要澄清的是,我并非意在表达以下观点:如果你在上大学课程,我不是说你应该每次都「低空飞过」;如果你在救人,我不是说你应该重速度轻谨慎;总而言之,我绝不是说你总应该把那个你只能勉强接受的最差结果定为目标。

    \n

    我想说的是,不要将质量标尺与你的偏好曲线混为一谈。当老师给某个分数点贴上「及格」或「不及格」的标签时,不要感到困惑,因为那只是标签,你更深层次的目标可能与这些标签仅有间接关联。记住你真正想要实现的目标,明确你的质量目标,然后瞄准它:既不超出,也不低于。

    \n

    (另外,别忘了「规划谬误」的存在!如果你瞄准 D,结果可能只拿到 F。人类往往容易过度自信。选择目标时,务必谨慎,给自己留出足够的缓冲空间。)

    \n

    3

    \n

    常见的「得过且过者」式反驳是:

    \n
    但如果「尽快拿到最低及格分」这件事本身也很无聊呢?如果这个任务也毫无意义呢?
    \n

    那还上什么大学!

    \n

    我个人发现,追求最低可接受质量标准通常是很有趣的。做家庭作业本身可能很无聊,但探索如何用尽可能少的总精力把作业做到可接受的水平,却是一个真正能调动我智慧的、有趣的优化问题——一个我内心的完美主义倾向和叛逆倾向都能认同的优化问题。

    \n

    但有时候,即使你想起了自己的奋斗目标,整个项目看起来仍然毫无价值。有时候,「以最小努力获得最低及格分」这个目标,仍然散发着别人试图将他们随意的标准硬塞给你、当作你真实偏好的那股「馊味」。若是这种情况,不妨考虑退课。

    \n

    更普遍地说,如果「以最小努力达成某某目标」的任何一种形式都显得不值得去做,那么你可能需要彻底放弃那个目标。

    \n
    \n

    相比之下,常见的「拼命努力者」式反驳如下:

    \n
    可我是个完美主义者!我生理上就无法容忍低质量的产出。我情不自禁就想做到最好。
    \n

    太好了!那就驾驭你内心的完美主义倾向,并将其导向「以最小努力达成目标」这个方向。

    \n

    与其对论文成品追求完美,不如对写论文的过程追求完美。在识别有效策略、果断放弃沉没成本、忍痛割爱(删掉自己喜欢但不必要的部分)、判断何时适可而止等方面追求完美;在不浪费丝毫注意力上追求完美;在从错误中学习上追求完美。完美主义可以是一种强大的工具,但没必要将它用于在你并不在乎的指标上过度追求。

    \n

    4

    \n

    从某种意义上说,追求以最小代价精确命中质量目标,比一味地在质量标尺上追求极致要困难得多。人总是可以通过投入更多时间在质量标尺上走得更远:当你想写出一篇惊世之作时,总能投入更多时间和精力去修正瑕疵。但当你试图以最小化无用功的方式完成论文时,犯下的错误是无法挽回的,时间上的浪费无法弥补。

    \n

    在这个意义上,如果你开始为每一次无用功而感到内疚,那么从一个「拼命努力者」转变为一个「倾尽全力去『敷衍』的人」,反而可能比平时引发更多的内疚和羞愧感。

    \n

    然而,我看到太多太多的人因为未能在质量标尺上走得足够远而感到内疚和羞愧。他们为没有在(自己讨厌的)工作上尽心尽力而内疚;为没能成为足够好的朋友(在自己都快崩溃的时候)而内疚;为没能满足父母的期望(那些期望既荒谬又不切实际)而内疚。要想用内在动力取代内疚和羞愧,首先必须打破「得过且过者 / 拼命努力者」的二分法。如果你非要内疚,那么请为未能达成自己的目标而内疚,而不是为没有将某个随意的质量标准当作自己的真实偏好而内疚。前一种内疚感,才是我有可能着手处理的那一种。

    \n

    (致一丝不苟的朋友们:在此期间,请不要为做了无用功而感到内疚!请将其视为人类行动的自然过程中的一个重要部分,而非什么需要羞愧的事。未来的文章会进一步阐述这个观点。)

    \n

    5

    \n

    大多数人似乎有两种处理问题的模式:一种是「得过且过者」那种只求省力、「滑行通过」的模式;另一种是「拼命努力者」那种力求杰作、「过度达成」的模式。面对问题时,多数人要么只付出最低限度的努力,勉强过关,只要不惹恼相关权威就行;要么就全身心投入,倾尽所有。

    \n

    几乎每个人都曾在两种模式间切换。有些人在历史课上追求卓越,在语法课上则选择「滑行」。有些人在工作上力求完美,在维系人际关系上则敷衍了事。事实上,大多数讲述「坏学生也能是好人」的暖心故事,讲的都是那些在多数领域都得过且过的学生,如何在舞蹈 / 体育 / 音乐 / 数论这些方面私下里拼命努力。

    \n

    这当然也是「拼命努力者」式宣传的又一例证:「别担心,」电影试图告诉我们,「这些得过且过者并非坏人,因为他们在其他领域是秘密的努力者!」——好像你只有把某个随意的质量标准当作自己的真实偏好,才算得上是个好人似的。

    \n

    大多数人都困在「得过且过者 / 拼命努力者」的二分法中。他们要么只做最少量的、能蒙混过关的事,要么就竭尽所能做到最多。他们的目标要么是勉强达标,要么是争做第一。极少有人能够选择一个中间的目标点,然后倾尽全力去实现它。极少有人能够调动全部力量,只为尽可能高效地达成「恰到好处」的目标。

    \n

    拒绝这种二分法。着眼于你的偏好曲线。请记住,偏好曲线所指向的,只有这一点:

    \n

    成功,不做多余的动作。

    \n

    你内心的「得过且过者」反感无意义的任务,而你内心的「拼命努力者」追求完美。那么,就同时满足这两者:瞄准那个最低限度的必要目标,并尽可能高效地达成它。

    \n

    倘若你在某个情境下忘记了「成功」究竟意味着什么,不妨花点时间停下来,想一想你的奋斗目标是什么。

    \n
    \n

    下一篇:

    \n

    破罐破摔 - 知乎

    \n
    \n
    Thoughts Memo 汉化组译制
    感谢主要译者 gemini-2.5-pro-exp,校对 Jarrett Ye
    原文:Half-assing it with everything you've got
    作者:Nate Soares
    发布日期:2015 年 3 月 12 日
    \n
    \n

    相关专栏

    \n

    超越内疚 - 知乎

    \n
    \n
    \n\n\n\n\n
    \n\n\n\n", + "content": "
    \"头图\"/
    \n

    「卷又卷不动,躺又躺不平」,以及因此为「放松」感到自责的心理,恰恰是我在《倾尽全力去「敷衍」》一文中试图剖析的困境。这是一种被困在「得过且过者」和「拼命努力者」这两种无效模式之间的状态,其核心在于人们混淆了外部强加的「质量标尺」与自身真实的「偏好曲线」,并因此产生了不必要的内疚感。

    \n

    让我来解释一下这种心理:

    \n
      \n
    1. 内化了「应该拼命」的社会规训: 社会、家长、老师从小就向我们灌输「你应该尽力做到最好」、「努力奋斗是一种美德」的观念。这种观念让你觉得,任何时候,只要没有在某个(往往是他人定义的)「质量标尺」上向右推进(即追求更高质量、更多产出),你就是在犯错,就是「不够好」。于是,「放松」这种在质量标尺上看起来是「停滞」甚至「倒退」的行为,自然会引发自责。你觉得自己在「浪费时间」,没有「尽力」。

    2. \n
    3. 对「卷」的无效性有所察觉,但又无法彻底「躺平」: 「卷又卷不动」意味着你可能已经意识到,像个「拼命努力者」一样,不问目标、不计成本地在某个质量标尺上盲目追求极致,往往是「做无用功」,投入巨大却可能偏离了你真正的需求,或者让你精疲力尽,难以为继。 然而,「躺又躺不平」则是因为你内心深处依然被那种「应该拼命」的规训所束缚,或者你无法完全抛弃所有目标。你害怕彻底的「得过且过」会让你一事无成,或者受到他人的负面评价。这种状态下,即使你尝试放松,内心那个「应该努力」的声音也不会停止,于是自责感便油然而生。

    4. \n
    5. 目标模糊,导致评价标准混乱: 当你因为放松而自责时,很可能你并没有清晰地定义自己「真正想要实现的目标」以及达成该目标所需的「质量目标」。你只是笼统地觉得「应该做得更多更好」。没有明确的目标,你就无法判断当前的「放松」是必要的休整(为了更高效地达成目标),还是真正的懈怠(偏离了目标)。
    6. \n
    \n

    如何调节这种心理?关键在于采取我所说的「第三条路」——倾尽全力去「敷衍」:

    \n
      \n
    1. 明确你的真实目标: 首先,停下来,认真思考:对于你当前面对的任务或人生阶段,你真正想要的是什么?是为了拿到一个特定的证书?是为了学到某项具体技能?是为了维持一个舒适的生活水平?还是为了某个更宏大的理想?不要被社会或他人告诉你「应该」想要什么。

    2. \n
    3. 根据真实目标,设定清晰的「质量目标」: 一旦明确了真实目标,再据此设定一个恰到好处的质量目标。这个目标应该是「精确命中」,不多也不少。例如,如果你的目标只是通过一门考试,那么你的质量目标就是及格线,而不是完美无缺的满分。如果你的目标是学习知识,那么分数可能就不是最重要的,重要的是你是否吸收了你需要的信息。

    4. \n
    5. 以「最小代价」精确命中你的质量目标: 将你的精力和聪明才智用于「如何以最小的努力(时间、精力、资源)精确达成你设定的质量目标」。这本身就是一个有趣的优化问题。这包括了高效工作,也包括了必要的休息和放松,因为持续的疲劳会降低效率,反而增加达成目标的总代价。

    6. \n
    7. 重新定义「放松」的意义: 如果「放松」和「休息」是你为了以最小代价、可持续地达成你的真实目标所必需的环节,那么它就不是「浪费」,而是高效策略的一部分。就像一台机器需要保养才能更好地运转一样。这种情况下,因为必要的放松而自责是毫无道理的。

    8. \n
    9. 将完美主义导向「过程」而非「结果」: 如果你是个完美主义者,不要强迫自己去容忍低质量的产出(如果那确实低于你为自己设定的合理目标)。但是,你可以将你的完美主义倾向导向「以最小努力达成目标」这个过程。追求策略的完美、执行的完美、对沉没成本的果断放弃、以及对何时「恰到好处」的精准判断。

    10. \n
    11. 为「做无用功」而内疚,而非为「未达他人标准」而内疚: 如果你非要内疚,那么请为你「未能高效达成自己的目标」(比如投入了过多精力却收效甚微,或者因为不必要的拖延而错失良机)而内疚,而不是为没有将某个随意的、他人设定的质量标准当作自己的真实偏好而内疚。
    12. \n
    \n

    当你能清晰地知道自己要去哪里(真实目标),需要做到什么程度(质量目标),并且专注于如何最高效地到达那里时,「放松」就不再是让你自责的洪水猛兽,而是你智慧策略的一部分。你会发现,「卷」可以是有方向、有效率的,「躺」也可以是策略性的休整,而不是彻底的放弃。这样,你就能从「卷又卷不动,躺又躺不平」的困境中解脱出来。

    \n
    以上内容为 gemini-2.5-pro 生成的序言
    以下内容摘自 @Thoughts Memo 汉化组的译文《倾尽全力去「敷衍」》
    \n

    我经常接触许多有效利他主义者(EA)。他们中的许多人,其主要驱动力往往类似于内疚感(因自身坐拥优渥资源与机遇而他人仍在受苦)或羞愧感(因觉得自己做得不够)。说实话,我许多非 EA 圈的朋友,其主要动力也源于内疚或羞愧。

    \n

    我无意指摘这种由内疚 / 羞愧驱动的动机:我个人遵循这样一条原则,当有人为了让世界变得更美好而付出巨大努力或投入大量资金时,我会极力避免去评判他们的动机。内疚感和羞愧感或许是将人们从自满状态中警醒过来的有效工具。然而,我担心从长远来看,内疚和羞愧并非健康的动力来源。在我许多朋友身上,内疚和羞愧往往会引发执行力缺失(akrasia)、降低效率,并消耗积极性。因此,在未来几周,我将撰写系列文章,探讨如何摆脱内疚 / 羞愧驱动的动机,并代之以更强大的动力。

    \n

    1

    \n

    假设你是一名大学生,需要交一篇论文。论文的质量取决于你投入的精力。我们假定你对这个项目相当了解:只需适度努力便能拿到 A,而若投入巨大精力,你能写出远超普通 A 级水准的佳作。

    \n
    \"\"
    img
    \n

    教育环境总是潜移默化地试图让学生相信,他们的偏好应当永远指向质量标尺的右端。家长和老师会说「你应该尽力做到最好」之类的话,并对那些不奋力追求更高质量的人大加指责或令其羞愧。

    \n

    面对这种规训,人们通常有两种反应。第一类人(「得过且过者」)不认同质量越高越符合个人偏好这种看法。他们不在乎课程,不停抱怨必须做的那些毫无意义的无用功,马马虎虎地应付作业,交上去的东西要么勉强及格,要么干脆不及格。「得过且过者」往往对强迫他们写论文的权威感到不满。

    \n

    第二类人(「拼命努力者」)则认同质量越高越符合个人偏好这一前提,在质量标尺上不断追求更高。「拼命努力者」涵盖了各种能力水平的人:有的拼尽全力只为拿到 C,有的则以能创作出杰作为傲。有些人试图讨好老师,另一些人则是完美主义者,绝不允许自己交出任何未尽全力的作品。他们中有些是一丝不苟的人,即便拿了 A 仍感内疚,因为他们知道自己本可以做得更好,也认为本应做得更好。他们中有的谦逊,有的爱炫耀,但无一例外都在向右推进。

    \n

    社会花费了大量时间规训我们,让我们认为「拼命努力者」优于「得过且过者」。努力奋斗是一种美德。「得过且过者」没抓住教育的意义;他们待在学校干嘛呢?「拼命努力者」前途无量,「得过且过者」则一事无成。

    \n

    但实际上,这两类人的做法都有问题。

    \n

    如果你想变得真正高效,请记住你的奋斗目标是什么。

    \n

    而且,剧透一下,你的目标并非「写一篇高质量的论文」。那会是一个相当愚蠢的奋斗目标。

    \n

    你选修这门课的目标究竟是什么?也许是出于社会压力(父母要求的)、社会惯性(别人都上大学)以及一种模糊的信念——认为这是通往好工作和舒适生活的必经之路。又或许,你想要好成绩,以便将来获得大量金钱和权力去「屠龙」(实现宏伟目标)。再或许,你纯粹是出于对知识的渴求。但无论原因为何,这个原因都会在质量标尺上确定一个具体的目标点。因此,你的目标应该是精确命中那个质量目标——既不超出,也不低于。

    \n

    你的偏好并非「在质量标尺上越往右越好」。你的偏好是以最小的代价(努力)精确命中那个质量目标。

    \n
    \"\"
    img
    \n

    如果你只想通过这门课,那就用最少的精力去通过。其他的努力都是在做无用功。

    \n

    如果你想拿高分,那就用最少的精力去拿高分。其他的努力都是在做无用功。

    \n

    如果你想尽可能透彻地学习知识内容,那就从作业中汲取所有知识养分,不必纠结于分数。其他的努力都是在做无用功。

    \n

    如果你想同时达成多个目标:好成绩(作为能力信号)、赢得尊重(出于社交需求)、获取知识(为了种种益处),那么就精准定位那个能让你获得好成绩、给老师留下好印象、并学到知识的最低质量目标,并尽可能高效地达成它。任何超出的部分都是在做无用功。

    \n

    你的质量目标可能远低于 F 线(不及格)——比如,你早已通过该课程,这份作业无关紧要。你的质量目标也可能远高于 A 线——比如,你是为了学习知识内容,而分数膨胀意味着拿 A 比以信息量最大的方式完成作业要容易得多。但无论如何,你的真实目标总会对应一个质量目标。

    \n

    「得过且过者」和「拼命努力者」的目标都偏离了方向。「得过且过者」嘲笑「拼命努力者」,认为他们将人为设定的质量标准奉为圭臬,在过度努力上浪费精力。「拼命努力者」则嘲笑「得过且过者」,认为他们身在课堂却拒绝学习。而双方的嘲笑都有道理!因为他们都在做无用功。

    \n

    「得过且过者」未能全力以赴,因为他们意识到那条质量标尺并非他们的偏好曲线。「拼命努力者」则将全部力量投入错误的目标,试图在标尺上走得越远越好,将精力浪费在一场并不属于他们的战斗中。所以,请选择第三条路:记住你的奋斗目标。永远全力以赴,只为尽快达成你的质量目标。

    \n

    倾尽全力去「敷衍」一切。

    \n

    (我的老师们过去常说,只要我肯努力,定能成就一番事业。我则回应他们,如果想让我更努力,他们得先发明出比 A 更高的分数等级才行。)

    \n

    2

    \n

    一个常见的反对意见在此出现:

    \n
    有些事情太重要了,不能「敷衍了事」。有些事情就值得你全力以赴去争取。敷衍家庭作业是一回事,敷衍地救人命则是完全不同的另一回事。有时候,你确实想在质量标尺上走得越远越好。
    \n

    这种说法对也不对,因为它混淆了一些概念。对于任何项目,目标始终不应高于预设的质量目标,并且永远要追求以最小的代价(精力、资源)达成目标。无论你是在写学期论文、从火场救人,还是在创建跨星系的人类文明——目标永远是以最小代价达到某个质量目标。有效资源是稀缺的。

    \n

    话虽如此,质量目标可以设得非常非常高。事实上,质量目标有时甚至高不可攀。通常,我们根本没有能力达到我们设定的质量目标,在这些情况下,我们确实想要沿着偏好曲线尽可能地向右推进。

    \n
    \"\"
    img
    \n

    当你从事的工作相对于你的技能水平而言非常困难时,或者在竞争激烈的环境中,又或者当你在发出你「能吃苦耐劳」的信号时,这种情况会自然发生。但别搞混了。即使你是为热爱而写作,最终也必须停止修改,宣告完成。即使你正在从燃烧的建筑中救人,最终也会停止为确保此人存活而投入更多精力,转而将精力用于拯救其他濒危者。即使你在建设一个跨星系的文明,也需要在建设文明的投入与享受文明生活的投入之间做出权衡。

    \n

    对于某些目标,你可能无法达到预设的质量要求,在这些情况下,你会不断向右推进(尽力而为)。但是,太多人会下意识地认为,当权威人士划定一条质量标准线时,他们就「理应」尽可能地向右靠拢。他们认为自己「应该」在意质量本身。这是很傻的:现实世界的问题并非要生产出最高质量的产品。在各行各业,目标都是以最小的代价达到某个质量目标。

    \n

    当然,这只是对现实的一个模糊且不准确的描述。时间、精力、能量、注意力和质量之间的相对成本通常是不断变化的,并随着信息和环境的改变而改变。关键在于能够区分开环境所暗示的质量标尺与你真实的偏好曲线。

    \n
    \n

    需要澄清的是,我并非意在表达以下观点:如果你在上大学课程,我不是说你应该每次都「低空飞过」;如果你在救人,我不是说你应该重速度轻谨慎;总而言之,我绝不是说你总应该把那个你只能勉强接受的最差结果定为目标。

    \n

    我想说的是,不要将质量标尺与你的偏好曲线混为一谈。当老师给某个分数点贴上「及格」或「不及格」的标签时,不要感到困惑,因为那只是标签,你更深层次的目标可能与这些标签仅有间接关联。记住你真正想要实现的目标,明确你的质量目标,然后瞄准它:既不超出,也不低于。

    \n

    (另外,别忘了「规划谬误」的存在!如果你瞄准 D,结果可能只拿到 F。人类往往容易过度自信。选择目标时,务必谨慎,给自己留出足够的缓冲空间。)

    \n

    3

    \n

    常见的「得过且过者」式反驳是:

    \n
    但如果「尽快拿到最低及格分」这件事本身也很无聊呢?如果这个任务也毫无意义呢?
    \n

    那还上什么大学!

    \n

    我个人发现,追求最低可接受质量标准通常是很有趣的。做家庭作业本身可能很无聊,但探索如何用尽可能少的总精力把作业做到可接受的水平,却是一个真正能调动我智慧的、有趣的优化问题——一个我内心的完美主义倾向和叛逆倾向都能认同的优化问题。

    \n

    但有时候,即使你想起了自己的奋斗目标,整个项目看起来仍然毫无价值。有时候,「以最小努力获得最低及格分」这个目标,仍然散发着别人试图将他们随意的标准硬塞给你、当作你真实偏好的那股「馊味」。若是这种情况,不妨考虑退课。

    \n

    更普遍地说,如果「以最小努力达成某某目标」的任何一种形式都显得不值得去做,那么你可能需要彻底放弃那个目标。

    \n
    \n

    相比之下,常见的「拼命努力者」式反驳如下:

    \n
    可我是个完美主义者!我生理上就无法容忍低质量的产出。我情不自禁就想做到最好。
    \n

    太好了!那就驾驭你内心的完美主义倾向,并将其导向「以最小努力达成目标」这个方向。

    \n

    与其对论文成品追求完美,不如对写论文的过程追求完美。在识别有效策略、果断放弃沉没成本、忍痛割爱(删掉自己喜欢但不必要的部分)、判断何时适可而止等方面追求完美;在不浪费丝毫注意力上追求完美;在从错误中学习上追求完美。完美主义可以是一种强大的工具,但没必要将它用于在你并不在乎的指标上过度追求。

    \n

    4

    \n

    从某种意义上说,追求以最小代价精确命中质量目标,比一味地在质量标尺上追求极致要困难得多。人总是可以通过投入更多时间在质量标尺上走得更远:当你想写出一篇惊世之作时,总能投入更多时间和精力去修正瑕疵。但当你试图以最小化无用功的方式完成论文时,犯下的错误是无法挽回的,时间上的浪费无法弥补。

    \n

    在这个意义上,如果你开始为每一次无用功而感到内疚,那么从一个「拼命努力者」转变为一个「倾尽全力去『敷衍』的人」,反而可能比平时引发更多的内疚和羞愧感。

    \n

    然而,我看到太多太多的人因为未能在质量标尺上走得足够远而感到内疚和羞愧。他们为没有在(自己讨厌的)工作上尽心尽力而内疚;为没能成为足够好的朋友(在自己都快崩溃的时候)而内疚;为没能满足父母的期望(那些期望既荒谬又不切实际)而内疚。要想用内在动力取代内疚和羞愧,首先必须打破「得过且过者 / 拼命努力者」的二分法。如果你非要内疚,那么请为未能达成自己的目标而内疚,而不是为没有将某个随意的质量标准当作自己的真实偏好而内疚。前一种内疚感,才是我有可能着手处理的那一种。

    \n

    (致一丝不苟的朋友们:在此期间,请不要为做了无用功而感到内疚!请将其视为人类行动的自然过程中的一个重要部分,而非什么需要羞愧的事。未来的文章会进一步阐述这个观点。)

    \n

    5

    \n

    大多数人似乎有两种处理问题的模式:一种是「得过且过者」那种只求省力、「滑行通过」的模式;另一种是「拼命努力者」那种力求杰作、「过度达成」的模式。面对问题时,多数人要么只付出最低限度的努力,勉强过关,只要不惹恼相关权威就行;要么就全身心投入,倾尽所有。

    \n

    几乎每个人都曾在两种模式间切换。有些人在历史课上追求卓越,在语法课上则选择「滑行」。有些人在工作上力求完美,在维系人际关系上则敷衍了事。事实上,大多数讲述「坏学生也能是好人」的暖心故事,讲的都是那些在多数领域都得过且过的学生,如何在舞蹈 / 体育 / 音乐 / 数论这些方面私下里拼命努力。

    \n

    这当然也是「拼命努力者」式宣传的又一例证:「别担心,」电影试图告诉我们,「这些得过且过者并非坏人,因为他们在其他领域是秘密的努力者!」——好像你只有把某个随意的质量标准当作自己的真实偏好,才算得上是个好人似的。

    \n

    大多数人都困在「得过且过者 / 拼命努力者」的二分法中。他们要么只做最少量的、能蒙混过关的事,要么就竭尽所能做到最多。他们的目标要么是勉强达标,要么是争做第一。极少有人能够选择一个中间的目标点,然后倾尽全力去实现它。极少有人能够调动全部力量,只为尽可能高效地达成「恰到好处」的目标。

    \n

    拒绝这种二分法。着眼于你的偏好曲线。请记住,偏好曲线所指向的,只有这一点:

    \n

    成功,不做多余的动作。

    \n

    你内心的「得过且过者」反感无意义的任务,而你内心的「拼命努力者」追求完美。那么,就同时满足这两者:瞄准那个最低限度的必要目标,并尽可能高效地达成它。

    \n

    倘若你在某个情境下忘记了「成功」究竟意味着什么,不妨花点时间停下来,想一想你的奋斗目标是什么。

    \n
    \n

    下一篇:

    \n

    破罐破摔 - 知乎

    \n
    \n
    Thoughts Memo 汉化组译制
    感谢主要译者 gemini-2.5-pro-exp,校对 Jarrett Ye
    原文:Half-assing it with everything you've got
    作者:Nate Soares
    发布日期:2015 年 3 月 12 日
    \n
    \n

    相关专栏

    \n

    超越内疚 - 知乎

    \n
    \n
    \n\n\n\n\n
    \n\n\n\n", + "category": "", + "link": "https://daily.zhihu.com/story/9782503", + "creator": "", + "pubDate": "Sat, 12 Jul 2025 08:27:03 +0800", + "enclosure": "", + "enclosureType": "", + "image": "", + "id": "", + "language": "", + "folder": "资讯", + "feed": "知乎", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "7347ea4a725b254aac33e0555bd76f65", + "highlights": [] + }, + { + "title": "从进化的角度看,人和动物为何会产生抑郁?", + "description": "
    \"头图\"/
    \n

    你想象一下,假设十万年前,你无缘无故被部落首领爆揍了一顿,你会怎么做?

    \n

    那只大型直立无毛猿能成为首领,一定是有理由的,理由就写在他的肱二头肌的肌肉量上,所以你肯定不能硬上。

    \n

    因而你只能躲到你自己的山洞里面生闷气,减少在你首领面前现眼的机会,以避免再被揍一顿。

    \n

    你肯定还在琢磨着如何翻盘,心里嘀咕着等老子发达了,这个仇可一定得报!

    \n

    你可能还会一把鼻涕一把泪的向其它族人控诉首领的暴行,传递「快来看看老子有多惨」的信号。

    \n

    在弱肉强食的时代,这个场景是不是还挺自然的?

    \n
    \n

    咱逐条分析一下:

    \n

    你躲到山洞里避免再次挨揍,这个叫做「行为关闭模型(Behavioral shutdown model)」[1],指的是生物面临某项活动时,其风险大于回报,那么最佳策略就是退出这些活动。

    \n

    你躲到山洞里谋划着翻盘,这个叫做「分析性反思假说(Analytical rumination hypothesis)」[2],这个假说认为抑郁是一种适应性变化,它会使个体集中注意力,专注于某个复杂问题,以便分析解决它。

    \n

    你一把鼻涕一把泪的控诉首领的无理行为,传递求关注求同情的信号,这个叫做「诚实信号理论(Honest signaling theory)」[3],指的是当发生重大负面事件的时候,一些基础的情绪表现,比如哭泣、愤怒等「廉价」的需求信号可能被忽略,而抑郁这种极其痛苦代价极其高昂的信号,可以传递自己真正需求帮助的诚实可信的信号。

    \n
    \n

    好,现在,你把这个场景挪回现在。

    \n

    某天,你被你老板无缘无故骂了一顿……

    \n

    你肯定不能硬刚他,因为你下个月的房贷还指望着他发的工资。

    \n

    于是你躲到厕所,一边儿脑补着各种咸鱼翻身 Diao 丝逆袭的爽文情节,一边儿在朋友圈里发着「都毁灭吧,哥累了」的状态——顺便把你老板屏蔽掉。

    \n

    你看,这是不是一个套路?

    \n
    \n

    咱再把视角放宽一些,类似抑郁的行为表现,并不是人类的专属。

    \n

    你完全可以把一只老鼠搞抑郁,方法也挺简单,你就把它放笼子里不停电击它,开始的时候它会乱窜,当它发现无论怎么躲也都会被电的时候,它就直接躺平了,你把它笼子门打开它也不会跑。这个就是「习得性无助(Learned Helplessness)」[4],抑郁症的核心机制之一。

    \n

    这个机制似乎也很好理解,在自然界,如果大脑判断某个威胁无法避免,那么躺平无疑是比挣扎更好的选择。

    \n

    可是放到当下,你会发现生活中的「电击」都不是致命的或是不可避免的,比如 KPI、房贷、社交压力等等,只是你的大脑适应了这套打不过就躺平的机制,于是误判了这些「电击」的危险等级。

    \n
    \n

    你再看,在高度社会化和等级化的动物群体,比如狼群中,如果某只狼在争夺头狼的过程中失败,它也会表现出典型的抑郁症症,比如不再争抢食物、独自蜷缩在角落,也不再与其它狼交往。

    \n

    这其实是一种生存策略,减少攻击性是避免自己被赶尽杀绝,降低存在感是等待翻盘的机会。

    \n

    这个行为范式放到今天就是被领导打压后自动边缘化,被排挤后主动退出社交圈。

    \n

    这种生物的保命程序放到现在,就成了适应不良。

    \n
    \n

    写到这里,总结一下。

    \n

    抑郁原本是一种生物的短期应急程序。

    \n

    只是远古的压力是短期的、可预测的,而现代的压力是长期的、不可控的。

    \n

    食物短缺的旱季会结束,再凶猛的敌人也会离开,可是你的房贷却要还上 30 年,朋友圈里总能刷到各种精修的人设,钉钉总是在凌晨 3 点响起,职场的竞争也看上去绵绵无绝期。

    \n

    在这种情况下,你大脑的节能模式、战败者策略都无法自动关闭,就会导致长期的低动力和消极思维,也就是抑郁。

    \n

    那些在远古让我们的祖先活下来的抑郁特质,比如敏感、谨慎、过度反思,现在正批量制造着心理咨询的客户。

    \n

    从进化视角来看,抑郁症并不是大脑的系统故障,也不是脆弱,而是我们大脑一直运行的那套过时的生存算法,在当下这个时代导致的系统过载。

    \n
    \n
    \n\n\n\n\n
    \n\n\n\n", + "content": "
    \"头图\"/
    \n

    你想象一下,假设十万年前,你无缘无故被部落首领爆揍了一顿,你会怎么做?

    \n

    那只大型直立无毛猿能成为首领,一定是有理由的,理由就写在他的肱二头肌的肌肉量上,所以你肯定不能硬上。

    \n

    因而你只能躲到你自己的山洞里面生闷气,减少在你首领面前现眼的机会,以避免再被揍一顿。

    \n

    你肯定还在琢磨着如何翻盘,心里嘀咕着等老子发达了,这个仇可一定得报!

    \n

    你可能还会一把鼻涕一把泪的向其它族人控诉首领的暴行,传递「快来看看老子有多惨」的信号。

    \n

    在弱肉强食的时代,这个场景是不是还挺自然的?

    \n
    \n

    咱逐条分析一下:

    \n

    你躲到山洞里避免再次挨揍,这个叫做「行为关闭模型(Behavioral shutdown model)」[1],指的是生物面临某项活动时,其风险大于回报,那么最佳策略就是退出这些活动。

    \n

    你躲到山洞里谋划着翻盘,这个叫做「分析性反思假说(Analytical rumination hypothesis)」[2],这个假说认为抑郁是一种适应性变化,它会使个体集中注意力,专注于某个复杂问题,以便分析解决它。

    \n

    你一把鼻涕一把泪的控诉首领的无理行为,传递求关注求同情的信号,这个叫做「诚实信号理论(Honest signaling theory)」[3],指的是当发生重大负面事件的时候,一些基础的情绪表现,比如哭泣、愤怒等「廉价」的需求信号可能被忽略,而抑郁这种极其痛苦代价极其高昂的信号,可以传递自己真正需求帮助的诚实可信的信号。

    \n
    \n

    好,现在,你把这个场景挪回现在。

    \n

    某天,你被你老板无缘无故骂了一顿……

    \n

    你肯定不能硬刚他,因为你下个月的房贷还指望着他发的工资。

    \n

    于是你躲到厕所,一边儿脑补着各种咸鱼翻身 Diao 丝逆袭的爽文情节,一边儿在朋友圈里发着「都毁灭吧,哥累了」的状态——顺便把你老板屏蔽掉。

    \n

    你看,这是不是一个套路?

    \n
    \n

    咱再把视角放宽一些,类似抑郁的行为表现,并不是人类的专属。

    \n

    你完全可以把一只老鼠搞抑郁,方法也挺简单,你就把它放笼子里不停电击它,开始的时候它会乱窜,当它发现无论怎么躲也都会被电的时候,它就直接躺平了,你把它笼子门打开它也不会跑。这个就是「习得性无助(Learned Helplessness)」[4],抑郁症的核心机制之一。

    \n

    这个机制似乎也很好理解,在自然界,如果大脑判断某个威胁无法避免,那么躺平无疑是比挣扎更好的选择。

    \n

    可是放到当下,你会发现生活中的「电击」都不是致命的或是不可避免的,比如 KPI、房贷、社交压力等等,只是你的大脑适应了这套打不过就躺平的机制,于是误判了这些「电击」的危险等级。

    \n
    \n

    你再看,在高度社会化和等级化的动物群体,比如狼群中,如果某只狼在争夺头狼的过程中失败,它也会表现出典型的抑郁症症,比如不再争抢食物、独自蜷缩在角落,也不再与其它狼交往。

    \n

    这其实是一种生存策略,减少攻击性是避免自己被赶尽杀绝,降低存在感是等待翻盘的机会。

    \n

    这个行为范式放到今天就是被领导打压后自动边缘化,被排挤后主动退出社交圈。

    \n

    这种生物的保命程序放到现在,就成了适应不良。

    \n
    \n

    写到这里,总结一下。

    \n

    抑郁原本是一种生物的短期应急程序。

    \n

    只是远古的压力是短期的、可预测的,而现代的压力是长期的、不可控的。

    \n

    食物短缺的旱季会结束,再凶猛的敌人也会离开,可是你的房贷却要还上 30 年,朋友圈里总能刷到各种精修的人设,钉钉总是在凌晨 3 点响起,职场的竞争也看上去绵绵无绝期。

    \n

    在这种情况下,你大脑的节能模式、战败者策略都无法自动关闭,就会导致长期的低动力和消极思维,也就是抑郁。

    \n

    那些在远古让我们的祖先活下来的抑郁特质,比如敏感、谨慎、过度反思,现在正批量制造着心理咨询的客户。

    \n

    从进化视角来看,抑郁症并不是大脑的系统故障,也不是脆弱,而是我们大脑一直运行的那套过时的生存算法,在当下这个时代导致的系统过载。

    \n
    \n
    \n\n\n\n\n
    \n\n\n\n", + "category": "", + "link": "https://daily.zhihu.com/story/9782509", + "creator": "", + "pubDate": "Sat, 12 Jul 2025 08:27:03 +0800", + "enclosure": "", + "enclosureType": "", + "image": "", + "id": "", + "language": "", + "folder": "资讯", + "feed": "知乎", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "a07e49354573e8171fa8dc96e3dd8c6e", + "highlights": [] + }, + { + "title": "为什么除了人类,哺乳动物拉屎的时间都是 12 秒?", + "description": "
    \"头图\"/
    \n

    先说结论:人类也是 12 秒

    \n

    所有哺乳动物,包括人类,排便时间都是 12 秒左右[1]。

    \n

    这是乔治亚理工学院的研究团队,花了好几年时间,跑遍动物园,收集了 34 种哺乳动物的粪便数据得出的结论。

    \n

    从猫咪到大象,23 个物种的排便时间都在 12±7 秒之间[2]。

    \n

    人类的速度?每秒 2 厘米。

    \n

    正好在狗(每秒 1 厘米)和大象(每秒 6 厘米)之间。

    \n
    \"\"
    \n

    一只小猫,直肠长度 4 厘米。一头大象,直肠长度 40 厘米。体型差距 1000 倍。但排便时间?几乎一样。

    \n

    大象粪便体积 20 升,狗只有 10 毫升,相差近 1000 倍。

    \n

    为什么

    \n

    答案藏在一个你从未注意过的地方——大肠

    \n

    覆盖在大肠壁上的超薄黏液层,厚度只有头发丝般细微。

    \n

    科学家们怎么测量这么薄的东西?

    \n

    等粪便上的黏液蒸发,然后称重。就这么简单粗暴。但别小看这层黏液。它的黏度比粪便低 100 倍。

    \n

    什么概念?

    \n
    想象粪便是厚重的蜂蜜。黏液就像水一样滑溜。
    粪便在肠道里滑行,就像雪橇在冰道上飞驰。
    \n

    这是作者的原话。

    \n

    大动物有更多粪便要排出。

    \n

    按常理,应该需要更长时间才对。但进化论说不给。

    \n

    所以体型大的动物有更厚的黏液层。

    \n
    小动物黏液薄,但粪便也小。
    大动物黏液厚,粪便虽大但滑得更快。
    \n

    结果完成时间一样。

    \n

    大动物用同样的压力,却能达到更高的排便速度。

    \n

    这就是为什么大象能以每秒 6 厘米的速度飞速完成任务,而小狗只能慢慢悠悠每秒 1 厘米。

    \n
    \"\"
    \n

    我们人类完全符合这个规律。

    \n
    研究者明确指出,人类和大象、猫咪一样,都排出圆柱形粪便,遵循相同的物理定律。
    \n

    人类排便速度每秒 2 厘米,恰好处在狗和大象之间。

    \n

    我们的直肠长度、黏液厚度、排便压力,都完美契合这个 12 秒公式。

    \n
    \"\"
    \n

    这项研究不是随便测几个动物就下结论。

    \n

    研究团队跑到亚特兰大动物园,亲自拍摄大象、大熊猫、疣猪的排便过程。

    \n

    还在公园里偷偷观察狗狗。

    \n

    他们还用“流变仪”——一种精密搅拌器,可以测量粪便的密度和黏度。

    \n

    顺便发现了:食肉动物的粪便是“沉底派”,食草动物的是“漂浮派”。

    \n

    还给粪便臭味排了个名:老虎和犀牛最臭,熊猫最“香”。

    \n
    \"\"
    \n

    研究团队建立了一个数学模型。

    \n
    排便时间 = 粪便长度 ÷ 排便速度。
    \n

    粪便长度是直肠长度的两倍,说明不仅直肠,连结肠都在储存粪便。

    \n

    排便时,粪便像固体栓塞一样整体移动。

    \n

    大部分哺乳动物的直肠都是水平的,所以重力作用可以忽略。

    \n

    一切都靠肌肉压力和黏液润滑。

    \n
    \"\"
    \n

    腹泻时会怎样?

    \n

    研究团队计算出,一个 70 公斤的成年人腹泻时只需要 0.5 秒就能完成。

    \n

    因为此时不需要肌肉用力,重力就够了。

    \n

    便秘呢?

    \n

    最严重情况下,可能需要 6 小时才能完成。因为黏液被吸收,失去润滑作用。

    \n

    但实际上肠壁会变形帮助推进,时间不会这么长。

    \n
    \"\"
    \n

    你可能觉得这种研究很无聊。

    \n

    错了。

    \n

    这些排便数据帮助设计了宇航员专用尿布,在 NASA 太空排便挑战赛闯到最后一关。。

    \n
    \"\"
    \n

    理解黏液的作用机制,还可以开发出治疗便秘的新药物。

    \n

    如果便秘是黏液缺乏导致的,那么模拟黏液的药物可能就是治疗便秘的良药。。

    \n
    \"\"
    \n

    这个团队还有什么发现?

    \n

    同一个研究团队之前发现了另一个规律。

    \n

    所有重量超过 3 公斤的哺乳动物,排尿时间都是 21 秒。

    \n

    这项发现让他们获得了 2015 年搞笑诺贝尔奖[3]。

    \n
    \n
    \n\n\n\n\n
    \n\n\n\n", + "content": "
    \"头图\"/
    \n

    先说结论:人类也是 12 秒

    \n

    所有哺乳动物,包括人类,排便时间都是 12 秒左右[1]。

    \n

    这是乔治亚理工学院的研究团队,花了好几年时间,跑遍动物园,收集了 34 种哺乳动物的粪便数据得出的结论。

    \n

    从猫咪到大象,23 个物种的排便时间都在 12±7 秒之间[2]。

    \n

    人类的速度?每秒 2 厘米。

    \n

    正好在狗(每秒 1 厘米)和大象(每秒 6 厘米)之间。

    \n
    \"\"
    \n

    一只小猫,直肠长度 4 厘米。一头大象,直肠长度 40 厘米。体型差距 1000 倍。但排便时间?几乎一样。

    \n

    大象粪便体积 20 升,狗只有 10 毫升,相差近 1000 倍。

    \n

    为什么

    \n

    答案藏在一个你从未注意过的地方——大肠

    \n

    覆盖在大肠壁上的超薄黏液层,厚度只有头发丝般细微。

    \n

    科学家们怎么测量这么薄的东西?

    \n

    等粪便上的黏液蒸发,然后称重。就这么简单粗暴。但别小看这层黏液。它的黏度比粪便低 100 倍。

    \n

    什么概念?

    \n
    想象粪便是厚重的蜂蜜。黏液就像水一样滑溜。
    粪便在肠道里滑行,就像雪橇在冰道上飞驰。
    \n

    这是作者的原话。

    \n

    大动物有更多粪便要排出。

    \n

    按常理,应该需要更长时间才对。但进化论说不给。

    \n

    所以体型大的动物有更厚的黏液层。

    \n
    小动物黏液薄,但粪便也小。
    大动物黏液厚,粪便虽大但滑得更快。
    \n

    结果完成时间一样。

    \n

    大动物用同样的压力,却能达到更高的排便速度。

    \n

    这就是为什么大象能以每秒 6 厘米的速度飞速完成任务,而小狗只能慢慢悠悠每秒 1 厘米。

    \n
    \"\"
    \n

    我们人类完全符合这个规律。

    \n
    研究者明确指出,人类和大象、猫咪一样,都排出圆柱形粪便,遵循相同的物理定律。
    \n

    人类排便速度每秒 2 厘米,恰好处在狗和大象之间。

    \n

    我们的直肠长度、黏液厚度、排便压力,都完美契合这个 12 秒公式。

    \n
    \"\"
    \n

    这项研究不是随便测几个动物就下结论。

    \n

    研究团队跑到亚特兰大动物园,亲自拍摄大象、大熊猫、疣猪的排便过程。

    \n

    还在公园里偷偷观察狗狗。

    \n

    他们还用“流变仪”——一种精密搅拌器,可以测量粪便的密度和黏度。

    \n

    顺便发现了:食肉动物的粪便是“沉底派”,食草动物的是“漂浮派”。

    \n

    还给粪便臭味排了个名:老虎和犀牛最臭,熊猫最“香”。

    \n
    \"\"
    \n

    研究团队建立了一个数学模型。

    \n
    排便时间 = 粪便长度 ÷ 排便速度。
    \n

    粪便长度是直肠长度的两倍,说明不仅直肠,连结肠都在储存粪便。

    \n

    排便时,粪便像固体栓塞一样整体移动。

    \n

    大部分哺乳动物的直肠都是水平的,所以重力作用可以忽略。

    \n

    一切都靠肌肉压力和黏液润滑。

    \n
    \"\"
    \n

    腹泻时会怎样?

    \n

    研究团队计算出,一个 70 公斤的成年人腹泻时只需要 0.5 秒就能完成。

    \n

    因为此时不需要肌肉用力,重力就够了。

    \n

    便秘呢?

    \n

    最严重情况下,可能需要 6 小时才能完成。因为黏液被吸收,失去润滑作用。

    \n

    但实际上肠壁会变形帮助推进,时间不会这么长。

    \n
    \"\"
    \n

    你可能觉得这种研究很无聊。

    \n

    错了。

    \n

    这些排便数据帮助设计了宇航员专用尿布,在 NASA 太空排便挑战赛闯到最后一关。。

    \n
    \"\"
    \n

    理解黏液的作用机制,还可以开发出治疗便秘的新药物。

    \n

    如果便秘是黏液缺乏导致的,那么模拟黏液的药物可能就是治疗便秘的良药。。

    \n
    \"\"
    \n

    这个团队还有什么发现?

    \n

    同一个研究团队之前发现了另一个规律。

    \n

    所有重量超过 3 公斤的哺乳动物,排尿时间都是 21 秒。

    \n

    这项发现让他们获得了 2015 年搞笑诺贝尔奖[3]。

    \n
    \n
    \n\n\n\n\n
    \n\n\n\n", + "category": "", + "link": "https://daily.zhihu.com/story/9782515", + "creator": "", + "pubDate": "Sat, 12 Jul 2025 08:27:03 +0800", + "enclosure": "", + "enclosureType": "", + "image": "", + "id": "", + "language": "", + "folder": "资讯", + "feed": "知乎", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "b2b15d64c3e1f11c9dfe48553fe12c12", + "highlights": [] + }, { "title": "China urges U.S. to abandon excessive use of national security concept", "description": "

    BEIJING, June 5 (Xinhua) -- China has urged the United States to abandon its generalized and excessive use of the national security concept, and to collaborate with all parties in maintaining a rules-based multilateral trading system, the Ministry of Commerce said on Thursday.

    At a regular press conference, He Yongqian, spokesperson for the ministry, made the remarks when asked to comment on the U.S. decision to raise tariffs on imported steel, aluminium and their derivative products from 25 percent to 50 percent.

    He said that such U.S. moves not only harm others and itself, but also will severely disrupt the stability of global industrial and supply chains.

    The United States should abandon the zero-sum mentality, address respective concerns through equal dialogue, and jointly maintain the stability of global industrial and supply chains, He said.

    Source: Xinhua

    ", @@ -2150,6 +2590,138 @@ "image": null, "description": "知乎日报全文RSS。获取更多全文RSS:https://feedx.net", "items": [ + { + "title": "独腿博士研发智能假肢,让更多残疾人穿得起Wear robotic knees", + "description": "幼时截肢的他,最终发明、制造出一种独特而“有思想”的智能假肢!", + "content": "幼时截肢的他,最终发明、制造出一种独特而“有思想”的智能假肢!", + "category": "人物", + "link": "http://www.i21st.cn/story/3805.html", + "creator": "www.i21st.cn", + "pubDate": "Fri, 2 Sep 2022 07:00:00", + "enclosure": "", + "enclosureType": "", + "image": "", + "id": "", + "language": "zh", + "folder": "双语", + "feed": "21世纪", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "676d7c24905f895766b5d18b3fa0fd8c", + "highlights": [] + }, + { + "title": "侯逸凡:最年轻的“棋后”Conquering the world of chess", + "description": "16岁就成为最年轻的国际象棋世界“棋后”,她是真人版的《后翼弃兵》!", + "content": "16岁就成为最年轻的国际象棋世界“棋后”,她是真人版的《后翼弃兵》!", + "category": "人物", + "link": "http://www.i21st.cn/story/3807.html", + "creator": "www.i21st.cn", + "pubDate": "Thu, 1 Sep 2022 17:00:02", + "enclosure": "", + "enclosureType": "", + "image": "", + "id": "", + "language": "zh", + "folder": "双语", + "feed": "21世纪", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "3372e349617e8cdb5732ee4da3cdcdce", + "highlights": [] + }, + { + "title": "炫酷魔术背后隐藏的科学Spilling the magic beans", + "description": "空中飘物、液体消失、纸张燃烧……这些神奇的魔术背后都隐藏着什么科学原理?", + "content": "空中飘物、液体消失、纸张燃烧……这些神奇的魔术背后都隐藏着什么科学原理?", + "category": "科技", + "link": "http://www.i21st.cn/story/3806.html", + "creator": "www.i21st.cn", + "pubDate": "Wed, 17 Aug 2022 11:09:58", + "enclosure": "", + "enclosureType": "", + "image": "", + "id": "", + "language": "zh", + "folder": "双语", + "feed": "21世纪", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "e0627db8d1ede2acddc4a5574cefd5c9", + "highlights": [] + }, + { + "title": "“大老虎”被拿下!中共中央决定对周永康严重违纪问题立案审查", + "description": "", + "content": "", + "category": "", + "link": "/article/translate/5913_1.html", + "creator": "www.i21st.cn", + "pubDate": "Wed, 30 Jul 2014 10:35:02", + "enclosure": "", + "enclosureType": "", + "image": "", + "id": "", + "language": "zh", + "folder": "双语", + "feed": "21世纪", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "9f21f8fa51704a71191c0219c9793433", + "highlights": [] + }, + { + "title": "廖凡加冕柏林电影节影帝", + "description": "Berlin International Film Festival", + "content": "Berlin International Film Festival", + "category": "", + "link": "http://www.i21st.cn/article/translate/4929_1.html", + "creator": "www.i21st.cn", + "pubDate": "Mon, 17 Feb 2014 11:22:15", + "enclosure": "", + "enclosureType": "", + "image": "", + "id": "", + "language": "zh", + "folder": "双语", + "feed": "21世纪", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "f58695c9a7cdaddb9ab71b5698627132", + "highlights": [] + }, + { + "title": "索契冬奥会李坚柔夺中国首金", + "description": "Short track speed skating", + "content": "Short track speed skating", + "category": "", + "link": "http://www.i21st.cn/article/translate/4927_1.html", + "creator": "www.i21st.cn", + "pubDate": "Fri, 14 Feb 2014 10:50:53", + "enclosure": "", + "enclosureType": "", + "image": "", + "id": "", + "language": "zh", + "folder": "双语", + "feed": "21世纪", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "7371f6dbcab15edaf9642e52b8d5ac96", + "highlights": [] + }, { "title": "瞎扯 · 如何正确地吐槽", "description": "
    \"头图\"/
    \n\n
    \n
    @小队长 :你这话说的,就算全世界国家都提倡吃屎健康,老子也不会吃。
    \n
    \n\n
    \n
    @定序王子 :很明显的造谣,编都不会编,非常低级的那种。 8 亿只是什么概念? 2023 年 ,广东常住人口达 1.27 亿人,算下来平均一人一年只吃 6 只鸡。 改成 300 亿就比较可信了。
    \n
    \n\n
    \n
    @宠物王大拿 :被公司辞退了!! 越想越气!! 然后跑到公司楼下把公司里的小流浪猫打劫走了!!!
    \n
    \n\n
    \n
    @Anjicn :儿子第一次喝可乐,说:就像蜘蛛在舌头上织网。
    \n
    \n\n
    \n
    @摸摸狗头 :有个大胆的想法:每年把珠穆朗玛峰加高 4cm ,是不是就相当于月球就没离开?
    \n
    \n
    \n
    \n\n\n
    \n\n\n\n

    ", @@ -3307,6 +3879,116 @@ "image": "\n\t\t", "description": "中英文双语热点新闻,配以地道音频及词汇解析, 经典美剧赏析, 热门词汇翻译,分享最地道的英语表达, 国内外新鲜英语趣闻及时事热点- 21英语", "items": [ + { + "title": "研究:置身大自然会对你体验时间的方式产生深远影响", + "description": "
    \n\n \n\n \n \n\n

    研究发现,置身大自然会让你感觉时间过得更慢,有助于将你的注意力从眼前转移到未来的需求上。你将能够延迟即时满足感,转而追求长期回报,而不是永远生活在“只求过得去”的状态中。

    \n

     

    \n
    \n \n
    \n Photo/Pexels \n
    \n
    \n

     

    \n

    Do you ever get that feeling that there aren't enough hours in the day? That time is somehow racing away from you, and it is impossible to fit everything in. But then, you step outside into the countryside and suddenly everything seems slower, more relaxed, like time has somehow changed.

    \n

    你是否有过这样的感觉:一天的时间不够用?时间不知不觉地从你身边溜走,不可能把所有事情都安排好。但是,当你走到郊外,突然间一切都变得缓慢、轻松起来,就好像时间发生了某种变化。

    \n

     

    \n

    It's not just you – recent research showed nature can regulate our sense of time.

    \n

    不仅仅是你,最近的研究表明,大自然可以调节我们的时间感。

    \n

     

    \n

    For many of us, the combined demands of work, home and family mean that we are always feeling like we don't have enough time. Time poverty has also been exacerbated by digital technologies. Permanent connectivity extends working hours and can make it difficult to switch off from the demands of friends and family.

    \n

    对我们许多人来说,工作、家庭和家人的综合需求意味着我们总是感觉时间不够用。数字技术也加剧了时间贫乏。永久连接延长了工作时间,也使我们难以摆脱朋友和家人的要求。

    \n

     

    \n

    Recent research suggests that the antidote to our lack of time may lie in the natural world. Psychologist Richardo Correia, at the University of Turku in Finland, found that being in nature may change how we experience time and, perhaps, even give us the sense of time abundance.

    \n

    最新研究表明,解决我们时间匮乏问题的良方可能在于自然世界。芬兰图尔库大学的心理学家理查多·科雷亚发现,身处在大自然中可能会改变我们对时间的体验方式,甚至可能让我们感觉到时间的充裕。

    \n

     

    \n

    Correia examined research which compared people's experiences of time when they performed different types of tasks in urban and natural environments. These studies consistently showed that people report a sense of expanded time when they were in nature compared to when they were in an urban environment.

    \n

    科雷亚查阅了比较人们在城市和自然环境中执行不同类型任务时对时间体验的研究。这些研究一致表明,与在城市环境中相比,人们在大自然中会有时间更充裕的感觉。

    \n

     

    \n

    For example, people are more likely to perceive a walk in the countryside as longer than a walk of the same length in the city. Similarly, people report perceiving time as passing more slowly while performing tasks in natural green environments than in urban environments. Nature seems to slow and expand our sense of time.

    \n

    例如,与在城市中散步相比,人们更倾向于认为在郊外散步的时间更长。同样,与城市环境相比,人们在绿色自然环境中执行任务时会觉得时间过得更慢。大自然似乎减缓并扩展了我们的时间感。

    \n

     

    \n

    It's not just our sense of time in the moment which appears to be altered by the natural world, it's also our sense of the past and future. Previous research shows that spending time in nature helps to shift our focus from the immediate moment towards our future needs. So rather than focusing on the stress of the demands on our time, nature helps us to see the bigger picture.

    \n

    自然世界似乎不仅改变了我们当下的时间感,还改变了我们对过去和未来的时间感。先前的研究表明,在大自然中度过的时间有助于将我们的注意力从眼前转移到未来的需求上。因此,大自然会帮助我们看到更广阔的前景,而不是专注于时间要求带给我们的压力。

    \n

     

    \n

    This can help us to prioritise our actions so that we meet our long-term goals rather than living in a perpetual state of \"just about keeping our head above water\".

    \n

    这可以帮助我们确定行动的轻重缓急,从而实现我们的长期目标,而不是永远生活在“只求过得去”的状态中。

    \n

     

    \n

    This is in part because spending time in nature appears to make us less impulsive, enabling us to delay instant gratification in favour of long-term rewards.

    \n

    这在一定程度上是因为在大自然中度过的时间似乎会让我们不那么冲动,使我们能够延迟即时满足感,转而追求长期回报。

    \n

     

    \n
    \n \n
    \n Photo/Pexels \n
    \n
    \n

     

    \n

    Why does nature affect our sense of time?

    \n

    为什么大自然会影响我们的时间观念?

    \n

     

    \n

    Spending time in nature is known to have many benefits for health and wellbeing. Having access to natural spaces such as beaches, parks and woodlands is associated with reduced anxiety and depression, improved sleep, reduced levels of obesity and cardiovascular disease, and improved wellbeing.

    \n

    众所周知,在大自然中度过时光对健康和幸福有很多好处。拥有海滩、公园和林地等自然空间与减少焦虑和抑郁、改善睡眠、降低肥胖和心血管疾病水平以及提高幸福感有关。

    \n

     

    \n

    Some of these benefits may explain why being in nature alters our experience of time.

    \n

    其中一些好处或许可以解释为什么身处大自然中会改变我们对时间的体验。

    \n

     

    \n

    The way we experience time is shaped by our internal biological state and the events going on in the world around us. As a result, emotions such as stress, anxiety and fear can distort our sense of the passage of time.

    \n

    我们体验时间的方式是由我们的内在生理状态和周围世界发生的事件决定的。因此,压力、焦虑和恐惧等情绪会扭曲我们对时间流逝的感知。

    \n

     

    \n

    The relaxing effect of natural environments may counter stress and anxiety, resulting in a more stable experience of time. Indeed, the absence of access to nature during COVID-19 may help to explain why people's sense of time became so distorted during the pandemic lockdowns.

    \n

    自然环境的放松效果可以抵消压力和焦虑,使我们对时间的体验更加稳定。事实上,在新冠疫情期间无法接触大自然可能有助于解释为什么在大流行病封锁期间人们的时间感会变得如此扭曲。

    \n

     

    \n

    In the short term, being away from the demands of modern day life may provide the respite needed to re-prioritise life, and reduce time pressure by focusing on what actually needs to be done. In the longer term, time in nature may help to enhance our memory and attention capacity, making us better able to deal with the demands on our time.

    \n

    从短期来看,远离现代生活的需求可以让我们得到喘息的机会,从而重新确定生活的优先次序,并通过专注于真正需要做的事情来减轻时间压力。从长远来看,在大自然中度过的时间可能有助于增强我们的记忆力和注意力,使我们能够更好地处理对时间的需求。

    \n

     

    \n

    Accessing nature

    \n

    亲近大自然

    \n

     

    \n

    Getting out into nature may sound like a simple fix, but for many people, particularly those living in urban areas, nature can be hard to access. Green infrastructure such as trees, woodlands, parks and allotments in and around towns and cities are essential to making sure the benefits of time in nature are accessible to everyone.

    \n

    到大自然中去听起来似乎很简单,但对很多人来说,尤其是生活在城市地区的人,可能很难接触到大自然。要确保每个人都能享受到大自然带来的益处,城镇及周边地区的树木、林地、公园和种植园等绿色基础设施是必不可少的。

    \n

     

    \n

    If spending time in nature isn't possible for you, there are other ways that you can regain control of your time. Start by closely examining how you use time throughout your week. Auditing your time can help you see where time is being wasted and to identify action to help you to free up more time in your life.

    \n

    如果您无法在大自然中度过美好时光,您还可以通过其他方式重新掌控自己的时间。首先,仔细检查自己一周中如何利用时间。审核时间可以帮助您发现时间浪费在哪里,并确定行动,帮助您在生活中腾出更多时间。

    \n

     

    \n

    Alternatively, try to set yourself some boundaries in how you use time. This could be limiting when you access emails and social media, or it could be booking in time in your calendar to take a break. Taking control of your time and how you use it can help to you overcome the sense that time is running away from you.

    \n

    或者,在如何使用时间方面为自己设定一些界限。这可以是限制您访问电子邮件和社交媒体的时间,也可以是在您的日历中预定休息时间。控制好自己的时间和使用方式,有助于克服 “时间正在离你远去”的感觉。

    \n

     

    \n

    来源:The Conversation、Science Alert、煎蛋网

    \n

    编辑:丹妮

    \n

    审校:董静、齐磊

    \n\n
    ", + "content": "
    \n\n \n\n \n \n\n

    研究发现,置身大自然会让你感觉时间过得更慢,有助于将你的注意力从眼前转移到未来的需求上。你将能够延迟即时满足感,转而追求长期回报,而不是永远生活在“只求过得去”的状态中。

    \n

     

    \n
    \n \n
    \n Photo/Pexels \n
    \n
    \n

     

    \n

    Do you ever get that feeling that there aren't enough hours in the day? That time is somehow racing away from you, and it is impossible to fit everything in. But then, you step outside into the countryside and suddenly everything seems slower, more relaxed, like time has somehow changed.

    \n

    你是否有过这样的感觉:一天的时间不够用?时间不知不觉地从你身边溜走,不可能把所有事情都安排好。但是,当你走到郊外,突然间一切都变得缓慢、轻松起来,就好像时间发生了某种变化。

    \n

     

    \n

    It's not just you – recent research showed nature can regulate our sense of time.

    \n

    不仅仅是你,最近的研究表明,大自然可以调节我们的时间感。

    \n

     

    \n

    For many of us, the combined demands of work, home and family mean that we are always feeling like we don't have enough time. Time poverty has also been exacerbated by digital technologies. Permanent connectivity extends working hours and can make it difficult to switch off from the demands of friends and family.

    \n

    对我们许多人来说,工作、家庭和家人的综合需求意味着我们总是感觉时间不够用。数字技术也加剧了时间贫乏。永久连接延长了工作时间,也使我们难以摆脱朋友和家人的要求。

    \n

     

    \n

    Recent research suggests that the antidote to our lack of time may lie in the natural world. Psychologist Richardo Correia, at the University of Turku in Finland, found that being in nature may change how we experience time and, perhaps, even give us the sense of time abundance.

    \n

    最新研究表明,解决我们时间匮乏问题的良方可能在于自然世界。芬兰图尔库大学的心理学家理查多·科雷亚发现,身处在大自然中可能会改变我们对时间的体验方式,甚至可能让我们感觉到时间的充裕。

    \n

     

    \n

    Correia examined research which compared people's experiences of time when they performed different types of tasks in urban and natural environments. These studies consistently showed that people report a sense of expanded time when they were in nature compared to when they were in an urban environment.

    \n

    科雷亚查阅了比较人们在城市和自然环境中执行不同类型任务时对时间体验的研究。这些研究一致表明,与在城市环境中相比,人们在大自然中会有时间更充裕的感觉。

    \n

     

    \n

    For example, people are more likely to perceive a walk in the countryside as longer than a walk of the same length in the city. Similarly, people report perceiving time as passing more slowly while performing tasks in natural green environments than in urban environments. Nature seems to slow and expand our sense of time.

    \n

    例如,与在城市中散步相比,人们更倾向于认为在郊外散步的时间更长。同样,与城市环境相比,人们在绿色自然环境中执行任务时会觉得时间过得更慢。大自然似乎减缓并扩展了我们的时间感。

    \n

     

    \n

    It's not just our sense of time in the moment which appears to be altered by the natural world, it's also our sense of the past and future. Previous research shows that spending time in nature helps to shift our focus from the immediate moment towards our future needs. So rather than focusing on the stress of the demands on our time, nature helps us to see the bigger picture.

    \n

    自然世界似乎不仅改变了我们当下的时间感,还改变了我们对过去和未来的时间感。先前的研究表明,在大自然中度过的时间有助于将我们的注意力从眼前转移到未来的需求上。因此,大自然会帮助我们看到更广阔的前景,而不是专注于时间要求带给我们的压力。

    \n

     

    \n

    This can help us to prioritise our actions so that we meet our long-term goals rather than living in a perpetual state of \"just about keeping our head above water\".

    \n

    这可以帮助我们确定行动的轻重缓急,从而实现我们的长期目标,而不是永远生活在“只求过得去”的状态中。

    \n

     

    \n

    This is in part because spending time in nature appears to make us less impulsive, enabling us to delay instant gratification in favour of long-term rewards.

    \n

    这在一定程度上是因为在大自然中度过的时间似乎会让我们不那么冲动,使我们能够延迟即时满足感,转而追求长期回报。

    \n

     

    \n
    \n \n
    \n Photo/Pexels \n
    \n
    \n

     

    \n

    Why does nature affect our sense of time?

    \n

    为什么大自然会影响我们的时间观念?

    \n

     

    \n

    Spending time in nature is known to have many benefits for health and wellbeing. Having access to natural spaces such as beaches, parks and woodlands is associated with reduced anxiety and depression, improved sleep, reduced levels of obesity and cardiovascular disease, and improved wellbeing.

    \n

    众所周知,在大自然中度过时光对健康和幸福有很多好处。拥有海滩、公园和林地等自然空间与减少焦虑和抑郁、改善睡眠、降低肥胖和心血管疾病水平以及提高幸福感有关。

    \n

     

    \n

    Some of these benefits may explain why being in nature alters our experience of time.

    \n

    其中一些好处或许可以解释为什么身处大自然中会改变我们对时间的体验。

    \n

     

    \n

    The way we experience time is shaped by our internal biological state and the events going on in the world around us. As a result, emotions such as stress, anxiety and fear can distort our sense of the passage of time.

    \n

    我们体验时间的方式是由我们的内在生理状态和周围世界发生的事件决定的。因此,压力、焦虑和恐惧等情绪会扭曲我们对时间流逝的感知。

    \n

     

    \n

    The relaxing effect of natural environments may counter stress and anxiety, resulting in a more stable experience of time. Indeed, the absence of access to nature during COVID-19 may help to explain why people's sense of time became so distorted during the pandemic lockdowns.

    \n

    自然环境的放松效果可以抵消压力和焦虑,使我们对时间的体验更加稳定。事实上,在新冠疫情期间无法接触大自然可能有助于解释为什么在大流行病封锁期间人们的时间感会变得如此扭曲。

    \n

     

    \n

    In the short term, being away from the demands of modern day life may provide the respite needed to re-prioritise life, and reduce time pressure by focusing on what actually needs to be done. In the longer term, time in nature may help to enhance our memory and attention capacity, making us better able to deal with the demands on our time.

    \n

    从短期来看,远离现代生活的需求可以让我们得到喘息的机会,从而重新确定生活的优先次序,并通过专注于真正需要做的事情来减轻时间压力。从长远来看,在大自然中度过的时间可能有助于增强我们的记忆力和注意力,使我们能够更好地处理对时间的需求。

    \n

     

    \n

    Accessing nature

    \n

    亲近大自然

    \n

     

    \n

    Getting out into nature may sound like a simple fix, but for many people, particularly those living in urban areas, nature can be hard to access. Green infrastructure such as trees, woodlands, parks and allotments in and around towns and cities are essential to making sure the benefits of time in nature are accessible to everyone.

    \n

    到大自然中去听起来似乎很简单,但对很多人来说,尤其是生活在城市地区的人,可能很难接触到大自然。要确保每个人都能享受到大自然带来的益处,城镇及周边地区的树木、林地、公园和种植园等绿色基础设施是必不可少的。

    \n

     

    \n

    If spending time in nature isn't possible for you, there are other ways that you can regain control of your time. Start by closely examining how you use time throughout your week. Auditing your time can help you see where time is being wasted and to identify action to help you to free up more time in your life.

    \n

    如果您无法在大自然中度过美好时光,您还可以通过其他方式重新掌控自己的时间。首先,仔细检查自己一周中如何利用时间。审核时间可以帮助您发现时间浪费在哪里,并确定行动,帮助您在生活中腾出更多时间。

    \n

     

    \n

    Alternatively, try to set yourself some boundaries in how you use time. This could be limiting when you access emails and social media, or it could be booking in time in your calendar to take a break. Taking control of your time and how you use it can help to you overcome the sense that time is running away from you.

    \n

    或者,在如何使用时间方面为自己设定一些界限。这可以是限制您访问电子邮件和社交媒体的时间,也可以是在您的日历中预定休息时间。控制好自己的时间和使用方式,有助于克服 “时间正在离你远去”的感觉。

    \n

     

    \n

    来源:The Conversation、Science Alert、煎蛋网

    \n

    编辑:丹妮

    \n

    审校:董静、齐磊

    \n\n
    ", + "category": "", + "link": "https://language.chinadaily.com.cn/a/202409/05/WS66d95738a3108f29c1fca5a6.html", + "creator": "", + "pubDate": "", + "enclosure": "", + "enclosureType": "", + "image": "", + "id": "", + "language": "", + "folder": "双语", + "feed": "ChinaDaily", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "0ce69d63efd46364201bea63d1a192ab", + "highlights": [] + }, + { + "title": "双语百科:为什么鸟儿在黎明歌唱?", + "description": "
    \n\n \n\n \n \n\n

    清晨悦耳的鸟鸣让人心旷神怡,但是一天中的其他时间却很少能听到鸟叫。为什么鸟儿都喜欢在清晨歌唱?来本文中寻找答案吧。

    \n

     

    \n
    \n \n
    \n Photo/Pexels \n
    \n
    \n

     

    \n

    Wake up early enough and, depending on where you live, you’ll likely hear a pleasant chorus of birds chirping their tiny hearts out. This am avian arrangement is instinctual behavior for birds, but why?
    早上起得够早的话,在你所住的地方也许能听到一阵悦耳的鸟鸣。在清晨歌唱是鸟的本能行为,但是鸟鸣为什么都在清晨呢?

    \n

     

    \n

    According to the Woodland Trust, this early singing is known as the dawn chorus, and it can start as early as 4 am and last for several hours. Birds do it to attract mates and warn other birds to avoid their turf. One theory as to why they opt for the wee hours to communicate these important messages is because the low visibility makes it hard to do other bird activities, like foraging. Keeping their activity level low, they opt to sing instead.
    林地信托机构称,鸟儿在清晨鸣叫最早从凌晨4点开始,可持续数个小时。鸟鸣叫是为了吸引配偶,也是为了警告其他鸟不要入侵自己的领地。关于鸟类为什么选择在凌晨传递这些重要信息,有一个理论是,凌晨能见度低,鸟类难以进行觅食等其他活动,既然无事可做,那么鸟儿就转而选择鸣叫。

    \n

     

    \n

    Another theory argues that birds use the morning vocals to project an image of strength. By singing with fervor, they’re letting other birds know they survived the night—no guarantee in nature—and would make for an excellent mate.
    另一种理论认为,鸟类利用晨鸣来展示自己的力量。通过热情地歌唱,它们让其他鸟儿知道自己已经安然度过黑夜——要知道自然界的动物过着朝不保夕的日子,不一定能看见明天的太阳——并能够成为出色的伴侣。

    \n

     

    \n

    Like a good recording studio, the early morning hours also allow birds to transmit a clear tune thanks to the cooler, drier air. Because birds have distinctive chirps, that clarity helps them be more easily identified by birds within listening distance.
    清晨的空气更凉爽、更干燥,就像一个效果很好的录音棚,鸟儿在此时歌唱能传递出清晰的音质。因为鸟儿的声音各有不同,这种清晰度可以让听得见的鸟儿更容易识别出它们的声音。

    \n

     

    \n

    There used to be a prevailing theory that birds sang so much in the mornings because the sounds could travel greater distances due to less atmospheric turbulence, but it was disproven by researchers at the University of Western Ontario in 2003. They played recordings of sparrows at dawn and midday. The songs didn’t travel farther, but they were more consistent.
    过去有一个流行的理论认为,鸟儿最爱在清晨鸣叫是因为在气流稳定的时候声音能传播得更远。但是西安大略大学的研究人员在2003年否定了这一理论。研究人员在黎明和中午分别播放了麻雀的录音。然而麻雀声音并没有传播得更远,只不过黎明时分麻雀的叫声更连贯。

    \n

     

    \n

    Because birds only have so much energy to belt one out, singing when they stand the best chance of being heard makes sense. For a bird, an early morning session is like having the perfect acoustic environment for their performance.
    因为鸟儿只能用有限的精力来鸣叫,它们自然会选择在最可能被听到的时候歌唱。对一只鸟儿来说,清晨鸣叫相当于在最佳音效环境中表演。

    \n

     

    \n

    英文来源:Mental Floss

    \n

    编译:丹妮

    \n

    审校:董静、齐磊

    \n\n
    ", + "content": "
    \n\n \n\n \n \n\n

    清晨悦耳的鸟鸣让人心旷神怡,但是一天中的其他时间却很少能听到鸟叫。为什么鸟儿都喜欢在清晨歌唱?来本文中寻找答案吧。

    \n

     

    \n
    \n \n
    \n Photo/Pexels \n
    \n
    \n

     

    \n

    Wake up early enough and, depending on where you live, you’ll likely hear a pleasant chorus of birds chirping their tiny hearts out. This am avian arrangement is instinctual behavior for birds, but why?
    早上起得够早的话,在你所住的地方也许能听到一阵悦耳的鸟鸣。在清晨歌唱是鸟的本能行为,但是鸟鸣为什么都在清晨呢?

    \n

     

    \n

    According to the Woodland Trust, this early singing is known as the dawn chorus, and it can start as early as 4 am and last for several hours. Birds do it to attract mates and warn other birds to avoid their turf. One theory as to why they opt for the wee hours to communicate these important messages is because the low visibility makes it hard to do other bird activities, like foraging. Keeping their activity level low, they opt to sing instead.
    林地信托机构称,鸟儿在清晨鸣叫最早从凌晨4点开始,可持续数个小时。鸟鸣叫是为了吸引配偶,也是为了警告其他鸟不要入侵自己的领地。关于鸟类为什么选择在凌晨传递这些重要信息,有一个理论是,凌晨能见度低,鸟类难以进行觅食等其他活动,既然无事可做,那么鸟儿就转而选择鸣叫。

    \n

     

    \n

    Another theory argues that birds use the morning vocals to project an image of strength. By singing with fervor, they’re letting other birds know they survived the night—no guarantee in nature—and would make for an excellent mate.
    另一种理论认为,鸟类利用晨鸣来展示自己的力量。通过热情地歌唱,它们让其他鸟儿知道自己已经安然度过黑夜——要知道自然界的动物过着朝不保夕的日子,不一定能看见明天的太阳——并能够成为出色的伴侣。

    \n

     

    \n

    Like a good recording studio, the early morning hours also allow birds to transmit a clear tune thanks to the cooler, drier air. Because birds have distinctive chirps, that clarity helps them be more easily identified by birds within listening distance.
    清晨的空气更凉爽、更干燥,就像一个效果很好的录音棚,鸟儿在此时歌唱能传递出清晰的音质。因为鸟儿的声音各有不同,这种清晰度可以让听得见的鸟儿更容易识别出它们的声音。

    \n

     

    \n

    There used to be a prevailing theory that birds sang so much in the mornings because the sounds could travel greater distances due to less atmospheric turbulence, but it was disproven by researchers at the University of Western Ontario in 2003. They played recordings of sparrows at dawn and midday. The songs didn’t travel farther, but they were more consistent.
    过去有一个流行的理论认为,鸟儿最爱在清晨鸣叫是因为在气流稳定的时候声音能传播得更远。但是西安大略大学的研究人员在2003年否定了这一理论。研究人员在黎明和中午分别播放了麻雀的录音。然而麻雀声音并没有传播得更远,只不过黎明时分麻雀的叫声更连贯。

    \n

     

    \n

    Because birds only have so much energy to belt one out, singing when they stand the best chance of being heard makes sense. For a bird, an early morning session is like having the perfect acoustic environment for their performance.
    因为鸟儿只能用有限的精力来鸣叫,它们自然会选择在最可能被听到的时候歌唱。对一只鸟儿来说,清晨鸣叫相当于在最佳音效环境中表演。

    \n

     

    \n

    英文来源:Mental Floss

    \n

    编译:丹妮

    \n

    审校:董静、齐磊

    \n\n
    ", + "category": "", + "link": "https://language.chinadaily.com.cn/a/202409/04/WS66d7fb2da3108f29c1fca198.html", + "creator": "", + "pubDate": "", + "enclosure": "", + "enclosureType": "", + "image": "", + "id": "", + "language": "", + "folder": "双语", + "feed": "ChinaDaily", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "ce6345bc9ae331d44d7f2c1699c0a3c6", + "highlights": [] + }, + { + "title": "【双语财讯】“中国造”体育用品出口火热 中国已成为全球最大体育用品出口国", + "description": "
    \n\n \n\n \n \n\n

    海关总署近日发布的数据显示,今年上半年,中国体育用品及设备出口同比增长16.7%,高出同期出口整体增速9.8个百分点。世界贸易组织发布的数据报告显示,全球范围内的体育用品贸易在过去近30年里增长了近3倍,而在这30年间,中国已成为全球最大的体育用品出口国。

    \n
    \n \n
    \n A foreign businessman (right) picks ball products at the Yiwu International Trade City in Yiwu, Zhejiang province, on April 15, 2024. [Photo/VCG] \n
    \n
    \n

    The exports of China's sporting goods and equipment surged 16.7 percent year-on-year in the first half of 2024, outpacing the overall export growth during the same period by a whopping 9.8 percentage points, according to data recently released by the General Administration of Customs (GAC).
    海关总署近日发布的数据显示,今年上半年,中国体育用品及设备出口同比增长16.7%,高出同期出口整体增速9.8个百分点。

    \n

    Data from the World Trade Organization showed that global trade in sporting goods has nearly tripled in almost 30 years, and China has become the world's top exporter of sporting goods.
    世界贸易组织发布的数据显示,全球范围内的体育用品贸易在过去近30年里增长了近3倍,而在这30年间,中国已成为全球最大的体育用品出口国。

    \n

    In Jinping county of southwest China's Guizhou Province, a global production hub for badminton shuttlecocks, more than 100,000 shuttlecocks are sold worldwide a day.
    在全球重要的羽毛球生产基地——贵州省锦屏县,每天超过10万只羽毛球从这里“飞”向全球。

    \n

    \"We've calculated that about one in every 10 shuttlecocks in the world comes from Jinping,\" said Hu Bing, production manager of Guizhou RSL Sports Culture Development Co., Ltd.
    贵州亚狮龙体育文化产业发展有限公司生产经理胡兵说:“我们统计过,全球大约每10只羽毛球,就有1只产自锦屏。”

    \n

    Last year, the company produced over 4 million dozen shuttlecocks, generating a staggering output value of 340 million yuan ($47.73 million) and exporting nearly $9 million worth of shuttlecocks to over 60 countries and regions, Hu noted.
    胡兵说,公司去年产值3.4亿元,生产羽毛球超400多万打,出口总额近900万美元,产品销往60多个国家和地区。

    \n

    The first half of 2024 saw the company achieve even more remarkable growth, with the output value of shuttlecocks reaching 185 million yuan and exports skyrocketing by around 40 percent compared to the same period last year.
    今年上半年,该公司营业额进一步增长,产值达1.85亿元,出口比去年同期增长了40%左右。

    \n

    In the first quarter of 2024, China's exports of footballs, basketballs, and volleyballs saw a year-on-year increase of 33.57 percent in volume and 17.99 percent in value, while that of table tennis, badminton, and tennis equipment increased 0.93 percent from the same period last year to $204 million, according to GAC data.
    根据海关总署数据,2024年第一季度,中国足球、篮球、排球出口量同比增长33.57%,出口额同比增长17.99%;乒乓球、羽毛球、网球及相关用品累计出口额2.04亿美元,同比增长0.93%。

    \n

    In the first three months of this year, the country's exports of roller skates and skateboards surged 30.85 percent and 50.21 percent year-on-year, respectively. The inclusion of skateboarding as an Olympic sport has further fueled demand, particularly in countries like Russia and Japan.
    今年一季度,中国旱冰鞋和滑板出口量同比分别大幅增长30.85%和50.21%。随着滑板成为奥运项目,带动俄罗斯、日本等国消费,滑板出口额增长显著。

    \n

    In addition, treadmill exports jumped by 44.94 percent year-on-year in the first quarter, while artificial turf exports grew by 15.64 percent from the same period last year.
    此外,一季度跑步机和人造草同比也分别增长44.94%和15.64%。

    \n

    To boost the pace of \"going global,\" Chinese sporting goods companies need to rely on greener sporting goods with higher technological content, said Bao Mingxiao, director of the China Sports Policy Research Institute at Beijing Sport University.
    北京体育大学中国体育政策研究院院长鲍明晓表示,企业出海的步伐要更加坚实有力,靠的是更具科技含量、更加绿色环保的“硬实力”。

    \n

    Dingqi Sports Goods (Huai'an) Co., Ltd. in Huai'an city, east China's Jiangsu Province specializes in the production of ball bladders, including those used in the production of footballs, volleyballs, basketballs and rugby balls. 70 percent of its products are exported overseas. For the Paris Olympics, the company exported products made from 70 percent bio-based materials.
    江苏淮安的顶碁运动用品(淮安)有限公司专门生产运动用足球、排球、篮球、橄榄球等球类产品内胆,产品70%出口海外。巴黎奥运会就使用了这家企业研发的含有70%生物基的产品。

    \n

    \"This type of football is equipped with a chip capable of making 500 identifications per second to assist in determining a handball or offside instances,\" said Zhou Hongda, general manager of the company, adding that it produces ball bladders made from recycled and bio-based materials.
    顶碁运动用品(淮安)有限公司总经理周宏达表示:“这款足球植入了芯片,1秒内能做500次识别动作,通过实时数据辅助判断是否手球、越位等。” 他介绍,该公司的足球内胆用回收材料、生物基材料制成。

    \n

    Since the start of this year, the company has exported 4.07 million balls for training and games, including those used in the UEFA European Championship, Copa America, and the Paris Olympics.
    今年以来,这家公司已出口欧洲杯、美洲杯、奥运会等赛事用球及训练用球407万个。

    \n

    Zhou believes that the key to the popularity of \"Made-in-China\" sporting goods overseas lies in continuous innovation, higher technological content, and adapting to new market demand.
    周宏达认为,“中国制造”体育用品赢得海外市场的关键就在于不断创新,增加产品的科技含量,适应市场新需求。

    \n

    Wen Jia, deputy general secretary of the China Sporting Goods Federation, said fierce competition in the domestic market makes enterprises and merchants provide customized and personalized products and services by meeting customer demand and leveraging technologies.
    中国体育用品业联合会副秘书长温嘉表示,国内市场竞争激烈,倒逼企业和商家从客户需求出发,利用科技手段提供定制化、个性化的产品和服务。

    \n

    The booming sports event economy has attracted buyers across the world to purchase all kinds of sporting goods in Yiwu, known as the \"World's Supermarket\" in east China's Zhejiang Province. In the city, the improved efficiency of customs supervision provides a strong boost for Chinese companies to go global.
    赛事经济的带动下,世界各地的采购商抓住商机,来到“世界超市”浙江义乌采购各式各样的体育产品。在这里,海关监管效率的提升也为中国企业走出去提供了强大助力。

    \n

    Chinese enterprises are not just exporting sporting goods, but also technology, services, and brands.
    中国企业不仅出口体育产品,同时也出口技术、服务和品牌。

    \n

    \"Many foreign clients recognize our manufacturing capabilities and hope to introduce Chinese production technology to their own countries. Our future may not solely involve product exports, but also technology, service, and brand exports,\" said Yang Lei, general manager of the foreign trade department at Hebei Jadeqi Sports Goods Co Ltd.
    河北杰帝奇体育用品有限公司外贸部总经理杨磊表示:“国外很多客商对我们的制造业水平很认可,他们也希望把中国的生产技术引进过去。我们以后可能不单纯地进行产品出口,也要进行技术、服务、品牌的出口。”

    \n

    \"Our overseas strategy aligns with the Belt and Road Initiative. We not only produce products but also offer services and comprehensive solutions, facilitating integration with other industries and making our operations more convenient. Recently, we've been considering building a production base in Serbia, having just visited there, and found the environment and market conditions favorable,\" Yang added.
    杨磊说:“我们的出海思路其实就是跟着‘一带一路’走。因为我们不仅做产品,也做服务、做一体化的解决方式,这样可以更好地和其他行业进行整合,对我们来说也更便利。最近,我们正在研究去塞尔维亚建生产基地,刚去那里考察过,环境和市场都不错,很适合做市场。”

    \n

    英文来源:人民网
    编译:丹妮
    审校:万月英、齐磊

    \n\n
    ", + "content": "
    \n\n \n\n \n \n\n

    海关总署近日发布的数据显示,今年上半年,中国体育用品及设备出口同比增长16.7%,高出同期出口整体增速9.8个百分点。世界贸易组织发布的数据报告显示,全球范围内的体育用品贸易在过去近30年里增长了近3倍,而在这30年间,中国已成为全球最大的体育用品出口国。

    \n
    \n \n
    \n A foreign businessman (right) picks ball products at the Yiwu International Trade City in Yiwu, Zhejiang province, on April 15, 2024. [Photo/VCG] \n
    \n
    \n

    The exports of China's sporting goods and equipment surged 16.7 percent year-on-year in the first half of 2024, outpacing the overall export growth during the same period by a whopping 9.8 percentage points, according to data recently released by the General Administration of Customs (GAC).
    海关总署近日发布的数据显示,今年上半年,中国体育用品及设备出口同比增长16.7%,高出同期出口整体增速9.8个百分点。

    \n

    Data from the World Trade Organization showed that global trade in sporting goods has nearly tripled in almost 30 years, and China has become the world's top exporter of sporting goods.
    世界贸易组织发布的数据显示,全球范围内的体育用品贸易在过去近30年里增长了近3倍,而在这30年间,中国已成为全球最大的体育用品出口国。

    \n

    In Jinping county of southwest China's Guizhou Province, a global production hub for badminton shuttlecocks, more than 100,000 shuttlecocks are sold worldwide a day.
    在全球重要的羽毛球生产基地——贵州省锦屏县,每天超过10万只羽毛球从这里“飞”向全球。

    \n

    \"We've calculated that about one in every 10 shuttlecocks in the world comes from Jinping,\" said Hu Bing, production manager of Guizhou RSL Sports Culture Development Co., Ltd.
    贵州亚狮龙体育文化产业发展有限公司生产经理胡兵说:“我们统计过,全球大约每10只羽毛球,就有1只产自锦屏。”

    \n

    Last year, the company produced over 4 million dozen shuttlecocks, generating a staggering output value of 340 million yuan ($47.73 million) and exporting nearly $9 million worth of shuttlecocks to over 60 countries and regions, Hu noted.
    胡兵说,公司去年产值3.4亿元,生产羽毛球超400多万打,出口总额近900万美元,产品销往60多个国家和地区。

    \n

    The first half of 2024 saw the company achieve even more remarkable growth, with the output value of shuttlecocks reaching 185 million yuan and exports skyrocketing by around 40 percent compared to the same period last year.
    今年上半年,该公司营业额进一步增长,产值达1.85亿元,出口比去年同期增长了40%左右。

    \n

    In the first quarter of 2024, China's exports of footballs, basketballs, and volleyballs saw a year-on-year increase of 33.57 percent in volume and 17.99 percent in value, while that of table tennis, badminton, and tennis equipment increased 0.93 percent from the same period last year to $204 million, according to GAC data.
    根据海关总署数据,2024年第一季度,中国足球、篮球、排球出口量同比增长33.57%,出口额同比增长17.99%;乒乓球、羽毛球、网球及相关用品累计出口额2.04亿美元,同比增长0.93%。

    \n

    In the first three months of this year, the country's exports of roller skates and skateboards surged 30.85 percent and 50.21 percent year-on-year, respectively. The inclusion of skateboarding as an Olympic sport has further fueled demand, particularly in countries like Russia and Japan.
    今年一季度,中国旱冰鞋和滑板出口量同比分别大幅增长30.85%和50.21%。随着滑板成为奥运项目,带动俄罗斯、日本等国消费,滑板出口额增长显著。

    \n

    In addition, treadmill exports jumped by 44.94 percent year-on-year in the first quarter, while artificial turf exports grew by 15.64 percent from the same period last year.
    此外,一季度跑步机和人造草同比也分别增长44.94%和15.64%。

    \n

    To boost the pace of \"going global,\" Chinese sporting goods companies need to rely on greener sporting goods with higher technological content, said Bao Mingxiao, director of the China Sports Policy Research Institute at Beijing Sport University.
    北京体育大学中国体育政策研究院院长鲍明晓表示,企业出海的步伐要更加坚实有力,靠的是更具科技含量、更加绿色环保的“硬实力”。

    \n

    Dingqi Sports Goods (Huai'an) Co., Ltd. in Huai'an city, east China's Jiangsu Province specializes in the production of ball bladders, including those used in the production of footballs, volleyballs, basketballs and rugby balls. 70 percent of its products are exported overseas. For the Paris Olympics, the company exported products made from 70 percent bio-based materials.
    江苏淮安的顶碁运动用品(淮安)有限公司专门生产运动用足球、排球、篮球、橄榄球等球类产品内胆,产品70%出口海外。巴黎奥运会就使用了这家企业研发的含有70%生物基的产品。

    \n

    \"This type of football is equipped with a chip capable of making 500 identifications per second to assist in determining a handball or offside instances,\" said Zhou Hongda, general manager of the company, adding that it produces ball bladders made from recycled and bio-based materials.
    顶碁运动用品(淮安)有限公司总经理周宏达表示:“这款足球植入了芯片,1秒内能做500次识别动作,通过实时数据辅助判断是否手球、越位等。” 他介绍,该公司的足球内胆用回收材料、生物基材料制成。

    \n

    Since the start of this year, the company has exported 4.07 million balls for training and games, including those used in the UEFA European Championship, Copa America, and the Paris Olympics.
    今年以来,这家公司已出口欧洲杯、美洲杯、奥运会等赛事用球及训练用球407万个。

    \n

    Zhou believes that the key to the popularity of \"Made-in-China\" sporting goods overseas lies in continuous innovation, higher technological content, and adapting to new market demand.
    周宏达认为,“中国制造”体育用品赢得海外市场的关键就在于不断创新,增加产品的科技含量,适应市场新需求。

    \n

    Wen Jia, deputy general secretary of the China Sporting Goods Federation, said fierce competition in the domestic market makes enterprises and merchants provide customized and personalized products and services by meeting customer demand and leveraging technologies.
    中国体育用品业联合会副秘书长温嘉表示,国内市场竞争激烈,倒逼企业和商家从客户需求出发,利用科技手段提供定制化、个性化的产品和服务。

    \n

    The booming sports event economy has attracted buyers across the world to purchase all kinds of sporting goods in Yiwu, known as the \"World's Supermarket\" in east China's Zhejiang Province. In the city, the improved efficiency of customs supervision provides a strong boost for Chinese companies to go global.
    赛事经济的带动下,世界各地的采购商抓住商机,来到“世界超市”浙江义乌采购各式各样的体育产品。在这里,海关监管效率的提升也为中国企业走出去提供了强大助力。

    \n

    Chinese enterprises are not just exporting sporting goods, but also technology, services, and brands.
    中国企业不仅出口体育产品,同时也出口技术、服务和品牌。

    \n

    \"Many foreign clients recognize our manufacturing capabilities and hope to introduce Chinese production technology to their own countries. Our future may not solely involve product exports, but also technology, service, and brand exports,\" said Yang Lei, general manager of the foreign trade department at Hebei Jadeqi Sports Goods Co Ltd.
    河北杰帝奇体育用品有限公司外贸部总经理杨磊表示:“国外很多客商对我们的制造业水平很认可,他们也希望把中国的生产技术引进过去。我们以后可能不单纯地进行产品出口,也要进行技术、服务、品牌的出口。”

    \n

    \"Our overseas strategy aligns with the Belt and Road Initiative. We not only produce products but also offer services and comprehensive solutions, facilitating integration with other industries and making our operations more convenient. Recently, we've been considering building a production base in Serbia, having just visited there, and found the environment and market conditions favorable,\" Yang added.
    杨磊说:“我们的出海思路其实就是跟着‘一带一路’走。因为我们不仅做产品,也做服务、做一体化的解决方式,这样可以更好地和其他行业进行整合,对我们来说也更便利。最近,我们正在研究去塞尔维亚建生产基地,刚去那里考察过,环境和市场都不错,很适合做市场。”

    \n

    英文来源:人民网
    编译:丹妮
    审校:万月英、齐磊

    \n\n
    ", + "category": "", + "link": "https://language.chinadaily.com.cn/a/202409/03/WS66d6b777a3108f29c1fc9e35.html", + "creator": "", + "pubDate": "", + "enclosure": "", + "enclosureType": "", + "image": "", + "id": "", + "language": "", + "folder": "双语", + "feed": "ChinaDaily", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "9305b1b5e2881a059053edec7adb453b", + "highlights": [] + }, + { + "title": "开学啦!来看看新学期的正确打开方式", + "description": "
    \n\n \n\n \n \n\n

    A new semester means a fresh start. Get your semester started right by practicing these strategies, and your productivity will soar.
    新学期意味着新的开始。掌握了新学期的正确打开方式,你的学习效率将会直线上升。

    \n

     

    \n
    \n \n
    \n Photo/Pexels \n
    \n
    \n

     

    \n

    1. Get organized
    让一切井井有条

    \n

    Organization helps you reduce chaos and stress, thus making you more productive. Develop a system at the start of your semester. This may include:
    条理性可以帮助你减少混乱和压力,从而让你做事更有效率。在开学时把一切都整理得井井有条,包括:

    \n

    --Color coded notebooks and/or folders for each class
    ——给每门课程准备一个彩页笔记本和文件夹;

    \n

    -- A planner with important dates and deadlines filled out
    ——准备一个日程表,标注出重要的日子和各项任务的截止日期;

    \n

    --Cleaning out your backpack, desk, and dorm room to reduce clutter from previous semesters
    ——清理背包、书桌和宿舍,把前几个学期积攒下来的无用之物处理掉;

    \n

    --Organizing your computer files (e.g., by school year, semester, course, and assignment type or project)
    ——整理电脑文件,可以按照学年、学期、课程、作业或项目分门别类;

    \n

    If you keep track of deadlines, know where to find everything, and maintain a well-organized space, you free up your mind to focus on learning and achieving.
    如果你对截止日期了如指掌,知道每样东西放在哪儿,把空间整理得井井有条,你就可以把脑子腾出来专心学习,做出成绩。

    \n

     

    \n

    2. Get into a routine
    做事有规律

    \n

    Like being organized, establishing a routine helps your life run smoothly.
    和条理性一样,做事有规律可以让你的生活更顺利。

    \n

    It’s easier said than done, but try waking up and going to sleep around the same time each day (or at least on weekdays). Schedule time for the gym, any clubs or activities you participate in, and studying. Instead of waiting until the last minute to work on assignments or cram in information, set aside studying/homework blocks each day. Choose a time of day when you feel especially productive.
    尽管说起来容易做起来难,但是你可以试着每天都在同一时间起床和睡觉(至少在工作日如此)。规划好去健身、去参加社团或其他活动以及学习的时间。每天都要留出一整块时间用于学习和做作业,不要等到最后一刻才赶作业或背书。选择一天中你自认为效率最高的时间来学习。

    \n

    Start each day on a positive note by preparing the night before. For instance, set out the outfit you’ll wear to class and pack your backpack with the supplies you’ll need that day. Instead of frantically scrambling around in the morning, you’ll begin the day feeling confident and prepared. Ultimately, these small habits make a big difference in your overall mood and performance.
    通过在前一天晚上做好准备,以积极的态度开始每一天。举例而言,准备好你明天上课要穿的衣服,在书包里放好明天需要的东西。这样,你就可以在准备充分的情况下自信地开始新的一天,而不是一大早紧张忙乱地到处找东西。最终,这些小习惯将会让你的整体情绪状态和表现大为改观。

    \n

     

    \n

    3. Surround yourself with motivated peers
    结交好学的同伴

    \n

    Maybe you’ve heard the saying, “Show me your friends and I’ll show you your future.” It may sound dramatic, but it’s true that the people you spend the most time with have a major influence on you. If your friends are partying every night, skipping class, and seemingly unconcerned about their grades, staying motivated will be an uphill battle for you.
    也许你听过这句话:“你身边的朋友决定你的未来。”这也许听起来有些夸张,但是你与之交往时间最长的人对你会产生重大影响,这是事实。如果你的朋友夜夜狂欢,逃课,对成绩毫不在意,那么保持斗志对你来说会非常困难。

    \n

    Try to make at least one positive friend in every class. Meet ambitious people with clearly defined goals, good study habits, and healthy lifestyles. These friends can help you stay accountable, and they make great study buddies too.
    你应该试着每门课程都结交至少一位好学的朋友。结交那些有明确目标、良好学习习惯和健康生活方式的有志向的人。这些朋友可以帮助你保持责任心,也可以成为很好的学伴。

    \n

     

    \n

    4. Take good care of yourself
    照顾好自己

    \n

    Finally, remember to practice self-care and healthy habits. When you eat poorly and don’t get enough sleep, it impacts your mood and your mind. You may feel fuzzy and forgetful, and you’ll have a tough time learning new concepts. Obviously, this is not helpful for a successful semester.
    最后,记得要照顾好自己,养成健康的生活习惯。如果你吃不好,睡眠不足,就会影响你的情绪和头脑。你可能会觉得脑子迷迷糊糊,变得健忘,在学习新概念时也会感到费劲。显然,这对于成功开展新学期有害无益。

    \n

    Eat nutritious meals (as often as you can), exercise, and get plenty of rest. Studying in small chunks instead of relying on cram sessions is one way to ensure you sleep enough.
    平常多吃有营养的饭菜,坚持锻炼,充分休息。每天学一点,而不是临时抱佛脚,也是确保你睡眠充足的一个方式。

    \n

    Creating new habits is tough at first. Stick with it, and you’ll find that you’re far more productive and successful—and probably happier, too.
    养成新习惯一开始很难。坚持下来,你会发现你远比以前效率更高、更成功,应该也会更快乐。

    \n

     

    \n

    英文来源:Niche

    \n

    编译:丹妮

    \n

    审校:董静、齐磊

    \n\n
    ", + "content": "
    \n\n \n\n \n \n\n

    A new semester means a fresh start. Get your semester started right by practicing these strategies, and your productivity will soar.
    新学期意味着新的开始。掌握了新学期的正确打开方式,你的学习效率将会直线上升。

    \n

     

    \n
    \n \n
    \n Photo/Pexels \n
    \n
    \n

     

    \n

    1. Get organized
    让一切井井有条

    \n

    Organization helps you reduce chaos and stress, thus making you more productive. Develop a system at the start of your semester. This may include:
    条理性可以帮助你减少混乱和压力,从而让你做事更有效率。在开学时把一切都整理得井井有条,包括:

    \n

    --Color coded notebooks and/or folders for each class
    ——给每门课程准备一个彩页笔记本和文件夹;

    \n

    -- A planner with important dates and deadlines filled out
    ——准备一个日程表,标注出重要的日子和各项任务的截止日期;

    \n

    --Cleaning out your backpack, desk, and dorm room to reduce clutter from previous semesters
    ——清理背包、书桌和宿舍,把前几个学期积攒下来的无用之物处理掉;

    \n

    --Organizing your computer files (e.g., by school year, semester, course, and assignment type or project)
    ——整理电脑文件,可以按照学年、学期、课程、作业或项目分门别类;

    \n

    If you keep track of deadlines, know where to find everything, and maintain a well-organized space, you free up your mind to focus on learning and achieving.
    如果你对截止日期了如指掌,知道每样东西放在哪儿,把空间整理得井井有条,你就可以把脑子腾出来专心学习,做出成绩。

    \n

     

    \n

    2. Get into a routine
    做事有规律

    \n

    Like being organized, establishing a routine helps your life run smoothly.
    和条理性一样,做事有规律可以让你的生活更顺利。

    \n

    It’s easier said than done, but try waking up and going to sleep around the same time each day (or at least on weekdays). Schedule time for the gym, any clubs or activities you participate in, and studying. Instead of waiting until the last minute to work on assignments or cram in information, set aside studying/homework blocks each day. Choose a time of day when you feel especially productive.
    尽管说起来容易做起来难,但是你可以试着每天都在同一时间起床和睡觉(至少在工作日如此)。规划好去健身、去参加社团或其他活动以及学习的时间。每天都要留出一整块时间用于学习和做作业,不要等到最后一刻才赶作业或背书。选择一天中你自认为效率最高的时间来学习。

    \n

    Start each day on a positive note by preparing the night before. For instance, set out the outfit you’ll wear to class and pack your backpack with the supplies you’ll need that day. Instead of frantically scrambling around in the morning, you’ll begin the day feeling confident and prepared. Ultimately, these small habits make a big difference in your overall mood and performance.
    通过在前一天晚上做好准备,以积极的态度开始每一天。举例而言,准备好你明天上课要穿的衣服,在书包里放好明天需要的东西。这样,你就可以在准备充分的情况下自信地开始新的一天,而不是一大早紧张忙乱地到处找东西。最终,这些小习惯将会让你的整体情绪状态和表现大为改观。

    \n

     

    \n

    3. Surround yourself with motivated peers
    结交好学的同伴

    \n

    Maybe you’ve heard the saying, “Show me your friends and I’ll show you your future.” It may sound dramatic, but it’s true that the people you spend the most time with have a major influence on you. If your friends are partying every night, skipping class, and seemingly unconcerned about their grades, staying motivated will be an uphill battle for you.
    也许你听过这句话:“你身边的朋友决定你的未来。”这也许听起来有些夸张,但是你与之交往时间最长的人对你会产生重大影响,这是事实。如果你的朋友夜夜狂欢,逃课,对成绩毫不在意,那么保持斗志对你来说会非常困难。

    \n

    Try to make at least one positive friend in every class. Meet ambitious people with clearly defined goals, good study habits, and healthy lifestyles. These friends can help you stay accountable, and they make great study buddies too.
    你应该试着每门课程都结交至少一位好学的朋友。结交那些有明确目标、良好学习习惯和健康生活方式的有志向的人。这些朋友可以帮助你保持责任心,也可以成为很好的学伴。

    \n

     

    \n

    4. Take good care of yourself
    照顾好自己

    \n

    Finally, remember to practice self-care and healthy habits. When you eat poorly and don’t get enough sleep, it impacts your mood and your mind. You may feel fuzzy and forgetful, and you’ll have a tough time learning new concepts. Obviously, this is not helpful for a successful semester.
    最后,记得要照顾好自己,养成健康的生活习惯。如果你吃不好,睡眠不足,就会影响你的情绪和头脑。你可能会觉得脑子迷迷糊糊,变得健忘,在学习新概念时也会感到费劲。显然,这对于成功开展新学期有害无益。

    \n

    Eat nutritious meals (as often as you can), exercise, and get plenty of rest. Studying in small chunks instead of relying on cram sessions is one way to ensure you sleep enough.
    平常多吃有营养的饭菜,坚持锻炼,充分休息。每天学一点,而不是临时抱佛脚,也是确保你睡眠充足的一个方式。

    \n

    Creating new habits is tough at first. Stick with it, and you’ll find that you’re far more productive and successful—and probably happier, too.
    养成新习惯一开始很难。坚持下来,你会发现你远比以前效率更高、更成功,应该也会更快乐。

    \n

     

    \n

    英文来源:Niche

    \n

    编译:丹妮

    \n

    审校:董静、齐磊

    \n\n
    ", + "category": "", + "link": "https://language.chinadaily.com.cn/a/202409/02/WS66d5837ba3108f29c1fc9a74.html", + "creator": "", + "pubDate": "", + "enclosure": "", + "enclosureType": "", + "image": "", + "id": "", + "language": "", + "folder": "双语", + "feed": "ChinaDaily", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "c93cd7fea5ef9801a585814b525d20a3", + "highlights": [] + }, + { + "title": "【双语财讯】美经济学家警告:美国多个经济衰退“红灯”亮起", + "description": "
    \n\n \n\n \n \n\n
    \n \n
    \n
    \n

    美国商业内幕网8月30日报道称,美国知名经济学家、罗森伯格研究公司总裁大卫·罗森伯格表示,近期一系列信号提示,美国经济陷入衰退的可能性正在上升。文章摘编如下:

    \n

    The probability of a recession hitting the US economy is rising after a flurry of signals have flashed in recent weeks and months.
    在近期(最近几周和数月内)一系列信号亮起后,美国经济陷入衰退的可能性正在上升。

    \n

    That's according to economist David Rosenberg of Rosenberg Research, who compiled a list of recessionary indicators in a note on Friday.
    美国罗森伯格研究公司总裁大卫·罗森伯格在8月30日发布的一份报告中汇编了一份衰退指标清单。

    \n

    \"What is the best recession indicator? That's a hard question to answer — why not dodge it completely and just look at all of them?\" Rosenberg asked.
    他提出:“最准确的衰退指标是什么?这是一个很难回答的问题——为什么不避开这个问题,看看所有这些指标呢?”

    \n

    According to Rosenberg, some worrying signals have historically only flashed on the precipice of an economic downturn.
    罗森伯格表示,在历史上,一些令人担忧的信号只有在经济即将陷入衰退时才会出现。

    \n

    \"The 'indicator of indicators' indicates recession,\" Rosenberg said.
    罗森伯格称:“‘指标的指标’预示着经济衰退。”

    \n

    Of the 20 recession indicators compiled by Rosenberg, nine have been triggered.
    罗森伯格编制的20个衰退指标中,有9个已经触发。

    \n

    Some of the recession signals that have flashed include the Sahm Rule, the Leading Economic Indicator Index, and the inverted yield curve, among others.
    被触发的衰退指标包括萨姆规则、先行指数和倒挂的收益率曲线等。

    \n

    \"Currently, 45% of the recession indicators we tracked have been triggered. Going back to 1999, that's never happened without a recession occurring,\" Rosenberg said.
    罗森伯格说:“目前,我们追踪的经济衰退指标中有45%已经触发。自1999年以来,从来没有出现这种情况而不发生衰退的。”

    \n

    The list of signals flashing has steadily risen since 2022, when only 10% were triggered. That rose to about 25% in 2023 and the first half of 2024.
    自2022年以来,预示美国经济陷入衰退的信号数量一直都在稳步上升,当时只有10%被触发。2023年和2024年上半年,这一比例上升到了25%左右。

    \n

    But since then, the recession warnings have been growing.
    而自那以后,关于美国经济衰退的警告越来越多。

    \n

    \"Sometimes more is more, and this is a case in point. Looking at recession thresholds across different sectors of the economy makes it clear that something has been changing since mid-2024 — the long-anticipated slowdown may finally be arriving,\" Rosenberg said.
    罗森伯格说:“有时更多就是更多,这就是一个很好的例证。观察不同经济部门的衰退阈值可以清楚地看出,自2024年中期以来,一些事情一直在发生变化——经济衰退可能真的要来了。”

    \n

    编辑:董静
    审校:陈丹妮 万月英

    \n

     

    \n\n
    ", + "content": "
    \n\n \n\n \n \n\n
    \n \n
    \n
    \n

    美国商业内幕网8月30日报道称,美国知名经济学家、罗森伯格研究公司总裁大卫·罗森伯格表示,近期一系列信号提示,美国经济陷入衰退的可能性正在上升。文章摘编如下:

    \n

    The probability of a recession hitting the US economy is rising after a flurry of signals have flashed in recent weeks and months.
    在近期(最近几周和数月内)一系列信号亮起后,美国经济陷入衰退的可能性正在上升。

    \n

    That's according to economist David Rosenberg of Rosenberg Research, who compiled a list of recessionary indicators in a note on Friday.
    美国罗森伯格研究公司总裁大卫·罗森伯格在8月30日发布的一份报告中汇编了一份衰退指标清单。

    \n

    \"What is the best recession indicator? That's a hard question to answer — why not dodge it completely and just look at all of them?\" Rosenberg asked.
    他提出:“最准确的衰退指标是什么?这是一个很难回答的问题——为什么不避开这个问题,看看所有这些指标呢?”

    \n

    According to Rosenberg, some worrying signals have historically only flashed on the precipice of an economic downturn.
    罗森伯格表示,在历史上,一些令人担忧的信号只有在经济即将陷入衰退时才会出现。

    \n

    \"The 'indicator of indicators' indicates recession,\" Rosenberg said.
    罗森伯格称:“‘指标的指标’预示着经济衰退。”

    \n

    Of the 20 recession indicators compiled by Rosenberg, nine have been triggered.
    罗森伯格编制的20个衰退指标中,有9个已经触发。

    \n

    Some of the recession signals that have flashed include the Sahm Rule, the Leading Economic Indicator Index, and the inverted yield curve, among others.
    被触发的衰退指标包括萨姆规则、先行指数和倒挂的收益率曲线等。

    \n

    \"Currently, 45% of the recession indicators we tracked have been triggered. Going back to 1999, that's never happened without a recession occurring,\" Rosenberg said.
    罗森伯格说:“目前,我们追踪的经济衰退指标中有45%已经触发。自1999年以来,从来没有出现这种情况而不发生衰退的。”

    \n

    The list of signals flashing has steadily risen since 2022, when only 10% were triggered. That rose to about 25% in 2023 and the first half of 2024.
    自2022年以来,预示美国经济陷入衰退的信号数量一直都在稳步上升,当时只有10%被触发。2023年和2024年上半年,这一比例上升到了25%左右。

    \n

    But since then, the recession warnings have been growing.
    而自那以后,关于美国经济衰退的警告越来越多。

    \n

    \"Sometimes more is more, and this is a case in point. Looking at recession thresholds across different sectors of the economy makes it clear that something has been changing since mid-2024 — the long-anticipated slowdown may finally be arriving,\" Rosenberg said.
    罗森伯格说:“有时更多就是更多,这就是一个很好的例证。观察不同经济部门的衰退阈值可以清楚地看出,自2024年中期以来,一些事情一直在发生变化——经济衰退可能真的要来了。”

    \n

    编辑:董静
    审校:陈丹妮 万月英

    \n

     

    \n\n
    ", + "category": "", + "link": "https://language.chinadaily.com.cn/a/202409/02/WS66d582b8a3108f29c1fc9a70.html", + "creator": "", + "pubDate": "", + "enclosure": "", + "enclosureType": "", + "image": "", + "id": "", + "language": "", + "folder": "双语", + "feed": "ChinaDaily", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "99b651d543cad1d20b6bf4c9c9210913", + "highlights": [] + }, { "title": "独腿博士研发智能假肢,让更多残疾人穿得起Wear robotic knees", "description": "幼时截肢的他,最终发明、制造出一种独特而“有思想”的智能假肢!", @@ -3439,6 +4121,195 @@ "hash": "7371f6dbcab15edaf9642e52b8d5ac96", "highlights": [] }, + { + "title": "美国国家队名单出炉", + "description": "5月26日当地时间,美国国家队选拔赛老年团体赛2025年6月1日,美国桥牌协会(USBF)公布了参加2025年世界桥牌团体锦标赛的8支美国队参赛名单。由于美国选拔赛允许一名选手报名多项比赛,因此在选拔赛全部结束后,各个美国国家队进行了调整。", + "content": "
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n \n \n 新朋友\n \n \n \n \n \n
    \n
    \n

    \n \n \n 点击\n \n \n \n 标兵桥牌\n \n \n \n

    \n

    \n \n \n 关注微信\n \n \n

    \n
    \n
    \n \n \n 老朋友\n \n \n
    \n
    \n

    \n \n \n 点击\n \n \n \n 右上角\n \n \n \n

    \n

    \n \n \n 分享本文\n \n \n

    \n
    \n
    \n \n \n \n
    \n
    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n 2025年6月1日,美国桥牌协会(USBF)公布了参加2025年世界桥牌团体锦标赛的8支美国队参赛名单。\n \n

    \n

    \n \n 美国国家队选拔赛从4月24日开始,先后经历了公开团体、女子团体、混合团体、老年团体四个项目的选拔赛,产生了每个项目2支,共8支美国队。由于美国选拔赛允许一名选手报名多项比赛,因此在所选拔出来的原始团队中有需要更换队员和增加队员的。在选拔赛全部结束后,进行了调整。\n \n

    \n

    \n \n 根据美国国家队选拔规则,每名队员可以报名参加多个项目,但必须提前告知所参加项目的优先顺序,比如某位队员同时报名公开团体和混合团体项目,他需要选择哪一个项目是第一顺位,如果选择混合团体是第一顺位,则在公开团体入选后,仍然可以参加混合团体的选拔,如果混合团体选中,则必须代表混合团体放弃公开团体,如果混合团体没有选中,则可以代表公开团体。如果选择公开团体是第一顺位,在公开团体选中后,就不需要参加混合团体的选拔了。\n \n

    \n

    \n \n 这种现象,在公开团体选拔结束后,发生在了美国2队罗森塔尔队中,他们队中的大部分牌手都同时报名了公开团体和混合团体,优先顺序是公开团体,在入选公开团体美国2队后,该队队员都不再参加混合团体的选拔了。\n \n

    \n
    \n

    \n \n 下面是美国国家队的全部名单:\n \n

    \n

    \n \n Bermuda Bowl (Open teams):\n \n

    \n

    \n \n \n USA1\n \n \n
    \n \n Adam Kaplan - Finn Kolesnik\n \n
    \n \n Adam Grossack - Eddie Wold\n \n
    \n \n Kevin Bathurst* - John Hurd\n \n
    \n \n NPC:Bob Morris\n \n

    \n

    \n \n \n 由于美国1队是4人队入选,其中Bathurst/Hurd是他们增加的队员。\n \n \n

    \n

    \n \n \n USA2\n \n \n
    \n \n Andrew Rosenthal - Aaron Silverstein\n \n
    \n \n Eldad Ginossar - Chris Wilenken\n \n
    \n \n Zach Grossack - Michael Rosenberg\n \n
    \n \n NPC:Rob Barrington\n \n

    \n

    \n \n Venice Cup (Women's Teams):\n \n

    \n

    \n \n \n USA1\n \n \n
    \n \n Lynn Baker - Olivia Schireson\n \n
    \n \n Amber Lin - Debbie Rosenberg\n \n
    \n \n Emma Kolesnik* - JoAnna Stansby*\n \n
    \n \n NPC:Joe Stokes\n \n

    \n

    \n \n \n 威尼斯杯美国1队入选的队员还有瑞典Rimstedt姐妹,但她们的第一优先权是混合团体,同时又入选了混合团体美国2队,所以威尼斯杯美国1队增加了Kolesnik/Stansby一对。\n \n \n

    \n

    \n \n \n USA2\n \n \n
    \n \n Susan Zhang - Sylvia Shi\n \n
    \n \n Jill Meyers - Kerri Sanborn\n \n
    \n \n Janice Molson - Migry Campanile*\n \n
    \n \n NPC:Josh Donn\n \n

    \n

    \n \n \n 同样原因,苏珊·张的团队中Jenny Wolpert同时入选了混合团体美国1队,因此苏珊·张队增加了Campanile作为新队员。\n \n \n

    \n

    \n \n Wuhan Cup (Mixed Teams)\n \n

    \n

    \n \n \n USA1\n \n \n
    \n \n Stewart Rubenstein - Anam Tebha\n \n
    \n \n Giorgia Botta - John Kranyak\n \n
    \n \n Vince Demuy - Jenny Wolpert\n \n
    \n \n NPC: Harrison Luba\n \n

    \n

    \n \n \n USA2\n \n \n
    \n \n Joan Millens - Shan Huang\n \n
    \n \n Cecilia Dwyer Rimstedt - Kevin Dwyer Rimstedt\n \n
    \n \n Sandra Rimstedt - Joe Grue*\n \n
    \n \n NPC: Alex Hudson\n \n

    \n

    \n \n \n 混合团体美国2队是5人队参赛,增加了Joe Grue作为新队员。\n \n \n

    \n

    \n \n D'Orsi Senior Trophy (Senior Teams)\n \n

    \n

    \n \n \n USA1\n \n \n
    \n \n Jeff Wolfson - Steve Garner\n \n
    \n \n David Berkowitz - Larry Cohen\n \n
    \n \n Michael Kamil - Jeff Meckstroth\n \n
    \n \n NPC: Jan Martel\n \n

    \n

    \n \n \n USA2\n \n \n
    \n \n Hemant Lall - Reese Milner\n \n
    \n \n Bobby Levin - Eric Rodwell\n \n
    \n \n Zia Mahmood - Alan Sontag*\n \n
    \n \n NPC: Phil Clayton\n \n

    \n

    \n \n \n 老年团体美国2队更换了一位队员,Alan Sontag替换了Sam Lev。\n \n \n

    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n

    \n

    \n \n 相关链接:\n \n 美国老年队选拔赛结束\n \n \n

    \n

    \n \n 相关链接:\n \n 美国混团选拔赛结束\n \n \n

    \n

    \n \n 相关链接:\n \n 美国混团选拔赛第一轮淘汰赛结束\n \n \n

    \n

    \n \n 相关链接:\n \n 公开组美国2队与美国女队产生\n \n \n

    \n

    \n \n 相关链接:\n \n 美国国家队选拔赛战况及每日一牌——走过路过不要错过3NT\n \n \n

    \n

    \n \n 相关链接:\n \n 美国国家队选拔赛战况及每日一牌——及时转舵、躲开障眼法\n \n \n

    \n

    \n \n 相关链接:\n \n 美国国家队选拔赛战况及每日一牌——魏因斯坦的狡诈\n \n \n

    \n

    \n \n 相关链接:\n \n 小虎队直扑日德兰——卡普兰队获得美国1队资格\n \n \n

    \n

    \n \n 相关链接:\n \n 转载:通往努沙杜瓦——如风少年冲向巴厘岛——记Kranyak队获得美国1队资格\n \n \n

    \n

    \n \n 相关链接:\n \n 美国国家队选拔赛战况及每日一牌——老麦与小芬传统与现代的差别\n \n \n

    \n

    \n \n 相关链接:\n \n 美国国家队选拔赛战况及每日一牌——弗老板失去平衡\n \n \n

    \n

    \n \n \n 相关链接:\n \n 2025年美国国家队选拔赛战况及每日一牌——时机和控制\n \n \n \n

    \n

    \n \n \n 相关链接:\n \n 2025年美国国家队选拔赛战况及每日一牌——当老麦遇上老罗\n \n \n \n

    \n

    \n \n \n 相关链接:\n \n 2025年美国国家队选拔赛战况及每日一牌——小罗森博格的诈叫\n \n \n \n

    \n

    \n \n \n 相关链接:\n \n 2025年美国国家队选拔赛战况及每日一牌——致命的猜断\n \n \n \n

    \n

    \n \n \n 相关链接:\n \n 2025年美国国家队选拔赛战况及每日一牌——正常开叫还是阻击开叫\n \n \n \n

    \n

    \n \n \n 相关链接:\n \n 2025年美国国家队选拔赛\n \n \n \n

    \n

    \n \n \n 相关链接:\n \n 老罗重回世锦赛——美国老年队、女队选拔赛结束\n \n \n \n

    \n

    \n \n \n 相关链接:\n \n 美国混团选拔赛结束,苏珊·张队赢得资格\n \n \n \n

    \n

    \n \n 相关链接:\n \n 弗雷舍获得2024年美国队资格\n \n \n

    \n

    \n \n 相关链接:\n \n 2024年美国国家队选拔赛开赛\n \n \n

    \n

    \n \n \n 相关链接:\n \n 2023年美国国家队选拔赛争议中结束\n \n \n \n

    \n

    \n \n \n 相关链接:\n \n 2022年美国国家队选拔赛结束\n \n \n \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n 往期推荐\n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 尼克尔队率先拿到百慕大杯美国1队资格\n \n \n

    \n
    \n
    \n
    \n
    \n

    \n \n \n 平板电脑还是纸牌?职业牌手有分歧\n \n \n

    \n
    \n
    \n
    \n
    \n

    \n \n \n 通往武汉之路:美国国家队选拔赛\n \n \n

    \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n \n \n \n \n \n 点击\n \n \n \n \n 阅读原文\n \n \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 别忘了点个在看哦!转发那就太好了!\n \n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/GhzYkoR_DU1jwpJEaWo7yQ", + "creator": "标兵桥牌", + "pubDate": "Sun, 08 Jun 2025 00:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDR7X9icVUiczjyqmaA8vgG0PGfZLhCfDludjw1D8I8qm28RhEg2ibgWIey41EY5icTic2IQboZhr6qqf3A/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDR7X9icVUiczjyqmaA8vgG0PGfZLhCfDludjw1D8I8qm28RhEg2ibgWIey41EY5icTic2IQboZhr6qqf3A/0?wx_fmt=jpeg", + "id": "2247512604_1", + "language": "", + "folder": "桥牌", + "feed": "标兵1桥牌", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "904cfda1cd5a396451f15015355b3b1e", + "highlights": [] + } + ], + "folder": "双语", + "name": "21世纪", + "language": "zh", + "hash": "ee7294aadb9599f93460cd631ace5a90" + }, + { + "title": "中国日报: 双语新闻", + "subtitle": "", + "link": "http://language.chinadaily.com.cn/news_bilingual.html", + "image": null, + "description": "", + "items": [ + { + "title": "第十九届世界青年桥牌团体锦标赛即将开赛", + "description": "第十九届世界青年桥牌团体锦标赛(19th World Youth Team Championships)将于7月12-17日在意大利萨尔索马焦雷举办。共有来自世界各地的94支青年队参加U26,U21,U26女子,U16,U31五个组别的比赛", + "content": "
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n \n \n 新朋友\n \n \n \n \n \n
    \n
    \n

    \n \n \n 点击\n \n \n \n 标兵桥牌\n \n \n \n

    \n

    \n \n \n 关注微信\n \n \n

    \n
    \n
    \n \n \n 老朋友\n \n \n
    \n
    \n

    \n \n \n 点击\n \n \n \n 右上角\n \n \n \n

    \n

    \n \n \n 分享本文\n \n \n

    \n
    \n
    \n \n \n \n
    \n
    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n
    \n
    \n \n
    \n
    \n \n 世青赛即将开赛\n \n
    \n
    \n

    \n \n 第十九届世界青年桥牌团体锦标赛(19th World Youth Team Championships)将于7月12日-17日在意大利萨尔索马焦雷举办。共有来自世界各地的94支青年队参加U26,U21,U26女子,U16,U31五个组别的比赛。\n \n

    \n

    \n \n 这五个组别分别是:\n \n

    \n

    \n \n U26公开团体、冠军奖杯为帕蒂诺杯;\n \n

    \n

    \n \n U21公开团体、冠军奖杯为达米亚尼杯;\n \n

    \n

    \n \n U26女子团体、冠军奖杯为罗纳杯;\n \n

    \n

    \n \n U16公开团体、冠军奖杯为科驰大学杯;\n \n

    \n

    \n \n U31公开团体、冠军奖杯为豪威斯杯。\n \n

    \n

    \n \n 与双数年举办的世界青年跨国桥牌锦标赛(World Youth Transnational Bridge Championships)不同的是,WYTC每个世界桥联会员单位只能派一个队参加一个组别的比赛,因此这是代表国家和地区荣誉的世界青年比赛。与世界桥牌团体锦标赛一样的流程,参加世界青年桥牌团体锦标赛的参赛队必须通过世界桥联下属大区的选拔赛才能参加。\n \n

    \n

    \n \n 本次比赛中国将参加全部5个组别的比赛,5支中国代表队均是通过2025年初举办的全国青少年团体赛选拔产生,继续在2025年5月举办的亚太青年桥牌锦标赛拿到世青赛参赛资格的。\n \n

    \n

    \n \n 本次世青赛U26、 U21、U31组各有20支队,U26女子组有16队, U16有18队参加。比赛的办法是预赛进行单循环,前8名进入淘汰赛,经过1/4淘汰赛、半决赛、决赛产生最后的冠军。\n \n

    \n

    \n \n 中国队在世界青年桥牌团体锦标赛上最好成绩是2018年,在江苏吴江举办的第十七届世界青年桥牌团体锦标赛上,中国队获得了U26女子团体和U16公开团体两项冠军,这是中国青年队首次在世青赛上夺冠。\n \n

    \n

    \n \n 本次比赛世界桥联依然使用“平板叫牌、纸牌打牌”的比赛方式,这种方式对于青少年来说可能更为适合。\n \n

    \n

    \n \n \n
    \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n \n \n \n \n \n 点击\n \n \n \n \n 阅读原文\n \n \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 别忘了点个在看哦!转发那就太好了!\n \n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 第八届世界青年跨国桥牌锦标赛\n \n \n

    \n

    \n \n 相关链接:\n \n 第十八届世界青年桥牌团体锦标赛闭幕,欧洲瓜分五大冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 第十八届世界青年桥牌团体锦标赛即将开赛\n \n \n

    \n

    \n \n 相关链接:\n \n 世界青年桥牌大赛产生团体冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 世界青年桥牌大赛结束双人赛争夺\n \n \n \n
    \n
    \n

    \n

    \n \n \n 相关链接:世界青年桥牌大赛今日开赛\n \n \n

    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/t0X7c2vCHHEfbl4ZMBZf9g", + "creator": "标兵桥牌", + "pubDate": "Thu, 10 Jul 2025 00:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDTjauh0lK8VQ3veVyoLen2EPQQTSPaNn8uxPWCZlBFqMtsplTiakVeTL8qE7FgXWO685E5aicMvr7MQ/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDTjauh0lK8VQ3veVyoLen2EPQQTSPaNn8uxPWCZlBFqMtsplTiakVeTL8qE7FgXWO685E5aicMvr7MQ/0?wx_fmt=jpeg", + "id": "2247512710_1", + "language": "", + "folder": "桥牌", + "feed": "标兵1桥牌", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "f5d9ae560fa138188f746b83f0493403", + "highlights": [] + }, + { + "title": "欧公赛决赛的两副牌", + "description": "这副牌非常关键,这是第二节的最后一副,此前土耳其人一路飙升,打出一波凌厉的攻势,将比分从落后近30IMP变为领先15IMP,在上半场结束前的最后一副,齐老板将比分追回13IMP,总比分才落后2分,至关重要。", + "content": "
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n \n \n 新朋友\n \n \n \n \n \n
    \n
    \n

    \n \n \n 点击\n \n \n \n 标兵桥牌\n \n \n \n

    \n

    \n \n \n 关注微信\n \n \n

    \n
    \n
    \n \n \n 老朋友\n \n \n
    \n
    \n

    \n \n \n 点击\n \n \n \n 右上角\n \n \n \n

    \n

    \n \n \n 分享本文\n \n \n

    \n
    \n
    \n \n \n \n
    \n
    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n 下面两副牌,出现在决赛中,先看叫牌问题:\n \n

    \n
    \n
    \n \n
    \n
    \n \n 北家叫什么\n \n
    \n
    \n

    \n \n 你是北家,叫牌简单的,开叫1NT,同伴2♣,你2♦没有高花,同伴3♦自然,现在你准备怎么叫?\n \n

    \n

    \n \n 同伴显示♦的同时,还有4张高花,因为他开始用的是斯台曼,无论是4张哪一门高花,如果你叫3NT,都有可能有危险,但你又不能轻易越过3NT,所以你准备怎么办呢?\n \n

    \n

    \n \n 实战中,土耳其牌手选择叫3NT,全手牌如下:\n \n

    \n
    \n
    \n \n
    \n
    \n \n 土耳其北家3NT\n \n
    \n
    \n

    \n \n ♥是2-2,不幸的是东家天然做出♥首攻,3NT打不成。\n \n

    \n

    \n \n 你又能怎么办呢?\n \n

    \n

    \n \n 来看齐老板的搭档诺沃萨德茨基是怎么处理的:\n \n

    \n
    \n
    \n \n
    \n
    \n \n 齐老板的搭档叫3♠\n \n
    \n
    \n

    \n \n 诺沃萨德茨基面临的局面与土耳其人一样,但他没选择叫3NT,而是叫个3♠,3♠的好处是,在表示♦有配合的同时,如果同伴的高花是♥,3♠有抑制首攻的作用,而如果同伴的高花是♠,他巧妙地表示了♥的弱点,可谓一举三得。\n \n

    \n

    \n \n 齐老板知道同伴是3张♠,而且也知道同伴的♥不好,所以选择4♠。\n \n

    \n

    \n \n 由于将牌分布友好,4♠是个不错的定约,实战中也做成了。\n \n

    \n

    \n \n 这副牌非常关键,这是第二节的最后一副,此前土耳其人一路飙升,打出一波凌厉的攻势,将比分从落后近30IMP变为领先15IMP,在上半场结束前的最后一副,齐老板将比分追回13IMP,总比分才落后2分,至关重要。\n \n

    \n
    \n

    \n \n 再看首攻问题:\n \n

    \n
    \n
    \n \n
    \n
    \n \n 你是东家,首攻哪一张\n \n
    \n
    \n

    \n \n 这里的叫牌也是自然性的,2NT是邀请,同伴加倍3NT,你是东家,准备首攻哪一张?\n \n

    \n

    \n \n 现在要搞清楚对方是什么类型的牌,然后再研究同伴是为何加倍。\n \n

    \n

    \n \n 同伴有加倍但不能争叫的实力,第一感觉是♦长套,无法叫牌,所以是不是要首攻♦?\n \n

    \n

    \n \n 不过,一般来说,开叫1♦的人2NT是邀请,如果是18-19点的平均型,应该直接3NT,而如果是♦套,则可以叫3♦,所以2NT大概是(4-3)-5-1或者4-4-4-1、4-4-5-0等♣短的非均型,点力在15-16点之间。\n \n

    \n

    \n \n 而同伴是♣长套,有没有可能不争叫呢?从你的♣结构来看,也不是不可能,而同伴是♦长套的可能性不高,因为叫1NT的人有♦张数,开叫人也有♦张数,你还有3张,所以综合考虑,同伴是♣的可能性大。\n \n

    \n

    \n \n 实战中, 卡利塔首攻♦,全手牌如下:\n \n

    \n
    \n
    \n \n
    \n
    \n \n 卡利塔首攻♦,轻松超一\n \n
    \n
    \n

    \n \n ♦的位置不利,定约人轻松超一。虽然首攻♣也无法击败定约,但只能说是卡酷司机加倍太激进了,而他所希望的,则是应该攻♣。\n \n

    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 齐老板首夺欧洲公开桥牌锦标赛冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 齐老板与土耳其进入决赛\n \n \n

    \n

    \n \n 相关链接:\n \n 瑞波土法打进欧公赛半决赛\n \n \n

    \n

    \n \n 相关链接:\n \n 当舒柏林遇见舒柏林\n \n \n

    \n

    \n \n 相关链接:\n \n 芬奇压哨淘汰罗森塔尔\n \n \n

    \n

    \n \n 相关链接:\n \n 进攻性叫牌与防守性叫牌——波兹南的牌局\n \n \n

    \n

    \n \n 相关链接:\n \n 技惊四座的Deep Finesse打法\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开桥牌锦标赛第一阶段结束、弗姆队获得欧洲混团冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲混团赛场上一副牌的两次安全打法\n \n \n

    \n

    \n \n 相关链接:\n \n 第11届欧洲公开桥牌锦标赛混团预赛结束\n \n \n

    \n

    \n \n 相关链接:\n \n 第11届欧洲桥牌公开锦标赛\n \n \n

    \n

    \n \n 相关链接:\n \n 2023年欧洲公开桥牌锦标赛每日一牌——韦尔萨切的失误,还是对手的高明?\n \n \n

    \n

    \n \n 相关链接:\n \n 第10届欧洲桥牌公开锦标赛\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开桥牌锦标赛结束\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开赛结束、土耳其人获得公开双人赛冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲冬季运动会结束,奥卡队获得冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲冬季桥牌运动会开赛在即\n \n \n

    \n

    \n \n 相关链接:\n \n 劳伦特当选欧洲桥联主席、期待开启新时代\n \n \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 欧洲桥牌新纪元——欧洲桥牌团体锦标赛回顾\n \n \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n 往期推荐\n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 2022年欧洲桥牌团体锦标赛即将开赛\n \n \n

    \n
    \n
    \n
    \n
    \n

    \n \n \n 荷兰加冕欧洲之王\n \n \n

    \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n \n \n \n \n \n 点击\n \n \n \n \n 阅读原文\n \n \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 别忘了点个在看哦!转发那就太好了!\n \n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/y-1dlbaX_Ochsj5wyC8RzA", + "creator": "标兵桥牌", + "pubDate": "Tue, 08 Jul 2025 00:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDQoJpic0ibsIxaF7GAQVwVTT6eGyFJ2u6u8uRPPuRgRemxz9bYpyyQ9DDobFOOPU8ndsqr7ibKoF7EOQ/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDQoJpic0ibsIxaF7GAQVwVTT6eGyFJ2u6u8uRPPuRgRemxz9bYpyyQ9DDobFOOPU8ndsqr7ibKoF7EOQ/0?wx_fmt=jpeg", + "id": "2247512705_1", + "language": "", + "folder": "桥牌", + "feed": "标兵1桥牌", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "6c66baaf54362bf7e0ae12c2ceeae462", + "highlights": [] + }, + { + "title": "齐老板首夺欧洲公开桥牌锦标赛冠军", + "description": "经过8天鏖战,齐老板率领的瑞士队决赛战胜土耳其的Jeo Hidro队夺得欧洲公开桥牌锦标赛公开团体赛冠军,这是齐老板的大鳄队首次在该比赛中夺得冠军。来自波兰的Rafal Marks/Jakub Wojcieszek获得公开双人赛冠军。", + "content": "
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n \n \n 新朋友\n \n \n \n \n \n
    \n
    \n

    \n \n \n 点击\n \n \n \n 标兵桥牌\n \n \n \n

    \n

    \n \n \n 关注微信\n \n \n

    \n
    \n
    \n \n \n 老朋友\n \n \n
    \n
    \n

    \n \n \n 点击\n \n \n \n 右上角\n \n \n \n

    \n

    \n \n \n 分享本文\n \n \n

    \n
    \n
    \n \n \n \n
    \n
    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n 经过8天鏖战,齐老板率领的瑞士队决赛战胜土耳其的Jeo Hidro队夺得欧洲公开桥牌锦标赛公开团体赛冠军,这是齐老板的大鳄队首次在该比赛中夺得冠军。来自波兰的Rafal Marks/Jakub Wojcieszek获得公开双人赛冠军,女子双人赛冠军是Cathy/Sophia Baldysz,公开一副一比团体赛冠军由Daisy Chain队获得。\n \n

    \n
    \n
    \n \n
    \n
    \n \n 齐老板队击败土耳其团队获得冠军\n \n
    \n
    \n

    \n \n 虽然齐老板的团队已经蝉联两届百慕大杯的冠军,但在欧洲公开桥牌锦标赛这一长达22年的赛事中却从未夺冠,本次比赛齐老板队在预赛中勇夺第一,淘汰赛也算顺利,决赛迎战来自土耳其的团队,开局也不错,不过第二节被土耳其团队反超,打到决胜节中,快到比赛结束时遇到一副满贯,如果土耳其选手首攻正确,将会给巴司机带来一些麻烦,比赛还有悬念,但随着满贯做成,比赛也提前结束。\n \n

    \n

    \n \n 这一冠军不光是齐老板团队的第一个冠军,也成就了巴司机和舒柏林两位在一次大赛中同时拿到混团和公团两项冠军的伟业,他们在前后15天的比赛中都是从头打到尾,这一成绩也是迄今为止首次在综合锦标赛上连夺两大团体赛冠军的纪录。\n \n

    \n
    \n

    \n \n 下面两副牌,出现在决赛中,一副是叫牌问题,一副是首攻问题:\n \n

    \n
    \n
    \n \n
    \n
    \n \n 北家叫什么?\n \n
    \n
    \n

    \n \n 你是北家,叫牌简单的,开叫1NT,同伴2♣,你2♦没有高花,同伴3♦自然,现在你准备怎么叫?\n \n

    \n
    \n
    \n \n
    \n
    \n \n 你是东家,首攻哪一张\n \n
    \n
    \n

    \n \n 这里的叫牌也是自然性的,2NT是邀请,同伴加倍3NT,你是东家,准备首攻哪一张?\n \n

    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 齐老板与土耳其进入决赛\n \n \n

    \n

    \n \n 相关链接:\n \n 瑞波土法打进欧公赛半决赛\n \n \n

    \n

    \n \n 相关链接:\n \n 当舒柏林遇见舒柏林\n \n \n

    \n

    \n \n 相关链接:\n \n 芬奇压哨淘汰罗森塔尔\n \n \n

    \n

    \n \n 相关链接:\n \n 进攻性叫牌与防守性叫牌——波兹南的牌局\n \n \n

    \n

    \n \n 相关链接:\n \n 技惊四座的Deep Finesse打法\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开桥牌锦标赛第一阶段结束、弗姆队获得欧洲混团冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲混团赛场上一副牌的两次安全打法\n \n \n

    \n

    \n \n 相关链接:\n \n 第11届欧洲公开桥牌锦标赛混团预赛结束\n \n \n

    \n

    \n \n 相关链接:\n \n 第11届欧洲桥牌公开锦标赛\n \n \n

    \n

    \n \n 相关链接:\n \n 2023年欧洲公开桥牌锦标赛每日一牌——韦尔萨切的失误,还是对手的高明?\n \n \n

    \n

    \n \n 相关链接:\n \n 第10届欧洲桥牌公开锦标赛\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开桥牌锦标赛结束\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开赛结束、土耳其人获得公开双人赛冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲冬季运动会结束,奥卡队获得冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲冬季桥牌运动会开赛在即\n \n \n

    \n

    \n \n 相关链接:\n \n 劳伦特当选欧洲桥联主席、期待开启新时代\n \n \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 欧洲桥牌新纪元——欧洲桥牌团体锦标赛回顾\n \n \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n 往期推荐\n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 2022年欧洲桥牌团体锦标赛即将开赛\n \n \n

    \n
    \n
    \n
    \n
    \n

    \n \n \n 荷兰加冕欧洲之王\n \n \n

    \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n \n \n \n \n \n 点击\n \n \n \n \n 阅读原文\n \n \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 别忘了点个在看哦!转发那就太好了!\n \n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/1asxdMpKOlbBDgCeiMwCwQ", + "creator": "标兵桥牌", + "pubDate": "Mon, 07 Jul 2025 00:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDQoJpic0ibsIxaF7GAQVwVTT6eGyFJ2u6u8uRPPuRgRemxz9bYpyyQ9DDobFOOPU8ndsqr7ibKoF7EOQ/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDQoJpic0ibsIxaF7GAQVwVTT6eGyFJ2u6u8uRPPuRgRemxz9bYpyyQ9DDobFOOPU8ndsqr7ibKoF7EOQ/0?wx_fmt=jpeg", + "id": "2247512692_1", + "language": "", + "folder": "桥牌", + "feed": "标兵1桥牌", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "56af1ade66565c9c643b6bcf9bd96f7d", + "highlights": [] + }, + { + "title": "齐老板与土耳其进入决赛", + "description": "欧洲公开桥牌锦标赛公团结束1/4淘汰赛,齐老板的瑞士队、波兰的Naukabrydzapl队、土耳其的JEO Hidro队、法国绿队进入半决赛。对于一个有123个队参加的跨国比赛来说,最终的四强恰好均由同一国家团队组成,是很少见的。", + "content": "
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n \n \n 新朋友\n \n \n \n \n \n
    \n
    \n

    \n \n \n 点击\n \n \n \n 标兵桥牌\n \n \n \n

    \n

    \n \n \n 关注微信\n \n \n

    \n
    \n
    \n \n \n 老朋友\n \n \n
    \n
    \n

    \n \n \n 点击\n \n \n \n 右上角\n \n \n \n

    \n

    \n \n \n 分享本文\n \n \n

    \n
    \n
    \n \n \n \n
    \n
    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n 欧洲公开桥牌锦标赛公团半决赛结束,齐老板率领的瑞士军团战胜波兰梅花教父贾瑟姆的家庭桥牌队,奥斯曼土耳其部队击败法国人,两队相会决赛。\n \n

    \n

    \n \n 齐老板与波兰队的半决赛在第一节就奠定了胜局,齐老板与巴舒上阵打出了71:14IMP的比分,队中主力KK组合还没出场就领先超过50IMP,后来的比赛就进入到了松弛时间,最后3节波兰团队还赢了5分。\n \n

    \n

    \n \n 与齐老板轻松获胜不同,土法大战非常激烈,双方比分一直非常接近,直到最后一节的第25副牌,也就是全场比赛的第倒数第4副,土耳其人面临首攻局面:\n \n

    \n
    \n
    \n \n
    \n
    \n \n 6♦定约首攻哪张\n \n
    \n
    \n

    \n \n 假如你是东家,右手方开叫1♦,你争叫2♠,左手方3NT,右手方拉出4♦,然后他们叫到6♦,你准备首攻什么?\n \n

    \n
    \n
    \n \n
    \n
    \n \n 东家首攻♣,击败满贯\n \n
    \n
    \n

    \n \n 实战中土耳其东家基兹洛克首攻♣9,一击命中,击败对方的满贯。实际上,只要不首攻♠,满贯都打不成。\n \n

    \n

    \n \n 这副牌也成为半决赛决定胜负的关键,如果法国人做成满贯,最后获胜的就是法国人。\n \n

    \n

    \n \n 在另外一场半决赛中,齐老板队和贾瑟姆队都叫到满贯,但首攻都是♥,定约同样没有做成。\n \n

    \n
    \n
    \n \n
    \n
    \n \n 卡利塔首攻♥\n \n
    \n
    \n
    \n
    \n \n
    \n
    \n \n 波兰人首攻♥\n \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 瑞波土法打进欧公赛半决赛\n \n \n

    \n

    \n \n 相关链接:\n \n 当舒柏林遇见舒柏林\n \n \n

    \n

    \n \n 相关链接:\n \n 芬奇压哨淘汰罗森塔尔\n \n \n

    \n

    \n \n 相关链接:\n \n 进攻性叫牌与防守性叫牌——波兹南的牌局\n \n \n

    \n

    \n \n 相关链接:\n \n 技惊四座的Deep Finesse打法\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开桥牌锦标赛第一阶段结束、弗姆队获得欧洲混团冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲混团赛场上一副牌的两次安全打法\n \n \n

    \n

    \n \n 相关链接:\n \n 第11届欧洲公开桥牌锦标赛混团预赛结束\n \n \n

    \n

    \n \n 相关链接:\n \n 第11届欧洲桥牌公开锦标赛\n \n \n

    \n

    \n \n 相关链接:\n \n 2023年欧洲公开桥牌锦标赛每日一牌——韦尔萨切的失误,还是对手的高明?\n \n \n

    \n

    \n \n 相关链接:\n \n 第10届欧洲桥牌公开锦标赛\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开桥牌锦标赛结束\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开赛结束、土耳其人获得公开双人赛冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲冬季运动会结束,奥卡队获得冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲冬季桥牌运动会开赛在即\n \n \n

    \n

    \n \n 相关链接:\n \n 劳伦特当选欧洲桥联主席、期待开启新时代\n \n \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 欧洲桥牌新纪元——欧洲桥牌团体锦标赛回顾\n \n \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n 往期推荐\n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 2022年欧洲桥牌团体锦标赛即将开赛\n \n \n

    \n
    \n
    \n
    \n
    \n

    \n \n \n 荷兰加冕欧洲之王\n \n \n

    \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n \n \n \n \n \n 点击\n \n \n \n \n 阅读原文\n \n \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 别忘了点个在看哦!转发那就太好了!\n \n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/1eNZvX1Cy9F56u7fZpeU8A", + "creator": "标兵桥牌", + "pubDate": "Sun, 06 Jul 2025 00:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDQoJpic0ibsIxaF7GAQVwVTT6eGyFJ2u6u8uRPPuRgRemxz9bYpyyQ9DDobFOOPU8ndsqr7ibKoF7EOQ/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDQoJpic0ibsIxaF7GAQVwVTT6eGyFJ2u6u8uRPPuRgRemxz9bYpyyQ9DDobFOOPU8ndsqr7ibKoF7EOQ/0?wx_fmt=jpeg", + "id": "2247512687_1", + "language": "", + "folder": "桥牌", + "feed": "标兵1桥牌", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "f8a177f20f224372b56418786352912a", + "highlights": [] + }, + { + "title": "瑞波土法打进欧公赛半决赛", + "description": "欧洲公开桥牌锦标赛公团结束1/4淘汰赛,齐老板的瑞士队、波兰的Naukabrydzapl队、土耳其的JEO Hidro队、法国绿队进入半决赛。对于一个有123个队参加的跨国比赛来说,最终的四强恰好均由同一国家团队组成,是很少见的。", + "content": "
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n \n \n 新朋友\n \n \n \n \n \n
    \n
    \n

    \n \n \n 点击\n \n \n \n 标兵桥牌\n \n \n \n

    \n

    \n \n \n 关注微信\n \n \n

    \n
    \n
    \n \n \n 老朋友\n \n \n
    \n
    \n

    \n \n \n 点击\n \n \n \n 右上角\n \n \n \n

    \n

    \n \n \n 分享本文\n \n \n

    \n
    \n
    \n \n \n \n
    \n
    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n 欧洲公开桥牌锦标赛公团结束1/4淘汰赛,齐老板的瑞士队、波兰的Naukabrydzapl队、土耳其的JEO Hidro队、法国绿队进入半决赛。对于一个有123个队参加的跨国比赛来说,最终的四强恰好均由同一国家团队组成,是很少见的。\n \n

    \n

    \n \n 在女子团体决赛中,美国/波兰联军Parker队战胜意大利的Tersch队获得冠军。\n \n

    \n
    \n

    \n \n 齐老板队1/4淘汰赛的对手是保加利亚队,齐老板从开局就领先,始终将主动权握在手中,在比赛的最后一节,出现了这样一副牌:\n \n

    \n
    \n
    \n \n
    \n
    \n \n 3♠后东家如何叫牌?\n \n
    \n
    \n

    \n \n 北家开叫2♠,你是东家,选择加倍,南家加叫3♠到你这里,选择什么叫品?\n \n

    \n

    \n \n 可以选择的叫品大概有:\n \n

    \n

    \n \n 加倍\n \n

    \n

    \n \n 4♦\n \n

    \n

    \n \n 5♦\n \n

    \n

    \n \n 4♠/4NT等显示3套牌的叫品\n \n

    \n
    \n

    \n \n 我觉得4♦ 和5♦应该第一个被排除,因为这牌不是♦单套,而是还有♥和♣可以打,特别是♥定约不应该被忽视。\n \n

    \n

    \n \n 加倍的好处是可以照顾到♥,如果同伴选择♣,再回到♦上来,但问题是万一同伴没什么好叫的,就可能Pass,对方9张以上将牌,加上位置因素,防守3♠可能不是那么合算。\n \n

    \n

    \n \n 如果认为同伴有些实力并且在♠上没什么点力,那么这牌可以积极一些,考虑往满贯走,办法是4♠或者4NT,取决于你们的约定。\n \n

    \n

    \n \n 另一个问题,无论选择加倍还是4♠、4NT,同伴叫牌后,这牌是满足成局,还是往满贯走?\n \n

    \n

    \n \n 实战中共有3桌遇到了这一进程,他们的选择都不相同。\n \n

    \n
    \n
    \n \n
    \n
    \n \n 卡利塔选择继续加倍\n \n
    \n
    \n

    \n \n 东西方是齐老板队的KK组合,卡利塔第二次叫牌选择加倍,卡酷司机60%的点力都在♠上,他虽然有♥张数,但还是Pass,得到800分。\n \n

    \n
    \n
    \n \n
    \n
    \n \n 斯特法诺夫选择4NT\n \n
    \n
    \n

    \n \n 保加利亚的斯特法诺夫非常积极,他先叫4NT,同伴5♣,他叫5♦,显示还有♥,然后在同伴选择5♥后扣叫5♠表示大满贯兴趣,后面他们的叫牌发生了误会,西家5NT显然是Pickup-Slam,让同伴在♦和♥中选择满贯,但斯特法诺夫觉得5NT是有大满贯兴趣,直叫7♥。\n \n

    \n

    \n \n 我觉得即使斯特法诺夫不认为5NT是选满贯,也应该考虑到,同伴没有叫6♣,没有♣K,将牌最多4-4,还是应该谨慎,而西家的5NT叫得很好,6♦定约要更加安全一些。斯特法诺夫叫品前后矛盾,他的5♦是可以停下来的,但听到同伴♥有配合就想打大满贯,这符合逻辑吗?根据统计,在阻击叫后,进攻性叫牌要低一格通常是比较合适的策略。这副牌也是,东西方停在小满贯上就能赢11-12分,而不是现在输14分。\n \n

    \n
    \n
    \n \n
    \n
    \n \n 土耳其人叫4♠显示有♥的满贯兴趣\n \n
    \n
    \n

    \n \n 这一桌东西方是土耳其选手,东家第二次叫牌选择的是4♠,他们的约定是有♥,西家选5♥,东家直接加叫6♥。这进程也可以再探讨,此时的5NT应该仍然是Pickup-Slam,防止西家♥没有J10的时候,打到错误的满贯。\n \n

    \n

    \n \n 如果不准备往满贯走,现在KK组合的800分就是不错的结果。\n \n

    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 当舒柏林遇见舒柏林\n \n \n

    \n

    \n \n 相关链接:\n \n 芬奇压哨淘汰罗森塔尔\n \n \n

    \n

    \n \n 相关链接:\n \n 进攻性叫牌与防守性叫牌——波兹南的牌局\n \n \n

    \n

    \n \n 相关链接:\n \n 技惊四座的Deep Finesse打法\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开桥牌锦标赛第一阶段结束、弗姆队获得欧洲混团冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲混团赛场上一副牌的两次安全打法\n \n \n

    \n

    \n \n 相关链接:\n \n 第11届欧洲公开桥牌锦标赛混团预赛结束\n \n \n

    \n

    \n \n 相关链接:\n \n 第11届欧洲桥牌公开锦标赛\n \n \n

    \n

    \n \n 相关链接:\n \n 2023年欧洲公开桥牌锦标赛每日一牌——韦尔萨切的失误,还是对手的高明?\n \n \n

    \n

    \n \n 相关链接:\n \n 第10届欧洲桥牌公开锦标赛\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开桥牌锦标赛结束\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开赛结束、土耳其人获得公开双人赛冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲冬季运动会结束,奥卡队获得冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲冬季桥牌运动会开赛在即\n \n \n

    \n

    \n \n 相关链接:\n \n 劳伦特当选欧洲桥联主席、期待开启新时代\n \n \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 欧洲桥牌新纪元——欧洲桥牌团体锦标赛回顾\n \n \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n 往期推荐\n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 2022年欧洲桥牌团体锦标赛即将开赛\n \n \n

    \n
    \n
    \n
    \n
    \n

    \n \n \n 荷兰加冕欧洲之王\n \n \n

    \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n \n \n \n \n \n 点击\n \n \n \n \n 阅读原文\n \n \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 别忘了点个在看哦!转发那就太好了!\n \n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/eA-wD7vnZ1ZS4yp7pWCkVQ", + "creator": "标兵桥牌", + "pubDate": "Sat, 05 Jul 2025 00:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDQoJpic0ibsIxaF7GAQVwVTT6eGyFJ2u6u8uRPPuRgRemxz9bYpyyQ9DDobFOOPU8ndsqr7ibKoF7EOQ/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDQoJpic0ibsIxaF7GAQVwVTT6eGyFJ2u6u8uRPPuRgRemxz9bYpyyQ9DDobFOOPU8ndsqr7ibKoF7EOQ/0?wx_fmt=jpeg", + "id": "2247512681_1", + "language": "", + "folder": "桥牌", + "feed": "标兵1桥牌", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "c7547f58f9af78c31a0279f5bd363abd", + "highlights": [] + }, + { + "title": "当舒柏林遇见舒柏林", + "description": "荷兰小将是舒柏林巴司机的晚辈,各种技巧自然也学得滚瓜纯属,德莱昂在单方无局第三家,觉得开叫3♥没什么用,于是祭起了舒柏林的独门法宝,没想到舒柏林遇见舒柏林,马上被舒柏林识破,可惜的是舒柏林没能完成3NT,他还是失败了。", + "content": "
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n \n \n 新朋友\n \n \n \n \n \n
    \n
    \n

    \n \n \n 点击\n \n \n \n 标兵桥牌\n \n \n \n

    \n

    \n \n \n 关注微信\n \n \n

    \n
    \n
    \n \n \n 老朋友\n \n \n
    \n
    \n

    \n \n \n 点击\n \n \n \n 右上角\n \n \n \n

    \n

    \n \n \n 分享本文\n \n \n

    \n
    \n
    \n \n \n \n
    \n
    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n 欧洲公开桥牌锦标赛公团比赛7月2日产生8强,在介绍比赛进程之前,先看下面这副牌:\n \n

    \n
    \n
    \n \n
    \n
    \n \n 没轮到你,上家先开叫2NT\n \n
    \n
    \n

    \n \n 两家Pass,东家开叫2NT,20-21点,你是南家,如何考虑叫牌?提示一下,东家是瑞士荷兰世界冠军舒柏林。\n \n

    \n
    \n

    \n \n 回到比赛介绍。7月2日的16强战各有惊喜,其中最大的冷门是中国台北/新加坡华人队击败意大利国手组成的芬奇队。昨天我们的文章刚刚介绍过,芬奇队通过艰难的预赛打进32强,淘汰赛击败2号种子罗森塔尔队,正准备大展身手之际,遇到了中国台北/新加坡联军阿曼达队,第一节阿曼达取得优势开局,后面也没给对方机会,一共赢了39IMP,即使不算芬奇队客户所上的两节,阿曼达队与意大利国手对抗的后两节也获得了总分2IMP的优势。看起来现在亚太地区的桥牌水平正在大步提升,非常接近了世界级水平。\n \n

    \n

    \n \n 其他场次的比赛是齐老板战胜荷兰橙队,保加利亚战胜印度/瑞典联队,两个波兰队,美国的舒里队,法国绿队,土耳其JEO队一起晋级。\n \n

    \n

    \n \n 女子团体半决赛结束,来自意大利的Tersch队和美国/波兰联军Parker队进入决赛。\n \n

    \n

    \n \n 根据欧洲桥联的报道,未来5年,即2026-2030年的欧洲冬季跨国桥牌锦标赛将落户捷克布拉格,这是欧洲桥联首次将同一比赛连续在同一举办地举办。\n \n

    \n
    \n

    \n \n 下面回到刚才的牌局:\n \n

    \n
    \n
    \n \n
    \n
    \n \n 没轮到你,上家开叫2NT\n \n
    \n
    \n

    \n \n 南家是开叫2NT的牌,但东家在前面先开叫了,你会不会认为东家19点进行了升级,也就是你和东家有40点。不过由你首攻,你肯定能够击败2NT,你是否要行动呢?\n \n

    \n
    \n
    \n \n
    \n
    \n \n 南家舒柏林询问西家,2NT开叫是什么牌?\n \n
    \n
    \n

    \n \n 这是齐老板队与荷兰橙队的比赛,东家并不是舒柏林,南家才是舒柏林,他询问西家,开叫2NT的情况,西家斯普林克许泽对他说,\n \n “你开叫2NT是什么牌,他就是什么牌!”\n \n \n

    \n
    \n

    \n \n 于是舒柏林加倍,叫牌过程如下发展:\n \n

    \n
    \n
    \n \n
    \n
    \n \n 舒柏林加倍,东家跑3♥,舒柏林3NT\n \n
    \n
    \n

    \n \n 东家是荷兰小将盖伊门德斯德莱昂,这对小将曾经在2022年的罗森布卢姆杯上表现出色,打进了决赛。\n \n

    \n
    \n
    \n \n
    \n
    \n \n 巴司机微笑问德莱昂,”你学的不错啊“\n \n
    \n
    \n

    \n \n 舒柏林加倍他的2NT,德莱昂跑到3♥,舒柏林自己叫3NT,西家首攻♥6,明手的牌摊下来,舒柏林心说:\n \n “别跟我来这套,这是我玩剩下的!”\n \n \n

    \n

    \n \n 舒柏林忍让♥,第二墩西家告缺,果然东家是凭♥长套诈叫2NT的,现在舒柏林已有8墩,第9墩如何而来?\n \n

    \n

    \n \n 舒柏林吃进第三墩♥,西家垫2张♦,明手垫♠,舒柏林兑现4圈♦和1墩♣,东家只有2张♦,垫了2张♣。现在怎么打?\n \n

    \n
    \n
    \n \n
    \n
    \n \n 是飞♠还是不飞\n \n
    \n
    \n

    \n \n 已知东家有6张♥,跟出2张♦和3张♣,还有2张牌。如果东家有♠K,则拔♣看看他是否还有♣,如果没有了,就是2张♠,他不能垫♠,用♥投入东家即可,但如果东家跟出♣,他就只有1张♠,这张♠是K还是小,就需要猜断了,舒柏林决定打东家单张♠,于是他出小♠到A,准备送给西家:\n \n

    \n
    \n
    \n \n
    \n
    \n \n 舒柏林没打对\n \n
    \n
    \n

    \n \n 可惜东家有♠K,舒柏林的打法失败了。\n \n

    \n

    \n \n \n “难道你开叫2NT还有♠K吗?这一点也不正宗啊!”\n \n 舒柏林瞪着眼睛对他的小兄弟说。\n \n

    \n

    \n \n \n 用2NT开叫做诈叫,是舒柏林的标志性叫牌之一\n \n ,他们这一叫牌,是写进约定卡的。在2024年雷辛格尔杯的决赛中,舒柏林就在我们桌上表演了一次,可惜被睿智的张永革识破,请看:\n \n

    \n

    \n \n \n \n 博取最佳——雷辛格尔杯之路——齐老板与尼老板(三)\n \n \n \n 之识破舒柏林的诡计\n \n \n

    \n

    \n \n 荷兰小将是舒柏林巴司机的晚辈,各种技巧自然也学得滚瓜纯属,德莱昂在单方无局第三家,觉得开叫3♥没什么用,于是祭起了舒柏林的独门法宝,没想到\n \n \n \n 舒柏林遇见舒柏林\n \n \n \n ,马上被舒柏林识破,可惜的是舒柏林没能完成3NT,他还是失败了。\n \n

    \n

    \n \n 从打法上看,舒柏林忽视了德莱昂♣的垫牌顺序,他表示的是3张信号,所以舒柏林如果相信他的小兄弟,应该拔♣,东家没有♣,他只能垫成单张♠K,舒柏林再考虑拔♠A。但他不能这么打的原因,是因为多忍让了一轮♥,送♠就会有1个♠、2个♥、2个♣输墩;如果他第二墩♥就停住,最后就可以安全地拔♠A,最多输1墩♠、1墩♥、2墩♣。\n \n

    \n

    \n \n 舒柏林还是没把握德莱昂的2NT到底有多少张♥。\n \n

    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 芬奇压哨淘汰罗森塔尔\n \n \n

    \n

    \n \n 相关链接:\n \n 进攻性叫牌与防守性叫牌——波兹南的牌局\n \n \n

    \n

    \n \n 相关链接:\n \n 技惊四座的Deep Finesse打法\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开桥牌锦标赛第一阶段结束、弗姆队获得欧洲混团冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲混团赛场上一副牌的两次安全打法\n \n \n

    \n

    \n \n 相关链接:\n \n 第11届欧洲公开桥牌锦标赛混团预赛结束\n \n \n

    \n

    \n \n 相关链接:\n \n 第11届欧洲桥牌公开锦标赛\n \n \n

    \n

    \n \n 相关链接:\n \n 2023年欧洲公开桥牌锦标赛每日一牌——韦尔萨切的失误,还是对手的高明?\n \n \n

    \n

    \n \n 相关链接:\n \n 第10届欧洲桥牌公开锦标赛\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开桥牌锦标赛结束\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开赛结束、土耳其人获得公开双人赛冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲冬季运动会结束,奥卡队获得冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲冬季桥牌运动会开赛在即\n \n \n

    \n

    \n \n 相关链接:\n \n 劳伦特当选欧洲桥联主席、期待开启新时代\n \n \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 欧洲桥牌新纪元——欧洲桥牌团体锦标赛回顾\n \n \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n 往期推荐\n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 2022年欧洲桥牌团体锦标赛即将开赛\n \n \n

    \n
    \n
    \n
    \n
    \n

    \n \n \n 荷兰加冕欧洲之王\n \n \n

    \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n \n \n \n \n \n 点击\n \n \n \n \n 阅读原文\n \n \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 别忘了点个在看哦!转发那就太好了!\n \n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/GYUyZ7Na50SVMA9upv-n5A", + "creator": "标兵桥牌", + "pubDate": "Fri, 04 Jul 2025 00:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDQoJpic0ibsIxaF7GAQVwVTT6eGyFJ2u6u8uRPPuRgRemxz9bYpyyQ9DDobFOOPU8ndsqr7ibKoF7EOQ/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDQoJpic0ibsIxaF7GAQVwVTT6eGyFJ2u6u8uRPPuRgRemxz9bYpyyQ9DDobFOOPU8ndsqr7ibKoF7EOQ/0?wx_fmt=jpeg", + "id": "2247512674_1", + "language": "", + "folder": "桥牌", + "feed": "标兵1桥牌", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "5e7f7ba8baf49614c379c8f6b16dd620", + "highlights": [] + }, + { + "title": "芬奇压哨淘汰罗森塔尔", + "description": "2025年欧洲桥牌公开锦标赛公团32进16的比赛异常激烈,来自意大利的芬奇队在最后一副牌逆转淘汰赛会2号种子罗森塔尔队。", + "content": "
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n \n \n 新朋友\n \n \n \n \n \n
    \n
    \n

    \n \n \n 点击\n \n \n \n 标兵桥牌\n \n \n \n

    \n

    \n \n \n 关注微信\n \n \n

    \n
    \n
    \n \n \n 老朋友\n \n \n
    \n
    \n

    \n \n \n 点击\n \n \n \n 右上角\n \n \n \n

    \n

    \n \n \n 分享本文\n \n \n

    \n
    \n
    \n \n \n \n
    \n
    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n 2025年欧洲桥牌公开锦标赛公团32进16的比赛异常激烈,来自意大利的芬奇队在最后一副牌逆转淘汰赛会2号种子罗森塔尔队。\n \n

    \n

    \n \n 芬奇队的主力队员有4名意大利国手,韦尔萨切/塞门塔、多纳蒂/佩卡里奥。这是一支实力强劲的团队,不过在预赛中他们成绩并不好,最终是得益于欧洲桥联新的赛制才得以复活。\n \n

    \n

    \n \n 根据本届欧公赛的赛制规定,如果公开团体赛报名少于110个队,则按原计划,即预赛3天,前32进入淘汰赛;如果公开团体赛报名超过110个队,则使用一种新的办法,预赛前两天结束后,前60名的队直接进入淘汰赛,61名以后的队则继续进行第三天的预赛,第三天同时进行前60名的淘汰赛和61名以后的预赛,第三天的预赛前2名进入淘汰赛,与第一场淘汰赛的30个胜者构成32队进行完整的淘汰赛。\n \n

    \n

    \n \n 拥有4名意大利国手的芬奇队预赛表现实在不怎么样,他们最低名次是第102名,几经挣扎,最高曾到过第33名,但马上被打倒60名开外,2天结束后第62名,只能继续第三天的预赛。第三天的预赛还有63个队,打到前两名谈何容易,芬奇队在最后一轮的倒数第三副牌才成功上岸,这副牌就是我昨天发出的第26副,他们也得益于叫牌的精准,请看韦尔萨切和塞门塔的叫牌:\n \n

    \n
    \n
    \n \n
    \n
    \n \n 东西方只有10点,但4♠却是铁牌\n \n
    \n
    \n

    \n \n 西家是韦尔萨切,同伴Pass后,他5个点既不阻击也不Pass,而是在有局方开叫1♠,北家加倍,东家塞门塔2♠是弱加叫,南家应叫性加倍,韦尔萨切4♠,南北方加倍正常,但却无法击败联手只有10个点的4♠定约。\n \n

    \n

    \n \n 另外一桌多纳蒂和佩卡里奥叫到并完成5♣加倍定约,两牌相加,芬奇队赢17IMP,他们马上跳到了第一名的位置,最终艰难出线进入32强。\n \n

    \n

    \n \n 佩服韦尔萨切的勇气和胆识,这副牌的处理在淘汰赛60桌中,大部分都是开叫3♠或者4♠,只有两桌西家开叫1♠,其中一桌叫牌过程和结果,与意大利人这里完全一样。\n \n

    \n

    \n \n 芬奇队进入淘汰赛后,如梦初醒,立刻变成一头雄狮,遇到他们的2号种子罗森塔尔可谓不幸,两队大战4节,打完55副不分上下,罗森塔尔领先1分,下面是最后一副:\n \n

    \n
    \n
    \n \n
    \n
    \n \n 东西方2♥正常\n \n
    \n
    \n

    \n \n 西家佩卡里奥开叫1♥再叫2♥,最终做成定约,怎么看也不像有输赢的,2号种子似乎过关成功。\n \n

    \n

    \n \n 但另一室则出现了变数:\n \n

    \n
    \n
    \n \n
    \n
    \n \n 东家叫3♣\n \n
    \n
    \n

    \n \n 东西方是临时搭档的丹麦人舒尔茨和美国牌手韦兰肯,他们使用法兰纳雷约定叫,即开叫2♦表示5张以上♥+4张♠,开叫实力,所以韦兰肯6-4高花开叫2♥,舒尔茨的3♣不逼叫,但他忽视了同伴6-4的可能,3♣定约根本无法完成,最终以宕三收场,芬奇队依靠此牌死里逃生再过一关。\n \n

    \n

    \n \n 看来舒尔茨要好好学习连氏自然,把法兰纳雷约定叫运用得更流畅。\n \n

    \n

    \n \n 芬奇队下一轮的对手是由一位中国台北女牌手、三位新加坡牌手组成的阿曼达队,他们能够延续神奇之旅,还是亚太牌手更进一步,本文发出之际即将见分晓。\n \n

    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 进攻性叫牌与防守性叫牌——波兹南的牌局\n \n \n

    \n

    \n \n 相关链接:\n \n 技惊四座的Deep Finesse打法\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开桥牌锦标赛第一阶段结束、弗姆队获得欧洲混团冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲混团赛场上一副牌的两次安全打法\n \n \n

    \n

    \n \n 相关链接:\n \n 第11届欧洲公开桥牌锦标赛混团预赛结束\n \n \n

    \n

    \n \n 相关链接:\n \n 第11届欧洲桥牌公开锦标赛\n \n \n

    \n

    \n \n 相关链接:\n \n 2023年欧洲公开桥牌锦标赛每日一牌——韦尔萨切的失误,还是对手的高明?\n \n \n

    \n

    \n \n 相关链接:\n \n 第10届欧洲桥牌公开锦标赛\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开桥牌锦标赛结束\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开赛结束、土耳其人获得公开双人赛冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲冬季运动会结束,奥卡队获得冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲冬季桥牌运动会开赛在即\n \n \n

    \n

    \n \n 相关链接:\n \n 劳伦特当选欧洲桥联主席、期待开启新时代\n \n \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 欧洲桥牌新纪元——欧洲桥牌团体锦标赛回顾\n \n \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n 往期推荐\n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 2022年欧洲桥牌团体锦标赛即将开赛\n \n \n

    \n
    \n
    \n
    \n
    \n

    \n \n \n 荷兰加冕欧洲之王\n \n \n

    \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n \n \n \n \n \n 点击\n \n \n \n \n 阅读原文\n \n \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 别忘了点个在看哦!转发那就太好了!\n \n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/HEupnXfrM99UVCW4V0pGWg", + "creator": "标兵桥牌", + "pubDate": "Thu, 03 Jul 2025 00:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDQoJpic0ibsIxaF7GAQVwVTT6eGyFJ2u6u8uRPPuRgRemxz9bYpyyQ9DDobFOOPU8ndsqr7ibKoF7EOQ/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDQoJpic0ibsIxaF7GAQVwVTT6eGyFJ2u6u8uRPPuRgRemxz9bYpyyQ9DDobFOOPU8ndsqr7ibKoF7EOQ/0?wx_fmt=jpeg", + "id": "2247512665_1", + "language": "", + "folder": "桥牌", + "feed": "标兵1桥牌", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "d025249010f2c3c97cb73f1c5df5a217", + "highlights": [] + }, { "title": "进攻性叫牌与防守性叫牌——波兹南的牌局", "description": "桥牌其实也一样,只不过桥牌的“进攻”和“防守”,有多重的意义,除了简单的在桥牌打法中表现为做庄与防守外,还有在叫牌中也分为自己抢过来打的“进攻性叫牌”和放给对方打或者叫得较低的“防守性叫牌”。", @@ -3461,6 +4332,160 @@ "hash": "8cf9f3254a25c91aade7bc229bc8c13b", "highlights": [] }, + { + "title": "技惊四座的Deep Finesse打法", + "description": "欧洲桥牌团体锦标赛公开团体赛在波兰波兹南激战正酣,目前还在预赛阶段,在预赛第7轮的比赛中,我们看到了一副技惊四座的深飞打法,这是罗森塔尔队与齐默尔曼队之间发生的,主打人是挪威明星布格兰德,防守方则是卡酷司机与卡利塔。", + "content": "
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n \n \n 新朋友\n \n \n \n \n \n
    \n
    \n

    \n \n \n 点击\n \n \n \n 标兵桥牌\n \n \n \n

    \n

    \n \n \n 关注微信\n \n \n

    \n
    \n
    \n \n \n 老朋友\n \n \n
    \n
    \n

    \n \n \n 点击\n \n \n \n 右上角\n \n \n \n

    \n

    \n \n \n 分享本文\n \n \n

    \n
    \n
    \n \n \n \n
    \n
    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n 欧洲桥牌团体锦标赛公开团体赛在波兰波兹南激战正酣,目前还在预赛阶段,在预赛第7轮的比赛中,我们看到了一副技惊四座的深飞(Deep Finesse)打法,这是罗森塔尔队与齐默尔曼队之间发生的,主打人是挪威明星布格兰德,防守方则是卡酷司机与卡利塔。\n \n

    \n
    \n
    \n \n
    \n
    \n \n 南家主打5♦,首攻将牌如何做庄?\n \n
    \n
    \n

    \n \n 叫牌过程显示了不同寻常的进取。北家巴克开叫2♣强牌,南家布格兰德一个点都没有,2♦等待,此时3♠是4张♠+5张以上♦的进局逼叫,布格兰德虽然白皮,但他有3张♦,直叫5♦,因为他不能叫4♦,这会显示更强的牌。\n \n

    \n

    \n \n 西家是卡酷司机,首攻将牌,如果你是布格兰德,如何计划这副牌的主打?\n \n

    \n

    \n \n 将牌首攻十分刁钻,即使将牌对你的分布友好,你也面临如何解决♠输张的问题,♥的输张跑不掉,必须把♠输张限制在1个,而且还不能输将牌。\n \n

    \n

    \n \n 假如将牌是2-2,你无法从手中向着明手的♠出,那么你怎么限制♠的输墩呢,如果第一墩明手将牌吃住,拔♠A再送♠,对方一定是第二张将牌,那么恐怕你只能希望♠是KQ双张或者KQx了。\n \n

    \n

    \n \n 所以一定要从手中出♠,明手的♠结构非常奇妙,如果从手中出,你总有很多机会来解决问题。\n \n

    \n

    \n \n 布格兰德就是这样考虑的,他必须回到手中出♠,以解决♠的输张问题,所以即使卡酷司机首攻将牌,他也决定打卡酷司机有将牌Q,因为他必须第一张牌就进手。当♦10吃进后,布格兰德出♠,卡酷司机顶上K,你现在准备怎么打?\n \n

    \n

    \n \n 你不能再到手中出♠了,所以这张♠一定要打对,如果你不吃,第二张将牌从西家发出,你就只剩下♠KQx的机会了。\n \n

    \n
    \n
    \n \n
    \n
    \n \n ♠的打法决定定约的成败\n \n
    \n
    \n

    \n \n 不管桥友们怎么想,只见布格兰德A吃住♠K,然后从明手送♠J,西家Q吃住,第二张将牌到明手,布格兰德再出小♠,将下东家的10,完成定约!!\n \n

    \n
    \n
    \n \n
    \n
    \n \n 送♠J给西家,然后将吃3张10\n \n
    \n
    \n

    \n \n 全手牌如下:\n \n

    \n
    \n
    \n \n
    \n
    \n \n 布格兰德一张不差打成定约\n \n
    \n
    \n

    \n \n 这副牌如果不首攻将牌,庄家完成定约不难,他只需找到♦Q即可,因为可以将吃两次♠,有些牌桌是北家主打5♦,东首攻♥K,然后看到庄家送♠,再从东家吊将牌,不仅没用,而且还出卖了同伴的将牌Q。\n \n

    \n

    \n \n 但南家主打,几乎无人成功,特别是首攻将牌的情况下,南家主打首攻将牌的还有意大利明星韦尔萨切。\n \n

    \n
    \n

    \n \n 这副牌另一室的巴司机和舒柏林搞错了约定,叫到4♠定约,也算是奇葩一件。\n \n

    \n
    \n
    \n \n
    \n
    \n \n 巴司机舒柏林搞错了约定\n \n
    \n
    \n

    \n \n 布格兰德这副牌的主打,丝丝入扣,每一张牌都如双明手分析软件指引一般,张张不错,令人佩服,相信这副牌也一定能够登上公报,并且在未来的年度最佳做庄奖的评选中占有一席之地。\n \n

    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 欧洲公开桥牌锦标赛第一阶段结束、弗姆队获得欧洲混团冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲混团赛场上一副牌的两次安全打法\n \n \n

    \n

    \n \n 相关链接:\n \n 第11届欧洲公开桥牌锦标赛混团预赛结束\n \n \n

    \n

    \n \n 相关链接:\n \n 第11届欧洲桥牌公开锦标赛\n \n \n

    \n

    \n \n 相关链接:\n \n 2023年欧洲公开桥牌锦标赛每日一牌——韦尔萨切的失误,还是对手的高明?\n \n \n

    \n

    \n \n 相关链接:\n \n 第10届欧洲桥牌公开锦标赛\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开桥牌锦标赛结束\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开赛结束、土耳其人获得公开双人赛冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲冬季运动会结束,奥卡队获得冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲冬季桥牌运动会开赛在即\n \n \n

    \n

    \n \n 相关链接:\n \n 劳伦特当选欧洲桥联主席、期待开启新时代\n \n \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 欧洲桥牌新纪元——欧洲桥牌团体锦标赛回顾\n \n \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n 往期推荐\n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 2022年欧洲桥牌团体锦标赛即将开赛\n \n \n

    \n
    \n
    \n
    \n
    \n

    \n \n \n 荷兰加冕欧洲之王\n \n \n

    \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n \n \n \n \n \n 点击\n \n \n \n \n 阅读原文\n \n \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 别忘了点个在看哦!转发那就太好了!\n \n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/v1PcmPbdlPEcc8B5sGL_fA", + "creator": "标兵桥牌", + "pubDate": "Tue, 01 Jul 2025 00:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDQoJpic0ibsIxaF7GAQVwVTT6eGyFJ2u6u8uRPPuRgRemxz9bYpyyQ9DDobFOOPU8ndsqr7ibKoF7EOQ/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDQoJpic0ibsIxaF7GAQVwVTT6eGyFJ2u6u8uRPPuRgRemxz9bYpyyQ9DDobFOOPU8ndsqr7ibKoF7EOQ/0?wx_fmt=jpeg", + "id": "2247512654_1", + "language": "", + "folder": "桥牌", + "feed": "标兵1桥牌", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "97bf92eabb331dfd5a9793e814e4cbc7", + "highlights": [] + }, + { + "title": "欧洲公开桥牌锦标赛第一阶段结束、弗姆队获得欧洲混团冠军", + "description": "6月27日,正在波兰波兹南进行的第11届欧洲公开桥牌锦标赛第一阶段比赛结束,弗姆队获得混合团体冠军,11Jaszczak队获得老年团体冠军,埃德蒙斯队获得混合一副一比团体冠军,Narkiewicz/Czajka获得混合双人赛冠军。", + "content": "
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n \n \n 新朋友\n \n \n \n \n \n
    \n
    \n

    \n \n \n 点击\n \n \n \n 标兵桥牌\n \n \n \n

    \n

    \n \n \n 关注微信\n \n \n

    \n
    \n
    \n \n \n 老朋友\n \n \n
    \n
    \n

    \n \n \n 点击\n \n \n \n 右上角\n \n \n \n

    \n

    \n \n \n 分享本文\n \n \n

    \n
    \n
    \n \n \n \n
    \n
    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n 6月27日,正在波兰波兹南进行的第11届欧洲公开桥牌锦标赛第一阶段比赛结束,弗姆队(Ferm)获得混合团体冠军,11Jaszczak队获得老年团体冠军,埃德蒙斯队(Edmonds)获得混合一副一比团体冠军,Narkiewicz/Czajka获得混合双人赛冠军。\n \n

    \n
    \n
    \n \n
    \n
    \n \n Ferm队获得混合团体赛冠军\n \n
    \n
    \n
    \n
    \n \n
    \n
    \n \n 老年团体赛前三名\n \n
    \n
    \n

    \n \n 欧洲公开桥牌锦标赛混合团体赛共有94队参加,10轮瑞士移位赛过后,前32强进入淘汰赛,淘汰赛每天一轮,每轮56副牌分4节进行,经过5天的争夺,2022年世界桥牌综合锦标赛混合团体赛巴厘杯冠军弗姆队决赛战胜阿什队(Ashe)夺得冠军。\n \n

    \n

    \n \n 弗姆队由美国客户老太太弗姆组建,核心成员是来自瑞士的巴司机和舒柏林,还有丹麦美女马德森,3年前他们在波兰夺得世界冠军,如今再返波兰又夺欧洲冠军,看起来波兰是老太太的福地。与3年前的阵容相比,弗姆队更换了一对牌手,由瑞典美女格隆奎斯特和意大利的曼诺,替换了德国名将阿尼姆与荷兰的魏斯,显然更年轻的情侣表现更好,半决赛他们击败了阿尼姆与魏斯的Yoshka队,整个比赛格隆奎斯特/曼诺的表现都很抢眼,冠军实至名归。\n \n

    \n
    \n
    \n \n
    \n
    \n \n Ferm队的情侣组合半决赛战胜他们的前任\n \n
    \n
    \n

    \n \n 从6月28日开始,欧公赛进入第二阶段,重头戏公开团体赛开赛,目前共有123个队报名,超过了2017年意大利蒙特卡蒂尼121个队,也超过了2011年在波兹南的第5届120个队,成为此项比赛参赛队数最多的一次。比赛先进行3天15轮的瑞士移位赛,前32队进入淘汰赛,淘汰赛每天一场,7月5日结束。\n \n

    \n

    \n \n 公开团体赛才是真正的重头戏,欧洲强队以及当打知名牌手均出场亮相,这是百慕大杯赛前一次非常好的热身和预演,对于中国队的备战也很重要。\n \n

    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 欧洲混团赛场上一副牌的两次安全打法\n \n \n

    \n

    \n \n 相关链接:\n \n 第11届欧洲公开桥牌锦标赛混团预赛结束\n \n \n

    \n

    \n \n 相关链接:\n \n 第11届欧洲桥牌公开锦标赛\n \n \n

    \n

    \n \n 相关链接:\n \n 2023年欧洲公开桥牌锦标赛每日一牌——韦尔萨切的失误,还是对手的高明?\n \n \n

    \n

    \n \n 相关链接:\n \n 第10届欧洲桥牌公开锦标赛\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开桥牌锦标赛结束\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开赛结束、土耳其人获得公开双人赛冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲冬季运动会结束,奥卡队获得冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲冬季桥牌运动会开赛在即\n \n \n

    \n

    \n \n 相关链接:\n \n 劳伦特当选欧洲桥联主席、期待开启新时代\n \n \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 欧洲桥牌新纪元——欧洲桥牌团体锦标赛回顾\n \n \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n 往期推荐\n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 2022年欧洲桥牌团体锦标赛即将开赛\n \n \n

    \n
    \n
    \n
    \n
    \n

    \n \n \n 荷兰加冕欧洲之王\n \n \n

    \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n \n \n \n \n \n 点击\n \n \n \n \n 阅读原文\n \n \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 别忘了点个在看哦!转发那就太好了!\n \n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/EaSBtOh1aMr3oPsaJMlDHw", + "creator": "标兵桥牌", + "pubDate": "Sat, 28 Jun 2025 12:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDQoJpic0ibsIxaF7GAQVwVTT6eGyFJ2u6u8uRPPuRgRemxz9bYpyyQ9DDobFOOPU8ndsqr7ibKoF7EOQ/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDQoJpic0ibsIxaF7GAQVwVTT6eGyFJ2u6u8uRPPuRgRemxz9bYpyyQ9DDobFOOPU8ndsqr7ibKoF7EOQ/0?wx_fmt=jpeg", + "id": "2247512647_1", + "language": "", + "folder": "桥牌", + "feed": "标兵1桥牌", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "8b7e76707b846119bb7a39057abcdd4f", + "highlights": [] + }, + { + "title": "欧洲混团赛场上一副牌的两次安全打法", + "description": "桥牌赛场上,打牌是永恒的主题,只有打牌功夫过硬,才有立足于桥牌竞技中不被淘汰的资本。正在波兰波兹南进行的第11届欧洲桥牌公开锦标赛混合团体赛赛场上,不乏精彩之作,但也有令人遗憾的牌局。", + "content": "
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n \n \n 新朋友\n \n \n \n \n \n
    \n
    \n

    \n \n \n 点击\n \n \n \n 标兵桥牌\n \n \n \n

    \n

    \n \n \n 关注微信\n \n \n

    \n
    \n
    \n \n \n 老朋友\n \n \n
    \n
    \n

    \n \n \n 点击\n \n \n \n 右上角\n \n \n \n

    \n

    \n \n \n 分享本文\n \n \n

    \n
    \n
    \n \n \n \n
    \n
    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n 桥牌赛场上,打牌是永恒的主题,只有打牌功夫过硬,才有立足于桥牌竞技中不被淘汰的资本。\n \n

    \n

    \n \n 正在波兰波兹南进行的第11届欧洲桥牌公开锦标赛混合团体赛赛场上,不乏精彩之作,但也有令人遗憾的牌局。\n \n

    \n

    \n \n 下面这副牌发生在首轮淘汰赛中,南家是来自拉脱维亚的女牌手罗曼诺夫斯卡,她面临下面这副6♣的主打:\n \n

    \n
    \n
    \n \n
    \n
    \n \n 如何主打6♣\n \n
    \n
    \n

    \n \n 南家主打6♣,东西方没有叫牌,首攻是♠3,如何计划主打?\n \n

    \n

    \n \n 看上去这副牌似乎丢了大满贯,将牌如果不输,♥是3-2分布的话,就有13墩了,即使西家有4张将牌,也完全有机会做成大满贯。\n \n

    \n

    \n \n 不过现在不是考虑是否丢大满贯的时候,而是要把现在的小满贯做成。南家A吃住首攻,拔将牌A,打将牌到Q,西家只有1张将牌,然后出将牌到A,现在拔♥A,西家跟小,东家跟Q,现在如何继续?\n \n

    \n
    \n
    \n \n
    \n
    \n \n 拔♥对方掉出Q\n \n
    \n
    \n

    \n \n 现在要计算一下赢墩的情况,记住,不是打大满贯,你需要的不是13墩而是12墩。\n \n

    \n

    \n \n 有6墩将牌,还有两个高花A,拿到4墩♥就可以,你并不需要5墩♥,也就是说,你现在不需要♥3-2,而是要防止4-1如何做成,假设如果东家是单张♥Q,你继续拔♥K,就会发现手中缺少桥路,无法建立♥了,所以你怎么办呢?\n \n

    \n

    \n \n 你是不是后悔拔♥A了?没错,要防止♥的4-1分布,最好的打法是吊完将牌后,联手送小♥,因为你手中只有一张将牌是进手了,所以在♥4-1的时候,必须保留♥本身的桥路,而防守方也无法用♦逼你将吃来用掉你的将牌桥路。\n \n

    \n

    \n \n 实战中,南家罗曼诺夫斯卡没有注意桥路的问题,她简单打♥3-2,继续拔♥K,全手牌如下:\n \n

    \n
    \n
    \n \n
    \n
    \n \n 拔♥K定约就做不成了\n \n
    \n
    \n

    \n \n ♥4-1的,6♣再也无法打成。\n \n

    \n

    \n \n 虽然南家忽视了♥上联手送小,保持桥路的打法,但在拔出♥Q后,仍然有机会完成定约,她应该送小♥,如果原本就是3-2的,♥已经建立,如果是4-1的,西家最顽强的防守是让掉这墩♥,让明手大,但由于♦K在东家,庄家还能打成定约。\n \n

    \n

    \n \n 庄家错过了第一个安全打法后,及时补救,依靠将吃飞♦K仍然能让定约回家:\n \n

    \n
    \n
    \n \n
    \n
    \n \n 送小♥还有机会打成\n \n
    \n
    \n

    \n \n 我们此前经常说:安全打法要像呼吸一样伴随在身。而安全打法的熟练程度,也是基本功之一。在同一副牌中能够出现两次安全打法,错过第一次还有第二次机会弥补,也是难得而珍贵的牌局之一。\n \n

    \n
    \n

    \n \n 另外,再提一个话题,就是关于满贯定约的首攻。满贯定约(此处特指小满贯)不同于成局定约,对方叫到满贯,通常不差赢墩,所以满贯定约最好的首攻是积极首攻而非消极首攻,希望在对方脱手时建立自己的防守赢墩。这副牌就是一个很好的例子,首攻♠打掉了庄家一个桥,是给庄家制造最大困难的首攻,而如果不首攻♠,就会像另外一桌,让对方打成大满贯。\n \n

    \n

    \n \n 另一桌南北方奥肯和韦兰德叫到了大满贯,在常规的将牌首攻后,有足够的桥路建立♥,并且将吃飞♦拿到13墩。\n \n

    \n

    \n \n \n 如果你担心小满贯不好打,请叫大满贯!\n \n \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 第11届欧洲公开桥牌锦标赛混团预赛结束\n \n \n

    \n

    \n \n 相关链接:\n \n 第11届欧洲桥牌公开锦标赛\n \n \n

    \n

    \n \n 相关链接:\n \n 2023年欧洲公开桥牌锦标赛每日一牌——韦尔萨切的失误,还是对手的高明?\n \n \n

    \n

    \n \n 相关链接:\n \n 第10届欧洲桥牌公开锦标赛\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开桥牌锦标赛结束\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开赛结束、土耳其人获得公开双人赛冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲冬季运动会结束,奥卡队获得冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲冬季桥牌运动会开赛在即\n \n \n

    \n

    \n \n 相关链接:\n \n 劳伦特当选欧洲桥联主席、期待开启新时代\n \n \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 欧洲桥牌新纪元——欧洲桥牌团体锦标赛回顾\n \n \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n 往期推荐\n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 2022年欧洲桥牌团体锦标赛即将开赛\n \n \n

    \n
    \n
    \n
    \n
    \n

    \n \n \n 荷兰加冕欧洲之王\n \n \n

    \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n \n \n \n \n \n 点击\n \n \n \n \n 阅读原文\n \n \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 别忘了点个在看哦!转发那就太好了!\n \n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/hs5rc1xmnOHOS65BEAotew", + "creator": "标兵桥牌", + "pubDate": "Thu, 26 Jun 2025 23:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDQoJpic0ibsIxaF7GAQVwVTT6eGyFJ2u6u8uRPPuRgRemxz9bYpyyQ9DDobFOOPU8ndsqr7ibKoF7EOQ/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDQoJpic0ibsIxaF7GAQVwVTT6eGyFJ2u6u8uRPPuRgRemxz9bYpyyQ9DDobFOOPU8ndsqr7ibKoF7EOQ/0?wx_fmt=jpeg", + "id": "2247512642_1", + "language": "", + "folder": "桥牌", + "feed": "标兵1桥牌", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "ed1d317745b122353f0e669c44fe631f", + "highlights": [] + }, + { + "title": "第11届欧洲公开桥牌锦标赛混团预赛结束", + "description": "欧洲桥牌公开锦标赛每两年举办一次,从2003年到2019年共举办了9届,但由于疫情原因2021年中断,2023年再度恢复,今年是第11届比赛,本次比赛将于6月21日-7月5日在波兰波茨南举办,波茨南曾经于2011年举办过欧洲桥牌公开锦标赛。", + "content": "
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n \n \n 新朋友\n \n \n \n \n \n
    \n
    \n

    \n \n \n 点击\n \n \n \n 标兵桥牌\n \n \n \n

    \n

    \n \n \n 关注微信\n \n \n

    \n
    \n
    \n \n \n 老朋友\n \n \n
    \n
    \n

    \n \n \n 点击\n \n \n \n 右上角\n \n \n \n

    \n

    \n \n \n 分享本文\n \n \n

    \n
    \n
    \n \n \n \n
    \n
    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n 6月22日,在波兰波兹南进行的第11届欧洲公开桥牌锦标赛混合团体赛预赛结束,共有94个队参加了混合团体赛的预赛,预赛进行10轮瑞士移位赛,前32名进入淘汰赛。\n \n

    \n
    \n
    \n \n
    \n
    \n \n 混团32强淘汰赛对阵\n \n
    \n
    \n

    \n \n 10轮结束后,以波兰职业高手率领的埃德蒙斯队获得第一名,波兰/法国牌手组成的SZPILIKA队获得第二名。有趣而遗憾的是,由美国年轻牌手组成的蜜月队在转播桌坐了6轮,第一天结束还是第一名,第二天最后四场全负,最终没能晋级,他们后面有时间继续蜜月之旅了。\n \n

    \n

    \n \n 淘汰赛每轮进行一天,一共5天结束。\n \n

    \n

    \n \n 下面是比赛中的一些图片。\n \n

    \n
    \n
    \n \n
    \n
    \n \n 混团比赛赛场\n \n
    \n
    \n
    \n
    \n \n
    \n
    \n \n 欧洲比赛使用平板叫牌,纸牌打牌,每个牌桌有摄像头,所有的记录都存在\n \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 第11届欧洲桥牌公开锦标赛\n \n \n

    \n

    \n \n 相关链接:\n \n 2023年欧洲公开桥牌锦标赛每日一牌——韦尔萨切的失误,还是对手的高明?\n \n \n

    \n

    \n \n 相关链接:\n \n 第10届欧洲桥牌公开锦标赛\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开桥牌锦标赛结束\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开赛结束、土耳其人获得公开双人赛冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲冬季运动会结束,奥卡队获得冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲冬季桥牌运动会开赛在即\n \n \n

    \n

    \n \n 相关链接:\n \n 劳伦特当选欧洲桥联主席、期待开启新时代\n \n \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 欧洲桥牌新纪元——欧洲桥牌团体锦标赛回顾\n \n \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n 往期推荐\n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 2022年欧洲桥牌团体锦标赛即将开赛\n \n \n

    \n
    \n
    \n
    \n
    \n

    \n \n \n 荷兰加冕欧洲之王\n \n \n

    \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n \n \n \n \n \n 点击\n \n \n \n \n 阅读原文\n \n \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 别忘了点个在看哦!转发那就太好了!\n \n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/9XZTqX8gjcy09YAq4dCbnQ", + "creator": "标兵桥牌", + "pubDate": "Mon, 23 Jun 2025 23:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDQoJpic0ibsIxaF7GAQVwVTT6eGyFJ2u6u8uRPPuRgRemxz9bYpyyQ9DDobFOOPU8ndsqr7ibKoF7EOQ/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDQoJpic0ibsIxaF7GAQVwVTT6eGyFJ2u6u8uRPPuRgRemxz9bYpyyQ9DDobFOOPU8ndsqr7ibKoF7EOQ/0?wx_fmt=jpeg", + "id": "2247512636_1", + "language": "", + "folder": "桥牌", + "feed": "标兵1桥牌", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "926d81696677395d95a23772a9ea152c", + "highlights": [] + }, + { + "title": "第11届欧洲桥牌公开锦标赛", + "description": "欧洲桥牌公开锦标赛每两年举办一次,从2003年到2019年共举办了9届,但由于疫情原因2021年中断,2023年再度恢复,今年是第11届比赛,本次比赛将于6月21日-7月5日在波兰波茨南举办,波茨南曾经于2011年举办过欧洲桥牌公开锦标赛。", + "content": "
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n \n \n 新朋友\n \n \n \n \n \n
    \n
    \n

    \n \n \n 点击\n \n \n \n 标兵桥牌\n \n \n \n

    \n

    \n \n \n 关注微信\n \n \n

    \n
    \n
    \n \n \n 老朋友\n \n \n
    \n
    \n

    \n \n \n 点击\n \n \n \n 右上角\n \n \n \n

    \n

    \n \n \n 分享本文\n \n \n

    \n
    \n
    \n \n \n \n
    \n
    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n 始于2003年的欧洲桥牌公开锦标赛(11th European Transnational Championships)是欧洲桥联主办的跨国公开性赛事,包涵了公开、女子、老年、混合四个组别的团体赛和双人赛,是对标世界桥牌综合锦标赛的一项大赛,我国的全国桥牌锦标赛和全国桥牌综合锦标赛也属于这类赛事。虽说名称为欧洲桥牌公开赛,但参赛对象可以是世界各地的桥牌爱好者和专业牌手。\n \n

    \n
    \n
    \n \n
    \n
    \n \n 欧洲桥牌公开锦标赛在波兰波兹南举办\n \n
    \n
    \n

    \n \n 欧洲桥牌公开锦标赛每两年举办一次,从2003年到2019年共举办了9届,但由于疫情原因2021年中断,2023年再度恢复,今年是第11届比赛,本次比赛将于6月21日-7月5日在波兰波茨南举办,波茨南曾经于2011年举办过第5届欧洲桥牌公开锦标赛。\n \n

    \n

    \n \n 本次比赛从报名参加的情况来看,全面超过2023年的上一届比赛,考虑到波兰还处于距离战争较近的地区,这个数据已经相当不错。截止到6月18日开赛前3天,参加公开团体赛的有116个队,大幅超过上届的87队,略多于与我国4月结束的全国桥牌锦标赛公开团体赛100个队,但还不及2019年在伊斯坦布尔的122队;女子团体赛20个队,超过上届15队,比全锦赛女子团体赛28队少;老年团体赛21队,超过上届16队,远远不及全锦赛47队;混合团体赛96队,大幅超过上届的69队,基本与全锦赛混团赛的99队接近;公开双人赛172对,超过上届121对,比全锦赛公开双人赛的193对少21对;女子双人赛23对,不如上届的29对,比全锦赛女子双人赛的49对少了一半以上;老年双人赛52对,上届36对;混合双人赛139对,上届113对,接近全锦赛混双的137对。\n \n

    \n

    \n \n 欧洲公开赛以混合团体赛和公开团体赛为头尾,两项比赛分别是2天预赛和3天预赛,淘汰赛均为5天,先进行混团、后进行公团。期间穿插女团、老年团体和双人赛。\n \n

    \n
    \n
    \n \n
    \n
    \n \n 比赛场馆外景\n \n
    \n
    \n

    \n \n 由于本次比赛适逢国内全运会桥牌预赛,因此没有国内团队前往波兰参赛。\n \n

    \n

    \n \n 所有比赛全程在BBO进行转播,比赛从北京时间每天下午4点开始,至午夜结束。\n \n

    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 2023年欧洲公开桥牌锦标赛每日一牌——韦尔萨切的失误,还是对手的高明?\n \n \n

    \n

    \n \n 相关链接:\n \n 第10届欧洲桥牌公开锦标赛\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开桥牌锦标赛结束\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开赛结束、土耳其人获得公开双人赛冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲冬季运动会结束,奥卡队获得冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲冬季桥牌运动会开赛在即\n \n \n

    \n

    \n \n 相关链接:\n \n 劳伦特当选欧洲桥联主席、期待开启新时代\n \n \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 欧洲桥牌新纪元——欧洲桥牌团体锦标赛回顾\n \n \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n 往期推荐\n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 2022年欧洲桥牌团体锦标赛即将开赛\n \n \n

    \n
    \n
    \n
    \n
    \n

    \n \n \n 荷兰加冕欧洲之王\n \n \n

    \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n \n \n \n \n \n 点击\n \n \n \n \n 阅读原文\n \n \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 别忘了点个在看哦!转发那就太好了!\n \n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/4UEOFqcgA5tbMw2LeQEk7g", + "creator": "标兵桥牌", + "pubDate": "Sat, 21 Jun 2025 00:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDQoJpic0ibsIxaF7GAQVwVTT6eGyFJ2u6u8uRPPuRgRemxz9bYpyyQ9DDobFOOPU8ndsqr7ibKoF7EOQ/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDQoJpic0ibsIxaF7GAQVwVTT6eGyFJ2u6u8uRPPuRgRemxz9bYpyyQ9DDobFOOPU8ndsqr7ibKoF7EOQ/0?wx_fmt=jpeg", + "id": "2247512631_1", + "language": "", + "folder": "桥牌", + "feed": "标兵1桥牌", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "42be146c34516610da8d260cffeec8fb", + "highlights": [] + }, + { + "title": "世界桥联公布2024年大师分排行榜", + "description": "2024年底,世界桥联公布了世界桥牌运动会结束后世界桥联大师分的排行榜。牌坛奇人齐老板仍然领跑公开组,这已经是他自从2021年登顶之后,连续第四年蝉联世界桥联大师分第一人的宝座了。女子排行榜、老年排行榜、混合排行榜的榜首也各有其主。", + "content": "
    \n
    \n
    \n
    \n
    \n
    \n \n \n 新朋友\n \n \n \n \n \n
    \n
    \n

    \n \n \n 点击\n \n \n \n 标兵桥牌\n \n \n \n

    \n

    \n \n \n 关注微信\n \n \n

    \n
    \n
    \n \n \n 老朋友\n \n \n
    \n
    \n

    \n \n \n 点击\n \n \n \n 右上角\n \n \n \n

    \n

    \n \n \n 分享本文\n \n \n

    \n
    \n
    \n \n \n \n
    \n
    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n 2024年底,世界桥联公布了世界桥牌运动会结束后世界桥联大师分的排行榜。\n \n

    \n

    \n \n 牌坛奇人齐老板仍然领跑公开组,这已经是他自从2021年登顶之后,连续第四年蝉联世界桥联大师分第一人的宝座了。女子大师分排行榜、老年大师分排行榜、混合大师分排行榜的榜首,分别是瑞典、丹麦、法国牌手。\n \n

    \n

    \n \n 下面是公开组前40名牌手的排名:\n \n

    \n
    \n
    \n \n
    \n
    \n \n 2024年世界桥联公开组大师分排行榜\n \n
    \n
    \n

    \n \n 我们能够看到,排在前六名的是齐刷刷的瑞士队牌手,其中齐老板高居第一,超过了第二名卡酷司机近500分,相当于一个世界季军的名次差距。最晚加入瑞士队的诺沃萨德茨基分数最低,但也比第7名的海尔格莫多了100多分。\n \n

    \n

    \n \n 瑞士大鳄从2021年开始在世界牌坛的兴风作浪,先后拿到了2022年、2023年百慕大杯冠军,2024年世界桥牌运动会季军,年年登上世界锦标赛的领奖台,大师分增值无人能比。\n \n

    \n

    \n \n 瑞士队之后的排名相对分散,原因是这些牌手无法在世界比赛中集中火力,形成统一团队竞争。\n \n

    \n

    \n \n 其中小海借助早年在摩纳哥队的成绩,加上近年代表挪威,还有去年世界桥牌运动会跨国团体赛的冠军,得以领跑除瑞士团队的世界牌手。\n \n

    \n

    \n \n 小海之下,是波兰队的新科世界冠军布雷茨排在第8名,布雷茨在阿根廷拿到了世界桥牌运动会冠军后,加上他2019年百慕大杯冠军、2022年世界桥牌双人赛冠军,他已经成为波兰唯一的大三冠成员,波兰牌手即使强如加夫雷西、卡利塔、卡酷司机,也没有拿到3种世界冠军的牌手。\n \n

    \n

    \n \n 再往下排名,是第9名和第12名意大利的韦尔萨切与塞门塔。自从劳爷退休后,“小沃”的发小塞门塔成为他的新搭档,两人很快进入状态,成为意大利队的新台柱,先后拿下2023年百慕大杯季军和2024年世界桥牌运动会亚军,也在世界桥联世界桥牌巡回赛的积分榜上连续两年排名第一。\n \n

    \n

    \n \n 插在两位意大利牌手中间的是荷兰明星穆勒和魏斯,他们虽然从2024年开始退出了荷兰国家队,但凭借2019年、2022年百慕大杯的亚军、2022年欧锦赛冠军等成绩,仍然领跑荷兰牌手。不过老穆勒与魏斯的状态从2023年开始急剧下滑,已有逐步退出世界级牌手的趋势,最近他们参加了中国的A类俱乐部联赛。\n \n

    \n

    \n \n 美国的莫斯排在12名,超过了14名和15名的尼老板与搭档凯茨,波兰老将加夫雷西退出瑞士队后,仍然依靠此前的积累,排在第13名。\n \n

    \n

    \n \n 新西兰的威布利(Michael Whibley)占据了第一页的位置,他排在第17名,是亚太地区排名最高的牌手,得益于新西兰最近不错的成绩,以及去年世界桥牌运动会跨国团体赛的亚军。\n \n

    \n

    \n \n 在第一页位置的,多数是2024年世界桥牌运动会成绩不错的牌手。\n \n

    \n

    \n \n 中国牌手排名最高的是现役国家队队员连勇,他以971.75分排在第81名,刚好超过劳爷1.05分。\n \n

    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 世界桥联大师分排行榜齐老板遥遥领先\n \n \n

    \n

    \n \n 相关链接:\n \n 世界桥联公报最新大师分、齐老板坐稳世界第一\n \n \n

    \n

    \n \n 相关链接:\n \n 世界桥联最新大师分排行榜、摩纳哥众将排名靠前\n \n \n

    \n

    \n \n 相关链接:\n \n 世界桥联公布最新大师分、Helgemo登顶世界第一人\n \n \n

    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n 往期推荐\n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 世界桥联大师分介绍及排行榜解读\n \n \n

    \n
    \n
    \n
    \n
    \n

    \n \n \n 世界桥联大师分介绍及排行榜解读(二)男子特级大师排行榜解读\n \n \n

    \n
    \n
    \n
    \n
    \n

    \n \n \n 世界桥联大师分介绍及排行榜解读(三)世界桥联女子及中国牌手排行榜\n \n \n

    \n
    \n
    \n
    \n
    \n

    \n \n \n 世界桥联大师分介绍及排行榜解读(四)世界桥联大师分政策和等级制度的变迁对中国牌手的影响\n \n \n

    \n
    \n
    \n
    \n
    \n

    \n \n \n 一览众山小——齐老板登顶世界第一之路\n \n \n

    \n
    \n
    \n
    \n
    \n

    \n \n \n 一览众山小——齐老板登顶世界第一之路(二)\n \n \n

    \n
    \n
    \n
    \n
    \n

    \n \n \n 一览众山小——齐老板登顶世界第一之路 (续完)\n \n \n

    \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n \n \n \n \n \n 点击\n \n \n \n \n 阅读原文\n \n \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 别忘了点个在看哦!转发那就太好了!\n \n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/i8ozGVJzORLNLKLSgY9RYg", + "creator": "标兵桥牌", + "pubDate": "Mon, 16 Jun 2025 00:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDSibjfuNWY4LAB2pN77qbPPaynicibuLW2nEp8bqVrvuouOCgYlbWfciaLXGkM5VzZpZdpb8Z3HAAS28A/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDSibjfuNWY4LAB2pN77qbPPaynicibuLW2nEp8bqVrvuouOCgYlbWfciaLXGkM5VzZpZdpb8Z3HAAS28A/0?wx_fmt=jpeg", + "id": "2247512614_1", + "language": "", + "folder": "桥牌", + "feed": "标兵1桥牌", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "d493b9eff95dc7208df749197028ae29", + "highlights": [] + }, + { + "title": "2025年北欧桥牌锦标赛结束", + "description": "6月5-8日,2025年北欧桥牌锦标赛(The Nordic Championship)在冰岛小镇Laugravatn举行。瑞典、芬兰、挪威、法罗群岛、丹麦、冰岛这6家北欧桥联的成员参加了比赛。丹麦获得公开团体冠军,挪威获得女子团体冠军。", + "content": "
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n \n \n 新朋友\n \n \n \n \n \n
    \n
    \n

    \n \n \n 点击\n \n \n \n 标兵桥牌\n \n \n \n

    \n

    \n \n \n 关注微信\n \n \n

    \n
    \n
    \n \n \n 老朋友\n \n \n
    \n
    \n

    \n \n \n 点击\n \n \n \n 右上角\n \n \n \n

    \n

    \n \n \n 分享本文\n \n \n

    \n
    \n
    \n \n \n \n
    \n
    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n 6月5-8日,2025年北欧桥牌锦标赛(The Nordic Championship)在冰岛小镇Laugravatn举行。瑞典、芬兰、挪威、法罗群岛、丹麦、冰岛这6家北欧桥联的成员参加了比赛。\n \n

    \n
    \n
    \n \n
    \n
    \n \n 谁能说出北欧六国国旗对应的国家\n \n
    \n
    \n

    \n \n 比赛分为公开团体和女子团体两个项目,由于今年世界桥牌团体锦标赛在丹麦举行,北欧六国中瑞典、挪威、丹麦均获得了参加世锦赛的资格,这次比赛也是他们世锦赛前非常好的练兵机会。\n \n

    \n

    \n \n 经过4天比赛,丹麦获得公开团体冠军,挪威、瑞典分获二、三名。挪威获得女子团体冠军,丹麦、瑞典分获二、三名。获得两项团体赛前三名的恰好均是参加世锦赛的代表,这也恰如其分地说明了他们在北欧桥牌中的地位。\n \n

    \n

    \n \n 本次比赛中,丹麦男女队派出的选手基本上是参加世锦赛的队员,挪威女队派出了参加威尼斯杯的队员,挪威男队有一对牌手是参加百慕大杯的队员,对即将参加世锦赛的中国国家队而言,研究和观察他们的比赛,也是赛前备战的内容之一。\n \n

    \n

    \n \n 从6月开始,欧洲以及北美包括网络上将陆续进行一系列的重要比赛,欧美列强将参加这些比赛进行世锦赛前热身,这也是中国国家队研究对手的良好机会。\n \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 2025年欧洲小联赛布加勒斯特完赛\n \n \n

    \n

    \n \n 相关链接:\n \n 叫牌用平板、打牌用纸牌——2022年欧洲小联盟赛结束\n \n \n

    \n

    \n \n 相关链接:\n \n 劳伦特当选欧洲桥联主席、期待开启新时代\n \n \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 罗纳时代结束——世界桥联主席换届\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲桥牌新纪元——欧洲桥牌团体锦标赛回顾\n \n \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n \n \n \n \n \n 点击\n \n \n \n \n 阅读原文\n \n \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 别忘了点个在看哦!转发那就太好了!\n \n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n \n
    \n
    \n

    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/4FObKuBiU2eBA8lw2Zy-3w", + "creator": "标兵桥牌", + "pubDate": "Tue, 10 Jun 2025 00:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDRYno20Pe5bW627sL0rMdJqVCibbphJ8abNtTPtwf2CxWwiaouKfUbnD60ASZT2oDDUNicJDPRzvySPg/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDRYno20Pe5bW627sL0rMdJqVCibbphJ8abNtTPtwf2CxWwiaouKfUbnD60ASZT2oDDUNicJDPRzvySPg/0?wx_fmt=jpeg", + "id": "2247512609_1", + "language": "", + "folder": "桥牌", + "feed": "标兵1桥牌", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "6e60cc8d1cf3de1b74a9ea47f0bffcc4", + "highlights": [] + }, { "title": "美国国家队名单出炉", "description": "5月26日当地时间,美国国家队选拔赛老年团体赛2025年6月1日,美国桥牌协会(USBF)公布了参加2025年世界桥牌团体锦标赛的8支美国队参赛名单。由于美国选拔赛允许一名选手报名多项比赛,因此在选拔赛全部结束后,各个美国国家队进行了调整。", @@ -3483,6 +4508,160 @@ "hash": "904cfda1cd5a396451f15015355b3b1e", "highlights": [] }, + { + "title": "最后一张世锦赛门票发出,全华班加拿大女队艰难获得", + "description": "北京时间6月2日上午,加拿大与墨西哥女队的附加赛结束,全华班加拿大女队以202:167IMP战胜对手拿到前往丹麦参加世界桥牌团体锦标赛的最后一张门票。", + "content": "
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n \n \n 新朋友\n \n \n \n \n \n
    \n
    \n

    \n \n \n 点击\n \n \n \n 标兵桥牌\n \n \n \n

    \n

    \n \n \n 关注微信\n \n \n

    \n
    \n
    \n \n \n 老朋友\n \n \n
    \n
    \n

    \n \n \n 点击\n \n \n \n 右上角\n \n \n \n

    \n

    \n \n \n 分享本文\n \n \n

    \n
    \n
    \n \n \n \n
    \n
    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n 北京时间6月2日上午,加拿大与墨西哥女队的附加赛结束,全华班加拿大女队以202:167IMP战胜对手拿到前往丹麦参加世界桥牌团体锦标赛的最后一张门票。\n \n

    \n
    \n
    \n \n
    \n
    \n \n 加拿大女队获得威尼斯杯资格\n \n
    \n
    \n

    \n \n 加拿大与墨西哥同属世界桥联第2区,与美国一个区,该区有3个参加世锦赛的名额,其中美国独占两个资格,余下该区的加拿大、墨西哥争夺另一个名额。在大部分的情况下,墨西哥是不参与竞争世锦赛资格的,因此我们在世界桥牌团体锦标赛上看到的基本上都是加拿大的身影,本次世锦赛资格赛也是一样,公团、混团、老团的资格均由加拿大获得,入选加拿大国家队,就相当于直接进入了世锦赛。\n \n

    \n

    \n \n 不过今年有点特殊情况,墨西哥宣布要参加女子团体赛的争夺,因此在加拿大女队选拔赛结束后,还要与墨西哥打附加赛。这比赛的时间定在了5月31日,此时所有地区的选拔赛均已结束,只剩下女子团体威尼斯杯这最后一个资格了。\n \n

    \n

    \n \n 与墨西哥的比赛进行的十分艰苦,8节比赛打完前7节,比分始终胶着,最后的决胜节前,加拿大女队还落后3分,决胜节的发挥堪称完美,大胜对手拿到了通往丹麦的门票。\n \n

    \n

    \n \n 加拿大女队全部由活跃在北美地区的华人组成,她们热爱桥牌,能够参加世锦赛威尼斯杯是她们的梦想,预祝她们丹麦的圆梦之旅顺利。\n \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n \n 相关链接:\n \n 2025年美国国家队选拔赛\n \n \n \n

    \n

    \n \n \n 相关链接:\n \n 老罗重回世锦赛——美国老年队、女队选拔赛结束\n \n \n \n

    \n

    \n \n \n 相关链接:\n \n 美国混团选拔赛结束,苏珊·张队赢得资格\n \n \n \n

    \n

    \n \n 相关链接:\n \n 弗雷舍获得2024年美国队资格\n \n \n

    \n

    \n \n 相关链接:\n \n 2024年美国国家队选拔赛开赛\n \n \n

    \n

    \n \n \n 相关链接:\n \n 2023年美国国家队选拔赛争议中结束\n \n \n \n

    \n

    \n \n \n 相关链接:\n \n 2022年美国国家队选拔赛结束\n \n \n \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n 往期推荐\n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 尼克尔队率先拿到百慕大杯美国1队资格\n \n \n

    \n
    \n
    \n
    \n
    \n

    \n \n \n 平板电脑还是纸牌?职业牌手有分歧\n \n \n

    \n
    \n
    \n
    \n
    \n

    \n \n \n 通往武汉之路:美国国家队选拔赛\n \n \n

    \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n \n \n \n \n \n 点击\n \n \n \n \n 阅读原文\n \n \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 别忘了点个在看哦!转发那就太好了!\n \n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/oMvOUnizaYy4eFU6jMLozg", + "creator": "标兵桥牌", + "pubDate": "Fri, 06 Jun 2025 00:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDRda8jbPFNWBhWgicGibibbjjNDKCMicY72yicOcSHiaicK2b3J2GCQFoYE8gLvyYicBPDwwXwXicRXMPTX0lA/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDRda8jbPFNWBhWgicGibibbjjNDKCMicY72yicOcSHiaicK2b3J2GCQFoYE8gLvyYicBPDwwXwXicRXMPTX0lA/0?wx_fmt=jpeg", + "id": "2247512600_1", + "language": "", + "folder": "桥牌", + "feed": "标兵1桥牌", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "c3ebf678a16026350b5dc82df17d4d69", + "highlights": [] + }, + { + "title": "亚太赛每日一牌回顾:一副小牌的攻防与叫牌", + "description": "亚太锦标赛结束了,中国男队夺得冠军,在比赛期间所写的“每日一牌”,比赛期间并没有发出,从今天开始,给桥友们提供迟到的每日一牌。", + "content": "
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n \n \n 新朋友\n \n \n \n \n \n
    \n

    \n \n \n 点击\n \n \n \n 标兵桥牌\n \n \n \n

    \n

    \n \n \n 关注微信\n \n \n

    \n
    \n \n \n 老朋友\n \n \n
    \n

    \n \n \n 点击\n \n \n \n 右上角\n \n \n \n

    \n

    \n \n \n 分享本文\n \n \n

    \n
    \n \n \n \n
    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n 亚太锦标赛结束了,中国男队夺得冠军,在比赛期间所写的“每日一牌”,比赛期间并没有发出,从今天开始,给桥友们提供迟到的每日一牌。\n \n \n

    \n
    \n

    \n \n 5月23日每日一牌:一副小牌的攻防与叫牌\n \n

    \n

    \n \n 桥友们大多数喜欢大牌,满贯、高阶竞叫、加倍等等,因为大牌来的分数多,让人们看的热血沸腾。相比来说,小牌就很乏味,小牌攻防环节多,也比较复杂,产生的输赢较小,所以通常不被重视。\n \n

    \n

    \n \n 不过小牌同样重要,小牌产生的输赢,也是非常可观的。\n \n

    \n

    \n \n 下面这副牌是第二循环第11轮,中国对澳大利亚,此前澳大利亚领先中国,这副小牌在这场双VP的比赛中显得异常重要。\n \n

    \n
    \n
    \n \n
    \n
    \n \n 东家主打2♣\n \n
    \n
    \n

    \n \n 我是东家,开叫2♣成为最后定约,南家首攻♦A,怎么考虑?\n \n

    \n

    \n \n 联手有4-4的♠配合,但却难以找到,这是强梅花体系中的2♣开叫所带来不利之处,但好处是对方的8张♥配合也找不到了。从牌面上看,可能的输张有2墩♠、2墩♥、2墩♦、1墩♣,如果♠J、♥A、♣Q的位置有利,也是极有希望做成定约的。\n \n

    \n

    \n \n 南北方使用反式张数和姿态信号,在有将定约中,他们主要使用张数信号,南家的♦A后,同伴跟3,他改为拔♥A,解决了我的♥问题,北家跟8,然后南家再打♥,我的K吃住。\n \n

    \n

    \n \n 现在出♣J飞牌,南家有Q,吃住后,他略作思考,又出♣给明手,现在局面如下:\n \n

    \n
    \n
    \n \n
    \n
    \n \n 现在明手出牌\n \n
    \n
    \n

    \n \n 明手最后一个桥被用掉了,已经输了3墩,还有一个♦要输,看起来就要马上决战♠了,最大的概率就是直接出10飞J,你准备这样打吗?\n \n

    \n
    \n

    \n \n 实战中,我将吃第三张♥,然后清将牌,南家有4张,我把他全清光,明手垫♠,手中送出♦J,南家吃住,全手牌如下:\n \n

    \n
    \n
    \n \n
    \n
    \n \n 南家被投入,只能再拿一墩♠A了\n \n
    \n
    \n

    \n \n 南家只有♠和♦可出了,无论出哪一门,我都只输一墩♠A,完成定约。我为何要这样打,因为南家的防守清楚地告诉我,所有大牌都在他手里,我不能飞♠J,而他的牌型,我也判断他没有很长的套,因为如果有长套,他很可能在2♣后争叫,他的牌很像现在这个牌型。\n \n

    \n

    \n \n 如果南家在我的♦J不出K如何?我可以明手的Q超打,然后再出小♦,送给南家,还原为实战中的情况。\n \n

    \n
    \n
    \n \n
    \n
    \n \n 不用♦K吃也只是延缓被投入\n \n
    \n
    \n
    \n

    \n \n 值得一提的是叫牌,现在很多打精确法的牌手都不愿意开叫2♣,但有所不知2♣也并非都是弱点,也有它的长处,这副牌在另一室东家开叫10-13点的1NT,南家加倍,然后南北方抢到♥定约做成140分。假如东家开叫自然法的1♣或者精确法的1♦,南家都是争叫1NT,北家转移,东西方很难再行动抢出♠定约,实战中,有8桌都是南北方打♥定约吃到140分,而东西方打2♣的,只有我这一桌。\n \n

    \n

    \n \n \n
    \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n \n
    \n
    \n
    \n

    \n

    \n \n \n

    \n
    \n
    \n
    \n
    \n \n \n \n \n \n 点击\n \n \n \n \n 阅读原文\n \n \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 别忘了点个在看哦!转发那就太好了!\n \n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n \n \n \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 亚太赛每日一牌回顾:满贯要有想象力\n \n \n

    \n

    \n \n 相关链接:\n \n 亚太赛每日一牌回顾:剥光投入显身手\n \n \n

    \n

    \n \n 相关链接:\n \n 亚太赛每日一牌回顾:连氏自然轻松满贯\n \n \n

    \n

    \n \n 相关链接:\n \n 亚太赛每日一牌回顾:心中有挡、无将不慌\n \n \n

    \n

    \n \n 相关链接:\n \n 亚太赛每日一牌回顾:精彩的四红心\n \n \n

    \n

    \n \n 相关链接:\n \n 亚太赛每日一牌回顾:有趣的四红心\n \n \n

    \n

    \n \n 相关链接:\n \n 风雨十四载、再夺亚太冠\n \n \n

    \n

    \n \n 相关链接:\n \n 荏苒十四载、再战亚太赛\n \n \n

    \n

    \n \n 相关链接:\n \n 十四年归来仍是追风少年\n \n \n

    \n

    \n \n 相关链接:\n \n 曼谷重启亚太大赛\n \n \n

    \n

    \n \n 相关链接:\n \n 第53届亚太桥牌锦标赛落幕,欧公赛决赛法国内战\n \n \n

    \n

    \n \n 相关链接:\n \n 中国桥牌队亚太锦标赛大四喜,欧公赛女子团体冠军产生\n \n \n

    \n

    \n \n 相关链接:\n \n 欧公赛公团齐老板被淘汰、亚太赛迎来最后高潮\n \n \n

    \n

    \n \n 相关链接:\n \n 欧公赛公团结束首轮淘汰赛、亚太赛接近尾声\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开桥牌锦标赛公团预赛结束、亚太赛完成上半程比赛\n \n \n

    \n

    \n \n 相关链接:\n \n 亚太桥牌锦标赛结束女团、混团第一循环比赛\n \n \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 欧洲公开桥牌锦标赛公团女团开赛,亚太赛中国占据三项第一\n \n \n

    \n

    \n \n 相关链接:\n \n 亚太赛第一天中国女队占优,欧洲公开桥牌锦标赛混团冠军产生\n \n \n

    \n

    \n \n 相关链接:\n \n 亚太桥牌锦标赛开幕,欧洲公开桥牌锦标赛产生老年团体冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 第53届亚太桥牌锦标赛即将开赛\n \n \n

    \n

    \n \n 相关链接:\n \n 第四届亚洲杯结束、中国香港捧起“宁波杯”\n \n \n

    \n

    \n \n 相关链接:\n \n 2022年亚洲杯开赛\n \n \n

    \n

    \n \n 相关链接:\n \n 第三届亚洲杯桥牌赛开幕\n \n \n

    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/mOns8vzMGRhHVuKF9DajUQ", + "creator": "标兵桥牌", + "pubDate": "Wed, 04 Jun 2025 00:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDTH0sTiajz6mMGDUjHnftbibdib7FEvMaiahjxG8C2mKODhpuRaUtBEnLTyDUl3T8iciaqCAYSUSymeB1ZA/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDTH0sTiajz6mMGDUjHnftbibdib7FEvMaiahjxG8C2mKODhpuRaUtBEnLTyDUl3T8iciaqCAYSUSymeB1ZA/0?wx_fmt=jpeg", + "id": "2247512595_1", + "language": "", + "folder": "桥牌", + "feed": "标兵1桥牌", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "30306c42a579ceff24b8e7ac06bc2a90", + "highlights": [] + }, + { + "title": "亚太赛每日一牌回顾:满贯要有想象力", + "description": "亚太锦标赛结束了,中国男队夺得冠军,在比赛期间所写的“每日一牌”,比赛期间并没有发出,从今天开始,给桥友们提供迟到的每日一牌。", + "content": "
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n \n \n 新朋友\n \n \n \n \n \n
    \n

    \n \n \n 点击\n \n \n \n 标兵桥牌\n \n \n \n

    \n

    \n \n \n 关注微信\n \n \n

    \n
    \n \n \n 老朋友\n \n \n
    \n

    \n \n \n 点击\n \n \n \n 右上角\n \n \n \n

    \n

    \n \n \n 分享本文\n \n \n

    \n
    \n \n \n \n
    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n 亚太锦标赛结束了,中国男队夺得冠军,在比赛期间所写的“每日一牌”,比赛期间并没有发出,从今天开始,给桥友们提供迟到的每日一牌。\n \n \n

    \n
    \n

    \n \n 5月22日每日一牌:满贯要有想象力\n \n

    \n

    \n \n 桥牌比赛中的满贯叫牌是胜负的关键,满贯叫牌是一个很复杂的工程,不仅需要搭档间非常好的默契、系统中有相应的工具、最重要的还要有想象力。\n \n

    \n

    \n \n 下面这副牌,发生在第一循环第4轮对阵新西兰的比赛中,中国男队的张永革,就做了一个非常有想象力的叫品,达到了优秀的大满贯。\n \n

    \n
    \n
    \n \n
    \n
    \n \n 当了解到同伴没有将牌Q后,如何发展?\n \n
    \n
    \n

    \n \n 南北方是强梅花体系,南家开叫1♣,北家2♣自然叫且进局逼叫,南家2♥,北家直接3♠显示4张♥支持和♠单缺,张永革马上4NT问关键张,当知道关键张不缺时,询问将牌Q。\n \n

    \n

    \n \n 但同伴告知没有将牌Q。\n \n

    \n

    \n \n 这时怎么办?张永革已经有3墩♠、2墩♥、1墩♦、3墩♣9个顶张大牌赢墩,如果♣3-2分布或者有J甚至有10,则还有2墩♣长牌赢墩,同时以♣做将牌还可以将吃一次♦,这样就有了12墩,如果同伴有♦K,或者单张♠J,或者有♥J时♥是单张Q,或者♥2-2等,这些综合概率,已经远超50%,经过综合考虑,张永革决定叫出颇具想象力的叫品:7♣。\n \n

    \n

    \n \n 全手牌如下:\n \n

    \n
    \n
    \n \n
    \n
    \n \n 7♣是铁牌\n \n
    \n
    \n

    \n \n 联手牌配置非常完美,首攻是将牌,张永革明手放小吃住东家的9,拔掉♦A,打将牌到明手的A,将吃♦,拔♠A,大将吃♠,吊将牌吃到13墩。\n \n

    \n
    \n

    \n \n 这副牌在公开团体所有牌桌中,有硬拼7♥的,但由于将牌的不利分布而宕掉,也有做得好一些的叫7NT,7NT确实要好于7♥,在没有首攻♠的情况下,虽然打不对♥,但却可以对东西方施行双挤。\n \n

    \n

    \n \n 可是,7♣才是联手的最佳定约。\n \n

    \n

    \n \n \n
    \n
    \n
    \n

    \n

    \n \n \n

    \n
    \n \n \n
    \n

    \n \n \n
    \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n \n \n \n \n \n 点击\n \n \n \n \n 阅读原文\n \n \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 别忘了点个在看哦!转发那就太好了!\n \n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n \n \n \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 亚太赛每日一牌回顾:剥光投入显身手\n \n \n

    \n

    \n \n 相关链接:\n \n 亚太赛每日一牌回顾:连氏自然轻松满贯\n \n \n

    \n

    \n \n 相关链接:\n \n 亚太赛每日一牌回顾:心中有挡、无将不慌\n \n \n

    \n

    \n \n 相关链接:\n \n 亚太赛每日一牌回顾:精彩的四红心\n \n \n

    \n

    \n \n 相关链接:\n \n 亚太赛每日一牌回顾:有趣的四红心\n \n \n

    \n

    \n \n 相关链接:\n \n 风雨十四载、再夺亚太冠\n \n \n

    \n

    \n \n 相关链接:\n \n 荏苒十四载、再战亚太赛\n \n \n

    \n

    \n \n 相关链接:\n \n 十四年归来仍是追风少年\n \n \n

    \n

    \n \n 相关链接:\n \n 曼谷重启亚太大赛\n \n \n

    \n

    \n \n 相关链接:\n \n 第53届亚太桥牌锦标赛落幕,欧公赛决赛法国内战\n \n \n

    \n

    \n \n 相关链接:\n \n 中国桥牌队亚太锦标赛大四喜,欧公赛女子团体冠军产生\n \n \n

    \n

    \n \n 相关链接:\n \n 欧公赛公团齐老板被淘汰、亚太赛迎来最后高潮\n \n \n

    \n

    \n \n 相关链接:\n \n 欧公赛公团结束首轮淘汰赛、亚太赛接近尾声\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开桥牌锦标赛公团预赛结束、亚太赛完成上半程比赛\n \n \n

    \n

    \n \n 相关链接:\n \n 亚太桥牌锦标赛结束女团、混团第一循环比赛\n \n \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 欧洲公开桥牌锦标赛公团女团开赛,亚太赛中国占据三项第一\n \n \n

    \n

    \n \n 相关链接:\n \n 亚太赛第一天中国女队占优,欧洲公开桥牌锦标赛混团冠军产生\n \n \n

    \n

    \n \n 相关链接:\n \n 亚太桥牌锦标赛开幕,欧洲公开桥牌锦标赛产生老年团体冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 第53届亚太桥牌锦标赛即将开赛\n \n \n

    \n

    \n \n 相关链接:\n \n 第四届亚洲杯结束、中国香港捧起“宁波杯”\n \n \n

    \n

    \n \n 相关链接:\n \n 2022年亚洲杯开赛\n \n \n

    \n

    \n \n 相关链接:\n \n 第三届亚洲杯桥牌赛开幕\n \n \n

    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/krlt_NpTNqw1TntMjo3n-Q", + "creator": "标兵桥牌", + "pubDate": "Tue, 03 Jun 2025 00:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDTH0sTiajz6mMGDUjHnftbibdib7FEvMaiahjxG8C2mKODhpuRaUtBEnLTyDUl3T8iciaqCAYSUSymeB1ZA/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDTH0sTiajz6mMGDUjHnftbibdib7FEvMaiahjxG8C2mKODhpuRaUtBEnLTyDUl3T8iciaqCAYSUSymeB1ZA/0?wx_fmt=jpeg", + "id": "2247512589_1", + "language": "", + "folder": "桥牌", + "feed": "标兵1桥牌", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "dac49435f96482edfe68df0fb36a008c", + "highlights": [] + }, + { + "title": "亚太赛每日一牌回顾:剥光投入显身手", + "description": "亚太锦标赛结束了,中国男队夺得冠军,在比赛期间所写的“每日一牌”,比赛期间并没有发出,从今天开始,给桥友们提供迟到的每日一牌。", + "content": "
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n \n \n 新朋友\n \n \n \n \n \n
    \n

    \n \n \n 点击\n \n \n \n 标兵桥牌\n \n \n \n

    \n

    \n \n \n 关注微信\n \n \n

    \n
    \n \n \n 老朋友\n \n \n
    \n

    \n \n \n 点击\n \n \n \n 右上角\n \n \n \n

    \n

    \n \n \n 分享本文\n \n \n

    \n
    \n \n \n \n
    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n 亚太锦标赛结束了,中国男队夺得冠军,在比赛期间所写的“每日一牌”,比赛期间并没有发出,从今天开始,给桥友们提供迟到的每日一牌。\n \n \n

    \n
    \n

    \n \n 5月21日每日一牌:剥光投入显身手\n \n

    \n

    \n \n 打牌的基本技术是读牌,认真阅读对方的牌情后,方能找到正确的路线。\n \n

    \n

    \n \n 下面这副牌发生在第二循环对阵印度尼西亚,孙纲的主打堪称精准。\n \n

    \n
    \n
    \n \n
    \n
    \n \n 孙纲主打5♣\n \n
    \n
    \n

    \n \n 西家是孙纲,开叫强梅花,北家加倍显示双高花,蔡阳再加倍表示8点以上,进局逼叫,南家阻击3♠,孙纲先Pass,在同伴加倍后,跳叫5♣成为最后定约。\n \n

    \n

    \n \n 首攻是♠A,继续拔K,庄家将吃后,如何考虑做庄?\n \n

    \n
    \n
    \n \n
    \n
    \n \n 防守从两张♠大牌开始\n \n
    \n
    \n

    \n \n 做成定约的关键是如何避免♦有两个输张,飞♥是没有用的,解决不了♦的问题。♦的普通打法是飞J,但这副牌成立吗?\n \n

    \n

    \n \n 孙纲先出将牌到明手,都有跟出,再拔♥AK明手将吃♥,南北方都跟出,然后孙纲出将牌到手中,北家跟出了两张将牌,南家垫♠。局面如下:\n \n

    \n
    \n
    \n \n
    \n
    \n \n 北家有2张将牌\n \n
    \n
    \n

    \n \n 现在仔细想一想,北家是什么牌?\n \n

    \n

    \n \n 南北方的♠很像是5-5配合,而北家的牌也很像是5-5高花,他已经跟出两张将牌,那么他的♦就是单张,即使不是单张,也不会超过2张,所以飞♦J的概率大大降低了。\n \n

    \n

    \n \n 孙纲判断北家是5-5-1-2牌型,于是他打小♦,北家跟小,他明手上Q,全手牌如下:\n \n

    \n
    \n
    \n \n
    \n
    \n \n 打对♦,南家被投入\n \n
    \n
    \n

    \n \n 南家陷入困境,如果A吃,则被投入,如果A不吃,则明手再出♦,就只输一墩♦了。\n \n

    \n

    \n \n 实战中南家A吃住Q,出小♦,孙纲放小,明手10吃到,完成5♣。\n \n

    \n

    \n \n 北家必须第二墩换攻♦,南家马上出A给北家将吃,才能击败5♣。如果南家不上A,而是出小,庄家则可以剥光♥,然后从明手出♠Q,手中垫♦,投入北家,北家只有高花可出,被迫给庄家将吃垫牌,♦输张消失。\n \n

    \n

    \n \n \n

    \n

    \n \n \n
    \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n \n
    \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n \n \n \n \n \n 点击\n \n \n \n \n 阅读原文\n \n \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 别忘了点个在看哦!转发那就太好了!\n \n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n \n \n \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 亚太赛每日一牌回顾:连氏自然轻松满贯\n \n \n

    \n

    \n \n 相关链接:\n \n 亚太赛每日一牌回顾:心中有挡、无将不慌\n \n \n

    \n

    \n \n 相关链接:\n \n 亚太赛每日一牌回顾:精彩的四红心\n \n \n

    \n

    \n \n 相关链接:\n \n 亚太赛每日一牌回顾:有趣的四红心\n \n \n

    \n

    \n \n 相关链接:\n \n 风雨十四载、再夺亚太冠\n \n \n

    \n

    \n \n 相关链接:\n \n 荏苒十四载、再战亚太赛\n \n \n

    \n

    \n \n 相关链接:\n \n 十四年归来仍是追风少年\n \n \n

    \n

    \n \n 相关链接:\n \n 曼谷重启亚太大赛\n \n \n

    \n

    \n \n 相关链接:\n \n 第53届亚太桥牌锦标赛落幕,欧公赛决赛法国内战\n \n \n

    \n

    \n \n 相关链接:\n \n 中国桥牌队亚太锦标赛大四喜,欧公赛女子团体冠军产生\n \n \n

    \n

    \n \n 相关链接:\n \n 欧公赛公团齐老板被淘汰、亚太赛迎来最后高潮\n \n \n

    \n

    \n \n 相关链接:\n \n 欧公赛公团结束首轮淘汰赛、亚太赛接近尾声\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开桥牌锦标赛公团预赛结束、亚太赛完成上半程比赛\n \n \n

    \n

    \n \n 相关链接:\n \n 亚太桥牌锦标赛结束女团、混团第一循环比赛\n \n \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 欧洲公开桥牌锦标赛公团女团开赛,亚太赛中国占据三项第一\n \n \n

    \n

    \n \n 相关链接:\n \n 亚太赛第一天中国女队占优,欧洲公开桥牌锦标赛混团冠军产生\n \n \n

    \n

    \n \n 相关链接:\n \n 亚太桥牌锦标赛开幕,欧洲公开桥牌锦标赛产生老年团体冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 第53届亚太桥牌锦标赛即将开赛\n \n \n

    \n

    \n \n 相关链接:\n \n 第四届亚洲杯结束、中国香港捧起“宁波杯”\n \n \n

    \n

    \n \n 相关链接:\n \n 2022年亚洲杯开赛\n \n \n

    \n

    \n \n 相关链接:\n \n 第三届亚洲杯桥牌赛开幕\n \n \n

    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/wN9dVtuhJM97x5qk8PsV-A", + "creator": "标兵桥牌", + "pubDate": "Mon, 02 Jun 2025 00:02:32 ", + "enclosure": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDTH0sTiajz6mMGDUjHnftbibdib7FEvMaiahjxG8C2mKODhpuRaUtBEnLTyDUl3T8iciaqCAYSUSymeB1ZA/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDTH0sTiajz6mMGDUjHnftbibdib7FEvMaiahjxG8C2mKODhpuRaUtBEnLTyDUl3T8iciaqCAYSUSymeB1ZA/0?wx_fmt=jpeg", + "id": "2247512585_1", + "language": "", + "folder": "桥牌", + "feed": "标兵1桥牌", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "1477f7b86cceef32ddd423d3016b516f", + "highlights": [] + }, + { + "title": "亚太赛每日一牌回顾:连氏自然轻松满贯", + "description": "亚太锦标赛结束了,中国男队夺得冠军,在比赛期间所写的“每日一牌”,比赛期间并没有发出,从今天开始,给桥友们提供迟到的每日一牌。", + "content": "
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n \n \n 新朋友\n \n \n \n \n \n
    \n

    \n \n \n 点击\n \n \n \n 标兵桥牌\n \n \n \n

    \n

    \n \n \n 关注微信\n \n \n

    \n
    \n \n \n 老朋友\n \n \n
    \n

    \n \n \n 点击\n \n \n \n 右上角\n \n \n \n

    \n

    \n \n \n 分享本文\n \n \n

    \n
    \n \n \n \n
    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n 亚太锦标赛结束了,中国男队夺得冠军,在比赛期间所写的“每日一牌”,比赛期间并没有发出,从今天开始,给桥友们提供迟到的每日一牌。\n \n \n

    \n
    \n

    \n \n 5月20日每日一牌:连氏自然轻松满贯\n \n

    \n

    \n \n 中国男队的队长连若旸,使用的叫牌武器是独具风格的“连氏自然“体系,虽然在某些方面不够前卫,但使用熟练、搭档默契,同样具有很强的战斗力。\n \n

    \n

    \n \n 下面这副牌的满贯叫牌,简洁而清晰,没有使用一个约定叫:\n \n

    \n
    \n
    \n \n
    \n
    \n \n 没有使用一个约定叫的满贯\n \n
    \n
    \n

    \n \n 连若旸与连勇使用连氏自然体系,连勇开叫1♦,连若旸虽然♥单张,但牌力不足二盖一,又没有4张高花,所以先应叫1NT,连勇逆叫2♥,显示额外实力,♦和♥至少5-4以上,连若旸加叫3♦,连勇3♠,显示♠大牌的同时,也显示了♣单张,连若旸扣叫4♣,连勇跳叫5♦。这时连若旸面临选择,他认为同伴跳叫显示6张♦,他有两个黑花A,且还有单张,于是加叫6♦。\n \n

    \n

    \n \n 双方叫牌没有使用一个约定叫,全部是自然而流畅地叫品,这也正是连氏自然的特点。\n \n

    \n

    \n \n 6♦是个非常好的定约,首攻♠后,连勇从明手出♥,然后将吃两次♥轻松做成。\n \n

    \n

    \n \n 这副牌全场只有一半牌桌叫到满贯,泰国队停在3NT上,中国男队赢10IMP,缩小了本场比赛的差距。\n \n

    \n

    \n \n \n
    \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n \n
    \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n \n \n \n \n \n 点击\n \n \n \n \n 阅读原文\n \n \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 别忘了点个在看哦!转发那就太好了!\n \n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n \n \n \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 亚太赛每日一牌回顾:心中有挡、无将不慌\n \n \n

    \n

    \n \n 相关链接:\n \n 亚太赛每日一牌回顾:精彩的四红心\n \n \n

    \n

    \n \n 相关链接:\n \n 亚太赛每日一牌回顾:有趣的四红心\n \n \n

    \n

    \n \n 相关链接:\n \n 风雨十四载、再夺亚太冠\n \n \n

    \n

    \n \n 相关链接:\n \n 荏苒十四载、再战亚太赛\n \n \n

    \n

    \n \n 相关链接:\n \n 十四年归来仍是追风少年\n \n \n

    \n

    \n \n 相关链接:\n \n 曼谷重启亚太大赛\n \n \n

    \n

    \n \n 相关链接:\n \n 第53届亚太桥牌锦标赛落幕,欧公赛决赛法国内战\n \n \n

    \n

    \n \n 相关链接:\n \n 中国桥牌队亚太锦标赛大四喜,欧公赛女子团体冠军产生\n \n \n

    \n

    \n \n 相关链接:\n \n 欧公赛公团齐老板被淘汰、亚太赛迎来最后高潮\n \n \n

    \n

    \n \n 相关链接:\n \n 欧公赛公团结束首轮淘汰赛、亚太赛接近尾声\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开桥牌锦标赛公团预赛结束、亚太赛完成上半程比赛\n \n \n

    \n

    \n \n 相关链接:\n \n 亚太桥牌锦标赛结束女团、混团第一循环比赛\n \n \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 欧洲公开桥牌锦标赛公团女团开赛,亚太赛中国占据三项第一\n \n \n

    \n

    \n \n 相关链接:\n \n 亚太赛第一天中国女队占优,欧洲公开桥牌锦标赛混团冠军产生\n \n \n

    \n

    \n \n 相关链接:\n \n 亚太桥牌锦标赛开幕,欧洲公开桥牌锦标赛产生老年团体冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 第53届亚太桥牌锦标赛即将开赛\n \n \n

    \n

    \n \n 相关链接:\n \n 第四届亚洲杯结束、中国香港捧起“宁波杯”\n \n \n

    \n

    \n \n 相关链接:\n \n 2022年亚洲杯开赛\n \n \n

    \n

    \n \n 相关链接:\n \n 第三届亚洲杯桥牌赛开幕\n \n \n

    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/L75dvQEdNwHh-Zfx5SPVUA", + "creator": "标兵桥牌", + "pubDate": "Sun, 01 Jun 2025 00:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDTH0sTiajz6mMGDUjHnftbibdib7FEvMaiahjxG8C2mKODhpuRaUtBEnLTyDUl3T8iciaqCAYSUSymeB1ZA/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDTH0sTiajz6mMGDUjHnftbibdib7FEvMaiahjxG8C2mKODhpuRaUtBEnLTyDUl3T8iciaqCAYSUSymeB1ZA/0?wx_fmt=jpeg", + "id": "2247512579_1", + "language": "", + "folder": "桥牌", + "feed": "标兵1桥牌", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "7f580d300fabb2b9fe49f6576a73d9f3", + "highlights": [] + }, + { + "title": "亚太赛每日一牌回顾:心中有挡、无将不慌", + "description": "亚太锦标赛结束了,中国男队夺得冠军,在比赛期间所写的“每日一牌”,比赛期间并没有发出,从今天开始,给桥友们提供迟到的每日一牌。", + "content": "
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n \n \n 新朋友\n \n \n \n \n \n
    \n

    \n \n \n 点击\n \n \n \n 标兵桥牌\n \n \n \n

    \n

    \n \n \n 关注微信\n \n \n

    \n
    \n \n \n 老朋友\n \n \n
    \n

    \n \n \n 点击\n \n \n \n 右上角\n \n \n \n

    \n

    \n \n \n 分享本文\n \n \n

    \n
    \n \n \n \n
    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n 亚太锦标赛结束了,中国男队夺得冠军,在比赛期间所写的“每日一牌”,比赛期间并没有发出,从今天开始,给桥友们提供迟到的每日一牌。\n \n \n

    \n
    \n

    \n \n 5月19日每日一牌:心中有挡、无将不慌\n \n

    \n

    \n \n 桥友们都知道著名的哈曼规则第一条:如果3NT是一个可以选择的定约,就叫3NT。\n \n

    \n

    \n \n 套用我们的俗话,叫做:走过路过,不要错过3NT。\n \n

    \n

    \n \n 当我们在叫牌中可以选择时,3NT是优选,这个很容易理解。但是,如果看上去,3NT并不是看上去一个可以选择的定约时,还能选3NT吗?\n \n

    \n

    \n \n 中国男队的蔡阳,在下面这副牌的处理,就给桥友们做了一个榜样:\n \n

    \n
    \n
    \n \n
    \n
    \n \n 南家如何选择叫品?\n \n
    \n
    \n

    \n \n 西家开叫3♥,两个Pass转到南家,选择什么叫品?\n \n

    \n

    \n \n 可供选择的大概有:Pass,加倍,4♦,3NT。\n \n

    \n

    \n \n 没有一个叫品是舒服的:\n \n

    \n

    \n \n · Pass太委屈,自己有15点,对方阻击叫,如果同伴有一些点力,就被对方的阻击叫拦住了,绝不情愿。\n \n

    \n

    \n \n · 加倍的问题是♠太短,同伴如果叫3♠,还是没解决任何问题,如果跳叫4♠,也很不舒服。\n \n

    \n

    \n \n · 4♦的好处是叫出了自己最长最强的花色,但这牌要打什么呢?5♦太远,4♦都有可能打不成。\n \n

    \n

    \n \n · 3NT不可选的原因是对方长套花色没有挡张,但除了这一缺点,就没有任何缺陷了。点力足够,赢墩也有,至于挡张嘛…… 管它呢,别忘了你还有个同伴,同伴说不定能帮你挡住,就算同伴挡不住,没准西家间张结构还不攻呢。\n \n

    \n
    \n

    \n \n 蔡阳选择3NT,他成功了吗?\n \n

    \n

    \n \n 全手牌如下:\n \n

    \n
    \n
    \n \n
    \n
    \n \n 3NT是最佳定约\n \n
    \n
    \n

    \n \n 这是中国男队第一循环对阵中国香港的一副牌,实战中西家首攻♠Q,蔡阳先取6墩♠,东家放弃了♦,蔡阳再拿6墩♦,加上♥A,全卷13墩。\n \n

    \n

    \n \n 看,西家♥连接张都没首攻,假如明手的♥是Kx或者什么都没有,3NT都做成啦!\n \n

    \n

    \n \n \n
    \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n \n
    \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n \n \n \n \n \n 点击\n \n \n \n \n 阅读原文\n \n \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 别忘了点个在看哦!转发那就太好了!\n \n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n \n \n \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 亚太赛每日一牌回顾:精彩的四红心\n \n \n

    \n

    \n \n 相关链接:\n \n 亚太赛每日一牌回顾:有趣的四红心\n \n \n

    \n

    \n \n 相关链接:\n \n 风雨十四载、再夺亚太冠\n \n \n

    \n

    \n \n 相关链接:\n \n 荏苒十四载、再战亚太赛\n \n \n

    \n

    \n \n 相关链接:\n \n 十四年归来仍是追风少年\n \n \n

    \n

    \n \n 相关链接:\n \n 曼谷重启亚太大赛\n \n \n

    \n

    \n \n 相关链接:\n \n 第53届亚太桥牌锦标赛落幕,欧公赛决赛法国内战\n \n \n

    \n

    \n \n 相关链接:\n \n 中国桥牌队亚太锦标赛大四喜,欧公赛女子团体冠军产生\n \n \n

    \n

    \n \n 相关链接:\n \n 欧公赛公团齐老板被淘汰、亚太赛迎来最后高潮\n \n \n

    \n

    \n \n 相关链接:\n \n 欧公赛公团结束首轮淘汰赛、亚太赛接近尾声\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开桥牌锦标赛公团预赛结束、亚太赛完成上半程比赛\n \n \n

    \n

    \n \n 相关链接:\n \n 亚太桥牌锦标赛结束女团、混团第一循环比赛\n \n \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 欧洲公开桥牌锦标赛公团女团开赛,亚太赛中国占据三项第一\n \n \n

    \n

    \n \n 相关链接:\n \n 亚太赛第一天中国女队占优,欧洲公开桥牌锦标赛混团冠军产生\n \n \n

    \n

    \n \n 相关链接:\n \n 亚太桥牌锦标赛开幕,欧洲公开桥牌锦标赛产生老年团体冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 第53届亚太桥牌锦标赛即将开赛\n \n \n

    \n

    \n \n 相关链接:\n \n 第四届亚洲杯结束、中国香港捧起“宁波杯”\n \n \n

    \n

    \n \n 相关链接:\n \n 2022年亚洲杯开赛\n \n \n

    \n

    \n \n 相关链接:\n \n 第三届亚洲杯桥牌赛开幕\n \n \n

    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/z0GqwfNMdTFUEdkOglYv1A", + "creator": "标兵桥牌", + "pubDate": "Sat, 31 May 2025 00:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDTH0sTiajz6mMGDUjHnftbibdib7FEvMaiahjxG8C2mKODhpuRaUtBEnLTyDUl3T8iciaqCAYSUSymeB1ZA/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDTH0sTiajz6mMGDUjHnftbibdib7FEvMaiahjxG8C2mKODhpuRaUtBEnLTyDUl3T8iciaqCAYSUSymeB1ZA/0?wx_fmt=jpeg", + "id": "2247512576_1", + "language": "", + "folder": "桥牌", + "feed": "标兵1桥牌", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "bf069e2911ea545aad791222bdeddb68", + "highlights": [] + }, + { + "title": "亚太赛每日一牌回顾:精彩的四红心", + "description": "亚太锦标赛结束了,中国男队夺得冠军,在比赛期间所写的“每日一牌”,比赛期间并没有发出,从今天开始,给桥友们提供迟到的每日一牌。", + "content": "
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n \n \n 新朋友\n \n \n \n \n \n
    \n

    \n \n \n 点击\n \n \n \n 标兵桥牌\n \n \n \n

    \n

    \n \n \n 关注微信\n \n \n

    \n
    \n \n \n 老朋友\n \n \n
    \n

    \n \n \n 点击\n \n \n \n 右上角\n \n \n \n

    \n

    \n \n \n 分享本文\n \n \n

    \n
    \n \n \n \n
    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n 亚太锦标赛结束了,中国男队夺得冠军,在比赛期间所写的“每日一牌”,比赛期间并没有发出,从今天开始,给桥友们提供迟到的每日一牌。\n \n \n

    \n
    \n

    \n \n 5月18日每日一牌:精彩的4♥\n \n

    \n

    \n \n 我们常说,桥牌比的就是判断力和执行力,特别是攻防环节,先判断牌型和牌点位置,再执行完成目标。\n \n

    \n

    \n \n 本届亚太赛上,中国男队的连勇,在第一循环迎战澳大利亚时,打出了下面这副精彩的做庄。\n \n

    \n
    \n
    \n \n
    \n
    \n \n 东家主打4♥\n \n
    \n
    \n

    \n \n 东家连勇开叫1♥,西家连若旸的3♦是限制性加叫或更好,北家争叫4♣,西家4♥结束叫牌。\n \n

    \n

    \n \n 首攻是♣,北家兑现两墩♣后,出♦脱手,现在如何考虑?\n \n

    \n
    \n
    \n \n
    \n
    \n \n 如何把♠和♦的输张控制在一个\n \n
    \n
    \n

    \n \n ♦的飞牌如果成功,什么时候都可以进行,连勇♦A拿住,两轮将牌停在明手,北家都跟出,如果♦Q在南家,就需要做好♠垫掉♦,而联手的♠,有可能输1-2墩,在限制只输1墩♠的情况下,能否获得3墩就是焦点。\n \n

    \n

    \n \n 北家在4阶上争叫,他很可能有♠K,但也不是绝对的,如果北家是3张♠且有K,庄家几乎无法避免输掉两墩♠,如果是两张♠且有K,则可以先拔♠A,北家不解封,就再拔掉♦K,打♠投入北家。\n \n

    \n

    \n \n 但北家如果没有♠K呢?可以飞中♠K且送3-3,用第4张♠垫♦,也可以打南家4张以上♠和♦Q,挤住南家。\n \n

    \n

    \n \n 所以,现在首先需要进行的,就是♠K的判断。\n \n

    \n

    \n \n 连勇找到一个好办法,他从明手出小♠,此时北家有♠K很难不上,至少是给北家增加了一定的压力,如果北家出小牌,可以假设南家有♠K,然后根据北家的牌型对南家实施不同的操作:\n \n

    \n

    \n \n 兑现♦K,北家如果跟出♦,则♠不超过2张,可以兑现将牌挤住南家;如果北家跟不出♦,则♠是3-3,下一圈飞♠K就可以做好第四张♠。\n \n

    \n
    \n
    \n \n
    \n
    \n \n 从明手出♠做试探打法\n \n
    \n
    \n

    \n \n 实战中,当连勇打小♠到10,南家打出K,意图迷惑连勇,但连勇仍然采取挤牌路线做成了定约。\n \n

    \n

    \n \n 全手牌如下:\n \n

    \n
    \n
    \n \n
    \n
    \n \n 南家被挤住\n \n
    \n
    \n

    \n \n 这副牌在锦标赛公团中,只有连勇一桌做成了4♥。与中国台北队对阵的新西兰的庄家,因为北家争叫♣,判断北家有♠K,他决定打北家双张K,但没有成功,亚洲桥王黄光辉建议,如果打♠K在北家,可以先从明手出小♠,这正是连勇实战的打法。\n \n

    \n

    \n \n 澳大利亚的庄家则飞♠K,虽然成功,但他却追求3-3,定约宕掉。\n \n

    \n

    \n \n \n
    \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n \n \n \n \n \n 点击\n \n \n \n \n 阅读原文\n \n \n \n \n \n
    \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n
    \n
    \n
    \n
    \n
    \n

    \n \n \n 别忘了点个在看哦!转发那就太好了!\n \n \n

    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n \n \n \n
    \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 亚太赛每日一牌回顾:有趣的四红心\n \n \n

    \n

    \n \n 相关链接:\n \n 风雨十四载、再夺亚太冠\n \n \n

    \n

    \n \n 相关链接:\n \n 荏苒十四载、再战亚太赛\n \n \n

    \n

    \n \n 相关链接:\n \n 十四年归来仍是追风少年\n \n \n

    \n

    \n \n 相关链接:\n \n 曼谷重启亚太大赛\n \n \n

    \n

    \n \n 相关链接:\n \n 第53届亚太桥牌锦标赛落幕,欧公赛决赛法国内战\n \n \n

    \n

    \n \n 相关链接:\n \n 中国桥牌队亚太锦标赛大四喜,欧公赛女子团体冠军产生\n \n \n

    \n

    \n \n 相关链接:\n \n 欧公赛公团齐老板被淘汰、亚太赛迎来最后高潮\n \n \n

    \n

    \n \n 相关链接:\n \n 欧公赛公团结束首轮淘汰赛、亚太赛接近尾声\n \n \n

    \n

    \n \n 相关链接:\n \n 欧洲公开桥牌锦标赛公团预赛结束、亚太赛完成上半程比赛\n \n \n

    \n

    \n \n 相关链接:\n \n 亚太桥牌锦标赛结束女团、混团第一循环比赛\n \n \n \n
    \n
    \n

    \n

    \n \n 相关链接:\n \n 欧洲公开桥牌锦标赛公团女团开赛,亚太赛中国占据三项第一\n \n \n

    \n

    \n \n 相关链接:\n \n 亚太赛第一天中国女队占优,欧洲公开桥牌锦标赛混团冠军产生\n \n \n

    \n

    \n \n 相关链接:\n \n 亚太桥牌锦标赛开幕,欧洲公开桥牌锦标赛产生老年团体冠军\n \n \n

    \n

    \n \n 相关链接:\n \n 第53届亚太桥牌锦标赛即将开赛\n \n \n

    \n

    \n \n 相关链接:\n \n 第四届亚洲杯结束、中国香港捧起“宁波杯”\n \n \n

    \n

    \n \n 相关链接:\n \n 2022年亚洲杯开赛\n \n \n

    \n

    \n \n 相关链接:\n \n 第三届亚洲杯桥牌赛开幕\n \n \n

    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/YsbU0ZqyoOzWP_V3IZXtzA", + "creator": "标兵桥牌", + "pubDate": "Fri, 30 May 2025 00:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDTH0sTiajz6mMGDUjHnftbibdib7FEvMaiahjxG8C2mKODhpuRaUtBEnLTyDUl3T8iciaqCAYSUSymeB1ZA/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/iauJ3TcMNYDTH0sTiajz6mMGDUjHnftbibdib7FEvMaiahjxG8C2mKODhpuRaUtBEnLTyDUl3T8iciaqCAYSUSymeB1ZA/0?wx_fmt=jpeg", + "id": "2247512572_1", + "language": "", + "folder": "桥牌", + "feed": "标兵1桥牌", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "e8464543f510223436e2028719dd5ce0", + "highlights": [] + }, { "title": "亚太赛每日一牌回顾:有趣的四红心", "description": "亚太锦标赛结束了,中国男队夺得冠军,在比赛期间所写的“每日一牌”,比赛期间并没有发出,从今天开始,给桥友们提供迟到的每日一牌。", @@ -3504,20 +4683,7 @@ "tags": [], "hash": "b2ba7615ea76a0fee1a3212e0c63f7bd", "highlights": [] - } - ], - "folder": "双语", - "name": "21世纪", - "language": "zh", - "hash": "ee7294aadb9599f93460cd631ace5a90" - }, - { - "title": "中国日报: 双语新闻", - "subtitle": "", - "link": "http://language.chinadaily.com.cn/news_bilingual.html", - "image": null, - "description": "", - "items": [ + }, { "title": "研究:置身大自然会对你体验时间的方式产生深远影响", "description": "
    \n\n \n\n \n \n\n

    研究发现,置身大自然会让你感觉时间过得更慢,有助于将你的注意力从眼前转移到未来的需求上。你将能够延迟即时满足感,转而追求长期回报,而不是永远生活在“只求过得去”的状态中。

    \n

     

    \n
    \n \n
    \n Photo/Pexels \n
    \n
    \n

     

    \n

    Do you ever get that feeling that there aren't enough hours in the day? That time is somehow racing away from you, and it is impossible to fit everything in. But then, you step outside into the countryside and suddenly everything seems slower, more relaxed, like time has somehow changed.

    \n

    你是否有过这样的感觉:一天的时间不够用?时间不知不觉地从你身边溜走,不可能把所有事情都安排好。但是,当你走到郊外,突然间一切都变得缓慢、轻松起来,就好像时间发生了某种变化。

    \n

     

    \n

    It's not just you – recent research showed nature can regulate our sense of time.

    \n

    不仅仅是你,最近的研究表明,大自然可以调节我们的时间感。

    \n

     

    \n

    For many of us, the combined demands of work, home and family mean that we are always feeling like we don't have enough time. Time poverty has also been exacerbated by digital technologies. Permanent connectivity extends working hours and can make it difficult to switch off from the demands of friends and family.

    \n

    对我们许多人来说,工作、家庭和家人的综合需求意味着我们总是感觉时间不够用。数字技术也加剧了时间贫乏。永久连接延长了工作时间,也使我们难以摆脱朋友和家人的要求。

    \n

     

    \n

    Recent research suggests that the antidote to our lack of time may lie in the natural world. Psychologist Richardo Correia, at the University of Turku in Finland, found that being in nature may change how we experience time and, perhaps, even give us the sense of time abundance.

    \n

    最新研究表明,解决我们时间匮乏问题的良方可能在于自然世界。芬兰图尔库大学的心理学家理查多·科雷亚发现,身处在大自然中可能会改变我们对时间的体验方式,甚至可能让我们感觉到时间的充裕。

    \n

     

    \n

    Correia examined research which compared people's experiences of time when they performed different types of tasks in urban and natural environments. These studies consistently showed that people report a sense of expanded time when they were in nature compared to when they were in an urban environment.

    \n

    科雷亚查阅了比较人们在城市和自然环境中执行不同类型任务时对时间体验的研究。这些研究一致表明,与在城市环境中相比,人们在大自然中会有时间更充裕的感觉。

    \n

     

    \n

    For example, people are more likely to perceive a walk in the countryside as longer than a walk of the same length in the city. Similarly, people report perceiving time as passing more slowly while performing tasks in natural green environments than in urban environments. Nature seems to slow and expand our sense of time.

    \n

    例如,与在城市中散步相比,人们更倾向于认为在郊外散步的时间更长。同样,与城市环境相比,人们在绿色自然环境中执行任务时会觉得时间过得更慢。大自然似乎减缓并扩展了我们的时间感。

    \n

     

    \n

    It's not just our sense of time in the moment which appears to be altered by the natural world, it's also our sense of the past and future. Previous research shows that spending time in nature helps to shift our focus from the immediate moment towards our future needs. So rather than focusing on the stress of the demands on our time, nature helps us to see the bigger picture.

    \n

    自然世界似乎不仅改变了我们当下的时间感,还改变了我们对过去和未来的时间感。先前的研究表明,在大自然中度过的时间有助于将我们的注意力从眼前转移到未来的需求上。因此,大自然会帮助我们看到更广阔的前景,而不是专注于时间要求带给我们的压力。

    \n

     

    \n

    This can help us to prioritise our actions so that we meet our long-term goals rather than living in a perpetual state of \"just about keeping our head above water\".

    \n

    这可以帮助我们确定行动的轻重缓急,从而实现我们的长期目标,而不是永远生活在“只求过得去”的状态中。

    \n

     

    \n

    This is in part because spending time in nature appears to make us less impulsive, enabling us to delay instant gratification in favour of long-term rewards.

    \n

    这在一定程度上是因为在大自然中度过的时间似乎会让我们不那么冲动,使我们能够延迟即时满足感,转而追求长期回报。

    \n

     

    \n
    \n \n
    \n Photo/Pexels \n
    \n
    \n

     

    \n

    Why does nature affect our sense of time?

    \n

    为什么大自然会影响我们的时间观念?

    \n

     

    \n

    Spending time in nature is known to have many benefits for health and wellbeing. Having access to natural spaces such as beaches, parks and woodlands is associated with reduced anxiety and depression, improved sleep, reduced levels of obesity and cardiovascular disease, and improved wellbeing.

    \n

    众所周知,在大自然中度过时光对健康和幸福有很多好处。拥有海滩、公园和林地等自然空间与减少焦虑和抑郁、改善睡眠、降低肥胖和心血管疾病水平以及提高幸福感有关。

    \n

     

    \n

    Some of these benefits may explain why being in nature alters our experience of time.

    \n

    其中一些好处或许可以解释为什么身处大自然中会改变我们对时间的体验。

    \n

     

    \n

    The way we experience time is shaped by our internal biological state and the events going on in the world around us. As a result, emotions such as stress, anxiety and fear can distort our sense of the passage of time.

    \n

    我们体验时间的方式是由我们的内在生理状态和周围世界发生的事件决定的。因此,压力、焦虑和恐惧等情绪会扭曲我们对时间流逝的感知。

    \n

     

    \n

    The relaxing effect of natural environments may counter stress and anxiety, resulting in a more stable experience of time. Indeed, the absence of access to nature during COVID-19 may help to explain why people's sense of time became so distorted during the pandemic lockdowns.

    \n

    自然环境的放松效果可以抵消压力和焦虑,使我们对时间的体验更加稳定。事实上,在新冠疫情期间无法接触大自然可能有助于解释为什么在大流行病封锁期间人们的时间感会变得如此扭曲。

    \n

     

    \n

    In the short term, being away from the demands of modern day life may provide the respite needed to re-prioritise life, and reduce time pressure by focusing on what actually needs to be done. In the longer term, time in nature may help to enhance our memory and attention capacity, making us better able to deal with the demands on our time.

    \n

    从短期来看,远离现代生活的需求可以让我们得到喘息的机会,从而重新确定生活的优先次序,并通过专注于真正需要做的事情来减轻时间压力。从长远来看,在大自然中度过的时间可能有助于增强我们的记忆力和注意力,使我们能够更好地处理对时间的需求。

    \n

     

    \n

    Accessing nature

    \n

    亲近大自然

    \n

     

    \n

    Getting out into nature may sound like a simple fix, but for many people, particularly those living in urban areas, nature can be hard to access. Green infrastructure such as trees, woodlands, parks and allotments in and around towns and cities are essential to making sure the benefits of time in nature are accessible to everyone.

    \n

    到大自然中去听起来似乎很简单,但对很多人来说,尤其是生活在城市地区的人,可能很难接触到大自然。要确保每个人都能享受到大自然带来的益处,城镇及周边地区的树木、林地、公园和种植园等绿色基础设施是必不可少的。

    \n

     

    \n

    If spending time in nature isn't possible for you, there are other ways that you can regain control of your time. Start by closely examining how you use time throughout your week. Auditing your time can help you see where time is being wasted and to identify action to help you to free up more time in your life.

    \n

    如果您无法在大自然中度过美好时光,您还可以通过其他方式重新掌控自己的时间。首先,仔细检查自己一周中如何利用时间。审核时间可以帮助您发现时间浪费在哪里,并确定行动,帮助您在生活中腾出更多时间。

    \n

     

    \n

    Alternatively, try to set yourself some boundaries in how you use time. This could be limiting when you access emails and social media, or it could be booking in time in your calendar to take a break. Taking control of your time and how you use it can help to you overcome the sense that time is running away from you.

    \n

    或者,在如何使用时间方面为自己设定一些界限。这可以是限制您访问电子邮件和社交媒体的时间,也可以是在您的日历中预定休息时间。控制好自己的时间和使用方式,有助于克服 “时间正在离你远去”的感觉。

    \n

     

    \n

    来源:The Conversation、Science Alert、煎蛋网

    \n

    编辑:丹妮

    \n

    审校:董静、齐磊

    \n\n
    ", @@ -3628,28 +4794,6 @@ "hash": "99b651d543cad1d20b6bf4c9c9210913", "highlights": [] }, - { - "title": "自学python必看的书籍《Python编程:从入门到实践》", - "description": "内容简介本书是一本针对所有层次的Python 读者而作的Python 入门书。", - "content": "
    \n

    \n \n
    \n
    \n

    \n

    \n \n \"图片\"\n \n

    \n

    \n \n \n 内容简介\n \n \n

    \n

    \n \n 本书是一本针对所有层次的Python 读者而作的Python 入门书。全书分两部分:\n \n

    \n

    \n \n 第一部分介绍用Python 编程所必须了解的基本概念,包括matplotlib、NumPy 和Pygal 等强大的Python 库和工具介绍,以及列表、字典、if 语句、类、文件与异常、代码测试等内容;\n \n

    \n

    \n \n 第二部分将理论付诸实践,讲解如何开发三个项目,包括简单的Python 2D 游戏开发如何利用数据生成交互式的信息图,以及创建和定制简单的Web 应用,并帮读者解决常见编程问题和困惑。\n \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n \n 目录\n \n \n

    \n

    \n \n
    \n
    \n

    \n
    \n \n \"图片\"\n \n
    \n
    \n \n \"图片\"\n \n
    \n
    \n \n \"图片\"\n \n
    \n
    \n \n \"图片\"\n \n
    \n
    \n \n \"图片\"\n \n
    \n
    \n \n \"图片\"\n \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n 书籍的分享格式为PDF电子版,小伙伴们手机也可以阅读学习哦!\n \n \n

    \n

    \n \n \n 书籍终归只是辅助,想要真的深入学习python,当然还要有系统的学习方法和学习教程才可以让你在学习的路上一帆风顺哦!\n \n \n

    \n

    \n \n \n 本套视频一共400集,本套视频共分3季:\n \n \n

    \n

    \n \n 第一季 【基础篇】Python基础 (115集)\n \n

    \n

    \n \n 第二季 【提高篇】Python深入和扩展(100集)\n \n

    \n

    \n \n 第三季 【扩展篇】网络编程、多线程、扩展库(85集)\n \n

    \n

    \n \n 第四季 【高手篇】算法、Python源码、函数式编程、手工实现神经网络(100集)\n \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n
    \n \n \"图片\"\n \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n 以上这些教程我已经为大家打包准备好了,希望对正在学习的你有所帮助!\n \n \n

    \n
      \n
    1. \n

      \n \n \n \n 获取方式:公众号私信回复“学习”即可\n \n \n \n

      \n
    2. \n
    3. \n

      \n \n \n \n 需要的小伙伴可以多多点赞转发 让更多的人看到\n \n \n \n

      \n
    4. \n
    \n
    \n \n
    \n
    \n
    \n

    \n \n \n

    \n
    \n", - "category": "", - "link": "https://mp.weixin.qq.com/s/c_5LNvw3SAjwBdMs9TRztQ", - "creator": "python开发工程师", - "pubDate": "Sat, 05 Jul 2025 06:00:00 ", - "enclosure": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZxW21P2Wlme8ePMGec1BlUXOFYxYplAyK6CLl2x2bH4d2ZWosUWkQlGPFHSr7MpMNoggFfhVnvzag/0?wx_fmt=jpeg", - "enclosureType": "image/jpeg", - "image": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZxW21P2Wlme8ePMGec1BlUXOFYxYplAyK6CLl2x2bH4d2ZWosUWkQlGPFHSr7MpMNoggFfhVnvzag/0?wx_fmt=jpeg", - "id": "2247511294_2", - "language": "", - "folder": "IT技术", - "feed": "python开发工程师", - "read": false, - "favorite": false, - "created": false, - "tags": [], - "hash": "7dcfc8e3ce8ac73a506470cf4f75b40b", - "highlights": [] - }, { "title": "自学python必看的书籍《Python编程:从入门到实践》PDF版拿走不谢", "description": "内容简介本书是一本针对所有层次的Python 读者而作的Python 入门书。", @@ -3685,6 +4829,490 @@ "image": "http://mmbiz.qpic.cn/mmbiz_png/iauJ3TcMNYDQ33jO8a2zUczSSicibichw0bpGfYAkiaIRTKsGIKPOPFdBVOCiaug6swiaLbg17pPyNPp1xL2Hw2EE84AA/0?wx_fmt=png", "description": "", "items": [ + { + "title": "一周练完!100个Python实战项目,太实用了,绝对不能错过(附完整版源码+教程)", + "description": "Python 是一种极具可读性和通用性的编程语言。", + "content": "
    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n Python 是一种极具可读性和通用性的编程语言\n \n \n \n \n \n \n 。Python 这个名字的灵感来自于英国喜剧团体 Monty Python,它的开发团队有一个重要的基础目标,就是使语言使用起来很有趣。Python 易于设置,并且是用相对直接的风格来编写,对错误会提供即时反馈,对初学者而言是个很好的选择。\n \n

    \n

    \n \n Python 是一种多范式语言,也就是说,它支持多种编程风格,包括脚本和面向对象,这使得它适用于通用目的。随着越来越多地在工业中,被诸如联合太空联盟\n \n \n \n \n \n \n (NASA 的主要飞机支持承包商)和工业光魔\n \n \n \n \n \n \n (VFX 和卢卡斯影业的动画工作室\n \n \n \n \n \n \n )等组织使用,Python 为那些寻求额外编程语言的人提供了巨大的潜力 。\n \n

    \n

    \n \n 当一个重要的被称为 comp.lang.python 的 Python 新闻组\n \n \n \n \n \n \n 在 1994 年形成时,Python 的用户基础不断增长,这为 Python 成为开源开发中最受欢迎的编程语言之一铺平了道路。\n \n

    \n

    \n \n Python当下真的很火。Python实战项目,也一直尤为关注,接下来,和大家介绍下100个Python练手的实战项目。\n \n

    \n
    \n \"图片\"\n
    \n
    \n \"图片\"\n
    \n

    \n \n \n \n 每个项目都有详细的视频讲解,跟随老师带你一步步完成敲代码的过程,在过程中结合多学知识提升能力,帮助怒更好地完成练习。\n \n \n \n \n
    \n
    \n

    \n

    \n \n 100个Python项目列表:\n \n

    \n
      \n
    1. \n

      \n \n 1.python自动获取海量IP工具\n \n

      \n
    2. \n
    3. \n

      \n \n 2.python自动获取酷狗音乐工具\n \n

      \n
    4. \n
    5. \n

      \n \n 3.Python自动化开发-制作名片卡\n \n

      \n
    6. \n
    7. \n

      \n \n 4.Python自动化开发-制作名片卡\n \n

      \n
    8. \n
    9. \n

      \n \n 5.Python自动化开发-批量发邮件通知\n \n

      \n
    10. \n
    11. \n

      \n \n 6.Python自动化开发-考勤处理\n \n

      \n
    12. \n
    13. \n

      \n \n 7.Python双色球系统\n \n

      \n
    14. \n
    15. \n

      \n \n 8.Python批量获取王者荣耀皮肤\n \n

      \n
    16. \n
    17. \n

      \n \n 9.Python获取阴阳师壁纸\n \n

      \n
    18. \n
    19. \n

      \n \n 10.Python获取小说数据并分析\n \n

      \n
    20. \n
    21. \n

      \n \n 11.Python获取NBA数据\n \n

      \n
    22. \n
    23. \n

      \n \n 12.python获取拉钩工具\n \n

      \n
    24. \n
    25. \n

      \n \n 13.Python获取房天下数据\n \n

      \n
    26. \n
    27. \n

      \n \n 14.Python获取彩票信息\n \n

      \n
    28. \n
    29. \n

      \n \n 15.Python合成女神图片\n \n

      \n
    30. \n
    31. \n

      \n \n 16.Python法拍网数据\n \n

      \n
    32. \n
    33. \n

      \n \n 17.Python操作Excel自动化开发\n \n

      \n
    34. \n
    35. \n

      \n \n 18.python百行制作查询工具\n \n

      \n
    36. \n
    37. \n

      \n \n 19.python百行制作登录系统\n \n

      \n
    38. \n
    39. \n

      \n \n 20.Python百行代码实现点赞系统\n \n

      \n
    40. \n
    41. \n

      \n \n 21.Python百行代码实现抽奖系统\n \n

      \n
    42. \n
    43. \n

      \n \n 22.Python自动获取小说工具\n \n

      \n
    44. \n
    45. \n

      \n \n 23.python获取喜马拉雅音频\n \n

      \n
    46. \n
    47. \n

      \n \n 24.python快速获取斗图表情\n \n

      \n
    48. \n
    49. \n

      \n \n 25.python获取主播小视频\n \n

      \n
    50. \n
    51. \n

      \n \n 26.python自动工资条\n \n

      \n
    52. \n
    53. \n

      \n \n 27.python协程详解公开课\n \n

      \n
    54. \n
    55. \n

      \n \n 28.python制作12306查票工具\n \n

      \n
    56. \n
    57. \n

      \n \n 29.python爬虫30行拿LOL皮肤\n \n

      \n
    58. \n
    59. \n

      \n \n 30.python抓取相亲网数据\n \n

      \n
    60. \n
    61. \n

      \n \n 31.python抓取百合网数据\n \n

      \n
    62. \n
    63. \n

      \n \n 32.python游戏开发公开课\n \n

      \n
    64. \n
    \n

    \n \n ......\n \n

    \n

    \n \n \n 91.【Python 图片转字符画】\n \n \n \n
    \n
    \n \n \n 92.【200行Python代码实现2048】\n \n \n \n
    \n
    \n \n \n 93.【Python3 实现火车票查询工具】\n \n \n \n
    \n
    \n \n \n 94.【高德API+Python解决租房问题】\n \n \n \n
    \n
    \n \n \n 95.【Python3色情图片识别】\n \n \n \n
    \n
    \n \n \n 96.【Python 破解验证码】\n \n \n \n
    \n
    \n \n \n 97.【Python实现简单的Web服务器】\n \n \n \n
    \n
    \n \n \n 98.【pygame开发打飞机游戏】\n \n \n \n
    \n
    \n \n \n 99.【Django 搭建简易博客】\n \n \n \n
    \n
    \n \n \n 100.【Python基于共现提取《釜山行》人物关系】\n \n \n

    \n

    \n \n \n \n 完整项目已经打包整理好了,希望对正在学习的你带来帮助~多动手练习,才能不断地巩固知识,更快的进步!\n \n \n \n

    \n

    \n \n \n \n \n 获取方式:\n \n \n \n \n

    \n

    \n \n \n \n \n 1.点赞+在看\n \n \n \n \n

    \n

    \n \n \n \n \n 2.私信回复:学习  领取。\n \n \n \n \n

    \n
    \n \n
    \n
    \n
    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/FbDBJnT9ltXgkcS6vViYaA", + "creator": "python开发工程师", + "pubDate": "Mon, 14 Jul 2025 07:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZwm6QzeJkQsibiaKQWY9iaEoFlmS0lJ0jg9ou1icgCTBtGmsr8KagTiaWf98S2u6H1pR6JeNMSunLmsUMg/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZwm6QzeJkQsibiaKQWY9iaEoFlmS0lJ0jg9ou1icgCTBtGmsr8KagTiaWf98S2u6H1pR6JeNMSunLmsUMg/0?wx_fmt=jpeg", + "id": "2247511348_2", + "language": "", + "folder": "IT技术", + "feed": "python开发工程师", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "2352d4354695dde18515b9b7b64ead5e", + "highlights": [] + }, + { + "title": "自学python必看的书籍《Python编程:从入门到实践》PDF版拿走不谢", + "description": "自学python必看的书籍《Python编程:从入门到实践》内容简介本书是一本针对所有层次的Python 读者", + "content": "
    \n

    \n \n \n 自学python必看的书籍《Python编程:从入门到实践》\n \n \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n \"图片\"\n \n

    \n

    \n \n \n 内容简介\n \n \n

    \n

    \n \n 本书是一本针对所有层次的Python 读者而作的Python 入门书。全书分两部分:\n \n

    \n

    \n \n 第一部分介绍用Python 编程所必须了解的基本概念,包括matplotlib、NumPy 和Pygal 等强大的Python 库和工具介绍,以及列表、字典、if 语句、类、文件与异常、代码测试等内容;\n \n

    \n

    \n \n 第二部分将理论付诸实践,讲解如何开发三个项目,包括简单的Python 2D 游戏开发如何利用数据生成交互式的信息图,以及创建和定制简单的Web 应用,并帮读者解决常见编程问题和困惑。\n \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n \n 目录\n \n \n

    \n
    \n \n \"图片\"\n \n
    \n
    \n \n \"图片\"\n \n
    \n
    \n \n \"图片\"\n \n
    \n
    \n \n \"图片\"\n \n
    \n
    \n \n \"图片\"\n \n
    \n
    \n \n \"图片\"\n \n
    \n

    \n \n \n 书籍的分享格式为PDF电子版,小伙伴们手机也可以阅读学习哦!\n \n \n

    \n

    \n \n \n 书籍终归只是辅助,想要真的深入学习python,当然还要有系统的学习方法和学习教程才可以让你在学习的路上一帆风顺哦!\n \n \n

    \n

    \n \n \n 本套视频一共400集,本套视频共分3季:\n \n \n

    \n

    \n \n 第一季 【基础篇】Python基础 (115集)\n \n

    \n

    \n \n 第二季 【提高篇】Python深入和扩展(100集)\n \n

    \n

    \n \n 第三季 【扩展篇】网络编程、多线程、扩展库(85集)\n \n

    \n

    \n \n 第四季 【高手篇】算法、Python源码、函数式编程、手工实现神经网络(100集)\n \n

    \n

    \n \n
    \n
    \n

    \n
    \n \n \"图片\"\n \n
    \n

    \n \n \n 以上这些教程我已经为大家打包准备好了,希望对正在学习的你有所帮助!\n \n \n

    \n
      \n
    1. \n

      \n \n \n \n 获取方式:公众号私信回复“学习”即可\n \n \n \n

      \n
    2. \n
    3. \n

      \n \n \n \n 需要的小伙伴可以多多点赞转发 让更多的人看到\n \n \n \n

      \n
    4. \n
    \n
    \n \n
    \n
    \n
    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/CFfrCyYKJX5rJG0qS4_CqQ", + "creator": "python开发工程师", + "pubDate": "Sat, 12 Jul 2025 06:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZwm6QzeJkQsibiaKQWY9iaEoFlhj1dyuLP0Bqe2vJOHbia2M04j6GCRMERWfZVcZialbuOxhrZKTN0j3mg/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZwm6QzeJkQsibiaKQWY9iaEoFlhj1dyuLP0Bqe2vJOHbia2M04j6GCRMERWfZVcZialbuOxhrZKTN0j3mg/0?wx_fmt=jpeg", + "id": "2247511336_2", + "language": "", + "folder": "IT技术", + "feed": "python开发工程师", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "6ece793eae1a41b9db83f1da32129968", + "highlights": [] + }, + { + "title": "太强了!Python+Excel真的是神仙组合!", + "description": "这本书是由流行开源Python库xlwings 创始人:费利克斯 •朱姆斯坦(Felix Zumstein)所", + "content": "
    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n \n 这本书是由流行开源Python库xlwings 创始人:费利克斯 •朱姆斯坦(Felix Zumstein)所撰写,书中详细阐述了如何将Python与Excel结合使用,让任务自动化,从而实现效率飞跃!\n \n

    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n \n 为了让初学者克服对Python的恐惧,作者特意将教程内容设计成从简单到复杂的顺序进行介绍\n \n

    \n

    \n \n 这本教程共分为12章:\n \n

    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n \n 第一至三章主要是Python入门知识:如何搭建 Python环境、掌握基础知识,以及如何导入和使用Python模块\n \n

    \n

    \n \n 第四至八章通过丰富的实例教学,详细讲解了如何利用Python对Excel进行各种操作\n \n

    \n

    \n \n 第九至十二章则专注于如何在Excel中执行 Python代码,如何实现自动化,以及案例解析\n \n

    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n
    \n

    \n \n \n 以上这些教程我已经为大家打包准备好了,希望对正在学习的你有所帮助!\n \n \n

    \n
      \n
    1. \n

      \n \n \n \n 获取方式:公众号内回复“学习”即可\n \n \n \n

      \n
    2. \n
    3. \n

      \n \n \n \n 需要的小伙伴可以多多点赞转发 让更多的人看到\n \n \n \n

      \n
    4. \n
    \n
    \n

    \n \n
    \n
    \n

    \n
    \n \n
    \n
    \n
    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/1GdvQhdggH083l4xd2DsaQ", + "creator": "python开发工程师", + "pubDate": "Fri, 11 Jul 2025 06:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZwSZYGiaKv8xXtfjx0PZ3rnz7UVVAicqkGIn3hx7YibZjucNnNicZyITia0QzVDKickCAGud4ehhrFhVQog/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZwSZYGiaKv8xXtfjx0PZ3rnz7UVVAicqkGIn3hx7YibZjucNnNicZyITia0QzVDKickCAGud4ehhrFhVQog/0?wx_fmt=jpeg", + "id": "2247511330_2", + "language": "", + "folder": "IT技术", + "feed": "python开发工程师", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "d933a65ab5f209bc10d5d61a42d78b61", + "highlights": [] + }, + { + "title": "自学python必看的书籍《Python编程:从入门到实践》PDF版拿走不谢", + "description": "自学python必看的书籍《Python编程:从入门到实践》内容简介本书是一本针对所有层次的Python 读者", + "content": "
    \n

    \n \n \n 自学python必看的书籍《Python编程:从入门到实践》\n \n \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n \"图片\"\n \n

    \n

    \n \n \n 内容简介\n \n \n

    \n

    \n \n 本书是一本针对所有层次的Python 读者而作的Python 入门书。全书分两部分:\n \n

    \n

    \n \n 第一部分介绍用Python 编程所必须了解的基本概念,包括matplotlib、NumPy 和Pygal 等强大的Python 库和工具介绍,以及列表、字典、if 语句、类、文件与异常、代码测试等内容;\n \n

    \n

    \n \n 第二部分将理论付诸实践,讲解如何开发三个项目,包括简单的Python 2D 游戏开发如何利用数据生成交互式的信息图,以及创建和定制简单的Web 应用,并帮读者解决常见编程问题和困惑。\n \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n \n 目录\n \n \n

    \n
    \n \n \"图片\"\n \n
    \n
    \n \n \"图片\"\n \n
    \n
    \n \n \"图片\"\n \n
    \n
    \n \n \"图片\"\n \n
    \n
    \n \n \"图片\"\n \n
    \n
    \n \n \"图片\"\n \n
    \n

    \n \n \n 书籍的分享格式为PDF电子版,小伙伴们手机也可以阅读学习哦!\n \n \n

    \n

    \n \n \n 书籍终归只是辅助,想要真的深入学习python,当然还要有系统的学习方法和学习教程才可以让你在学习的路上一帆风顺哦!\n \n \n

    \n

    \n \n \n 本套视频一共400集,本套视频共分3季:\n \n \n

    \n

    \n \n 第一季 【基础篇】Python基础 (115集)\n \n

    \n

    \n \n 第二季 【提高篇】Python深入和扩展(100集)\n \n

    \n

    \n \n 第三季 【扩展篇】网络编程、多线程、扩展库(85集)\n \n

    \n

    \n \n 第四季 【高手篇】算法、Python源码、函数式编程、手工实现神经网络(100集)\n \n

    \n

    \n \n
    \n
    \n

    \n
    \n \n \"图片\"\n \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n 以上这些教程我已经为大家打包准备好了,希望对正在学习的你有所帮助!\n \n \n

    \n
      \n
    1. \n

      \n \n \n \n 获取方式:公众号私信回复“学习”即可\n \n \n \n

      \n
    2. \n
    3. \n

      \n \n \n \n 需要的小伙伴可以多多点赞转发 让更多的人看到\n \n \n \n

      \n
    4. \n
    \n
    \n \n
    \n
    \n
    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/4lrbYqMOMaeZY0ePHWyBVg", + "creator": "python开发工程师", + "pubDate": "Thu, 10 Jul 2025 06:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZwSZYGiaKv8xXtfjx0PZ3rnzgwZqTn10rhwticCVNicEzMpLia4Bvko08qBXYSYPcRNLtEm6ZiaJRHWpmw/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZwSZYGiaKv8xXtfjx0PZ3rnzgwZqTn10rhwticCVNicEzMpLia4Bvko08qBXYSYPcRNLtEm6ZiaJRHWpmw/0?wx_fmt=jpeg", + "id": "2247511324_2", + "language": "", + "folder": "IT技术", + "feed": "python开发工程师", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "fb4a24facd693493c2b06a9376b12688", + "highlights": [] + }, + { + "title": "2025神仙级Python入门教程(非常详细),从零基础入门到精通,从看这篇开始", + "description": "一、初聊Python1.为什么要学习Python?", + "content": "None", + "category": "", + "link": "https://mp.weixin.qq.com/s/aPfUq3xH50ek4TFkAOq2fA", + "creator": "python开发工程师", + "pubDate": "Thu, 10 Jul 2025 06:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZwSZYGiaKv8xXtfjx0PZ3rnzYppRh6oj0fAdf8PNtTib0Y6DV8edz3pEbyDBpb2Rk6SFAwibqrOaWHrQ/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZwSZYGiaKv8xXtfjx0PZ3rnzYppRh6oj0fAdf8PNtTib0Y6DV8edz3pEbyDBpb2Rk6SFAwibqrOaWHrQ/0?wx_fmt=jpeg", + "id": "2247511324_1", + "language": "", + "folder": "IT技术", + "feed": "python开发工程师", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "743550d894428decfbde5b814601cf70", + "highlights": [] + }, + { + "title": "每天五分钟—Python知识点总结13张思维导图(附全套教程)", + "description": "如果初学者接触的第一门语言是Python,学习曲线则会平滑得多,掌握一些基本语法和Python内置的数据结构,", + "content": "
    \n

    \n \n
    \n
    \n

    \n
    \n

    \n \n \n 如果初学者接触的第一门语言是Python,学习曲线则会平滑得多\n \n \n \n ,掌握一些基本语法和Python内置的数据结构,已经可以上手写一些小工具或者小型应用。这对初学者来说,非常重要。因为学习的过程是一个突破舒适区的过程,会面临很多痛苦,如果学习过程得不到激励,很容易半途而废,类似我们开玩笑说的「从入门到放弃」。\n \n \n \n Python还有很多优点:\n \n \n \n 上手快,第三方库丰富,资料丰富,很容易做出“可见可得”的应用。\n \n \n \n Python所需要学习的知识基础知识\n \n \n \n Python最基本的语言特性和语法:•变量•数据类型与数据结构•输入和输出•运算符、表达式、语句•控制流(顺序控制,选择控制和循环控制)•面向过程编程•面向对象编程•异常处理•组织代码•标准库、第三方库\n \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n
    \n \n
    \n
    \n
    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n \n 为大家准备了一些python的学习教程和PDF格式的python电子书籍,希望可以帮助到大家。\n \n \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n
    \n \n
    \n
    \n
    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n
    \n \n
    \n
    \n
    \n

    \n \n \n 以上这些教程我已经为大家打包准备好了,希望对正在学习的你有所帮助!\n \n \n

    \n
      \n
    1. \n

      \n \n \n \n 获取方式:公众号内回复“学习”即可\n \n \n \n

      \n
    2. \n
    3. \n

      \n \n \n \n 需要的小伙伴可以多多点赞转发 让更多的人看到\n \n \n \n

      \n
    4. \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n
    \n \n
    \n
    \n
    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/p1kNytp-ZuUyIa4g6FQemg", + "creator": "python开发工程师", + "pubDate": "Wed, 09 Jul 2025 06:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZzibdZyOl6Eib8HHXcT1nesbEDU5QxVTKl7ibKiaia6p9HI6PiausBDyjib4ibx7TlS7XX8xicFn6hJdXnpuRQ/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZzibdZyOl6Eib8HHXcT1nesbEDU5QxVTKl7ibKiaia6p9HI6PiausBDyjib4ibx7TlS7XX8xicFn6hJdXnpuRQ/0?wx_fmt=jpeg", + "id": "2247511318_2", + "language": "", + "folder": "IT技术", + "feed": "python开发工程师", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "5b731a170332c34c2ac69fa674d01432", + "highlights": [] + }, + { + "title": "一周练完!100个Python实战项目,太实用了,绝对不能错过(附完整版源码+教程)", + "description": "Python 是一种极具可读性和通用性的编程语言。", + "content": "
    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n Python 是一种极具可读性和通用性的编程语言\n \n \n \n \n \n \n 。Python 这个名字的灵感来自于英国喜剧团体 Monty Python,它的开发团队有一个重要的基础目标,就是使语言使用起来很有趣。Python 易于设置,并且是用相对直接的风格来编写,对错误会提供即时反馈,对初学者而言是个很好的选择。\n \n

    \n

    \n \n Python 是一种多范式语言,也就是说,它支持多种编程风格,包括脚本和面向对象,这使得它适用于通用目的。随着越来越多地在工业中,被诸如联合太空联盟\n \n \n \n \n \n \n (NASA 的主要飞机支持承包商)和工业光魔\n \n \n \n \n \n \n (VFX 和卢卡斯影业的动画工作室\n \n \n \n \n \n \n )等组织使用,Python 为那些寻求额外编程语言的人提供了巨大的潜力 。\n \n

    \n

    \n \n 当一个重要的被称为 comp.lang.python 的 Python 新闻组\n \n \n \n \n \n \n 在 1994 年形成时,Python 的用户基础不断增长,这为 Python 成为开源开发中最受欢迎的编程语言之一铺平了道路。\n \n

    \n

    \n \n Python当下真的很火。Python实战项目,也一直尤为关注,接下来,和大家介绍下100个Python练手的实战项目。\n \n

    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n \n 每个项目都有详细的视频讲解,跟随老师带你一步步完成敲代码的过程,在过程中结合多学知识提升能力,帮助怒更好地完成练习。\n \n \n \n \n
    \n
    \n

    \n

    \n \n 100个Python项目列表:\n \n

    \n
      \n
    1. \n

      \n \n 1.python自动获取海量IP工具\n \n

      \n
    2. \n
    3. \n

      \n \n 2.python自动获取酷狗音乐工具\n \n

      \n
    4. \n
    5. \n

      \n \n 3.Python自动化开发-制作名片卡\n \n

      \n
    6. \n
    7. \n

      \n \n 4.Python自动化开发-制作名片卡\n \n

      \n
    8. \n
    9. \n

      \n \n 5.Python自动化开发-批量发邮件通知\n \n

      \n
    10. \n
    11. \n

      \n \n 6.Python自动化开发-考勤处理\n \n

      \n
    12. \n
    13. \n

      \n \n 7.Python双色球系统\n \n

      \n
    14. \n
    15. \n

      \n \n 8.Python批量获取王者荣耀皮肤\n \n

      \n
    16. \n
    17. \n

      \n \n 9.Python获取阴阳师壁纸\n \n

      \n
    18. \n
    19. \n

      \n \n 10.Python获取小说数据并分析\n \n

      \n
    20. \n
    21. \n

      \n \n 11.Python获取NBA数据\n \n

      \n
    22. \n
    23. \n

      \n \n 12.python获取拉钩工具\n \n

      \n
    24. \n
    25. \n

      \n \n 13.Python获取房天下数据\n \n

      \n
    26. \n
    27. \n

      \n \n 14.Python获取彩票信息\n \n

      \n
    28. \n
    29. \n

      \n \n 15.Python合成女神图片\n \n

      \n
    30. \n
    31. \n

      \n \n 16.Python法拍网数据\n \n

      \n
    32. \n
    33. \n

      \n \n 17.Python操作Excel自动化开发\n \n

      \n
    34. \n
    35. \n

      \n \n 18.python百行制作查询工具\n \n

      \n
    36. \n
    37. \n

      \n \n 19.python百行制作登录系统\n \n

      \n
    38. \n
    39. \n

      \n \n 20.Python百行代码实现点赞系统\n \n

      \n
    40. \n
    41. \n

      \n \n 21.Python百行代码实现抽奖系统\n \n

      \n
    42. \n
    43. \n

      \n \n 22.Python自动获取小说工具\n \n

      \n
    44. \n
    45. \n

      \n \n 23.python获取喜马拉雅音频\n \n

      \n
    46. \n
    47. \n

      \n \n 24.python快速获取斗图表情\n \n

      \n
    48. \n
    49. \n

      \n \n 25.python获取主播小视频\n \n

      \n
    50. \n
    51. \n

      \n \n 26.python自动工资条\n \n

      \n
    52. \n
    53. \n

      \n \n 27.python协程详解公开课\n \n

      \n
    54. \n
    55. \n

      \n \n 28.python制作12306查票工具\n \n

      \n
    56. \n
    57. \n

      \n \n 29.python爬虫30行拿LOL皮肤\n \n

      \n
    58. \n
    59. \n

      \n \n 30.python抓取相亲网数据\n \n

      \n
    60. \n
    61. \n

      \n \n 31.python抓取百合网数据\n \n

      \n
    62. \n
    63. \n

      \n \n 32.python游戏开发公开课\n \n

      \n
    64. \n
    \n

    \n \n ......\n \n

    \n

    \n \n \n 91.【Python 图片转字符画】\n \n \n \n
    \n
    \n \n \n 92.【200行Python代码实现2048】\n \n \n \n
    \n
    \n \n \n 93.【Python3 实现火车票查询工具】\n \n \n \n
    \n
    \n \n \n 94.【高德API+Python解决租房问题】\n \n \n \n
    \n
    \n \n \n 95.【Python3色情图片识别】\n \n \n \n
    \n
    \n \n \n 96.【Python 破解验证码】\n \n \n \n
    \n
    \n \n \n 97.【Python实现简单的Web服务器】\n \n \n \n
    \n
    \n \n \n 98.【pygame开发打飞机游戏】\n \n \n \n
    \n
    \n \n \n 99.【Django 搭建简易博客】\n \n \n \n
    \n
    \n \n \n 100.【Python基于共现提取《釜山行》人物关系】\n \n \n

    \n

    \n \n \n \n 完整项目已经打包整理好了,希望对正在学习的你带来帮助~多动手练习,才能不断地巩固知识,更快的进步!\n \n \n \n

    \n

    \n \n \n \n \n 获取方式:\n \n \n \n \n

    \n

    \n \n \n \n \n 1.点赞+在看\n \n \n \n \n

    \n

    \n \n \n \n \n 2.私信回复:学习  领取。\n \n \n \n \n

    \n
    \n \n
    \n
    \n
    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/XrEsOr0cazZXOOMbBlWUqg", + "creator": "python开发工程师", + "pubDate": "Mon, 07 Jul 2025 06:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZxW21P2Wlme8ePMGec1BlUXI51Wbbiaf3qiadUcNt2w96yO3gInlACicOaN1ibuj14dxFPocqYkhs8grg/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZxW21P2Wlme8ePMGec1BlUXI51Wbbiaf3qiadUcNt2w96yO3gInlACicOaN1ibuj14dxFPocqYkhs8grg/0?wx_fmt=jpeg", + "id": "2247511306_2", + "language": "", + "folder": "IT技术", + "feed": "python开发工程师", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "d18f8425d7748ea43c815c70b404cbd4", + "highlights": [] + }, + { + "title": "自学python必看的书籍《Python编程:从入门到实践》", + "description": "内容简介本书是一本针对所有层次的Python 读者而作的Python 入门书。", + "content": "
    \n

    \n \n
    \n
    \n

    \n

    \n \n \"图片\"\n \n

    \n

    \n \n \n 内容简介\n \n \n

    \n

    \n \n 本书是一本针对所有层次的Python 读者而作的Python 入门书。全书分两部分:\n \n

    \n

    \n \n 第一部分介绍用Python 编程所必须了解的基本概念,包括matplotlib、NumPy 和Pygal 等强大的Python 库和工具介绍,以及列表、字典、if 语句、类、文件与异常、代码测试等内容;\n \n

    \n

    \n \n 第二部分将理论付诸实践,讲解如何开发三个项目,包括简单的Python 2D 游戏开发如何利用数据生成交互式的信息图,以及创建和定制简单的Web 应用,并帮读者解决常见编程问题和困惑。\n \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n \n 目录\n \n \n

    \n

    \n \n
    \n
    \n

    \n
    \n \n \"图片\"\n \n
    \n
    \n \n \"图片\"\n \n
    \n
    \n \n \"图片\"\n \n
    \n
    \n \n \"图片\"\n \n
    \n
    \n \n \"图片\"\n \n
    \n
    \n \n \"图片\"\n \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n 书籍的分享格式为PDF电子版,小伙伴们手机也可以阅读学习哦!\n \n \n

    \n

    \n \n \n 书籍终归只是辅助,想要真的深入学习python,当然还要有系统的学习方法和学习教程才可以让你在学习的路上一帆风顺哦!\n \n \n

    \n

    \n \n \n 本套视频一共400集,本套视频共分3季:\n \n \n

    \n

    \n \n 第一季 【基础篇】Python基础 (115集)\n \n

    \n

    \n \n 第二季 【提高篇】Python深入和扩展(100集)\n \n

    \n

    \n \n 第三季 【扩展篇】网络编程、多线程、扩展库(85集)\n \n

    \n

    \n \n 第四季 【高手篇】算法、Python源码、函数式编程、手工实现神经网络(100集)\n \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n
    \n \n \"图片\"\n \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n 以上这些教程我已经为大家打包准备好了,希望对正在学习的你有所帮助!\n \n \n

    \n
      \n
    1. \n

      \n \n \n \n 获取方式:公众号私信回复“学习”即可\n \n \n \n

      \n
    2. \n
    3. \n

      \n \n \n \n 需要的小伙伴可以多多点赞转发 让更多的人看到\n \n \n \n

      \n
    4. \n
    \n
    \n \n
    \n
    \n
    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/c_5LNvw3SAjwBdMs9TRztQ", + "creator": "python开发工程师", + "pubDate": "Sat, 05 Jul 2025 06:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZxW21P2Wlme8ePMGec1BlUXOFYxYplAyK6CLl2x2bH4d2ZWosUWkQlGPFHSr7MpMNoggFfhVnvzag/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZxW21P2Wlme8ePMGec1BlUXOFYxYplAyK6CLl2x2bH4d2ZWosUWkQlGPFHSr7MpMNoggFfhVnvzag/0?wx_fmt=jpeg", + "id": "2247511294_2", + "language": "", + "folder": "IT技术", + "feed": "python开发工程师", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "7dcfc8e3ce8ac73a506470cf4f75b40b", + "highlights": [] + }, + { + "title": "非常喜欢python!读懂这三本书,学python事半功倍。", + "description": "非常喜欢python我非常喜欢python,在前面5年里,它一直是我热衷使用并不断研究的语言,迄今为止,pyt", + "content": "
    \n

    \n \n \n 非常喜欢python\n \n \n

    \n \n 我非常喜欢python,在前面5年里,它一直是我热衷使用并不断研究的语言,迄今为止,python都非常友好并且易于学习!\n \n

    \n

    \n \n 它几乎可以做任何事,从简单的脚本创建、web,到数据可视化以及AI人工智能,使热衷python的人群变得越来越多。\n \n

    \n

    \n \n 接下来推荐3本学python一定要看的书籍,有了他们,至少能节省50%的时间少走一半的弯路!\n \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n

    \n \n 零基础学python必看\n \n

    \n

    \n \n 《父与子编程:与小卡特一起学python》\n \n

    \n

    \n \n 这本书是零基础看的书籍,对于很多刚接触编程的小白来说,对于很多名词是很难理解的,本书把知识点拆的很细,更难得的是那些难理解的技术名词,这本书都是用场景对话、动画的形式展现,让初学者更容易学会;\n \n \n \n 看了它,你就会发现python是多么容易!\n \n \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n \n python技能加强必看\n \n

    \n

    \n \n \n 《\n \n \n \n \n python cookbook\n \n \n \n \n 》\n \n \n

    \n

    \n \n \n 本书介绍了应用在各个领域的技巧和方法,涵盖了很多高级主题:元编程、网络、\n \n \n \n \n web编程\n \n \n \n \n 、实用脚本、系统管理、C语言拓展等。\n \n \n

    \n

    \n \n 本书还覆盖了许多python日常开发中的问题以及解决方案,在实际开发中的编程技巧,告诉你python如何工作并且为什么工作!\n \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n \n python项目经验\n \n

    \n

    \n \n 《python高手之路》\n \n

    \n

    \n \n 众所周知,项目经验以及底层知识拓展对于一个程序员的进阶来说是非常重要的!\n \n

    \n

    \n \n 《python高手之路》它是从一个项目的第一步开始,从整个项目的架构设计、对模块和库的处理来构建一个完整的项目。\n \n

    \n

    \n \n 它还设计了很多比较底层的知识,性能优化,架构设计等拓展知识来增加读者的专业技能!\n \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n \n 附赠三本python必学书电子档\n \n

    \n

    \n \n 对于初学python,或者才接触编程这个行业的人来说,这三本书将帮助你少走很多弯路,节省你许多的学习时间。\n \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n \n 如果你需要这三本书籍的电子档,\n \n \n \n 可以\n \n \n

    \n

    \n \n \n 1.点赞+再看\n \n \n

    \n

    \n \n \n 2.公众号私信内回复:“学习”\n \n \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n
    \n \n
    \n
    \n
    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/klHD6BcO82lOyTusTlp5Tg", + "creator": "python开发工程师", + "pubDate": "Fri, 04 Jul 2025 04:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZwqm1fyIIFpxnqmrUNyQznwvvWZ7aEib8uGSiambx2okPyrWWPPibmRz0queMXr6U7qvAgLME9r34ibpw/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZwqm1fyIIFpxnqmrUNyQznwvvWZ7aEib8uGSiambx2okPyrWWPPibmRz0queMXr6U7qvAgLME9r34ibpw/0?wx_fmt=jpeg", + "id": "2247511287_2", + "language": "", + "folder": "IT技术", + "feed": "python开发工程师", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "dcf70fe51f897760827e674a15584849", + "highlights": [] + }, + { + "title": "练完这102个项目,你的Python就牛了", + "description": "Python 初学者在迈过安装编程环境和基本语法的门槛,准备大展身手的时候,可能突然就会进入迷茫期:不知道做些", + "content": "
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n \n \n

    \n
    \n \"图片\"\n
    \n

    \n \n Python 初学者在迈过安装编程环境和基本语法的门槛,准备大展身手的时候,可能突然就会进入迷茫期:\n \n

    \n

    \n \n 不知道做些什么、再学些什么。\n \n

    \n

    \n \n 然后对编程的兴趣就会慢慢消退,找不到坚持下去的理由,从而慢慢淡忘之前学会的编程知识。\n \n

    \n

    \n \n 所以找到自己感兴趣、能够跟着动手和学习的 Python 项目是特别重要的❗❗❗\n \n

    \n

    \n \n 这样才能把学会的Python 知识用起来,不断地提高。最终从新手晋升为高手\n \n

    \n

    \n \n 不管学习哪门语言都希望能做出实际的东西来,这个实际的东西当然就是项目啦\n \n

    \n

    \n \n 不用多说大家都知道学编程语言一定要做项目才行。\n \n

    \n

    \n \n 1、[Python 图片转字符画]\n \n

    \n

    \n \n 2、【200行Python代码实现2048]\n \n

    \n

    \n \n 3、[Python3实现火车票查询工具]\n \n

    \n

    \n \n 4、【高德API+Python解决租房问题]\n \n

    \n

    \n \n 5、【Python3色情图片识别]\n \n

    \n

    \n \n 6、【Python破解验证码]\n \n

    \n

    \n \n 7、【Python实现简单的Web服务器]\n \n

    \n

    \n \n 8、【pygame开发打飞机游戏]\n \n

    \n

    \n \n 9、[Django搭建简易博客]\n \n

    \n

    \n \n 10、[Python基于共现提取《釜山行》人物关系]\n \n

    \n

    \n \n 11、【基于scrapy爬虫的天气数据采集(python)]\n \n

    \n

    \n \n 12、【Flask开发轻博客]\n \n

    \n

    \n \n 13、【Python3图片隐写术]\n \n

    \n

    \n \n 14、【Python 实现简易Shell)\n \n

    \n

    \n \n 15、【使用Python解数学方程]\n \n

    \n

    \n \n 16、【PyQt实现简易浏览器]\n \n

    \n

    \n \n 17、【神经网络实现手写字符识别系统]\n \n

    \n

    \n \n 18、【Python实现简单画板]\n \n

    \n

    \n \n 19、【Python实现3D建模工具]\n \n

    \n

    \n \n 20、【NBA常规赛结果预测--利用Python进行\n \n \n \n 比赛数据分析]\n \n \n

    \n

    \n \n 21、\n \n \n \n 【神经网络实现人脸识别任务]\n \n \n

    \n

    \n \n 22、【\n \n \n \n [Python文本解析器]\n \n \n

    \n

    \n \n 23、\n \n \n \n [Python3&OpenCV视频转字符动画]\n \n \n

    \n

    \n \n 24、[Python3实现淘女郎照片爬虫]\n \n

    \n

    \n \n 25、\n \n \n \n [Python3实现简单的FTP认证服务器]\n \n \n

    \n

    \n \n 26、\n \n \n \n [基于Flask与MySQL实现番剧推荐系统]\n \n \n

    \n

    \n \n 27、[Python实现端口扫描器]\n \n

    \n

    \n \n 28、\n \n \n \n 【使用Python3编写系列实]\n \n \n

    \n

    \n \n 29、【Python实现康威生命游戏实用脚本]\n \n

    \n

    \n \n 30、\n \n \n \n 【川普撞脸希拉里(基于OpenCV\n \n \n \n \n 征交换]\n \n \n

    \n

    \n \n 31、【Python 3实现Markdown解析器]\n \n

    \n

    \n \n 32、【Python气象数据分析--《Python数据分\n \n \n \n 析实战》]\n \n \n

    \n

    \n \n 33、\n \n \n \n 【Python实现键值数据库]\n \n \n

    \n

    \n \n 34、\n \n \n \n [k-近邻算法实现手写数字识别系统]\n \n \n

    \n

    \n \n 35、\n \n \n \n 【ebay在线拍卖数据分析]\n \n \n

    \n

    \n \n 36、\n \n \n \n [Python实现英文新闻摘要自动提取]\n \n \n

    \n

    \n \n 37、\n \n \n \n 【Python实现简易局域网视频聊大工具]\n \n \n

    \n

    \n \n 38、【基于Flask及爬虫实现微信娱乐机器人]\n \n

    \n

    \n \n 39、[Python实现Python解释器]\n \n

    \n

    \n \n 40、【Python3基于Scapy实现DDos]\n \n

    \n

    \n \n 41、[Python实现密码强度检测器]\n \n

    \n

    \n \n 42、【使用Python实现深度神经网络]\n \n

    \n

    \n \n 43、【Python实现从excel读取数据并绘制成精\n \n \n \n 美图像]\n \n \n

    \n

    \n \n 44、【人机对战初体验Python基于Pygame实现\n \n \n \n 四子棋游戏]\n \n \n

    \n

    \n \n 45、【Python3实现可控制肉鸡的反向Shell1\n \n

    \n

    \n \n \n 获取方式:\n \n \n

    \n

    \n \n \n \n 1.点赞+在看\n \n \n \n

    \n

    \n \n \n 2.点击下方小卡片发送‘学习’即可获取~\n \n \n

    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n
    \n \n
    \n
    \n
    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/uOAr9KwYg-6HvbJGRBLxvw", + "creator": "python开发工程师", + "pubDate": "Fri, 04 Jul 2025 04:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZwqm1fyIIFpxnqmrUNyQznwjRjhPW9ic9u3d6IlWcphPpBA01tZtwhmSZBXLxAa7YiajapwmwNq1gkw/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZwqm1fyIIFpxnqmrUNyQznwjRjhPW9ic9u3d6IlWcphPpBA01tZtwhmSZBXLxAa7YiajapwmwNq1gkw/0?wx_fmt=jpeg", + "id": "2247511287_1", + "language": "", + "folder": "IT技术", + "feed": "python开发工程师", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "28a2618c1b234cc3ef8658429dea6b8e", + "highlights": [] + }, + { + "title": "自学python必看的书籍《Python编程:从入门到实践》PDF版拿走不谢", + "description": "自学python必看的书籍《Python编程:从入门到实践》内容简介本书是一本针对所有层次的Python 读者", + "content": "
    \n

    \n \n \n 自学python必看的书籍《Python编程:从入门到实践》\n \n \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n \"图片\"\n \n

    \n

    \n \n \n 内容简介\n \n \n

    \n

    \n \n 本书是一本针对所有层次的Python 读者而作的Python 入门书。全书分两部分:\n \n

    \n

    \n \n 第一部分介绍用Python 编程所必须了解的基本概念,包括matplotlib、NumPy 和Pygal 等强大的Python 库和工具介绍,以及列表、字典、if 语句、类、文件与异常、代码测试等内容;\n \n

    \n

    \n \n 第二部分将理论付诸实践,讲解如何开发三个项目,包括简单的Python 2D 游戏开发如何利用数据生成交互式的信息图,以及创建和定制简单的Web 应用,并帮读者解决常见编程问题和困惑。\n \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n \n 目录\n \n \n

    \n
    \n \n \"图片\"\n \n
    \n
    \n \n \"图片\"\n \n
    \n
    \n \n \"图片\"\n \n
    \n
    \n \n \"图片\"\n \n
    \n
    \n \n \"图片\"\n \n
    \n
    \n \n \"图片\"\n \n
    \n

    \n \n \n 书籍的分享格式为PDF电子版,小伙伴们手机也可以阅读学习哦!\n \n \n

    \n

    \n \n \n 书籍终归只是辅助,想要真的深入学习python,当然还要有系统的学习方法和学习教程才可以让你在学习的路上一帆风顺哦!\n \n \n

    \n

    \n \n \n 本套视频一共400集,本套视频共分3季:\n \n \n

    \n

    \n \n 第一季 【基础篇】Python基础 (115集)\n \n

    \n

    \n \n 第二季 【提高篇】Python深入和扩展(100集)\n \n

    \n

    \n \n 第三季 【扩展篇】网络编程、多线程、扩展库(85集)\n \n

    \n

    \n \n 第四季 【高手篇】算法、Python源码、函数式编程、手工实现神经网络(100集)\n \n

    \n

    \n \n
    \n
    \n

    \n
    \n \n \"图片\"\n \n
    \n

    \n \n \n 以上这些教程我已经为大家打包准备好了,希望对正在学习的你有所帮助!\n \n \n

    \n
      \n
    1. \n

      \n \n \n \n 获取方式:公众号私信回复“学习”即可\n \n \n \n

      \n
    2. \n
    3. \n

      \n \n \n \n 需要的小伙伴可以多多点赞转发 让更多的人看到\n \n \n \n

      \n
    4. \n
    \n
    \n \n
    \n
    \n
    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/tcflJ9s2_9sz64YaYGR7Zg", + "creator": "python开发工程师", + "pubDate": "Wed, 02 Jul 2025 09:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZzTvd1vr2rzDSOOIRMB4TLicTUXCSzcuIR9Pbtic23niaoI1AzFXBprib5Iia7yvq2EvuBIIUjs3sGM5MQ/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZzTvd1vr2rzDSOOIRMB4TLicTUXCSzcuIR9Pbtic23niaoI1AzFXBprib5Iia7yvq2EvuBIIUjs3sGM5MQ/0?wx_fmt=jpeg", + "id": "2247511275_2", + "language": "", + "folder": "IT技术", + "feed": "python开发工程师", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "934dc0c78abe4f4d7b3496f12820db70", + "highlights": [] + }, + { + "title": "使用Python6年后,发现学Python看完这几本书少走一半弯路,别错过", + "description": "在前面6年里,它一直是我热衷使用并不断研究的语言,迄今为止,python都非常友好并且易于学习!", + "content": "
    \n

    \n \n 在前面6年里,它一直是我热衷使用并不断研究的语言,迄今为止,python都非常友好并且易于学习!它几乎可以做任何事,从简单的脚本创建、web,到数据可视化以及AI人工智能,使热衷python的人群变得越来越多。接下来推荐4本学python一定要看的书籍,有了他们,至少能节省50%的时间少走一半的弯路!\n \n

    \n
    \n \"图片\"\n
    \n

    \n \n \n
    \n
    \n
    \n

    \n

    \n \n \n 零基础学python必看两本 一\n \n \n \n 《21天学通Python》\n \"图片\"\n \n \n \n \n
    \n
    \n

    \n

    \n \n \n 《21天学通Python》\n \n \n \n 共分三篇。第一篇介绍Python语言的特点、安装、语法基础、程序流程控制、面向过程的编程方法、面向对象的编程方法、程序异常的处理;第二篇介绍了Python语言中的包与模块、迭代器、生成器、装饰器、上下文管理器等进阶语法,同时介绍了使用Python标准库中的文件系统处理、图形化界面编程、正则表达式、网络编程、多进(线)程编程、数据库编程。此外,还介绍了运用第三方库的Web网站编程、图片处理;第三篇通过两个案例介绍Python的综合编程技术。二《python 程序员指南》\n \n

    \n

    \n \n \"图片\"\n \n

    \n

    \n \n 1、本书主要讲解Python语言的基础知识、编程 及应用 2、本书的概念和思路清晰,并对相应的概念提供了大量的编程实例,具有很高的参考和应用价值 3、本书的内容深入浅出、通俗易懂、从最简单的例子着眼逐步进阶、不仅适合初学者,对使用Python编写各种大型、复杂应用的程序员来说也是一本实用的参考书 python技能获取拓展\n \n \n \n 《python数据可视化编程实战》\n \"图片\"\n \n \n

    \n

    \n \n 本书是一本使用 Python 实现数据可视化编程的实战指南,介绍了如何使用 Python 流行的库,通过 70 余种方法创建美观的数据可视化效果。全书共 9 章,分别介绍了准备工作环境、了解数据、绘制并定制化图表、学习更多图表和定制化、创建 3D 可视化图表、用图像和地图绘制图表、使用正确的图表理解数据、更多的 matplotlib 知识以及使用\n \n \n \n Plot.ly\n \n \n \n 进行云端可视化。本书适合那些对 Python 编程有一定基础的开发人员阅读,可以帮助读者从头开始了解数据、数据格式、数据可视化,并学会使用 Python 可视化数据。\n \n \n \n 《Python编程快速上手——让繁琐工作自动化》\n \"图片\"\n \n \n

    \n

    \n \n \n 电子版已经给大家准备好了,早日成为Python大佬!\n \n \n \n 由于篇幅原因,所有章节就不一一截图展示,有需要的小伙伴可以\n \n \n \n

    \n

    \n \n \n 获取方式:\n \n \n

    \n
      \n
    1. \n

      \n \n 点赞+再看\n \n

      \n
    2. \n
    3. \n

      \n \n 公众号内私信回复“学习”\n \n

      \n
    4. \n
    \n

    \n \n
    \n
    \n

    \n
    \n \n
    \n
    \n
    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/xiNrfMycIKR5iK9CRQLnRg", + "creator": "python开发工程师", + "pubDate": "Tue, 01 Jul 2025 09:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZzTvd1vr2rzDSOOIRMB4TLicSPxKqGGBPVOWu8SjWLRMExd0LYnWI4PqU879eNSmrLFzVTF7Q6R5pg/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZzTvd1vr2rzDSOOIRMB4TLicSPxKqGGBPVOWu8SjWLRMExd0LYnWI4PqU879eNSmrLFzVTF7Q6R5pg/0?wx_fmt=jpeg", + "id": "2247511269_2", + "language": "", + "folder": "IT技术", + "feed": "python开发工程师", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "25b3ac08e2f72fa4d222dc9703f673c2", + "highlights": [] + }, + { + "title": "一周练完!100个Python实战项目,太实用了,绝对不能错过(附完整版源码+教程)", + "description": "Python 是一种极具可读性和通用性的编程语言。", + "content": "
    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n Python 是一种极具可读性和通用性的编程语言\n \n \n \n \n \n \n 。Python 这个名字的灵感来自于英国喜剧团体 Monty Python,它的开发团队有一个重要的基础目标,就是使语言使用起来很有趣。Python 易于设置,并且是用相对直接的风格来编写,对错误会提供即时反馈,对初学者而言是个很好的选择。\n \n

    \n

    \n \n Python 是一种多范式语言,也就是说,它支持多种编程风格,包括脚本和面向对象,这使得它适用于通用目的。随着越来越多地在工业中,被诸如联合太空联盟\n \n \n \n \n \n \n (NASA 的主要飞机支持承包商)和工业光魔\n \n \n \n \n \n \n (VFX 和卢卡斯影业的动画工作室\n \n \n \n \n \n \n )等组织使用,Python 为那些寻求额外编程语言的人提供了巨大的潜力 。\n \n

    \n

    \n \n 当一个重要的被称为 comp.lang.python 的 Python 新闻组\n \n \n \n \n \n \n 在 1994 年形成时,Python 的用户基础不断增长,这为 Python 成为开源开发中最受欢迎的编程语言之一铺平了道路。\n \n

    \n

    \n \n Python当下真的很火。Python实战项目,也一直尤为关注,接下来,和大家介绍下100个Python练手的实战项目。\n \n

    \n
    \n \"图片\"\n
    \n
    \n \"图片\"\n
    \n

    \n \n \n \n 每个项目都有详细的视频讲解,跟随老师带你一步步完成敲代码的过程,在过程中结合多学知识提升能力,帮助怒更好地完成练习。\n \n \n \n \n
    \n
    \n

    \n

    \n \n 100个Python项目列表:\n \n

    \n
      \n
    1. \n

      \n \n 1.python自动获取海量IP工具\n \n

      \n
    2. \n
    3. \n

      \n \n 2.python自动获取酷狗音乐工具\n \n

      \n
    4. \n
    5. \n

      \n \n 3.Python自动化开发-制作名片卡\n \n

      \n
    6. \n
    7. \n

      \n \n 4.Python自动化开发-制作名片卡\n \n

      \n
    8. \n
    9. \n

      \n \n 5.Python自动化开发-批量发邮件通知\n \n

      \n
    10. \n
    11. \n

      \n \n 6.Python自动化开发-考勤处理\n \n

      \n
    12. \n
    13. \n

      \n \n 7.Python双色球系统\n \n

      \n
    14. \n
    15. \n

      \n \n 8.Python批量获取王者荣耀皮肤\n \n

      \n
    16. \n
    17. \n

      \n \n 9.Python获取阴阳师壁纸\n \n

      \n
    18. \n
    19. \n

      \n \n 10.Python获取小说数据并分析\n \n

      \n
    20. \n
    21. \n

      \n \n 11.Python获取NBA数据\n \n

      \n
    22. \n
    23. \n

      \n \n 12.python获取拉钩工具\n \n

      \n
    24. \n
    25. \n

      \n \n 13.Python获取房天下数据\n \n

      \n
    26. \n
    27. \n

      \n \n 14.Python获取彩票信息\n \n

      \n
    28. \n
    29. \n

      \n \n 15.Python合成女神图片\n \n

      \n
    30. \n
    31. \n

      \n \n 16.Python法拍网数据\n \n

      \n
    32. \n
    33. \n

      \n \n 17.Python操作Excel自动化开发\n \n

      \n
    34. \n
    35. \n

      \n \n 18.python百行制作查询工具\n \n

      \n
    36. \n
    37. \n

      \n \n 19.python百行制作登录系统\n \n

      \n
    38. \n
    39. \n

      \n \n 20.Python百行代码实现点赞系统\n \n

      \n
    40. \n
    41. \n

      \n \n 21.Python百行代码实现抽奖系统\n \n

      \n
    42. \n
    43. \n

      \n \n 22.Python自动获取小说工具\n \n

      \n
    44. \n
    45. \n

      \n \n 23.python获取喜马拉雅音频\n \n

      \n
    46. \n
    47. \n

      \n \n 24.python快速获取斗图表情\n \n

      \n
    48. \n
    49. \n

      \n \n 25.python获取主播小视频\n \n

      \n
    50. \n
    51. \n

      \n \n 26.python自动工资条\n \n

      \n
    52. \n
    53. \n

      \n \n 27.python协程详解公开课\n \n

      \n
    54. \n
    55. \n

      \n \n 28.python制作12306查票工具\n \n

      \n
    56. \n
    57. \n

      \n \n 29.python爬虫30行拿LOL皮肤\n \n

      \n
    58. \n
    59. \n

      \n \n 30.python抓取相亲网数据\n \n

      \n
    60. \n
    61. \n

      \n \n 31.python抓取百合网数据\n \n

      \n
    62. \n
    63. \n

      \n \n 32.python游戏开发公开课\n \n

      \n
    64. \n
    \n

    \n \n ......\n \n

    \n

    \n \n \n 91.【Python 图片转字符画】\n \n \n \n
    \n
    \n \n \n 92.【200行Python代码实现2048】\n \n \n \n
    \n
    \n \n \n 93.【Python3 实现火车票查询工具】\n \n \n \n
    \n
    \n \n \n 94.【高德API+Python解决租房问题】\n \n \n \n
    \n
    \n \n \n 95.【Python3色情图片识别】\n \n \n \n
    \n
    \n \n \n 96.【Python 破解验证码】\n \n \n \n
    \n
    \n \n \n 97.【Python实现简单的Web服务器】\n \n \n \n
    \n
    \n \n \n 98.【pygame开发打飞机游戏】\n \n \n \n
    \n
    \n \n \n 99.【Django 搭建简易博客】\n \n \n \n
    \n
    \n \n \n 100.【Python基于共现提取《釜山行》人物关系】\n \n \n

    \n

    \n \n \n \n 完整项目已经打包整理好了,希望对正在学习的你带来帮助~多动手练习,才能不断地巩固知识,更快的进步!\n \n \n \n

    \n

    \n \n \n \n \n 获取方式:\n \n \n \n \n

    \n

    \n \n \n \n \n 1.点赞+在看\n \n \n \n \n

    \n

    \n \n \n \n \n 2.私信回复:学习  领取。\n \n \n \n \n

    \n
    \n \n
    \n
    \n
    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/MhahxqkjJs3c-ltitQIUMw", + "creator": "python开发工程师", + "pubDate": "Tue, 01 Jul 2025 09:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZzTvd1vr2rzDSOOIRMB4TLicbInhfjo3GahVbwFdVO8AiaDEGG09Bpiawo4f109Gn26W6vkE2Cb14SpA/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZzTvd1vr2rzDSOOIRMB4TLicbInhfjo3GahVbwFdVO8AiaDEGG09Bpiawo4f109Gn26W6vkE2Cb14SpA/0?wx_fmt=jpeg", + "id": "2247511269_1", + "language": "", + "folder": "IT技术", + "feed": "python开发工程师", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "9a3ecdf1a8d48a28b17be98c6a04879f", + "highlights": [] + }, + { + "title": "Python是什么?为什么一定要学Python?", + "description": "一,Python是什么?Python是一种计算机编程语言。", + "content": "
    \n

    \n \n
    \n
    \n

    \n
    \n

    \n \n 一,Python是什么?\n \n

    \n

    \n \n Python是一种计算机编程语言。\n \n

    \n

    \n \n 对于“编程”,你可能或多或少听说过Java/C语言/C++ /PHP等语言,Python作为近几年特别火的一门编程语言,相较前面几种语言,更注重可读性和效率。\n \n

    \n

    \n \n 因此,Python对于注重开发效率的程序员来说是大受欢迎的,他们给Python的定位是“优雅”、“明确”、“简单”。\n \n

    \n

    \n \n 除此之外,Python还具有以下优势,获得人们的青睐:\n \n

    \n

    \n \n Python 易于学习——Python程序简单易懂,初学者只要识字、会简单的算术、看懂英文单词就能入门学习。因此很多小学生从小就开始通过学习python来培养编程思维。\n \n

    \n

    \n \n 少量的代码就能构建出很多功能—— 在Python中,几乎每个想法都可以通过20-30行代码快速验证。\n \n

    \n

    \n \n Python 拥有最成熟的程序包资源库之一——Python有满足不同需求的库,无论实现什么功能,都有现成的类库可以使用。\n \n

    \n

    \n \n Python 是人工智能/现代数据科学中最常使用的语言——Python应用领域广泛(如图),尤其是人工智能/大数据时代不可或缺的技能。\n \n

    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n \n 二、为什么一定要学Python?\n \n

    \n

    \n \n 1、时代之驱\n \n

    \n

    \n \n 这两年,最火的词莫过于“人工智能”。\n \n

    \n

    \n \n 随着人工智能和机器学习的发展,各行各业职位正在逐渐被智能机器取替,为了应对被取替的风险,越来越多人开始学习编程,学习火热的程度可谓“全民Python编程的时代”。\n \n

    \n

    \n \n 可以看看几个重要的新闻:\n \n

    \n
      \n
    • \n

      \n \n \n Python纳入浙江省高考!浙江省信息技术教材编程语言将会更换为 Python。\n \n \n

      \n
    • \n
    \n

    \n \n
    \n
    \n

    \n
      \n
    • \n

      \n \n \n Python纳入山东省的小学教材课程,小学生都开始接触 Python 语言了。\n \n \n

      \n
    • \n
    \n

    \n \n
    \n
    \n

    \n
      \n
    • \n

      \n \n \n Python 已加入全国计算机等级考试!教育部考试中心决定自2018年起,在计算机二级考试加入了“Python语言程序设计”科目。\n \n \n

      \n
    • \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n 现在国外国内很多家长已经给孩子报名学习Python编程课程了。在美国,就连婴幼儿也有专门的编程童书。\n \n

    \n

    \n \n 所以,如果不想被淘汰,现在就应该抓紧机会学起来!\n \n

    \n

    \n \n 2、Python最适合0基础学习者\n \n

    \n

    \n \n 对于编程小白来说,Python是最适合0基础学习的编程语言。\n \n

    \n

    \n \n Python的语法简单,代码可读性高,容易入门。从Python学起,你很快就能运用Python编程的底层逻辑去学习另外的语言。也就是说,学习Python是学习编程的最好起点。\n \n

    \n

    \n \n 3、Python极大提升现有岗位的竞争力\n \n

    \n

    \n \n 有许多在职程序员本身学了c语言、java等其他编程后,仍坚定多学一门Python开发,他们都被Python代码的优美和开发效率所折服。\n \n

    \n

    \n \n 比如有一些工程师,以前在做很多SEO优化的时候,苦于不会编程,一些程序上面的问题,得不到解决,只能做做简单的页面优化。\n \n

    \n

    \n \n 现在学会Python之后,可以编写一些查询收录,排名,自动生成网络地图的程序,解决棘手的SEO问题,这样,在原有专业基础上,他们就多了一项高效的技能。\n \n

    \n

    \n \n 此外,如果你关注新闻和就业情况,可以明显感觉到,随着 Python 语言的持续火热,对于 Python 技术人才的需求也在不断增加。\n \n

    \n

    \n \n 通过统计,各大招聘网中,Python岗位的日均需求量甚至达到了 15000+,北上广深等一线城市的需求更是数量庞大,可见这个技能的竞争压力之大。\n \n

    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n \n 以下通过职友集Python 工资实际查询截图:\n \n \n \n

    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n 在北京,Python平均工资20980,并且92.7%的Python从业人员薪资在10K以上。\n \n \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n 在互联网比较发达的二线城市,比如成都平均工资12260,6K以上占比85% ,10K以上占比也到达了70.4%。\n \n \n

    \n

    \n \n \n 可以说,Python是近两年投入性价比最高的学习技能之一了!\n \n \n

    \n

    \n \n \n 现在,相信你已经对Python有了初步的认知和了解。\n \n \n

    \n

    \n \n 最后\n \n

    \n

    \n \n 如果对Python感兴趣的话,可以试试我的学习方法以及相关的学习资料\n \n

    \n

    \n \n 对于0基础小白入门:\n \n

    \n

    \n \n 如果你是零基础小白,想快速入门Python是可以考虑培训的。\n \n

    \n

    \n \n 一方面是学习时间相对较短,学习内容更全面更集中。\n \n

    \n

    \n \n Python所有方向的学习路线\n \n

    \n

    \n \n Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。\n \n

    \n

    \n \n Python所有方向的学习路线\n \n

    \n

    \n \n \n Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。\n \n \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n \n Python必备开发工具\n \n

    \n

    \n \n \n 工欲善其事必先利其器。学习Python常用的开发软件都在这里了,给大家节省了很多时间。\n \n \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n \n Python精品PDF电子书\n \n

    \n

    \n \n \n 阅读一些书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。\n \n \n

    \n
    \n \"图片\"\n
    \n

    \n \n Python学习视频\n \n

    \n

    \n \n \n 观看零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。\n \n \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n \n 实战案例\n \n

    \n

    \n \n \n 光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。\n \n \n

    \n
    \n \"图片\"\n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n 获取方式:\n \n

    \n

    \n \n 1.点赞+再看\n \n

    \n

    \n \n 2.公众号内回复:“学习”\n \n

    \n

    \n \n
    \n
    \n

    \n
    \n \n
    \n
    \n
    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/il87sB37BUOUiln-4bMyzQ", + "creator": "python开发工程师", + "pubDate": "Mon, 30 Jun 2025 09:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZy6NhzQnQnn6fj5giafbrz2bCXCNXVa4kriaKonPngaWuYNXLfSuEOtL6Fu3Ga0XM2RExUB46rDL0eg/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZy6NhzQnQnn6fj5giafbrz2bCXCNXVa4kriaKonPngaWuYNXLfSuEOtL6Fu3Ga0XM2RExUB46rDL0eg/0?wx_fmt=jpeg", + "id": "2247511263_2", + "language": "", + "folder": "IT技术", + "feed": "python开发工程师", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "41aaa40f77c628b164b24e899c10bf40", + "highlights": [] + }, + { + "title": "每天五分钟—Python知识点总结13张思维导图(附全套教程)", + "description": "如果初学者接触的第一门语言是Python,学习曲线则会平滑得多,掌握一些基本语法和Python内置的数据结构,", + "content": "
    \n

    \n \n
    \n
    \n

    \n
    \n

    \n \n \n 如果初学者接触的第一门语言是Python,学习曲线则会平滑得多\n \n \n \n ,掌握一些基本语法和Python内置的数据结构,已经可以上手写一些小工具或者小型应用。这对初学者来说,非常重要。因为学习的过程是一个突破舒适区的过程,会面临很多痛苦,如果学习过程得不到激励,很容易半途而废,类似我们开玩笑说的「从入门到放弃」。\n \n \n \n Python还有很多优点:\n \n \n \n 上手快,第三方库丰富,资料丰富,很容易做出“可见可得”的应用。\n \n \n \n Python所需要学习的知识基础知识\n \n \n \n Python最基本的语言特性和语法:•变量•数据类型与数据结构•输入和输出•运算符、表达式、语句•控制流(顺序控制,选择控制和循环控制)•面向过程编程•面向对象编程•异常处理•组织代码•标准库、第三方库\n \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n
    \n \n
    \n
    \n
    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n \n 为大家准备了一些python的学习教程和PDF格式的python电子书籍,希望可以帮助到大家。\n \n \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n
    \n \n
    \n
    \n
    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n
    \n \n
    \n
    \n
    \n

    \n \n \n 以上这些教程我已经为大家打包准备好了,希望对正在学习的你有所帮助!\n \n \n

    \n
      \n
    1. \n

      \n \n \n \n 获取方式:公众号内回复“学习”即可\n \n \n \n

      \n
    2. \n
    3. \n

      \n \n \n \n 需要的小伙伴可以多多点赞转发 让更多的人看到\n \n \n \n

      \n
    4. \n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n
    \n \n
    \n
    \n
    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/dL5ozU3wGbvtiyoUNI9C2g", + "creator": "python开发工程师", + "pubDate": "Sun, 29 Jun 2025 09:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZy6NhzQnQnn6fj5giafbrz2bOJkSX76xZDY1ibH9oKrfibTxboy6FZicP5Io4FaByO8nc45ODaX9PY41Q/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZy6NhzQnQnn6fj5giafbrz2bOJkSX76xZDY1ibH9oKrfibTxboy6FZicP5Io4FaByO8nc45ODaX9PY41Q/0?wx_fmt=jpeg", + "id": "2247511257_2", + "language": "", + "folder": "IT技术", + "feed": "python开发工程师", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "6945f979532d2b5989ef04164d801403", + "highlights": [] + }, + { + "title": "自学python必看的书籍《Python编程:从入门到实践》PDF版拿走不谢", + "description": "内容简介本书是一本针对所有层次的Python 读者而作的Python 入门书。", + "content": "
    \n

    \n \n
    \n
    \n

    \n

    \n \n \"图片\"\n \n

    \n

    \n \n \n 内容简介\n \n \n

    \n

    \n \n 本书是一本针对所有层次的Python 读者而作的Python 入门书。全书分两部分:\n \n

    \n

    \n \n 第一部分介绍用Python 编程所必须了解的基本概念,包括matplotlib、NumPy 和Pygal 等强大的Python 库和工具介绍,以及列表、字典、if 语句、类、文件与异常、代码测试等内容;\n \n

    \n

    \n \n 第二部分将理论付诸实践,讲解如何开发三个项目,包括简单的Python 2D 游戏开发如何利用数据生成交互式的信息图,以及创建和定制简单的Web 应用,并帮读者解决常见编程问题和困惑。\n \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n \n 目录\n \n \n

    \n
    \n \n \"图片\"\n \n
    \n
    \n \n \"图片\"\n \n
    \n
    \n \n \"图片\"\n \n
    \n
    \n \n \"图片\"\n \n
    \n
    \n \n
    \n
    \n
    \n
    \n \n \"图片\"\n \n
    \n
    \n \n \"图片\"\n \n
    \n

    \n \n \n 书籍的分享格式为PDF电子版,小伙伴们手机也可以阅读学习哦!\n \n \n

    \n

    \n \n \n 书籍终归只是辅助,想要真的深入学习python,当然还要有系统的学习方法和学习教程才可以让你在学习的路上一帆风顺哦!\n \n \n

    \n

    \n \n \n 本套视频一共400集,本套视频共分3季:\n \n \n

    \n

    \n \n 第一季 【基础篇】Python基础 (115集)\n \n

    \n

    \n \n 第二季 【提高篇】Python深入和扩展(100集)\n \n

    \n

    \n \n 第三季 【扩展篇】网络编程、多线程、扩展库(85集)\n \n

    \n

    \n \n 第四季 【高手篇】算法、Python源码、函数式编程、手工实现神经网络(100集)\n \n

    \n
    \n \n \"图片\"\n \n
    \n

    \n \n \n 以上这些教程我已经为大家打包准备好了,希望对正在学习的你有所帮助!\n \n \n

    \n
      \n
    1. \n

      \n \n \n \n 获取方式:公众号私信回复“学习”即可\n \n \n \n

      \n
    2. \n
    3. \n

      \n \n \n \n 需要的小伙伴可以多多点赞转发 让更多的人看到\n \n \n \n

      \n
    4. \n
    \n
    \n \n
    \n
    \n
    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/OBYA9CqtM2SJz3V0OsMNMw", + "creator": "python开发工程师", + "pubDate": "Fri, 27 Jun 2025 09:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZy6NhzQnQnn6fj5giafbrz2bENYGODukDQ8iaKoSXOKkIEz9iaAdM2HiaTL77jqDuHqbple8NGD0NlMFQ/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZy6NhzQnQnn6fj5giafbrz2bENYGODukDQ8iaKoSXOKkIEz9iaAdM2HiaTL77jqDuHqbple8NGD0NlMFQ/0?wx_fmt=jpeg", + "id": "2247511245_2", + "language": "", + "folder": "IT技术", + "feed": "python开发工程师", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "761a8deb7a6670d138fbe73449a68ef1", + "highlights": [] + }, + { + "title": "56个java毕设项目!附源码+视频!", + "description": "Java毕设项目可从多个领域和方向进行选择,以下是一些推荐的项目方向,涵盖不同技术栈和应用场景,适合不同难度需", + "content": "None", + "category": "", + "link": "https://mp.weixin.qq.com/s/qI4OJXVOTKqqlrNRYkeFaQ", + "creator": "python开发工程师", + "pubDate": "Wed, 25 Jun 2025 09:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZykJYNAN6ERqWwaXLkUyZR9pjsye99eFgIia4mpKSs2EagFeEAyyNmYqkJLFGKGiciaM7iatIhDZKqv3w/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZykJYNAN6ERqWwaXLkUyZR9pjsye99eFgIia4mpKSs2EagFeEAyyNmYqkJLFGKGiciaM7iatIhDZKqv3w/0?wx_fmt=jpeg", + "id": "2247511233_2", + "language": "", + "folder": "IT技术", + "feed": "python开发工程师", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "b3fa55dd5b4feeaa370d84c7511f8eb9", + "highlights": [] + }, + { + "title": "计算机专业18个Java毕设项目(附源码+视频+数据库),拿走即用!期末毕设不用", + "description": "领取方式:点赞+私信留言“111”或主动给我打个招呼,我都会回复的~~~一定要关注我,不然我发不了消息哦~", + "content": "
    \n

    \n \n \n
    \n
    \n
    \n

    \n

    \n \n \n \n \n \n

    \n

    \n \n \n

    \n

    \n \n \n \n \n \n

    \n

    \n \n \n

    \n

    \n \n \n \n \n \n

    \n

    \n \n \n

    \n

    \n \n \n \n \n \n

    \n

    \n \n \n

    \n

    \n \n \n \n \n \n

    \n

    \n \n \n 领取方式:点赞+私信留言“111”或主动给我打个招呼,我都会回复的~~~\n \n \n

    \n

    \n \n \n 一定要关注我,不然我发不了消息哦~\n \n \n

    \n

    \n \n \n 整理不易,有帮助的话大家一定要 点赞 + 收藏 哦 ❤️!支持作者的同时,便于自己下次更方便地找到叭!\n \n \n

    \n
    \n \n
    \n
    \n
    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/jJOoSZywzYalMsmElLJDFw", + "creator": "python开发工程师", + "pubDate": "Wed, 25 Jun 2025 09:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZykJYNAN6ERqWwaXLkUyZR9fvibGb3ZJY6qI6g2LWABXT88R1dhIwRS6icaENdrkXtrtoIuBzJ0Od2w/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZykJYNAN6ERqWwaXLkUyZR9fvibGb3ZJY6qI6g2LWABXT88R1dhIwRS6icaENdrkXtrtoIuBzJ0Od2w/0?wx_fmt=jpeg", + "id": "2247511233_1", + "language": "", + "folder": "IT技术", + "feed": "python开发工程师", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "5f74af3509bb7119b65229e9a44505ee", + "highlights": [] + }, + { + "title": "自学python必看的书籍《Python编程:从入门到实践》PDF版拿走不谢", + "description": "自学python必看的书籍《Python编程:从入门到实践》内容简介本书是一本针对所有层次的Python 读者", + "content": "
    \n

    \n \n
    \n
    \n

    \n

    \n \n 自学python必看的书籍《Python编程:从入门到实践》\n \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n \"图片\"\n \n

    \n

    \n \n \n 内容简介\n \n \n

    \n

    \n \n 本书是一本针对所有层次的Python 读者而作的Python 入门书。全书分两部分:\n \n

    \n

    \n \n 第一部分介绍用Python 编程所必须了解的基本概念,包括matplotlib、NumPy 和Pygal 等强大的Python 库和工具介绍,以及列表、字典、if 语句、类、文件与异常、代码测试等内容;\n \n

    \n

    \n \n 第二部分将理论付诸实践,讲解如何开发三个项目,包括简单的Python 2D 游戏开发如何利用数据生成交互式的信息图,以及创建和定制简单的Web 应用,并帮读者解决常见编程问题和困惑。\n \n

    \n

    \n \n
    \n
    \n

    \n

    \n \n \n 目录\n \n \n

    \n
    \n \n \"图片\"\n \n
    \n
    \n \n \"图片\"\n \n
    \n
    \n \n \"图片\"\n \n
    \n
    \n \n \"图片\"\n \n
    \n
    \n \n
    \n
    \n
    \n
    \n \n \"图片\"\n \n
    \n
    \n \n \"图片\"\n \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n 书籍的分享格式为PDF电子版,小伙伴们手机也可以阅读学习哦!\n \n \n

    \n

    \n \n \n 书籍终归只是辅助,想要真的深入学习python,当然还要有系统的学习方法和学习教程才可以让你在学习的路上一帆风顺哦!\n \n \n

    \n

    \n \n \n 本套视频一共400集,本套视频共分3季:\n \n \n

    \n

    \n \n 第一季 【基础篇】Python基础 (115集)\n \n

    \n

    \n \n 第二季 【提高篇】Python深入和扩展(100集)\n \n

    \n

    \n \n 第三季 【扩展篇】网络编程、多线程、扩展库(85集)\n \n

    \n

    \n \n 第四季 【高手篇】算法、Python源码、函数式编程、手工实现神经网络(100集)\n \n

    \n

    \n \n
    \n
    \n

    \n
    \n \n \"图片\"\n \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n 以上这些教程我已经为大家打包准备好了,希望对正在学习的你有所帮助!\n \n \n

    \n
      \n
    1. \n

      \n \n \n \n 获取方式:公众号私信回复“学习”即可\n \n \n \n

      \n
    2. \n
    3. \n

      \n \n \n \n 需要的小伙伴可以多多点赞转发 让更多的人看到\n \n \n \n

      \n
    4. \n
    \n
    \n \n
    \n
    \n
    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/bQOggBBJ0dLp-eoSjOrnTg", + "creator": "python开发工程师", + "pubDate": "Tue, 24 Jun 2025 09:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZykJYNAN6ERqWwaXLkUyZR9J5SsenheZQaXX6YI26tYXNebNzDqdLr7y1R630Q9ic67PSicibDZZsWicg/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZykJYNAN6ERqWwaXLkUyZR9J5SsenheZQaXX6YI26tYXNebNzDqdLr7y1R630Q9ic67PSicibDZZsWicg/0?wx_fmt=jpeg", + "id": "2247511212_2", + "language": "", + "folder": "IT技术", + "feed": "python开发工程师", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "9397870631052e80a6854e64e07d7b1d", + "highlights": [] + }, + { + "title": "python入门教程完整版(PDF版)", + "description": "当年3w买的全套python教程用不着了,现在送给有缘人,不要钱,一个月教你从入门到精通本套视频共600集70", + "content": "
    \n

    \n \n
    \n
    \n

    \n
    \n

    \n \n \n 当年3w买的全套python教程用不着了,现在送给有缘人,不要钱,一个月教你从入门到精通\n \n \n

    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n 本套视频共600集\n \n \n

    \n

    \n \n \n 70个练手项目列表\n \n \n

    \n

    \n \n \n 300套Python书籍PDF版等等~\n \n \n

    \n

    \n \n \n (我现在正在发放中,如下图)\n \n \n

    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n 获取方式\n \n \n \n
    \n
    \n

    \n

    \n \n 1、点赞+在看\n \n

    \n

    \n \n 2、后台回复:\n \n \n \n 学习\n \n \n

    \n
    \n \n
    \n
    \n
    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/kZFae2Y-ENwLqQRWOyFvVA", + "creator": "python开发工程师", + "pubDate": "Mon, 23 Jun 2025 09:47:00 ", + "enclosure": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZzmibvbzGeIPHq0Sn1ibibWA4MIibpTZGza7gH3SRSCUFh8dZ8LKI27RBJY6wWn2yK5yqsjBY3axkYqSQ/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZzmibvbzGeIPHq0Sn1ibibWA4MIibpTZGza7gH3SRSCUFh8dZ8LKI27RBJY6wWn2yK5yqsjBY3axkYqSQ/0?wx_fmt=jpeg", + "id": "2247511206_2", + "language": "", + "folder": "IT技术", + "feed": "python开发工程师", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "11ddd29ebda4153b594d8c3b201af156", + "highlights": [] + }, + { + "title": "史上最详细python学习路线-从入门到精通,只需5个月时间", + "description": "Python是一种功能很强大的语言,对于零基础学习Python还是有难度的,但只要学习方法对,入门还是很快哒。", + "content": "
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n Python是一种功能很强大的语言,对于零基础学习Python还是有难度的,但只要学习方法对,入门还是很快哒。\n \n \n

    \n
    \n

    \n \n \n 针对Python的初学者,从无到有的Python语言如何入门,主要包括了:Python的简介,如何下载Python,如何安装Python,如何使用终端、Shell,IDE等各种开发环境进行Python开发,Python中的语法和基本知识、概念和逻辑,以及继续深入学习Python的方法。\n \n \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n 下面我将详细介绍python学习路线,给那些想学习python的小伙伴们一点帮助!\n \n \n

    \n

    \n \n \n 你可以按照下面的步骤学习:\n \n \n

    \n

    \n \n \n 第一阶段:Python基础编程\n \n \n

    \n

    \n \n \n 1:知识体系:\n \n \n

    \n
    \n

    \n \n \n 掌握基本概念,基本数据类型,高级容器类型,控制语句,函数,文件等等\n \n \n

    \n
    \n

    \n \n \n 1.1:学习目标:\n \n \n

    \n
      \n
    • \n

      \n \n 1:可掌握python的基础语法,能够使用python进行编程;\n \n

      \n
    • \n
    • \n

      \n \n 2:具有面向对象编程解决问题的思想;\n \n

      \n
    • \n
    • \n

      \n \n 3:掌握Linux使用技巧和方法;\n \n

      \n
    • \n
    • \n

      \n \n 4:熟悉Linux环境下的开发流程\n \n

      \n
    • \n
    \n

    \n \n \n 1.2:具备能力:\n \n \n

    \n
      \n
    • \n

      \n \n 1:能够熟练使用python解决一般问题,利用python的特性,写出简洁易懂的代码;\n \n

      \n
    • \n
    • \n

      \n \n 2:能够进行基于Linux的环境开发。具备企业中开发中必备的Linux技能。\n \n

      \n
    • \n
    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n 第二阶段:Python核心提升\n \n \n

    \n

    \n \n \n 1:知识体系:\n \n \n

    \n
    \n

    \n \n 关系型数据库(MySQL),健值型数据库(Redis),文档型数据库(MongoDB),网络编程,并发等等\n \n

    \n
    \n

    \n \n \n 1.1:学习目标:\n \n \n

    \n
      \n
    • \n

      \n \n 1:掌握三种类型数据库的使用\n \n

      \n
    • \n
    • \n

      \n \n 2:掌握python网络编程和并发的具体实践技能\n \n

      \n
    • \n
    \n

    \n \n \n 1.2:具备能力:\n \n \n

    \n
      \n
    • \n

      \n \n 1:能够熟练使用三种类型的数据库,以及各自的特点的使用场景有清晰的认识\n \n

      \n
    • \n
    • \n

      \n \n 2:对网络编程和并发有深入的了解,初步具备企业的用人需求。\n \n

      \n
    • \n
    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n 第三阶段:Web基础开发\n \n \n

    \n

    \n \n \n 1:知识体系:\n \n \n

    \n
    \n

    \n \n HTML与css,JavaScript,jQuery,Ajax\n \n

    \n
    \n

    \n \n \n 第四阶段:Web-Django框架与项目\n \n \n

    \n

    \n \n \n 1:知识体系:\n \n \n

    \n
    \n

    \n \n Django框架,路由系统,项目:网上商城等等\n \n

    \n
    \n

    \n \n \n 第五阶段:爬虫开发\n \n \n

    \n

    \n \n \n 1:知识体系:\n \n \n

    \n
    \n

    \n \n 爬虫入门,爬虫库和工具的使用,IP代理池的实现,常见反爬手段,爬虫高级框架等等\n \n

    \n
    \n

    \n \n \n 第六阶段:数据分析\n \n \n

    \n

    \n \n \n 1:知识体系:\n \n \n

    \n
    \n

    \n \n 数据分析初级,经典模块及其使用,高级算法等等\n \n

    \n
    \n

    \n \n 如果你有心学习Python,为了避免漫无目且零散地学习,小编整理好的学习资料可以分享给大家,即使你\n \n \n \n 非技术出身\n \n \n \n 也能快速入门Python获得\n \n \n \n 求职能力、工作效率、解决问题\n \n \n \n 等硬核能力的提升。\n \n

    \n

    \n \n Python从基础到进阶的教程\n \n

    \n

    \n \n \n 1.Python基础语法以及流程控制语句\n \n \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n 2.Python基础编程\n \n \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n 3.Python电子书大全(仅展示部分资料)\n \n \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n \n 文章到此结束了,希望对大家有帮助。还有小编把这些学习资料都已整理完毕,\n \n \n \n 需要的小伙伴可以\n \n \n

    \n

    \n \n \n \n 点赞+再看,\n \n \n \n

    \n
    \n \n \n \n 公众号内回复“学习”即可获取编程资料\n \n \n \n
    \n

    \n \n
    \n
    \n

    \n
    \n \n \n
    \n

    \n \n \n 就业方向一 web前后端开发(python开发工程师)\n \n \n

    \n

    \n \n 熟悉HTML/JavaScript/CSS,掌握React.js等前端框架开发,熟悉页面架构和布局,熟悉MySQL、Redis等,具备数据库应用、设计能力,熟练掌握Django/Flask等Web后端开发框架,熟练掌握网络通讯协议和多线程、多进程并发技术,了解架构设计、实现原理和安全防护加固,掌握高并发web网站、缓存加速技术,有1-2个完整项目经验。已经具备Python web中高级级开发工程师能力,市面薪资可达12K-18K。\n \n

    \n

    \n \n
    \n
    \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n 就业方向二 运维自动化开发\n \n \n

    \n

    \n \n 对持续集成和持续交付(CI/CD)有深入理解,并有实施经验,熟练 DevOps 工具,如:Jenkins, Ansible 等的经验;熟悉容器技术,有Kubernetes使用经验;对运维平台和中间件的设计、开发工作;掌握自动化脚本的设计、开发、测试,可实现运维平台自动化和可视化,具备Python运维开发工程师能力,市面薪资可达12K-25K。\n \n

    \n
    \n \"图片\"\n
    \n

    \n \n
    \n
    \n

    \n

    \n \n \n 业方向三 数据抓取(Python爬虫工程师)\n \n \n

    \n

    \n \n 可熟练使用Python语言,熟悉常用爬虫框架如Scrapy等;熟练使用selenium,lxml, bs4 对xml,html的文本进行抓取解析清理;对管理网站的cookie实效性处理有经验;熟悉反爬虫、验证码识别技术,有能力解决复杂的反爬限制;具备丰富的多线程、网络数据处理经验,掌握开源爬虫框架scrapy;掌握分布式爬虫技术。可设计数据采集策略,提升数据采集效率及质量。\n \n

    \n

    \n \n \n 就业方向四 数据分析&挖掘(数据分析&数据挖掘工程师)\n \n \n

    \n

    \n \n 熟悉主流的数据挖掘建模算法,如数据分析、分类预测、用户画像等;掌握数据分析模型的固化及部署工作,可进行进行数据分析、数据建模、挖掘、清洗,可实现包括数据交互、特征提取、数据挖掘、分析报告等;熟练使用pandas,numpy,matplotlib等工具分析数据,做数据的可视化,并能解释数据分布。已经具备Python数据分析&挖掘工程师能力,市面薪资可达10K-18K。\n \n

    \n

    \n \n \n 拓展就业方向五 机器学习&金融量化&人工智能\n \n \n

    \n

    \n \n 该领域为前沿技术领域,目前世界top100公司都在招募大量人工智能、量化交易、机器学习相关人才,对相关人才要求有较高的学习能力和数学能力,年薪50w+起步,对于刚接触python的人士来说,适合作为未来发展方向研究。\n \n

    \n

    \n \n
    \n
    \n

    \n
    \n

    \n \n
    \n
    \n

    \n
    \n \n
    \n
    \n
    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/VOE8IdoClmVi5Na8LEysnA", + "creator": "python开发工程师", + "pubDate": "Sat, 21 Jun 2025 09:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZzmibvbzGeIPHq0Sn1ibibWA4M8UWibW49xk7f75yJiaJRQ6zEB4sJDjLRcTFvJ8wBNFt44nsdgL6ibwYgg/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZzmibvbzGeIPHq0Sn1ibibWA4M8UWibW49xk7f75yJiaJRQ6zEB4sJDjLRcTFvJ8wBNFt44nsdgL6ibwYgg/0?wx_fmt=jpeg", + "id": "2247511199_2", + "language": "", + "folder": "IT技术", + "feed": "python开发工程师", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "563c4620e9d0adfc2c8821adbec2fe03", + "highlights": [] + }, + { + "title": "学习python的必读书籍《利用Python进行数据分析》附PDF版", + "description": "这是非常经典的一本Python数据分析书,每一章之间有递进关系,适合Python入门()如《Python编程,", + "content": "
    \n

    \n \n 这是非常经典的一本Python数据分析书,每一章之间有递进关系,适合Python入门()如《Python编程,从入门到实践》)之后阅读,本书专门针对数据分析领域的。我细致地读了一遍,敲了一遍代码,一开始没有头绪,进展缓慢,后来逐渐明朗了。学习编程要多实践,这对没有基础的人来说有困难,因为无从下手。我的体会是,在你基础不牢的时候,就多看,多记,多总结,搭建自己的思路框架。\n \n

    \n

    \n \n 书籍简介\n \n

    \n
    \n \"图片\"\n
    \n

    \n \n 利用Python进行数据分析\n \n

    \n

    \n \n \n 本书由pandas项目创始人Wes McKinney亲笔撰写,详细介绍利用Python进行操作、处理、清洗和规整数据等方面的具体细节和基本要点。第2版针对Python 3.6进行全面修订和更新,涵盖新版的pandas、NumPy、IPython和Jupyter,并增加大量实际案例,可以帮助你高效解决一系列数据分析问题。\n \n \n

    \n

    \n \n \n 第2版中的主要更新包括:\n \n \n

    \n

    \n \n \n • 所有的代码,包括把Python的教程更新到了Python 3.6版本(第1版中使用的是Python 2.7)\n \n \n

    \n

    \n \n \n • 更新了Python第三方发布版Anaconda和其他所需Python包的安装指引\n \n \n

    \n

    \n \n \n • 更新pandas库到2017年的新版\n \n \n

    \n

    \n \n \n • 新增一章,关于更多高级pandas工具和一些使用提示\n \n \n

    \n

    \n \n \n • 新增statsmodels和scikit-learn的简明使用介绍\n \n \n

    \n

    \n \n \n \"图片\"\n \n \n

    \n

    \n \n 目录\n \n

    \n

    \n \n 第1章 准备工作\n \n
    \n \n 第2章 Python语言基础、IPython及Jupyter notebook\n \n
    \n \n 第3章 内建数据结构、函数及文件\n \n
    \n \n 第4章 NumPy基础:数组与向量化计算\n \n
    \n \n 第5章 pandas入门\n \n
    \n \n 第6章 数据载入、存储及文件格式\n \n
    \n \n 第7章 数据清洗与准备\n \n
    \n \n 第8章 数据规整:连接、联合与重塑\n \n
    \n \n 第9章 绘图与可视化\n \n
    \n \n 第10章 数据聚合与分组操作\n \n
    \n \n 第11章 时间序列\n \n
    \n \n 第12章 高阶pandas\n \n
    \n \n 第13章 Python建模库介绍\n \n
    \n \n 第14章 数据分析示例\n \n

    \n
    \n \"图片\"\n
    \n
    \n \"图片\"\n
    \n

    \n \n \n \"图片\"\n \n \n

    \n
    \n \"图片\"\n
    \n

    \n \n \n \n 领取方式:\n \n
    \n
    \n
    \n

    \n
      \n
    1. \n

      \n \n \n \n 点赞+在看\n \n \n \n

      \n
    2. \n
    3. \n

      \n \n \n \n 私信留下回复:“学习”\n \n \n \n

      \n
    4. \n
    \n
    \n \n
    \n
    \n
    \n

    \n \n \n

    \n
    \n", + "category": "", + "link": "https://mp.weixin.qq.com/s/QzNsOfAyqx2sgOiavcalzQ", + "creator": "python开发工程师", + "pubDate": "Fri, 20 Jun 2025 09:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZzmibvbzGeIPHq0Sn1ibibWA4MtWvsSEdycjHjFSUVspgdgrZ63Tic9M30R85C32DVibmXTlKomqNnDrKw/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/mmbiz_jpg/Nc53lZXDjZzmibvbzGeIPHq0Sn1ibibWA4MtWvsSEdycjHjFSUVspgdgrZ63Tic9M30R85C32DVibmXTlKomqNnDrKw/0?wx_fmt=jpeg", + "id": "2247511178_1", + "language": "", + "folder": "IT技术", + "feed": "python开发工程师", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "1189f681b3a409d6c3f0d7e40085e750", + "highlights": [] + }, { "title": "第十九届世界青年桥牌团体锦标赛即将开赛", "description": "第十九届世界青年桥牌团体锦标赛(19th World Youth Team Championships)将于7月12-17日在意大利萨尔索马焦雷举办。共有来自世界各地的94支青年队参加U26,U21,U26女子,U16,U31五个组别的比赛", @@ -4226,6 +5854,28 @@ "image": "http://mmbiz.qpic.cn/mmbiz_png/Nc53lZXDjZwDTSISGR4xlicLicHSZQ2Az6yzuTwWm4iboHMSnJhn5s6HodjpicYZW8YxeqiaPoMJ1rJdCW8a0Ywllug/0?wx_fmt=png", "description": "", "items": [ + { + "title": "简单的Web云存储解决方案bewCloud", + "description": "bewCloud 是一个简化版的自托管云存储解决方案,可作为 Nextcloud 和 ownCloud 的更简单替代方案。", + "content": "None", + "category": "", + "link": "https://mp.weixin.qq.com/s/fuqL7r4ch20IJeyZ60IvDg", + "creator": "各种折腾", + "pubDate": "Sat, 14 Jun 2025 19:00:00 ", + "enclosure": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/Cibq2rFYLAaGTIGUkoPBlj8nznbeib15F9ElMNJuQeTicAjILG80dpvKQFpqfjAPvEpsK8OasR6I4wo2SRpoCy8iaQ/0?wx_fmt=jpeg", + "enclosureType": "image/jpeg", + "image": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/Cibq2rFYLAaGTIGUkoPBlj8nznbeib15F9ElMNJuQeTicAjILG80dpvKQFpqfjAPvEpsK8OasR6I4wo2SRpoCy8iaQ/0?wx_fmt=jpeg", + "id": "2247509021_1", + "language": "", + "folder": "IT技术", + "feed": "各种折腾", + "read": false, + "favorite": false, + "created": false, + "tags": [], + "hash": "2418ebb6657179d60b73596fead978e4", + "highlights": [] + }, { "title": "一周练完!100个Python实战项目,太实用了,绝对不能错过(附完整版源码+教程)", "description": "Python 是一种极具可读性和通用性的编程语言。", diff --git a/.obsidian/workspace.json b/.obsidian/workspace.json index 684bde9b..2a3f964c 100644 --- a/.obsidian/workspace.json +++ b/.obsidian/workspace.json @@ -23,12 +23,12 @@ "state": { "type": "markdown", "state": { - "file": "01-工作日记/2025-07-16.md", + "file": "08-Inbox/2025-07-04.md", "mode": "source", "source": false }, "icon": "lucide-file", - "title": "2025-07-16" + "title": "2025-07-04" } }, { @@ -70,7 +70,7 @@ "autoReveal": false }, "icon": "lucide-folder-closed", - "title": "Files" + "title": "文件列表" } }, { @@ -132,7 +132,7 @@ { "id": "6eb65634f662a76d", "type": "tabs", - "dimension": 46.58536585365854, + "dimension": 46.585365853658544, "children": [ { "id": "255862dc8754e160", @@ -202,13 +202,13 @@ "state": { "type": "outline", "state": { - "file": "01-工作日记/2025-07-16.md", + "file": "08-Inbox/2025-07-04.md", "followCursor": true, "showSearch": false, "searchQuery": "" }, "icon": "lucide-list", - "title": "Outline of 2025-07-16" + "title": "2025-07-04 的大纲" } }, { @@ -237,7 +237,7 @@ { "id": "ad86bdfc0cc9f06b", "type": "tabs", - "dimension": 53.41463414634146, + "dimension": 53.414634146341456, "children": [ { "id": "61523dba4992de80", @@ -278,17 +278,17 @@ }, "left-ribbon": { "hiddenItems": { - "switcher:Open quick switcher": false, - "graph:Open graph view": false, - "canvas:Create new canvas": false, - "daily-notes:Open today's daily note": false, - "templates:Insert template": false, - "command-palette:Open command palette": false, - "audio-recorder:Start/stop recording": true, - "workspaces:Manage workspace layouts": false, - "publish:Publish changes...": false, - "obsidian-excalidraw-plugin:New drawing": false, - "obsidian-wordpress:WordPress Publish": false, + "switcher:打开快速切换": false, + "graph:查看关系图谱": false, + "canvas:新建白板": false, + "daily-notes:打开/创建今天的日记": false, + "templates:插入模板": false, + "command-palette:打开命令面板": false, + "audio-recorder:开始/结束录音": true, + "workspaces:管理工作区布局": false, + "publish:发布更改": false, + "obsidian-excalidraw-plugin:新建绘图文件": false, + "obsidian-wordpress:发布到 WordPress": false, "obsidian-git:Open Git source control": false, "remotely-save:Remotely Save": false, "templater-obsidian:Templater": false, @@ -306,12 +306,14 @@ }, "active": "bf742a4685733cc9", "lastOpenFiles": [ + "08-Inbox/博文测试.md", + "08-Inbox/草稿.md", "03-学习笔记/微信机器人.md", - "03-学习笔记/微信公众号RSS.md", "03-学习笔记/Calibre电子书平台.md", - "03-学习笔记/OB插件.md", "03-学习笔记/Memos.md", "01-工作日记/2025-07-16.md", + "03-学习笔记/微信公众号RSS.md", + "03-学习笔记/OB插件.md", "未命名.md", "70-笔记归档/2024-", "70-笔记归档/2025", @@ -335,7 +337,6 @@ "Excalidraw", "01-工作日记/2025-07-15.md", "10-WebClipper/使用Obsidian打造个人图书馆!.md", - "60-DevonThink/一个测试.md", "50-Memos/2025/07/resources/kyhNGvh5iFc9eYXy2YuYWx_FDF8C04E-22D3-4D4C-9ED0-F52A326B9801.jpg", "50-Memos/2025/07/resources/6cb2VU3rkihszQLFDerqA2_2_20250712120414.png", "04-博客发布/0743-Bridge/assets/640.jpeg", diff --git a/08-Inbox/博文测试.md b/08-Inbox/博文测试.md index 9f9438fa..61420369 100644 --- a/08-Inbox/博文测试.md +++ b/08-Inbox/博文测试.md @@ -5,3 +5,5 @@ ![](http://img2.jintiankansha.me/get?src=http://mmbiz.qpic.cn/mmbiz_jpg/laADTC8BCK7wnO28nePDzWZ2JcH27v4KjFKTleVzHq5flE9NTUofI4xyKRgaVousvEPwXczHlZoZ7fpDcjPwZw/640?wx_fmt=jpeg) + +