Skip to content

Commit

Permalink
Fixes bug: links have title="undefined"
Browse files Browse the repository at this point in the history
  • Loading branch information
DougReeder committed Jan 29, 2024
1 parent b43d27f commit ccf6f53
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 20 deletions.
31 changes: 21 additions & 10 deletions src/slateHtml.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -686,7 +686,11 @@ function withHtml(editor) { // defines Slate plugin for Notes Together


const ELEMENT_TAGS = {
A: el => ({ type: 'link', url: decodeURI(el.getAttribute('href')), title: el.getAttribute('title') || undefined }),
A: el => ({
type: 'link',
url: decodeURI(el.getAttribute('href')),
title: el.getAttribute('title') && "undefined" !== el.getAttribute('title') ? el.getAttribute('title') : undefined
}),
BLOCKQUOTE: () => ({ type: 'quote' }),
H1: () => ({ type: 'heading-one' }),
H2: () => ({ type: 'heading-two' }),
Expand Down Expand Up @@ -1243,18 +1247,25 @@ function serializeHtml(slateNodes, substitutions = new Map()) {
case 'thematic-break':
return `<hr />`;
case 'link':
return `<a href="${encodeURI(slateNode.url)}" title="${slateNode.title}">${children}</a>`
if (slateNode.title) {
return `<a href="${encodeURI(slateNode.url)}" title="${slateNode.title}">${children}</a>`
} else {
return `<a href="${encodeURI(slateNode.url)}">${children}</a>`
}
case 'image':
if (slateNode.url.startsWith('blob:')) {
const dataUrl = substitutions.get(slateNode.url);
if (dataUrl) {
return `<img src="${encodeURI(dataUrl)}" alt="${SlateNode.string(slateNode) || ''}" title="${slateNode.title || ''}">`;
} else {
console.error("No substitution for", slateNode?.url);
return ''; // Doesn't save img tag.
const altText = SlateNode.string(slateNode) || '';
let url = slateNode.url;
if (url.startsWith('blob:')) {
url = substitutions.get(slateNode.url);
if (!url) {
console.error(`No substitution for${altText}”`, slateNode?.url);
return altText; // Substitutes
}
}
if (slateNode.title) {
return `<img src="${encodeURI(url)}" alt="${altText}" title="${slateNode.title}">`;
} else {
return `<img src="${encodeURI(slateNode.url)}" alt="${SlateNode.string(slateNode) || ''}" title="${slateNode.title || ''}">`;
return `<img src="${encodeURI(url)}" alt="${altText}">`;
}
case 'table':
return `<table><tbody>${children}</tbody></table>`;
Expand Down
35 changes: 30 additions & 5 deletions src/slateHtml.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2403,6 +2403,13 @@ describe("serializeHtml", () => {
expect(html).toEqual('<a href="https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B" title="Cool Example">a cool example</a>');
});

it("should encode links without titles", () => {
const html = serializeHtml([{type: 'link', url: 'https://blah.com/', title: undefined, children: [
{text: "the alt text"},
]}]);
expect(html).toEqual('<a href="https://blah.com/">the alt text</a>');
});

it("should encode images", () => {
const html = serializeHtml([{
type: 'image',
Expand All @@ -2416,21 +2423,26 @@ describe("serializeHtml", () => {
expect(html).toEqual('<img src="https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B" alt="Grapefruit slice atop a pile of other slices" title="Slice of grapefruit">');
});

it("should encode images without titles", () => {
const nodes = [{type: 'image', url: 'https://quux.org/', children: [{text: "The Quux Way"}]}];

expect(serializeHtml(nodes)).toEqual('<img src="https://quux.org/" alt="The Quux Way">')
})

it("should substitute data URLs for object URLs in images", () => {
const substitutions = new Map();
substitutions.set('blob:http://192.168.1.74:3000/2fd265e6-86f4-4826-9fc6-98812c4b0bb5',
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAABlBMVEUAAAD///+l2Z/dAAAACXBIWXMAAAAAAAAAAACdYiYyAAAACklEQVR4nGNgAAAAAgABSK+kcQAAAABJRU5ErkJggg==');
const html = serializeHtml([{
type: 'image',
url: 'blob:http://192.168.1.74:3000/2fd265e6-86f4-4826-9fc6-98812c4b0bb5',
title: "something",
children: [
{text: "a thing"}]
}], substitutions);
expect(html).toEqual('<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAABlBMVEUAAAD///+l2Z/dAAAACXBIWXMAAAAAAAAAAACdYiYyAAAACklEQVR4nGNgAAAAAgABSK+kcQAAAABJRU5ErkJggg==" alt="a thing" title="something">');
expect(html).toEqual('<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAABlBMVEUAAAD///+l2Z/dAAAACXBIWXMAAAAAAAAAAACdYiYyAAAACklEQVR4nGNgAAAAAgABSK+kcQAAAABJRU5ErkJggg==" alt="a thing">');
});

it("should drop images containing an object URL with no substitution", () => {
it("should encode alt text for images containing an object URL with no substitution", () => {
console.error = vitest.fn();

const html = serializeHtml([{
Expand All @@ -2440,9 +2452,9 @@ describe("serializeHtml", () => {
children: [
{text: "a thing"}]
}], new Map());
expect(html).toEqual('');
expect(html).toEqual('a thing');

expect(console.error).toHaveBeenCalledWith(expect.stringMatching("No substitution for"), expect.stringMatching("blob:http://192.168.1.74:3000/"));
expect(console.error).toHaveBeenCalledWith(expect.stringMatching("No substitution for “a thing”"), expect.stringMatching("blob:http://192.168.1.74:3000/"));
});
});

Expand Down Expand Up @@ -2598,6 +2610,7 @@ describe("deserializeHtml", () => {

expect(slateNodes[1].type).toEqual('image');
expect(slateNodes[1].url).toEqual('favicon-192x192.png');
expect(slateNodes[1].title).toBeFalsy();
expect(slateNodes[2].type).toEqual('paragraph');
expect(slateNodes[2].children[0].text).toMatch(/MDN Logo/);
expect(slateNodes[2].children[0].italic).toEqual(true);
Expand Down Expand Up @@ -2637,6 +2650,18 @@ describe("deserializeHtml", () => {
expect(slateNodes.length).toEqual(1);
});

it("should fix old notes by dropping link titles equalling string 'undefined'", () => {
const html = `<p><a href="https://spam.ni/" title="undefined">The link text</a></p>`;

const slateNodes = deserializeHtml(html);

expect(slateNodes).toEqual([
{type: 'paragraph', children: [
{type: 'link', url: 'https://spam.ni/', children: [{text: "The link text"}]},
]},
])
});

it("should parse an image inside emphasis as emphasis inside image", () => {
const html = `<em><img class="fit-picture"
src="/media/cc0-images/grapefruit-slice-332-332.jpg" alt="a slice of grapefruit"/></em>`;
Expand Down
19 changes: 14 additions & 5 deletions src/slateMark.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,9 @@ after`;
![description](https://chi.edu/b.gif "some title")
after`;
after
![alternative text](data:image/png;base64,DVFOIEGLJAGGILEJIFOAJI)`;

const slateNodes = deserializeMarkdown(mdText);

Expand All @@ -510,6 +512,9 @@ after`;
{type: 'paragraph', children: [
{text: "after"}
]},
{type: 'image', url: "data:image/png;base64,DVFOIEGLJAGGILEJIFOAJI", title: null, children: [
{text: "alternative text"},
]},
]);
});

Expand Down Expand Up @@ -686,7 +691,7 @@ let a = b**c, x = y**z;
]},
{type: "numbered-list", "listStart": 1, children: [
{type: 'list-item', children: [
{type: 'link', url: '#internal-2', title: null, children: [
{type: 'link', url: '#internal-2', title: undefined, children: [
{text: "one one"}
]},
]},
Expand All @@ -699,7 +704,7 @@ let a = b**c, x = y**z;
{type: 'list-item', children: [{text: "two one"}]},
{type: 'list-item', children: [
{type: 'paragraph', children: [
{type: 'link', url: '#internal-3', title: null, children: [
{type: 'link', url: '#internal-3', children: [
{text: "two two"},
]},
]},
Expand Down Expand Up @@ -740,12 +745,16 @@ let a = b**c, x = y**z;
const slateNodes = [
{ type: 'image', url: 'https://example.com/other', children: [
{text: "Not a pipe"},
]}
]},
{ type: 'image', url: 'https://example.fr/trahison', title: undefined, children: [
{text: "La Trahison des Images"},
]},
];

const md = serializeMarkdown(slateNodes);

expect(md).toEqual(`![Not a pipe](https://example.com/other)`);
expect(md).toEqual(`![Not a pipe](https://example.com/other)
![La Trahison des Images](https://example.fr/trahison)`);
});

it("should replace graphic w/ alt text, if data: URL & flagged", () => {
Expand Down

0 comments on commit ccf6f53

Please sign in to comment.