@diplodoc/transform
Version:
A simple transformer of text in YFM (Yandex Flavored Markdown) to HTML
612 lines (601 loc) • 21.5 kB
JavaScript
;
(() => {
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
// node_modules/get-root-node-polyfill/index.js
var require_get_root_node_polyfill = __commonJS({
"node_modules/get-root-node-polyfill/index.js"(exports, module) {
"use strict";
function getRootNode2(opt) {
var composed = typeof opt === "object" && Boolean(opt.composed);
return composed ? getShadowIncludingRoot(this) : getRoot(this);
}
function getShadowIncludingRoot(node) {
var root = getRoot(node);
if (isShadowRoot(root)) {
return getShadowIncludingRoot(root.host);
}
return root;
}
function getRoot(node) {
if (node.parentNode != null) {
return getRoot(node.parentNode);
}
return node;
}
function isShadowRoot(node) {
return node.nodeName === "#document-fragment" && node.constructor.name === "ShadowRoot";
}
if (typeof module === "object" && module.exports) {
module.exports = getRootNode2;
}
}
});
// src/js/polyfill.js
var import_get_root_node_polyfill = __toESM(require_get_root_node_polyfill());
if (typeof document !== "undefined") {
(function(e) {
const matches = e.matches || e.matchesSelector || e.webkitMatchesSelector || e.mozMatchesSelector || e.msMatchesSelector || e.oMatchesSelector;
if (matches) {
e.matches = e.matchesSelector = matches;
} else {
e.matches = e.matchesSelector = function matches2(selector) {
const rootNode = e.getRootNode ? e.getRootNode() : import_get_root_node_polyfill.default.call(e);
const matches3 = rootNode.querySelectorAll(selector);
const th = this;
return Array.prototype.some.call(matches3, (e2) => {
return e2 === th;
});
};
}
})(Element.prototype);
}
// src/js/utils.ts
var getEventTarget = (event) => {
const path = event.composedPath();
return Array.isArray(path) && path.length > 0 ? path[0] : event.target;
};
var isCustom = (event) => {
const target = getEventTarget(event);
return !target || !target.matches;
};
var copyToClipboard = async (text) => {
if (!text) {
return;
}
if (navigator.clipboard && typeof navigator.clipboard.writeText) {
return navigator.clipboard.writeText(text);
}
const textarea = document.createElement("textarea");
textarea.setAttribute("style", "position: absolute; left: 1000%");
textarea.textContent = text;
document.body.append(textarea);
textarea.select();
document.execCommand("copy");
document.body.removeChild(textarea);
};
// src/js/code.ts
var COPY_BUTTON_SELECTOR = ".yfm-clipboard-button";
var WRAP_BUTTON_SELECTOR = ".yfm-wrapping-button";
function notifySuccess(svgButton) {
if (!svgButton) {
return;
}
const id = svgButton.getAttribute("data-animation");
const icon = svgButton.getRootNode().getElementById(`visibileAnimation-${id}`);
if (!icon) {
return;
}
icon.beginElement();
}
function buttonCopyFn(target) {
var _a;
const container = (_a = target.parentNode) == null ? void 0 : _a.parentNode;
const code = container == null ? void 0 : container.querySelector("pre code");
if (!container || !code) {
return;
}
const textContent = Array.from(code.childNodes).filter((node) => {
if (node instanceof HTMLElement && node.classList.contains("yfm-line-number")) {
return false;
}
return true;
}).map((node) => node.textContent).join("");
copyToClipboard(textContent.trim()).then(() => {
notifySuccess(container.querySelector(".yfm-clipboard-icon"));
setTimeout(() => target.blur(), 1500);
});
}
function buttonWrapFn(target) {
var _a;
const container = (_a = target.parentNode) == null ? void 0 : _a.parentNode;
const code = container == null ? void 0 : container.querySelector("pre code");
if (!container || !code) {
return;
}
code.classList.toggle("wrap");
setTimeout(() => target.blur(), 500);
}
if (typeof document !== "undefined") {
document.addEventListener("click", (event) => {
if (isCustom(event)) {
return;
}
const target = getEventTarget(event);
if (target.matches(COPY_BUTTON_SELECTOR)) {
buttonCopyFn(target);
} else if (target.matches(WRAP_BUTTON_SELECTOR)) {
buttonWrapFn(target);
}
});
}
// src/js/tooltip/constant.ts
var PAGE_CONTAINER_SELECTOR = ".dc-doc-page__content";
var TOOLTIP_BASE_CLASS = "yfm yfm-tooltip";
var TOOLTIP_OPEN_CLASS = "open";
var TOOLTIP_DATA_ATTR = "data-tooltip-id";
var DEFAULT_OFFSET_VALUES = { mainAxis: 5 };
var OPPOSITE_SIDES = {
top: "bottom",
bottom: "top",
left: "right",
right: "left"
};
var DEFAULT_SIDE_OBJECT = {
top: 0,
bottom: 0,
left: 0,
right: 0
};
// src/js/tooltip/utils.ts
function isVerticalSide(side) {
return side === "top" || side === "bottom";
}
function createSideObject(value = 0) {
if (typeof value === "number") {
return { top: value, bottom: value, left: value, right: value };
}
return { ...DEFAULT_SIDE_OBJECT, ...value };
}
function parsePlacement(placement) {
const [side, alignment] = placement.split("-");
return { side, alignment };
}
function getOppositeSide(side) {
return OPPOSITE_SIDES[side];
}
function flipPlacement(placement) {
const { side, alignment } = parsePlacement(placement);
const opposite = getOppositeSide(side);
return alignment ? `${opposite}-${alignment}` : opposite;
}
function getOverflow(tooltip3, coords, viewport) {
const rect = updateRect(tooltip3, { top: coords.y, left: coords.x });
return detectOverflow(viewport, rect, 5);
}
function shouldFlip(overflow, flippedOverflow, side) {
const opposite = getOppositeSide(side);
return overflow[side] > 0 && flippedOverflow[opposite] < overflow[side];
}
function computePosition(reference, tooltip3, viewport, placement, offset, isRtl, flip = true) {
const coords = computeCoordsFromPlacement(reference, tooltip3, offset, placement, isRtl);
if (!flip) {
return { coords, placement };
}
const overflow = getOverflow(tooltip3, coords, viewport);
const { side } = parsePlacement(placement);
if (overflow[side] <= 0) {
return { coords, placement };
}
const flipped = flipPlacement(placement);
const flippedCoords = computeCoordsFromPlacement(reference, tooltip3, offset, flipped, isRtl);
const flippedOverflow = getOverflow(tooltip3, flippedCoords, viewport);
if (shouldFlip(overflow, flippedOverflow, side)) {
return { coords: flippedCoords, placement: flipped };
}
return { coords, placement };
}
function generateId() {
const random = Math.random().toString(36).substring(2, 6);
const now = Date.now().toString(36);
return `${random}${now}`;
}
function createRect(params) {
return {
...params,
right: params.left + params.width,
bottom: params.top + params.height
};
}
function updateRect(rect, params) {
var _a, _b, _c, _d;
return createRect({
top: (_a = params.top) != null ? _a : rect.top,
left: (_b = params.left) != null ? _b : rect.left,
width: (_c = params.width) != null ? _c : rect.width,
height: (_d = params.height) != null ? _d : rect.height
});
}
function getViewportRect() {
const { documentElement, body } = document;
const scrollTop = window.scrollY || documentElement.scrollTop || body.scrollTop;
const scrollLeft = window.scrollX || documentElement.scrollLeft || body.scrollLeft;
const clientTop = documentElement.clientTop || body.clientTop || 0;
const clientLeft = documentElement.clientLeft || body.clientLeft || 0;
return createRect({
top: Math.round(scrollTop - clientTop),
left: Math.round(scrollLeft - clientLeft),
width: document.body.clientWidth,
height: document.body.clientHeight
});
}
function getElementRect(element) {
const viewport = getViewportRect();
const box = element.getBoundingClientRect();
return createRect({
top: Math.round(box.top + viewport.top),
left: Math.round(box.left + viewport.left),
width: box.width,
height: box.height
});
}
function computeAxisOffset(offset, side, isRtl) {
const { mainAxis = 0, crossAxis = 0 } = offset;
const isVertical = isVerticalSide(side);
const mainDirection = side === "top" || side === "left" ? -1 : 1;
const crossDirection = isRtl && isVertical ? -1 : 1;
const mainOffset = mainAxis * mainDirection;
const crossOffset = crossAxis * crossDirection;
if (isVertical) {
return { x: crossOffset, y: mainOffset };
}
return { x: mainOffset, y: crossOffset };
}
function computeCoordsFromPlacement(reference, tooltip3, offset, placement, isRtl) {
const { side, alignment } = parsePlacement(placement);
const isVertical = isVerticalSide(side);
const alignmentAxis = isVertical ? "x" : "y";
const alignLength = alignmentAxis === "y" ? "height" : "width";
const centerX = reference.left + reference.width / 2 - tooltip3.width / 2;
const centerY = reference.top + reference.height / 2 - tooltip3.height / 2;
const alignmentOffset = reference[alignLength] / 2 - tooltip3[alignLength] / 2;
const coords = { x: reference.left, y: reference.top };
switch (side) {
case "top": {
coords.x = centerX;
coords.y = reference.top - tooltip3.height;
break;
}
case "bottom": {
coords.x = centerX;
coords.y = reference.top + reference.height;
break;
}
case "right": {
coords.x = reference.left + reference.width;
coords.y = centerY;
break;
}
case "left": {
coords.x = reference.left - tooltip3.width;
coords.y = centerY;
break;
}
}
switch (alignment) {
case "start": {
coords[alignmentAxis] -= alignmentOffset * (isRtl && isVertical ? -1 : 1);
break;
}
case "end": {
coords[alignmentAxis] += alignmentOffset * (isRtl && isVertical ? -1 : 1);
break;
}
}
const axisOffset = computeAxisOffset(offset, side, isRtl);
coords.x += axisOffset.x;
coords.y += axisOffset.y;
return coords;
}
function convertToRelativeToOffsetParentRect(rect, offsetParent) {
const offsetRect = getElementRect(offsetParent);
return createRect({
top: rect.top - offsetRect.top + offsetParent.offsetTop,
left: rect.left - offsetRect.left + offsetParent.offsetLeft,
width: rect.width,
height: rect.height
});
}
function detectOverflow(boundary, element, padding = 0) {
const { top, bottom, left, right } = createSideObject(padding);
return {
top: boundary.top - element.top + top,
bottom: element.bottom - boundary.bottom + bottom,
left: boundary.left - element.left + left,
right: element.right - boundary.right + right
};
}
// src/js/tooltip/tooltip.ts
function createTooltipFactory(options = {}) {
const { closeDelay = 1e3, additionalClassName } = options;
let initialized = false;
const state = {
currentId: null,
timer: null,
unsubscribe: null
};
const getActiveTooltip = () => {
if (!state.currentId) {
return null;
}
return document.getElementById(state.currentId);
};
const getActiveReference = () => {
if (!state.currentId) {
return null;
}
return getReferenceByTooltipId(state.currentId);
};
const hide = () => {
const tooltip3 = getActiveTooltip();
if (state.timer) {
clearTimeout(state.timer);
state.timer = null;
}
if (state.unsubscribe) {
state.unsubscribe();
state.unsubscribe = null;
}
if (tooltip3) {
tooltip3.classList.remove(TOOLTIP_OPEN_CLASS);
detachTooltip(tooltip3);
state.currentId = null;
}
};
const show = (reference, text) => {
hide();
const tooltip3 = createTooltipElement({ text, className: additionalClassName });
const update = updateTooltipPosition.bind(null, options, reference, tooltip3);
state.currentId = tooltip3.id;
attachTooltip(tooltip3, reference);
state.unsubscribe = subscribeToScroll(reference, update);
tooltip3.classList.add(TOOLTIP_OPEN_CLASS);
update();
if (closeDelay > 0) {
state.timer = setTimeout(hide, closeDelay);
}
};
const handleUpdate = () => {
const activeTooltip = getActiveTooltip();
const activeReference = getActiveReference();
if (activeTooltip && !activeReference) {
hide();
return;
}
if (activeTooltip && activeReference) {
updateTooltipPosition(options, activeReference, activeTooltip);
}
};
const init = () => {
if (!initialized) {
initialized = true;
window.addEventListener("scroll", handleUpdate);
window.addEventListener("resize", handleUpdate);
}
};
const cleanup = () => {
if (initialized) {
initialized = false;
window.removeEventListener("scroll", handleUpdate);
window.removeEventListener("resize", handleUpdate);
}
};
return {
get visible() {
return Boolean(state.currentId);
},
getActiveReference,
show,
hide,
init,
cleanup
};
}
function createTooltipElement(options) {
const { text, className } = options;
const id = generateId();
const tooltip3 = document.createElement("div");
tooltip3.id = id;
tooltip3.className = className ? `${TOOLTIP_BASE_CLASS} ${className}` : TOOLTIP_BASE_CLASS;
tooltip3.setAttribute("role", "tooltip");
tooltip3.setAttribute("aria-live", "polite");
tooltip3.textContent = text;
return tooltip3;
}
function attachTooltip(tooltip3, reference) {
const container = document.querySelector(PAGE_CONTAINER_SELECTOR) || document.body;
const ariaLive = reference.getAttribute("aria-live");
reference.setAttribute(TOOLTIP_DATA_ATTR, tooltip3.id);
if (ariaLive) {
tooltip3.setAttribute("aria-live", ariaLive);
}
container.appendChild(tooltip3);
}
function detachTooltip(tooltip3) {
if (tooltip3.id) {
const reference = getReferenceByTooltipId(tooltip3.id);
reference == null ? void 0 : reference.removeAttribute(TOOLTIP_DATA_ATTR);
}
tooltip3.remove();
}
function getReferenceByTooltipId(id) {
return document.querySelector(`[${TOOLTIP_DATA_ATTR}="${id}"]`);
}
function subscribeToScroll(reference, update) {
const scrollableElement = getParentScrollableElement(reference);
scrollableElement.addEventListener("scroll", update);
return () => {
scrollableElement.removeEventListener("scroll", update);
};
}
function getParentScrollableElement(target) {
const closestScrollableParent = target.closest("table") || target.closest("code");
return closestScrollableParent || target.parentElement || document.body;
}
function createTooltipContext(referenceElement, tooltipElement) {
const tooltipParent = tooltipElement.parentElement;
if (!tooltipParent) {
return null;
}
const elements = {
reference: referenceElement,
tooltip: tooltipElement,
offsetParent: tooltipParent
};
const viewport = getViewportRect();
const reference = getElementRect(referenceElement);
const { width, height } = tooltipElement.getBoundingClientRect();
return {
isRtl: document.dir === "rtl",
viewport,
reference,
tooltip: createRect({ top: 0, left: 0, width, height }),
elements
};
}
function updateTooltipPosition(options, referenceElement, tooltipElement) {
const context = createTooltipContext(referenceElement, tooltipElement);
if (!context) {
return;
}
const coords = getTooltipCoords(context, options);
tooltipElement.style.top = `${coords.y}px`;
tooltipElement.style.left = `${coords.x}px`;
}
function getTooltipCoords(context, options) {
const { placement = "bottom-start", offset = DEFAULT_OFFSET_VALUES, flip = true } = options;
const { reference, tooltip: tooltip3, viewport, isRtl } = context;
const { coords } = computePosition(reference, tooltip3, viewport, placement, offset, isRtl, flip);
const rect = updateRect(tooltip3, { top: coords.y, left: coords.x });
const relativeRect = convertToRelativeToOffsetParentRect(rect, context.elements.offsetParent);
return {
x: relativeRect.left,
y: relativeRect.top
};
}
// src/js/constant.ts
var COPIED_LANG_TOKEN = {
ru: "\u0421\u043A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u043D\u043E",
en: "Copied",
ar: "\u062A\u0645 \u0627\u0644\u0646\u0633\u062E",
cs: "Zkop\xEDrov\xE1no",
fr: "Copi\xE9",
es: "Copiado",
he: "\u05D4\u05D5\u05E2\u05EA\u05E7",
bg: "\u041A\u043E\u043F\u0438\u0440\u0430\u043D\u043E",
et: "Kopeeritud",
el: "\u0391\u03BD\u03C4\u03B9\u03B3\u03C1\u03AC\u03C6\u03B7\u03BA\u03B5",
pt: "Copiado",
zh: "\u5DF2\u590D\u5236",
"zh-tw": "\u5DF2\u8907\u88FD",
kk: "\u041A\u04E9\u0448\u0456\u0440\u0456\u043B\u0434\u0456",
tr: "Kopyaland\u0131",
uz: "Nusxalandi"
};
// src/js/anchor.ts
var ALLOWED_PROTOCOL_RE = /^(?:https?|file):$/;
var ANCHOR_BUTTON_SELECTOR = ".yfm-clipboard-anchor";
var tooltip = createTooltipFactory();
function getLink(target) {
const href = target.nodeName === "A" ? target.href : target.dataset.href;
const link = new URL(href || "", window.location.href);
if (ALLOWED_PROTOCOL_RE.test(link.protocol)) {
return link.href;
}
return window.location.href;
}
if (typeof document !== "undefined") {
tooltip.init();
document.addEventListener("click", (event) => {
const target = getEventTarget(event);
if (isCustom(event) || !target.matches(ANCHOR_BUTTON_SELECTOR)) {
return;
}
const link = getLink(target);
copyToClipboard(link).then(() => {
var _a;
const lang = document.documentElement.lang || "en";
const tooltipText = (_a = COPIED_LANG_TOKEN[lang]) != null ? _a : COPIED_LANG_TOKEN.en;
tooltip.show(target, tooltipText);
});
});
}
// src/js/inline-code/index.ts
var CLASS_INLINE_CODE = "yfm-clipboard-inline-code";
var INLINE_CODE = `.${CLASS_INLINE_CODE}`;
var tooltip2 = createTooltipFactory({
// NOTE: Add additional className for backward capability
additionalClassName: "inline_code_tooltip"
});
function inlineCopyFn(target) {
const innerText = target.innerText;
if (!innerText) {
return;
}
copyToClipboard(innerText).then(() => {
var _a;
const lang = document.documentElement.lang || "en";
const tooltipText = (_a = COPIED_LANG_TOKEN[lang]) != null ? _a : COPIED_LANG_TOKEN.en;
tooltip2.show(target, tooltipText);
});
}
if (typeof document !== "undefined") {
tooltip2.init();
document.addEventListener("click", (event) => {
const target = getEventTarget(event);
const inline = target.matches(INLINE_CODE);
if (isCustom(event) || !inline) {
return;
}
inlineCopyFn(target);
});
document.addEventListener("keydown", (event) => {
if (event.key === "Enter" && document.activeElement) {
const activeElement = document.activeElement;
if (!activeElement.classList.contains(CLASS_INLINE_CODE)) {
return;
}
inlineCopyFn(activeElement);
}
if (event.key === "Escape" && tooltip2.visible) {
const reference = tooltip2.getActiveReference();
tooltip2.hide();
reference == null ? void 0 : reference.focus();
}
});
}
})();
//# sourceMappingURL=base.js.map