2019-11-12 15:32:37 -05:00
|
|
|
function isUpload(token) {
|
|
|
|
return token.content.includes("upload://");
|
|
|
|
}
|
|
|
|
|
|
|
|
function hasMetadata(token) {
|
|
|
|
return token.content.match(/(\d{1,4}x\d{1,4})/);
|
|
|
|
}
|
|
|
|
|
|
|
|
function buildToken(state, type, tag, klass, nesting) {
|
|
|
|
const token = new state.Token(type, tag, nesting);
|
|
|
|
token.block = true;
|
|
|
|
token.attrs = [["class", klass]];
|
|
|
|
return token;
|
|
|
|
}
|
|
|
|
|
|
|
|
function wrapImage(tokens, index, state, imgNumber) {
|
|
|
|
const imgToken = tokens[index];
|
2019-11-22 16:19:02 -05:00
|
|
|
let selectedScale = imgToken.content
|
2019-11-12 15:32:37 -05:00
|
|
|
.split(",")
|
|
|
|
.pop()
|
|
|
|
.trim();
|
|
|
|
tokens.splice(
|
|
|
|
index,
|
|
|
|
0,
|
|
|
|
buildToken(state, "wrap_image_open", "div", "image-wrapper", 1)
|
|
|
|
);
|
|
|
|
|
|
|
|
const newElements = [];
|
|
|
|
const btnWrapper = buildToken(
|
|
|
|
state,
|
|
|
|
"wrap_button_open",
|
|
|
|
"div",
|
|
|
|
"button-wrapper",
|
|
|
|
1
|
|
|
|
);
|
|
|
|
btnWrapper.attrs.push(["data-image-index", imgNumber]);
|
|
|
|
newElements.push(btnWrapper);
|
|
|
|
|
|
|
|
const minimumScale = 50;
|
|
|
|
const scales = [100, 75, minimumScale];
|
2019-11-22 16:19:02 -05:00
|
|
|
const overwriteScale = !scales.find(scale => `${scale}%` === selectedScale);
|
|
|
|
if (overwriteScale) selectedScale = "100%";
|
|
|
|
|
2019-11-12 15:32:37 -05:00
|
|
|
scales.forEach(scale => {
|
|
|
|
const scaleText = `${scale}%`;
|
|
|
|
|
|
|
|
const btnClass =
|
|
|
|
scaleText === selectedScale ? "scale-btn active" : "scale-btn";
|
|
|
|
const scaleBtn = buildToken(
|
|
|
|
state,
|
|
|
|
"scale_button_open",
|
|
|
|
"span",
|
|
|
|
btnClass,
|
|
|
|
1
|
|
|
|
);
|
|
|
|
scaleBtn.attrs.push(["data-scale", scale]);
|
|
|
|
newElements.push(scaleBtn);
|
|
|
|
|
|
|
|
let textToken = buildToken(state, "text", "", "", 0);
|
|
|
|
textToken.content = scaleText;
|
|
|
|
newElements.push(textToken);
|
|
|
|
|
|
|
|
newElements.push(buildToken(state, "scale_button_close", "span", "", -1));
|
|
|
|
|
|
|
|
if (scale !== minimumScale) {
|
|
|
|
newElements.push(buildToken(state, "separator", "span", "separator", 1));
|
|
|
|
let separatorToken = buildToken(state, "text", "", "", 0);
|
|
|
|
separatorToken.content = " • ";
|
|
|
|
newElements.push(separatorToken);
|
|
|
|
newElements.push(buildToken(state, "separator_close", "span", "", -1));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
newElements.push(buildToken(state, "wrap_button_close", "div", "", -1));
|
|
|
|
|
|
|
|
newElements.push(buildToken(state, "wrap_image_close", "div", "", -1));
|
|
|
|
|
|
|
|
const afterImageIndex = index + 2;
|
|
|
|
tokens.splice(afterImageIndex, 0, ...newElements);
|
|
|
|
}
|
|
|
|
|
|
|
|
function removeParagraph(tokens, imageIndex) {
|
|
|
|
if (
|
|
|
|
tokens[imageIndex - 1] &&
|
|
|
|
tokens[imageIndex - 1].type === "paragraph_open"
|
|
|
|
)
|
|
|
|
tokens.splice(imageIndex - 1, 1);
|
|
|
|
if (tokens[imageIndex] && tokens[imageIndex].type === "paragraph_close")
|
|
|
|
tokens.splice(imageIndex, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateIndexes(indexes, name) {
|
|
|
|
indexes[name].push(indexes.current);
|
|
|
|
indexes.current++;
|
|
|
|
}
|
|
|
|
|
|
|
|
function wrapImages(tokens, tokenIndexes, state, imgNumberIndexes) {
|
|
|
|
//We do this in reverse order because it's easier for #wrapImage to manipulate the tokens array.
|
|
|
|
for (let j = tokenIndexes.length - 1; j >= 0; j--) {
|
|
|
|
let index = tokenIndexes[j];
|
|
|
|
removeParagraph(tokens, index);
|
|
|
|
wrapImage(tokens, index, state, imgNumberIndexes.pop());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function rule(state) {
|
|
|
|
let blockIndexes = [];
|
|
|
|
const indexNumbers = { current: 0, blocks: [], childrens: [] };
|
|
|
|
|
|
|
|
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)) {
|
|
|
|
blockIndexes.push(i);
|
|
|
|
updateIndexes(indexNumbers, "blocks");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!blockToken.children) continue;
|
|
|
|
|
|
|
|
const childrenIndexes = [];
|
|
|
|
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)) {
|
|
|
|
removeParagraph(state.tokens, i);
|
|
|
|
childrenIndexes.push(j);
|
|
|
|
updateIndexes(indexNumbers, "childrens");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
wrapImages(
|
|
|
|
blockToken.children,
|
|
|
|
childrenIndexes,
|
|
|
|
state,
|
|
|
|
indexNumbers.childrens
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
wrapImages(state.tokens, blockIndexes, state, indexNumbers.blocks);
|
|
|
|
}
|
|
|
|
|
|
|
|
export function setup(helper) {
|
|
|
|
const opts = helper.getOptions();
|
|
|
|
if (opts.previewing) {
|
|
|
|
helper.whiteList([
|
|
|
|
"div.image-wrapper",
|
|
|
|
"div.button-wrapper",
|
|
|
|
"span[class=scale-btn]",
|
|
|
|
"span[class=scale-btn active]",
|
|
|
|
"span.separator",
|
|
|
|
"span.scale-btn[data-scale]",
|
|
|
|
"span.button-wrapper[data-image-index]"
|
|
|
|
]);
|
|
|
|
|
|
|
|
helper.registerPlugin(md => {
|
|
|
|
md.core.ruler.after("upload-protocol", "resize-controls", rule);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|