2021-11-09 14:34:09 +08:00
|
|
|
import I18n from "I18n";
|
|
|
|
|
2020-04-09 23:13:15 +08:00
|
|
|
const SCALES = ["100", "75", "50"];
|
|
|
|
|
2019-11-13 04:32:37 +08:00
|
|
|
function isUpload(token) {
|
|
|
|
return token.content.includes("upload://");
|
|
|
|
}
|
|
|
|
|
|
|
|
function hasMetadata(token) {
|
|
|
|
return token.content.match(/(\d{1,4}x\d{1,4})/);
|
|
|
|
}
|
|
|
|
|
2020-04-09 23:13:15 +08:00
|
|
|
function appendMetaData(index, token) {
|
|
|
|
const sizePart = token.content
|
2020-02-06 23:19:24 +08:00
|
|
|
.split("|")
|
|
|
|
.find((x) => x.match(/\d{1,4}x\d{1,4}(,\s*\d{1,3}%)?/));
|
|
|
|
let selectedScale =
|
2020-04-09 23:13:15 +08:00
|
|
|
sizePart && sizePart.split(",").pop().trim().replace("%", "");
|
2020-09-04 19:42:47 +08:00
|
|
|
|
2020-04-09 23:13:15 +08:00
|
|
|
const overwriteScale = !SCALES.find((scale) => scale === selectedScale);
|
2020-09-22 22:28:28 +08:00
|
|
|
if (overwriteScale) {
|
|
|
|
selectedScale = "100";
|
|
|
|
}
|
2019-11-13 04:32:37 +08:00
|
|
|
|
2020-04-09 23:13:15 +08:00
|
|
|
token.attrs.push(["index-image", index]);
|
|
|
|
token.attrs.push(["scale", selectedScale]);
|
2019-11-13 04:32:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
function rule(state) {
|
2020-04-09 23:13:15 +08:00
|
|
|
let currentIndex = 0;
|
2019-11-13 04:32:37 +08:00
|
|
|
|
|
|
|
for (let i = 0; i < state.tokens.length; i++) {
|
|
|
|
let blockToken = state.tokens[i];
|
|
|
|
const blockTokenImage = blockToken.tag === "img";
|
|
|
|
|
|
|
|
if (blockTokenImage && isUpload(blockToken) && hasMetadata(blockToken)) {
|
2020-04-09 23:13:15 +08:00
|
|
|
appendMetaData(currentIndex, blockToken);
|
|
|
|
currentIndex++;
|
2019-11-13 04:32:37 +08:00
|
|
|
}
|
|
|
|
|
2020-09-22 22:28:28 +08:00
|
|
|
if (!blockToken.children) {
|
|
|
|
continue;
|
|
|
|
}
|
2019-11-13 04:32:37 +08:00
|
|
|
|
|
|
|
for (let j = 0; j < blockToken.children.length; j++) {
|
|
|
|
let token = blockToken.children[j];
|
|
|
|
const childrenImage = token.tag === "img";
|
|
|
|
|
|
|
|
if (childrenImage && isUpload(blockToken) && hasMetadata(token)) {
|
2020-04-09 23:13:15 +08:00
|
|
|
appendMetaData(currentIndex, token);
|
|
|
|
currentIndex++;
|
2019-11-13 04:32:37 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-04-09 23:13:15 +08:00
|
|
|
}
|
2019-11-13 04:32:37 +08:00
|
|
|
|
2020-04-09 23:13:15 +08:00
|
|
|
function buildScaleButton(selectedScale, scale) {
|
2020-07-07 07:11:58 +08:00
|
|
|
const activeScaleClass = selectedScale === scale ? " active" : "";
|
2020-04-09 23:13:15 +08:00
|
|
|
return (
|
|
|
|
"<span class='scale-btn" +
|
|
|
|
activeScaleClass +
|
|
|
|
"' data-scale='" +
|
|
|
|
scale +
|
|
|
|
"'>" +
|
|
|
|
scale +
|
|
|
|
"%</span>"
|
|
|
|
);
|
2019-11-13 04:32:37 +08:00
|
|
|
}
|
|
|
|
|
2021-11-09 14:34:09 +08:00
|
|
|
function buildImageAltTextButton(altText) {
|
|
|
|
return `
|
|
|
|
<span class="alt-text-readonly-container">
|
|
|
|
<span class="alt-text" aria-label="${I18n.t(
|
|
|
|
"composer.image_alt_text.aria_label"
|
|
|
|
)}">${altText}</span>
|
2021-11-09 17:49:37 +08:00
|
|
|
<span class="alt-text-edit-btn"><svg aria-hidden="true" class="fa d-icon d-icon-pencil svg-icon svg-string"><use href="#pencil-alt"></use></svg></span>
|
2021-11-09 14:34:09 +08:00
|
|
|
<input class="alt-text-input" hidden="true" type="text" value="${altText}" />
|
|
|
|
</span>
|
|
|
|
`;
|
|
|
|
}
|
|
|
|
|
2020-09-19 01:29:09 +08:00
|
|
|
// We need this to load after `upload-protocol` which is priority 0
|
|
|
|
export const priority = 1;
|
|
|
|
|
2021-11-09 14:34:09 +08:00
|
|
|
function ruleWithImageControls(oldRule) {
|
|
|
|
return function (tokens, idx, options, env, slf) {
|
|
|
|
const token = tokens[idx];
|
|
|
|
const scaleIndex = token.attrIndex("scale");
|
|
|
|
const imageIndex = token.attrIndex("index-image");
|
|
|
|
|
|
|
|
if (scaleIndex !== -1) {
|
|
|
|
let selectedScale = token.attrs[scaleIndex][1];
|
|
|
|
let index = token.attrs[imageIndex][1];
|
|
|
|
|
|
|
|
let result = `<span class="image-wrapper">`;
|
|
|
|
|
|
|
|
result += oldRule(tokens, idx, options, env, slf);
|
|
|
|
|
|
|
|
result += `<span class="button-wrapper" data-image-index="${index}">`;
|
|
|
|
|
|
|
|
result += `<span class="scale-btn-container">`;
|
|
|
|
result += SCALES.map((scale) =>
|
|
|
|
buildScaleButton(selectedScale, scale)
|
|
|
|
).join("");
|
|
|
|
result += `</span>`;
|
|
|
|
|
|
|
|
result += buildImageAltTextButton(token.attrs[token.attrIndex("alt")][1]);
|
|
|
|
|
|
|
|
result += "</span></span>";
|
|
|
|
|
|
|
|
return result;
|
|
|
|
} else {
|
|
|
|
return oldRule(tokens, idx, options, env, slf);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-11-13 04:32:37 +08:00
|
|
|
export function setup(helper) {
|
|
|
|
const opts = helper.getOptions();
|
|
|
|
if (opts.previewing) {
|
2020-10-28 10:22:06 +08:00
|
|
|
helper.allowList([
|
2020-01-16 01:01:14 +08:00
|
|
|
"span.image-wrapper",
|
|
|
|
"span.button-wrapper",
|
2021-11-09 14:34:09 +08:00
|
|
|
"span[class=scale-btn-container]",
|
2019-11-13 04:32:37 +08:00
|
|
|
"span[class=scale-btn]",
|
|
|
|
"span[class=scale-btn active]",
|
|
|
|
"span.separator",
|
|
|
|
"span.scale-btn[data-scale]",
|
|
|
|
"span.button-wrapper[data-image-index]",
|
2021-11-09 14:34:09 +08:00
|
|
|
"span[aria-label]",
|
|
|
|
"span.alt-text-readonly-container",
|
|
|
|
"span.alt-text-readonly-container.alt-text",
|
|
|
|
"span.alt-text-readonly-container.alt-text-edit-btn",
|
|
|
|
"svg[class=fa d-icon d-icon-pencil svg-icon svg-string]",
|
2021-11-09 17:49:37 +08:00
|
|
|
"use[href=#pencil-alt]",
|
2021-11-09 14:34:09 +08:00
|
|
|
"input[type=text]",
|
|
|
|
"input[hidden=true]",
|
|
|
|
"input[class=alt-text-input]",
|
2019-11-13 04:32:37 +08:00
|
|
|
]);
|
|
|
|
|
|
|
|
helper.registerPlugin((md) => {
|
2020-04-09 23:13:15 +08:00
|
|
|
const oldRule = md.renderer.rules.image;
|
|
|
|
|
2021-11-09 14:34:09 +08:00
|
|
|
md.renderer.rules.image = ruleWithImageControls(oldRule);
|
2020-04-09 23:13:15 +08:00
|
|
|
|
2019-11-13 04:32:37 +08:00
|
|
|
md.core.ruler.after("upload-protocol", "resize-controls", rule);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|