@ecency/render-helper 2.3.5 → 2.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/consts/allowed-attributes.const.js +9 -1
- package/lib/consts/allowed-attributes.const.js.map +1 -1
- package/lib/helper.d.ts +1 -0
- package/lib/helper.js +20 -1
- package/lib/helper.js.map +1 -1
- package/lib/methods/a.method.js +12 -4
- package/lib/methods/a.method.js.map +1 -1
- package/lib/methods/img.method.d.ts +4 -1
- package/lib/methods/img.method.js +20 -5
- package/lib/methods/img.method.js.map +1 -1
- package/lib/methods/linkify.method.js +6 -4
- package/lib/methods/linkify.method.js.map +1 -1
- package/lib/methods/text.method.js +6 -2
- package/lib/methods/text.method.js.map +1 -1
- package/lib/methods/traverse.method.d.ts +3 -1
- package/lib/methods/traverse.method.js +4 -3
- package/lib/methods/traverse.method.js.map +1 -1
- package/lib/render-helper.js +1 -1
- package/package.json +1 -1
- package/src/consts/allowed-attributes.const.ts +9 -1
- package/src/helper.ts +22 -0
- package/src/markdown-2-html.spec.ts +14 -14
- package/src/methods/a.method.ts +14 -8
- package/src/methods/img.method.ts +26 -5
- package/src/methods/linkify.method.ts +10 -6
- package/src/methods/text.method.ts +10 -9
- package/src/methods/traverse.method.ts +3 -3
- package/src/test/data/legacy/10.json +1 -1
- package/src/test/data/legacy/21.json +1 -1
- package/src/test/data/legacy/2112524.json +1 -1
- package/src/test/data/legacy/22.json +1 -1
- package/src/test/data/legacy/23.json +1 -1
- package/src/test/data/legacy/26.json +1 -1
package/package.json
CHANGED
|
@@ -21,7 +21,15 @@ export const ALLOWED_ATTRIBUTES: XSSWhiteList = {
|
|
|
21
21
|
'title',
|
|
22
22
|
'data-id'
|
|
23
23
|
],
|
|
24
|
-
'img': [
|
|
24
|
+
'img': [
|
|
25
|
+
'src',
|
|
26
|
+
'alt',
|
|
27
|
+
'class',
|
|
28
|
+
'loading',
|
|
29
|
+
'fetchpriority',
|
|
30
|
+
'decoding',
|
|
31
|
+
'itemprop'
|
|
32
|
+
],
|
|
25
33
|
'span': ['class'],
|
|
26
34
|
'iframe': ['src', 'class', 'frameborder', 'allowfullscreen', 'webkitallowfullscreen', 'mozallowfullscreen', 'sandbox'],
|
|
27
35
|
'div': ['class'],
|
package/src/helper.ts
CHANGED
|
@@ -36,4 +36,26 @@ export function isValidPermlink(permlink: string): boolean {
|
|
|
36
36
|
return isCleanFormat && !isImage && !hasSpecialChars;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
// Reference: https://en.wikipedia.org/wiki/Domain_Name_System#Domain_name_syntax
|
|
40
|
+
// Hive account names must follow similar rules to DNS (RFC 1035)
|
|
41
|
+
const LABEL_REGEX = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;
|
|
42
|
+
|
|
43
|
+
export function isValidUsername(username: string): boolean {
|
|
44
|
+
if (!username || typeof username !== 'string') return false;
|
|
45
|
+
if (username.length > 16) return false;
|
|
46
|
+
|
|
47
|
+
const labels = username.split('.');
|
|
48
|
+
|
|
49
|
+
return labels.every(label => {
|
|
50
|
+
return (
|
|
51
|
+
label.length >= 3 &&
|
|
52
|
+
label.length <= 16 &&
|
|
53
|
+
/^[a-z]/.test(label) && // must start with a letter
|
|
54
|
+
LABEL_REGEX.test(label) && // a-z0-9, hyphens, no start/end hyphen
|
|
55
|
+
!label.includes('..') // double dots are impossible after split, but just in case
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
39
61
|
|
|
@@ -4,13 +4,13 @@ import { markdown2Html } from './markdown-2-html'
|
|
|
4
4
|
const fs = require('fs')
|
|
5
5
|
const path = require('path')
|
|
6
6
|
const SNAPSHOT_JSON = {
|
|
7
|
-
"markdown_2_html_test_files_1_should_catch_images_in_table": "<p dir=\"auto\"><span>Congratulations <a class=\"markdown-author-link\" data-author=\"dunsky\">@dunsky</a>! You have completed the following achievement on the Steem blockchain and have been rewarded with new badge(s) :</span></p>\n<table><tr><td><span><img class=\"markdown-img-link\" src=\"https://images.ecency.com/p/9RTqgyyu8sX2kxvHt3ueNhDJEActwvqfV9wXt7pGKpjnWxBVz6r6Lv5n7t2RYBogkr5EjvnLjk.png?format=match&mode=fit\" /></span></td><td>You made more than 6000 upvotes. Your next target is to reach 7000 upvotes.</td></tr>\n<tr><td><span><img class=\"markdown-img-link\" src=\"https://images.ecency.com/p/9RTqgyyu8sX2kxvHt3ueNhDJEActwvqfV9wXt7pGKpjnVKjKxTD5Xwi6jJqBW9PAmtMWvTCvPF.png?format=match&mode=fit\" /></span></td><td>You published more than 250 posts. Your next target is to reach 300 posts.</td></tr>\n</table>\n<p dir=\"auto\"><sub><em><a class=\"markdown-external-link\" data-href=\"https://steemitboard.com/@dunsky\">Click here to view your Board of Honor</a></em></sub><br />\n<sub><em>If you no longer want to receive notifications, reply to this comment with the word</em> <code>STOP</code></sub></p>\n<p dir=\"auto\">To support your work, I also upvoted your post!</p>\n<blockquote>\n<p dir=\"auto\">Support <a class=\"markdown-external-link\" data-href=\"https://steemit.com/@steemitboard\">SteemitBoard's project</a>! <strong><a class=\"markdown-external-link\" data-href=\"https://v2.steemconnect.com/sign/account-witness-vote?witness=steemitboard&approve=1\">Vote for its witness</a></strong> and <strong>get one more award</strong>!</p>\n</blockquote>",
|
|
7
|
+
"markdown_2_html_test_files_1_should_catch_images_in_table": "<p dir=\"auto\"><span>Congratulations <a class=\"markdown-author-link\" data-author=\"dunsky\">@dunsky</a>! You have completed the following achievement on the Steem blockchain and have been rewarded with new badge(s) :</span></p>\n<table><tr><td><span><img class=\"markdown-img-link\" src=\"https://images.ecency.com/p/9RTqgyyu8sX2kxvHt3ueNhDJEActwvqfV9wXt7pGKpjnWxBVz6r6Lv5n7t2RYBogkr5EjvnLjk.png?format=match&mode=fit\" loading=\"eager\" fetchpriority=\"high\" itemprop=\"image\" /></span></td><td>You made more than 6000 upvotes. Your next target is to reach 7000 upvotes.</td></tr>\n<tr><td><span><img class=\"markdown-img-link\" src=\"https://images.ecency.com/p/9RTqgyyu8sX2kxvHt3ueNhDJEActwvqfV9wXt7pGKpjnVKjKxTD5Xwi6jJqBW9PAmtMWvTCvPF.png?format=match&mode=fit\" loading=\"eager\" fetchpriority=\"high\" itemprop=\"image\" /></span></td><td>You published more than 250 posts. Your next target is to reach 300 posts.</td></tr>\n</table>\n<p dir=\"auto\"><sub><em><a class=\"markdown-external-link\" data-href=\"https://steemitboard.com/@dunsky\">Click here to view your Board of Honor</a></em></sub><br />\n<sub><em>If you no longer want to receive notifications, reply to this comment with the word</em> <code>STOP</code></sub></p>\n<p dir=\"auto\">To support your work, I also upvoted your post!</p>\n<blockquote>\n<p dir=\"auto\">Support <a class=\"markdown-external-link\" data-href=\"https://steemit.com/@steemitboard\">SteemitBoard's project</a>! <strong><a class=\"markdown-external-link\" data-href=\"https://v2.steemconnect.com/sign/account-witness-vote?witness=steemitboard&approve=1\">Vote for its witness</a></strong> and <strong>get one more award</strong>!</p>\n</blockquote>",
|
|
8
8
|
"markdown_2_html_test_files_3_should_replace_busy_links_properly": "<p dir=\"auto\"><img src=\"https://.esteem.app/0x0/https://ipfs.busy.org/ipfs/QmPziJCkx8w62UZCz1TNxAMsgf1uTnPZdVKAYjLBWvMzeD\" alt=\"image.png\" /></p>\n<p dir=\"auto\"><a class=\"markdown-post-link\" data-tag=\"post\" data-author=\"muratkbesiroglu\" data-permlink=\"sci-fi-novel-underground-city-part-1-2\">Part 1 - 2</a><br />\n<a class=\"markdown-post-link\" data-tag=\"post\" data-author=\"muratkbesiroglu\" data-permlink=\"sci-fi-novel-underground-city-part-3\">Part 3</a><br />\n<a class=\"markdown-post-link\" data-tag=\"post\" data-author=\"muratkbesiroglu\" data-permlink=\"sci-fi-novel-underground-city-part-4\">Part 4</a><br />\n<a class=\"markdown-post-link\" data-tag=\"post\" data-author=\"muratkbesiroglu\" data-permlink=\"sci-fi-novel-underground-city-part-5\">Part 5</a><br />\n<a class=\"markdown-post-link\" data-tag=\"post\" data-author=\"muratkbesiroglu\" data-permlink=\"sci-fi-novel-underground-city-part-6\">Part 6</a><br />\n<a class=\"markdown-post-link\" data-tag=\"post\" data-author=\"muratkbesiroglu\" data-permlink=\"sci-fi-novel-underground-city-part-7\">Part 7</a><br />\n<a class=\"markdown-post-link\" data-tag=\"post\" data-author=\"muratkbesiroglu\" data-permlink=\"sci-fi-novel-underground-city-part-8\">Part 8</a><br />\n<a class=\"markdown-post-link\" data-tag=\"post\" data-author=\"muratkbesiroglu\" data-permlink=\"sci-fi-novel-underground-city-part-9\">Part 9</a><br />\n<a class=\"markdown-post-link\" data-tag=\"post\" data-author=\"muratkbesiroglu\" data-permlink=\"sci-fi-novel-underground-city-part-10\">Part 10</a><br />\n<a class=\"markdown-post-link\" data-tag=\"post\" data-author=\"muratkbesiroglu\" data-permlink=\"sci-fi-novel-underground-city-part-11\">Part 11</a><br />\n<a class=\"markdown-post-link\" data-tag=\"post\" data-author=\"muratkbesiroglu\" data-permlink=\"sci-fi-novel-underground-city-part-12\">Part 12</a></p>\n<p dir=\"auto\"><strong>Part 13</strong></p>\n<p dir=\"auto\">Members of the Yerşehir caravan have surrendered their weapons to imperial soldiers at the entrance of Istanbul by the agreement provided earlier. The imperial soldiers arrested them immediately after the surrender of the weapons, and they entered the city with their hands chained.</p>\n<p dir=\"auto\">“The Yerşehir administration will ask you the account of this disrespect,” said Tamra to the proud Commander Timor on the pony. Tamra was angry because an ordinary soldier pushed her.</p>\n<p dir=\"auto\">\"You should be thankful we didn't strip you naked and had you wandering the streets of the city,\" said Commander Timor.</p>\n<p dir=\"auto\">Doma turned to the commander and said, “That's not how we dealt with you. Still, I assume you're doing your duty. I ask you to inform your superiors as soon as possible.”</p>\n<p dir=\"auto\">”You may be executed for armed entry into the Imperial lands,\" said the commander.</p>\n<p dir=\"auto\">“You like to dramatize things,” Akman said. Although Commander Timor was angry with Akman's happy face, he chose to keep the sarcastic look on his face.</p>\n<p dir=\"auto\">When he was taken to the dungeon, Akman was watching the buildings on the road, the squares adorned with sculptures, statues, and facades reflecting the beauty of East Roman stone craftsmanship.</p>\n<p dir=\"auto\">After passing the Hippodrome, which is one of the rare works of the ancient world, they entered a narrow tunnel and went down a ladder descending by turning. The soldiers locked them in a large gallery and returned. The water was dripping from the high ceiling of the dungeon, and the air they breathed was quite damp. Akman saw two rats with the size of cats running to their holes, terrified by the sound of footsteps.</p>\n<p dir=\"auto\">”I don't think they're gonna keep us here for long,\" Akman said.</p>\n<p dir=\"auto\">Akman was wrong; they told stories to each other in the first few days while there was no authority to call and ask for them. One of the dungeon guards came once a day and left a cup of water and a disgusting wheat porridge in the cell. The amount of oatmeal brought by the guard was so small that it was even possible for them to starve if the situation went on like this.</p>\n<p dir=\"auto\">Akman was cheerful as if he was at the banquet table, not at the dungeon. His joy was so bizarre that the team members began to think he was out of his mind. As the days passed, he was weakened by malnutrition; his clothes were stained, and his face was blackened. Still, he didn't lose anything from his joy. When asked how it can be so enjoyable, he said, “peace and light are within us. Just let's call and find out.\"</p>\n<p dir=\"auto\">Days later, an officer finally appeared at the door of the dungeon. Now, even the sparkle in the eyes of Akman has been lost, and Doma was unable to get up from where he was sleeping because of his illness. “The emperor will meet with the two representatives of your choice in two days,” said Chlorus, who brought plenty of food with him. Chlorus was handling his business with care. He brought water and ash to the cell to make sure they didn't smell bad, and he arranged a basin for their clothes. After washing, a barber would cut off their hair and beard. When they talk to the emperor, they should not extend the word and that they should give satisfying answers to the questions asked.</p>\n<p dir=\"auto\">Akman and Doma were taken from prison by the guards to be brought to the emperor. They agreed to hold a reasonable discourse during the interview. They were ready to suffer the maltreatment for the well-being of their country. The Imperial Palace adjacent to the Hippodrome was quite magnificent. They couldn't retrieve their eyes from the mosaics on the floor, the tiles on the walls and the stained glass windows. They passed through the large gate of the palace and entered the inner courtyard, where they waited for a while and then came to the emperor's presence.</p>\n<p dir=\"auto\">Since Doma's disease has not passed yet, Akman was holding his arm. As a matter of fact, he hasn't been able to speak; he agreed to join the meeting to show The Emperor what a terrible treatment they had.</p>\n<p dir=\"auto\">As soon as Emperor Valens saw Doma, he noticed his discomfort and told the servants to bring him a chair to sit down.</p>\n<p dir=\"auto\">“You wanted to see me,” said the Emperor, sitting on a plain throne compared to the splendor of the palace.</p>\n<p dir=\"auto\">”In the name of my people, I share your sorrow,\" said Akman.</p>\n<p dir=\"auto\">The emperor looked at Akman with the eyes asking, \"what is it about?”</p>\n<p dir=\"auto\">“I think robots attack your stables,” Akman said. Doma, who saw the environment as blurry due to high fever, turned and looked at Akman with amazement.</p>\n<p dir=\"auto\">”Few people know that yet, \" said Emperor Valens. Akman heard the news from the two soldiers they encountered on the way to the dungeon; the soldiers thought others did not understand them because they were whispering. As the security of his country is concerned, Akman chose to transfer the incident to the emperor in a different manner.</p>\n<p dir=\"auto\">\"We came to Istanbul to warn you about this. When the imperial authorities said in the dungeon that we could finally come to your peace, I understood that the attack has occurred,” Akman said.</p>\n<p dir=\"auto\">“You speak our language very well,” said the emperor.</p>\n<p dir=\"auto\">“Master Doma is much better than me, I hoped he'd be able to talk, but I was wrong,\" Akman said.</p>\n<p dir=\"auto\">\"I give you my regards and my gratitude for showing us the grace of acceptance. Because of my illness, I beg forgiveness for the imperfections that I can handle,” Doma said.</p>\n<p dir=\"auto\">“I will ask without hesitation: what could we have done to draw the wrath of God upon us, and what should we do to be merciful to his forgiveness?”</p>\n<p dir=\"auto\">Akman did not tell the emperor that events had begun with Ordin rising to Earth. He didn't want him to think they were in charge of the activities. Moreover, there was no evidence that the attacks on the stables in Yerşehir and Istanbul were related to Ordin's action. It was more than three months ago, and it could have been a coincidence.</p>\n<p dir=\"auto\">“I do not think that what happened is related to any sin.”</p>\n<p dir=\"auto\">“Foreigners come to our city with magical weapons, and ten days later, such an attack is taking place. Is that a coincidence?”</p>\n<p dir=\"auto\">\"I suspect that your scientists have already studied our weapons and have partially solved their operational principles. I assure you, our weapons work in accordance with scientific rules. I don't know why we're being treated so hard in your country.”</p>\n<p dir=\"auto\">“Could it be because you closed your doors to our country with a unilateral decision you made two centuries ago? According to historical records, despite the insistence of our empire, we have not even been able to negotiate the matter.”</p>\n<p dir=\"auto\">“It was a great travesty that half of our people died because of the plague. But that's not a valid excuse. I think the decision we made on time was wrong. During the past, we have been deprived of the benefits that we can achieve mutually. Given that the tunnel system makes wars almost impossible, it is not reasonable for us to continue such a mistake,\" Akman said.</p>\n<p dir=\"auto\">“I haven't received a satisfactory answer to my questions yet?\" the emperor said, despite the tone of his voice, his eyes were warm to Akman.</p>\n<p dir=\"auto\">\"I have to admit I don't know the answer to your questions, even if it's embarrassing for a philosopher. We came to Istanbul to find the answer to this question together.”</p>\n<p dir=\"auto\">\"I want to learn the working principles and how to produce the weapons you bring with you. This is the only way we can initiate an equality-based relationship.”</p>\n<p dir=\"auto\">\"I am not authorized to make such a decision. But I don't think your offer will be rejected at a time when we need cooperation to defend our cities,\" Akman said.</p>\n<p dir=\"auto\">After the meeting with Emperor Valens, the Yerşehir delegation was removed from prison, their belongings were returned, and they were sent from Istanbul with a ceremony in accordance with diplomatic traditions.</p>\n<p dir=\"auto\">Image Source: pixabay.com</p>",
|
|
9
9
|
"markdown_2_html_test_files_4_should_not_convert_markdown_links": "<p dir=\"auto\">lorem <a class=\"markdown-external-link\" data-href=\"https://images.ecency.com/0x0/https://d1vof77qrk4l5q.cloudfront.net/img/5752638e6965247789bc20cef34727263aaa41e1.png\">this error</a> ipsum</p>",
|
|
10
10
|
"markdown_2_html_traversing_20_should_not_convert_markdown_links": "<p dir=\"auto\">lorem <a class=\"markdown-external-link\" data-href=\"https://images.ecency.com/0x0/https://d1vof77qrk4l5q.cloudfront.net/img/5752638e6965247789bc20cef34727263aaa41e1.png\">this error</a> ipsum</p>",
|
|
11
11
|
"markdown_2_html_traversing_22_should_replace_busy_links_properly": "<p dir=\"auto\"><img src=\"https://images.ecency.com/p/7ohP4GDMGPrUMp8dW6yuJTR9MKNu8P8DCXDU9qmmhgT2GEFsgcwp6r41v9wnLUULgcVqE59AhpBJjFXHVP6KTGZbAszsNTZrVUY3.png?format=match&mode=fit\" alt=\"image.png\" /></p>\n<p dir=\"auto\"><a class=\"markdown-post-link\" data-tag=\"post\" data-author=\"muratkbesiroglu\" data-permlink=\"sci-fi-novel-underground-city-part-1-2\">Part 1 - 2</a><br />\n<a class=\"markdown-post-link\" data-tag=\"post\" data-author=\"muratkbesiroglu\" data-permlink=\"sci-fi-novel-underground-city-part-3\">Part 3</a><br />\n<a class=\"markdown-post-link\" data-tag=\"post\" data-author=\"muratkbesiroglu\" data-permlink=\"sci-fi-novel-underground-city-part-4\">Part 4</a><br />\n<a class=\"markdown-post-link\" data-tag=\"post\" data-author=\"muratkbesiroglu\" data-permlink=\"sci-fi-novel-underground-city-part-5\">Part 5</a><br />\n<a class=\"markdown-post-link\" data-tag=\"post\" data-author=\"muratkbesiroglu\" data-permlink=\"sci-fi-novel-underground-city-part-6\">Part 6</a><br />\n<a class=\"markdown-post-link\" data-tag=\"post\" data-author=\"muratkbesiroglu\" data-permlink=\"sci-fi-novel-underground-city-part-7\">Part 7</a><br />\n<a class=\"markdown-post-link\" data-tag=\"post\" data-author=\"muratkbesiroglu\" data-permlink=\"sci-fi-novel-underground-city-part-8\">Part 8</a><br />\n<a class=\"markdown-post-link\" data-tag=\"post\" data-author=\"muratkbesiroglu\" data-permlink=\"sci-fi-novel-underground-city-part-9\">Part 9</a><br />\n<a class=\"markdown-post-link\" data-tag=\"post\" data-author=\"muratkbesiroglu\" data-permlink=\"sci-fi-novel-underground-city-part-10\">Part 10</a><br />\n<a class=\"markdown-post-link\" data-tag=\"post\" data-author=\"muratkbesiroglu\" data-permlink=\"sci-fi-novel-underground-city-part-11\">Part 11</a><br />\n<a class=\"markdown-post-link\" data-tag=\"post\" data-author=\"muratkbesiroglu\" data-permlink=\"sci-fi-novel-underground-city-part-12\">Part 12</a></p>\n<p dir=\"auto\"><strong>Part 13</strong></p>\n<p dir=\"auto\">Members of the Yerşehir caravan have surrendered their weapons to imperial soldiers at the entrance of Istanbul by the agreement provided earlier. The imperial soldiers arrested them immediately after the surrender of the weapons, and they entered the city with their hands chained.</p>\n<p dir=\"auto\">“The Yerşehir administration will ask you the account of this disrespect,” said Tamra to the proud Commander Timor on the pony. Tamra was angry because an ordinary soldier pushed her.</p>\n<p dir=\"auto\">\"You should be thankful we didn't strip you naked and had you wandering the streets of the city,\" said Commander Timor.</p>\n<p dir=\"auto\">Doma turned to the commander and said, “That's not how we dealt with you. Still, I assume you're doing your duty. I ask you to inform your superiors as soon as possible.”</p>\n<p dir=\"auto\">”You may be executed for armed entry into the Imperial lands,\" said the commander.</p>\n<p dir=\"auto\">“You like to dramatize things,” Akman said. Although Commander Timor was angry with Akman's happy face, he chose to keep the sarcastic look on his face.</p>\n<p dir=\"auto\">When he was taken to the dungeon, Akman was watching the buildings on the road, the squares adorned with sculptures, statues, and facades reflecting the beauty of East Roman stone craftsmanship.</p>\n<p dir=\"auto\">After passing the Hippodrome, which is one of the rare works of the ancient world, they entered a narrow tunnel and went down a ladder descending by turning. The soldiers locked them in a large gallery and returned. The water was dripping from the high ceiling of the dungeon, and the air they breathed was quite damp. Akman saw two rats with the size of cats running to their holes, terrified by the sound of footsteps.</p>\n<p dir=\"auto\">”I don't think they're gonna keep us here for long,\" Akman said.</p>\n<p dir=\"auto\">Akman was wrong; they told stories to each other in the first few days while there was no authority to call and ask for them. One of the dungeon guards came once a day and left a cup of water and a disgusting wheat porridge in the cell. The amount of oatmeal brought by the guard was so small that it was even possible for them to starve if the situation went on like this.</p>\n<p dir=\"auto\">Akman was cheerful as if he was at the banquet table, not at the dungeon. His joy was so bizarre that the team members began to think he was out of his mind. As the days passed, he was weakened by malnutrition; his clothes were stained, and his face was blackened. Still, he didn't lose anything from his joy. When asked how it can be so enjoyable, he said, “peace and light are within us. Just let's call and find out.\"</p>\n<p dir=\"auto\">Days later, an officer finally appeared at the door of the dungeon. Now, even the sparkle in the eyes of Akman has been lost, and Doma was unable to get up from where he was sleeping because of his illness. “The emperor will meet with the two representatives of your choice in two days,” said Chlorus, who brought plenty of food with him. Chlorus was handling his business with care. He brought water and ash to the cell to make sure they didn't smell bad, and he arranged a basin for their clothes. After washing, a barber would cut off their hair and beard. When they talk to the emperor, they should not extend the word and that they should give satisfying answers to the questions asked.</p>\n<p dir=\"auto\">Akman and Doma were taken from prison by the guards to be brought to the emperor. They agreed to hold a reasonable discourse during the interview. They were ready to suffer the maltreatment for the well-being of their country. The Imperial Palace adjacent to the Hippodrome was quite magnificent. They couldn't retrieve their eyes from the mosaics on the floor, the tiles on the walls and the stained glass windows. They passed through the large gate of the palace and entered the inner courtyard, where they waited for a while and then came to the emperor's presence.</p>\n<p dir=\"auto\">Since Doma's disease has not passed yet, Akman was holding his arm. As a matter of fact, he hasn't been able to speak; he agreed to join the meeting to show The Emperor what a terrible treatment they had.</p>\n<p dir=\"auto\">As soon as Emperor Valens saw Doma, he noticed his discomfort and told the servants to bring him a chair to sit down.</p>\n<p dir=\"auto\">“You wanted to see me,” said the Emperor, sitting on a plain throne compared to the splendor of the palace.</p>\n<p dir=\"auto\">”In the name of my people, I share your sorrow,\" said Akman.</p>\n<p dir=\"auto\">The emperor looked at Akman with the eyes asking, \"what is it about?”</p>\n<p dir=\"auto\">“I think robots attack your stables,” Akman said. Doma, who saw the environment as blurry due to high fever, turned and looked at Akman with amazement.</p>\n<p dir=\"auto\">”Few people know that yet, \" said Emperor Valens. Akman heard the news from the two soldiers they encountered on the way to the dungeon; the soldiers thought others did not understand them because they were whispering. As the security of his country is concerned, Akman chose to transfer the incident to the emperor in a different manner.</p>\n<p dir=\"auto\">\"We came to Istanbul to warn you about this. When the imperial authorities said in the dungeon that we could finally come to your peace, I understood that the attack has occurred,” Akman said.</p>\n<p dir=\"auto\">“You speak our language very well,” said the emperor.</p>\n<p dir=\"auto\">“Master Doma is much better than me, I hoped he'd be able to talk, but I was wrong,\" Akman said.</p>\n<p dir=\"auto\">\"I give you my regards and my gratitude for showing us the grace of acceptance. Because of my illness, I beg forgiveness for the imperfections that I can handle,” Doma said.</p>\n<p dir=\"auto\">“I will ask without hesitation: what could we have done to draw the wrath of God upon us, and what should we do to be merciful to his forgiveness?”</p>\n<p dir=\"auto\">Akman did not tell the emperor that events had begun with Ordin rising to Earth. He didn't want him to think they were in charge of the activities. Moreover, there was no evidence that the attacks on the stables in Yerşehir and Istanbul were related to Ordin's action. It was more than three months ago, and it could have been a coincidence.</p>\n<p dir=\"auto\">“I do not think that what happened is related to any sin.”</p>\n<p dir=\"auto\">“Foreigners come to our city with magical weapons, and ten days later, such an attack is taking place. Is that a coincidence?”</p>\n<p dir=\"auto\">\"I suspect that your scientists have already studied our weapons and have partially solved their operational principles. I assure you, our weapons work in accordance with scientific rules. I don't know why we're being treated so hard in your country.”</p>\n<p dir=\"auto\">“Could it be because you closed your doors to our country with a unilateral decision you made two centuries ago? According to historical records, despite the insistence of our empire, we have not even been able to negotiate the matter.”</p>\n<p dir=\"auto\">“It was a great travesty that half of our people died because of the plague. But that's not a valid excuse. I think the decision we made on time was wrong. During the past, we have been deprived of the benefits that we can achieve mutually. Given that the tunnel system makes wars almost impossible, it is not reasonable for us to continue such a mistake,\" Akman said.</p>\n<p dir=\"auto\">“I haven't received a satisfactory answer to my questions yet?\" the emperor said, despite the tone of his voice, his eyes were warm to Akman.</p>\n<p dir=\"auto\">\"I have to admit I don't know the answer to your questions, even if it's embarrassing for a philosopher. We came to Istanbul to find the answer to this question together.”</p>\n<p dir=\"auto\">\"I want to learn the working principles and how to produce the weapons you bring with you. This is the only way we can initiate an equality-based relationship.”</p>\n<p dir=\"auto\">\"I am not authorized to make such a decision. But I don't think your offer will be rejected at a time when we need cooperation to defend our cities,\" Akman said.</p>\n<p dir=\"auto\">After the meeting with Emperor Valens, the Yerşehir delegation was removed from prison, their belongings were returned, and they were sent from Istanbul with a ceremony in accordance with diplomatic traditions.</p>\n<p dir=\"auto\">Image Source: pixabay.com</p>",
|
|
12
12
|
"markdown_2_html_traversing_54_should_highlight_code": "<pre><code><span class=\"ll-spc\"> </span><span class=\"ll-key\">def</span><span class=\"ll-spc\"> </span><span class=\"ll-nam\">factorial</span><span class=\"ll-pct\">(</span><span class=\"ll-nam\">n</span><span class=\"ll-pct\">)</span><span class=\"ll-pct\">:</span><span class=\"ll-spc\">\n </span><span class=\"ll-com\"># a comment</span><span class=\"ll-spc\">\n </span><span class=\"ll-key\">return</span><span class=\"ll-spc\"> </span><span class=\"ll-num\">1</span><span class=\"ll-spc\"> </span><span class=\"ll-key\">if</span><span class=\"ll-spc\"> </span><span class=\"ll-pct\">(</span><span class=\"ll-nam\">n</span><span class=\"ll-pct\">=</span><span class=\"ll-pct\">=</span><span class=\"ll-num\">1</span><span class=\"ll-spc\"> </span><span class=\"ll-key\">or</span><span class=\"ll-spc\"> </span><span class=\"ll-nam\">n</span><span class=\"ll-pct\">=</span><span class=\"ll-pct\">=</span><span class=\"ll-num\">0</span><span class=\"ll-pct\">)</span><span class=\"ll-spc\"> </span><span class=\"ll-key\">else</span><span class=\"ll-spc\"> </span><span class=\"ll-nam\">n</span><span class=\"ll-spc\"> </span><span class=\"ll-pct\">*</span><span class=\"ll-spc\"> </span><span class=\"ll-nam\">factorial</span><span class=\"ll-pct\">(</span><span class=\"ll-nam\">n</span><span class=\"ll-spc\"> </span><span class=\"ll-pct\">-</span><span class=\"ll-spc\"> </span><span class=\"ll-num\">1</span><span class=\"ll-pct\">)</span><span class=\"ll-spc\">\n </span><span class=\"ll-str\">``</span><span class=\"ll-unk\">`</span><span class=\"ll-spc\">\n</span></code></pre>",
|
|
13
|
-
"markdown_2_html_webp_support_should_render_images_in_webp_format":
|
|
13
|
+
"markdown_2_html_webp_support_should_render_images_in_webp_format": `<p dir=\"auto\">lorem ipsum <img class=\"markdown-img-link\" src=\"https://images.ecency.com/p/2BCfkBRHmbhyg7yeHaumxJq2oMZjKUDK5rv2tmGQzQHxPvTmaiv6Ar.webp?format=webp&mode=fit\" loading=\"lazy\" decoding=\"async\" itemprop=\"image\" /> dolor sit amet</p>`
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
describe('Markdown2Html', () => {
|
|
@@ -22,7 +22,7 @@ describe('Markdown2Html', () => {
|
|
|
22
22
|
last_update: '2019-05-10T09:15:21',
|
|
23
23
|
body: 'https://img.esteem.ws/bbq3ob1idy.png'
|
|
24
24
|
}
|
|
25
|
-
const expected = '<p dir=\"auto\"><img class="markdown-img-link" src="https://images.ecency.com/p/o1AJ9qDyyJNSpZWhUgGYc3MngFqoAMwgbeMkkd8SVxyfRVjiN.png?format=match&mode=fit" /></p>'
|
|
25
|
+
const expected = '<p dir=\"auto\"><img class="markdown-img-link" src="https://images.ecency.com/p/o1AJ9qDyyJNSpZWhUgGYc3MngFqoAMwgbeMkkd8SVxyfRVjiN.png?format=match&mode=fit" loading="lazy" decoding="async" itemprop="image" /></p>'
|
|
26
26
|
|
|
27
27
|
expect(markdown2Html(input)).toBe(expected)
|
|
28
28
|
})
|
|
@@ -70,7 +70,7 @@ describe('Markdown2Html', () => {
|
|
|
70
70
|
last_update: '2019-05-10T09:15:21',
|
|
71
71
|
body: 'https://www.youtube.com/watch?v=qK3d1eoH-Qs'
|
|
72
72
|
}
|
|
73
|
-
const expected = '<p dir=\"auto\"><a class="markdown-video-link markdown-video-link-youtube" data-embed-src="https://www.youtube.com/embed/qK3d1eoH-Qs?autoplay=1" data-youtube="qK3d1eoH-Qs"><img class="no-replace video-thumbnail" src="https://images.ecency.com/p/S5Eokt4BcQdk7EHeT1aYjzebg2hC7hkthT45eMZRVYW6mkGBWKemLWWzXbRhNG7Z3h1qjGS.png?format=match&mode=fit" /><span class="markdown-video-play"></span></a></p>'
|
|
73
|
+
const expected = '<p dir=\"auto\"><a class="markdown-video-link markdown-video-link-youtube" data-embed-src="https://www.youtube.com/embed/qK3d1eoH-Qs?autoplay=1" data-youtube="qK3d1eoH-Qs"><img class="no-replace video-thumbnail" itemprop="image" src="https://images.ecency.com/p/S5Eokt4BcQdk7EHeT1aYjzebg2hC7hkthT45eMZRVYW6mkGBWKemLWWzXbRhNG7Z3h1qjGS.png?format=match&mode=fit" loading="eager" fetchpriority="high" /><span class="markdown-video-play"></span></a></p>'
|
|
74
74
|
|
|
75
75
|
expect(markdown2Html(input)).toBe(expected)
|
|
76
76
|
})
|
|
@@ -94,7 +94,7 @@ describe('Markdown2Html', () => {
|
|
|
94
94
|
last_update: '2019-05-10T09:15:21',
|
|
95
95
|
body: '<a href="https://d.tube/#!/v/scottcbusiness/g04n2bbp" title="This link will take you away from steemit.com"><img src="https://images.ecency.com/640x0/https://ipfs.io/ipfs/QmPhb9HA1gASFiNAUPFqMdSidTAj17L5SSoV3zbXUx8M7t"></a>'
|
|
96
96
|
}
|
|
97
|
-
const expected = '<p dir=\"auto\"><a title="This link will take you away from steemit.com" class="markdown-video-link markdown-video-link-dtube" data-embed-src="https://emb.d.tube/#!/scottcbusiness/g04n2bbp"><img class="no-replace video-thumbnail" src="https://images.ecency.com/p/46aP2QbqUqBqwzwxM6L1P6uLNceBDDCM6xyDJFx6ANhENRd3gJWJH7TiVR91QZ1KBcdAdZruQE35PBpQ3jUvkNK4mJqZ.png?format=match&mode=fit" /><span class="markdown-video-play"></span></a></p>'
|
|
97
|
+
const expected = '<p dir=\"auto\"><a title="This link will take you away from steemit.com" class="markdown-video-link markdown-video-link-dtube" data-embed-src="https://emb.d.tube/#!/scottcbusiness/g04n2bbp"><img class="no-replace video-thumbnail" itemprop="image" src="https://images.ecency.com/p/46aP2QbqUqBqwzwxM6L1P6uLNceBDDCM6xyDJFx6ANhENRd3gJWJH7TiVR91QZ1KBcdAdZruQE35PBpQ3jUvkNK4mJqZ.png?format=match&mode=fit" loading="eager" fetchpriority="high" /><span class="markdown-video-play"></span></a></p>'
|
|
98
98
|
|
|
99
99
|
expect(markdown2Html(input)).toBe(expected)
|
|
100
100
|
})
|
|
@@ -270,7 +270,7 @@ describe('Markdown2Html', () => {
|
|
|
270
270
|
body: '@lorem @ipsum @dolor \n @Sit amet'
|
|
271
271
|
}
|
|
272
272
|
expected =
|
|
273
|
-
'<p dir=\"auto\"><span><a class="markdown-author-link" data-author="lorem">@lorem</a> <a class="markdown-author-link" data-author="ipsum">@ipsum</a> <a class="markdown-author-link" data-author="dolor">@dolor</a></span><br
|
|
273
|
+
'<p dir=\"auto\"><span><a class="markdown-author-link" data-author="lorem">@lorem</a> <a class="markdown-author-link" data-author="ipsum">@ipsum</a> <a class="markdown-author-link" data-author="dolor">@dolor</a></span><br />\n@Sit amet</p>'
|
|
274
274
|
expect(markdown2Html(input)).toBe(expected)
|
|
275
275
|
})
|
|
276
276
|
|
|
@@ -424,7 +424,7 @@ describe('Markdown2Html', () => {
|
|
|
424
424
|
last_update: '2019-05-10T09:15:21',
|
|
425
425
|
body: '[](https://3speak.online/watch?v=wehmoen/xrhjxocx)'
|
|
426
426
|
}
|
|
427
|
-
const expected = '<p dir=\"auto\"><a class="markdown-video-link markdown-video-link-speak" data-embed-src="https://3speak.online/embed?v=wehmoen/xrhjxocx"><img class="no-replace video-thumbnail" src="https://images.ecency.com/p/2ufhwNgM3qHKBGVeU2TMMqPBjdB17MRuf4Q7vGrmGMtTn6yFtvW3Lt9t5v1c3so7UFhWDYh9B.png?format=match&mode=fit" /><span class="markdown-video-play"></span></a></p>'
|
|
427
|
+
const expected = '<p dir=\"auto\"><a class="markdown-video-link markdown-video-link-speak" data-embed-src="https://3speak.online/embed?v=wehmoen/xrhjxocx"><img class="no-replace video-thumbnail" itemprop="image" src="https://images.ecency.com/p/2ufhwNgM3qHKBGVeU2TMMqPBjdB17MRuf4Q7vGrmGMtTn6yFtvW3Lt9t5v1c3so7UFhWDYh9B.png?format=match&mode=fit" loading="eager" fetchpriority="high" /><span class="markdown-video-play"></span></a></p>'
|
|
428
428
|
|
|
429
429
|
expect(markdown2Html(input)).toBe(expected)
|
|
430
430
|
})
|
|
@@ -436,7 +436,7 @@ describe('Markdown2Html', () => {
|
|
|
436
436
|
last_update: '2029-05-10T09:15:21',
|
|
437
437
|
body: '[](https://3speak.co/watch?v=theycallmedan/blnmdkjt) [Watch on 3Speak](https://3speak.co/watch?v=theycallmedan/blnmdkjt)'
|
|
438
438
|
}
|
|
439
|
-
const expected = '<p dir=\"auto\"><a class="markdown-video-link markdown-video-link-speak" data-embed-src="https://3speak.co/embed?v=theycallmedan/blnmdkjt"><img class="no-replace video-thumbnail" src="https://images.ecency.com/p/CQdwDW6BZfWWtctopKyTJuDRdBH4KXwm9ijE6sZXe5MveWF3nUu4zXXBFUau8NS.png?format=match&mode=fit" /><span class="markdown-video-play"></span></a> <a class="markdown-external-link" data-href="https://3speak.co/watch?v=theycallmedan/blnmdkjt">Watch on 3Speak</a></p>'
|
|
439
|
+
const expected = '<p dir=\"auto\"><a class="markdown-video-link markdown-video-link-speak" data-embed-src="https://3speak.co/embed?v=theycallmedan/blnmdkjt"><img class="no-replace video-thumbnail" itemprop="image" src="https://images.ecency.com/p/CQdwDW6BZfWWtctopKyTJuDRdBH4KXwm9ijE6sZXe5MveWF3nUu4zXXBFUau8NS.png?format=match&mode=fit" loading="eager" fetchpriority="high" /><span class="markdown-video-play"></span></a> <a class="markdown-external-link" data-href="https://3speak.co/watch?v=theycallmedan/blnmdkjt">Watch on 3Speak</a></p>'
|
|
440
440
|
|
|
441
441
|
expect(markdown2Html(input)).toBe(expected)
|
|
442
442
|
})
|
|
@@ -460,7 +460,7 @@ describe('Markdown2Html', () => {
|
|
|
460
460
|
last_update: '2019-05-10T09:15:21',
|
|
461
461
|
body: 'https://youtu.be/UuyS7YAkECA?t=295s'
|
|
462
462
|
}
|
|
463
|
-
const expected = '<p dir=\"auto\"><a class="markdown-video-link markdown-video-link-youtube" data-embed-src="https://www.youtube.com/embed/UuyS7YAkECA?autoplay=1" data-youtube="UuyS7YAkECA" data-start-time="295"><img class="no-replace video-thumbnail" src="https://images.ecency.com/p/S5Eokt4BcQdk7EHeT1aYjzebg2hC7hkthT45eAMp88bZ44hfAQDm6BtJw2H53aq1Tpn1cu4.png?format=match&mode=fit" /><span class="markdown-video-play"></span></a></p>'
|
|
463
|
+
const expected = '<p dir=\"auto\"><a class="markdown-video-link markdown-video-link-youtube" data-embed-src="https://www.youtube.com/embed/UuyS7YAkECA?autoplay=1" data-youtube="UuyS7YAkECA" data-start-time="295"><img class="no-replace video-thumbnail" itemprop="image" src="https://images.ecency.com/p/S5Eokt4BcQdk7EHeT1aYjzebg2hC7hkthT45eAMp88bZ44hfAQDm6BtJw2H53aq1Tpn1cu4.png?format=match&mode=fit" loading="eager" fetchpriority="high" /><span class="markdown-video-play"></span></a></p>'
|
|
464
464
|
|
|
465
465
|
expect(markdown2Html(input)).toBe(expected)
|
|
466
466
|
})
|
|
@@ -520,7 +520,7 @@ describe('Markdown2Html', () => {
|
|
|
520
520
|
last_update: '2019-05-10T09:15:21',
|
|
521
521
|
body: 'this is link https://ipfs.io/ipfs/bafybeihbqfrcrbr6jkf77rdve3nbxjzkfgmeneaw2x5s43qdgpe26cha6q'
|
|
522
522
|
}
|
|
523
|
-
const expected = '<p dir=\"auto\">this is link <a data-href="https://ipfs.io/ipfs/bafybeihbqfrcrbr6jkf77rdve3nbxjzkfgmeneaw2x5s43qdgpe26cha6q" class="markdown-img-link"><img src="https://images.ecency.com/p/2923mN3pnd7PiPXAMdj9UuE6SsjvQJDHj5VpTTCNs3tkJu9JC9Pu9qXSi5Ys5PYtkaRx6ErTnFVzh1WQxWS45rvr6Q4rfUooAM242oyKeihwnx.png?format=match&mode=fit" /></a></p>'
|
|
523
|
+
const expected = '<p dir=\"auto\">this is link <a data-href="https://ipfs.io/ipfs/bafybeihbqfrcrbr6jkf77rdve3nbxjzkfgmeneaw2x5s43qdgpe26cha6q" class="markdown-img-link"><img src="https://images.ecency.com/p/2923mN3pnd7PiPXAMdj9UuE6SsjvQJDHj5VpTTCNs3tkJu9JC9Pu9qXSi5Ys5PYtkaRx6ErTnFVzh1WQxWS45rvr6Q4rfUooAM242oyKeihwnx.png?format=match&mode=fit" itemprop=\"image\" loading=\"eager\" fetchpriority=\"high\" /></a></p>'
|
|
524
524
|
|
|
525
525
|
expect(markdown2Html(input)).toBe(expected)
|
|
526
526
|
})
|
|
@@ -722,7 +722,7 @@ describe('Markdown2Html', () => {
|
|
|
722
722
|
last_update: '2021-05-10T09:15:49',
|
|
723
723
|
body: '<a href="/@hivesql"><img src="https://i.imgur.com/EPN8RW6.png"></a>'
|
|
724
724
|
}
|
|
725
|
-
const expected = '<p dir=\"auto\"><a class=\"markdown-author-link\" data-author=\"hivesql\"><img src=\"https://images.ecency.com/p/2bP4pJr4wVimqCWjYimXJe2cnCgn8gbL3c5wQPJF23G.png?format=match&mode=fit\" /></a></p>'
|
|
725
|
+
const expected = '<p dir=\"auto\"><a class=\"markdown-author-link\" data-author=\"hivesql\"><img src=\"https://images.ecency.com/p/2bP4pJr4wVimqCWjYimXJe2cnCgn8gbL3c5wQPJF23G.png?format=match&mode=fit\" itemprop=\"image\" loading=\"eager\" fetchpriority=\"high\" /></a></p>'
|
|
726
726
|
expect(markdown2Html(input)).toBe(expected)
|
|
727
727
|
})
|
|
728
728
|
|
|
@@ -898,7 +898,7 @@ describe('Markdown2Html', () => {
|
|
|
898
898
|
last_update: '2019-05-10T09:15:21',
|
|
899
899
|
body: 'https://www.youtube.com/embed/UuyS7YAkECA?start=295&autoplay=1'
|
|
900
900
|
}
|
|
901
|
-
const expected = '<p dir=\"auto\"><a class="markdown-video-link markdown-video-link-youtube" data-embed-src="https://www.youtube.com/embed/UuyS7YAkECA?autoplay=1" data-youtube="UuyS7YAkECA" data-start-time="295"><img class="no-replace video-thumbnail" src="https://images.ecency.com/p/S5Eokt4BcQdk7EHeT1aYjzebg2hC7hkthT45eAMp88bZ44hfAQDm6BtJw2H53aq1Tpn1cu4.png?format=match&mode=fit" /><span class="markdown-video-play"></span></a></p>'
|
|
901
|
+
const expected = '<p dir=\"auto\"><a class="markdown-video-link markdown-video-link-youtube" data-embed-src="https://www.youtube.com/embed/UuyS7YAkECA?autoplay=1" data-youtube="UuyS7YAkECA" data-start-time="295"><img class="no-replace video-thumbnail" itemprop="image" src="https://images.ecency.com/p/S5Eokt4BcQdk7EHeT1aYjzebg2hC7hkthT45eAMp88bZ44hfAQDm6BtJw2H53aq1Tpn1cu4.png?format=match&mode=fit" loading="eager" fetchpriority="high" /><span class="markdown-video-play"></span></a></p>'
|
|
902
902
|
|
|
903
903
|
expect(markdown2Html(input)).toBe(expected)
|
|
904
904
|
})
|
|
@@ -958,7 +958,7 @@ describe('Markdown2Html', () => {
|
|
|
958
958
|
last_update: '2019-05-10T09:15:21',
|
|
959
959
|
body: 'https://www.youtube.com/shorts/IaehbZnsi4w'
|
|
960
960
|
}
|
|
961
|
-
const expected = '<p dir=\"auto\"><a class="markdown-video-link markdown-video-link-youtube" data-embed-src="https://www.youtube.com/embed/IaehbZnsi4w?autoplay=1" data-youtube="IaehbZnsi4w"><img class="no-replace video-thumbnail" src="https://images.ecency.com/p/S5Eokt4BcQdk7EHeT1aYjzebg2hC7hkthT45e5VviwaTq13pYBZesC7Hh3idYK26Q1RMUHU.png?format=match&mode=fit" /><span class="markdown-video-play"></span></a></p>'
|
|
961
|
+
const expected = '<p dir=\"auto\"><a class="markdown-video-link markdown-video-link-youtube" data-embed-src="https://www.youtube.com/embed/IaehbZnsi4w?autoplay=1" data-youtube="IaehbZnsi4w"><img class="no-replace video-thumbnail" itemprop="image" src="https://images.ecency.com/p/S5Eokt4BcQdk7EHeT1aYjzebg2hC7hkthT45e5VviwaTq13pYBZesC7Hh3idYK26Q1RMUHU.png?format=match&mode=fit" loading="eager" fetchpriority="high" /><span class="markdown-video-play"></span></a></p>'
|
|
962
962
|
|
|
963
963
|
expect(markdown2Html(input)).toBe(expected)
|
|
964
964
|
})
|
|
@@ -1161,7 +1161,7 @@ describe('Markdown2Html', () => {
|
|
|
1161
1161
|
last_update: '2019-05-10T09:15:21',
|
|
1162
1162
|
body: 'https://img.esteem.ws/bbq3ob1idy.png <a href="https://steemit.com/esteem/@esteemapp/esteem-monthly-guest-curation-program-4">fooo</a> <a href="/esteem/@esteemapp/esteem-monthly-guest-curation-program-4">bar</a> <a href="http://external.com/loromoro">baz</a> #lorem @ipsum <a href=\'https://steemit.com/~witnesses\'>vote me</a>'
|
|
1163
1163
|
}
|
|
1164
|
-
const expected = '<p dir=\"auto\"><img class="markdown-img-link" src="https://images.ecency.com/p/o1AJ9qDyyJNSpZWhUgGYc3MngFqoAMwgbeMkkd8SVxyfRVjiN.png?format=match&mode=fit" /> <a href="https://steemit.com/esteem/@esteemapp/esteem-monthly-guest-curation-program-4" class="markdown-external-link" target="_blank" rel="noopener">fooo</a> <a href="/esteem/@esteemapp/esteem-monthly-guest-curation-program-4" class="markdown-post-link">bar</a> <a href="http://external.com/loromoro" class="markdown-external-link" target="_blank" rel="noopener">baz</a><span> <a class="markdown-tag-link" href="/trending/lorem">#lorem</a> <a class="markdown-author-link" href="/@ipsum">@ipsum</a> </span><a href="https://steemit.com/~witnesses" class="markdown-external-link" target="_blank" rel="noopener">vote me</a></p>'
|
|
1164
|
+
const expected = '<p dir=\"auto\"><img class="markdown-img-link" src="https://images.ecency.com/p/o1AJ9qDyyJNSpZWhUgGYc3MngFqoAMwgbeMkkd8SVxyfRVjiN.png?format=match&mode=fit" loading="lazy" decoding="async" itemprop="image" /> <a href="https://steemit.com/esteem/@esteemapp/esteem-monthly-guest-curation-program-4" class="markdown-external-link" target="_blank" rel="noopener">fooo</a> <a href="/esteem/@esteemapp/esteem-monthly-guest-curation-program-4" class="markdown-post-link">bar</a> <a href="http://external.com/loromoro" class="markdown-external-link" target="_blank" rel="noopener">baz</a><span> <a class="markdown-tag-link" href="/trending/lorem">#lorem</a> <a class="markdown-author-link" href="/@ipsum">@ipsum</a> </span><a href="https://steemit.com/~witnesses" class="markdown-external-link" target="_blank" rel="noopener">vote me</a></p>'
|
|
1165
1165
|
|
|
1166
1166
|
expect(markdown2Html(input, false)).toBe(expected)
|
|
1167
1167
|
})
|
package/src/methods/a.method.ts
CHANGED
|
@@ -30,7 +30,8 @@ import {
|
|
|
30
30
|
import { getSerializedInnerHTML } from './get-inner-html.method'
|
|
31
31
|
import { proxifyImageSrc } from '../proxify-image-src'
|
|
32
32
|
import { removeChildNodes } from './remove-child-nodes.method'
|
|
33
|
-
import { extractYtStartTime, isValidPermlink } from '../helper'
|
|
33
|
+
import { extractYtStartTime, isValidPermlink, isValidUsername } from '../helper'
|
|
34
|
+
import { createImageHTML } from "./img.method";
|
|
34
35
|
|
|
35
36
|
|
|
36
37
|
export function a(el: HTMLElement, forApp: boolean, webp: boolean): void {
|
|
@@ -61,11 +62,9 @@ export function a(el: HTMLElement, forApp: boolean, webp: boolean): void {
|
|
|
61
62
|
href.trim().replace(/&/g, '&') ===
|
|
62
63
|
getSerializedInnerHTML(el).trim().replace(/&/g, '&')
|
|
63
64
|
) {
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
const replaceNode = DOMParser.parseFromString(
|
|
67
|
-
`<img ${attrs}/>`
|
|
68
|
-
)
|
|
65
|
+
const isLCP = false; // LCP handled elsewhere
|
|
66
|
+
const imgHTML = createImageHTML(href, isLCP, webp);
|
|
67
|
+
const replaceNode = DOMParser.parseFromString(imgHTML);
|
|
69
68
|
|
|
70
69
|
el.parentNode.replaceChild(replaceNode, el)
|
|
71
70
|
|
|
@@ -129,8 +128,12 @@ export function a(el: HTMLElement, forApp: boolean, webp: boolean): void {
|
|
|
129
128
|
// If a hive user with url
|
|
130
129
|
const mentionMatch = href.match(MENTION_REGEX)
|
|
131
130
|
if (mentionMatch && WHITE_LIST.includes(mentionMatch[1].replace(/www./,'')) && mentionMatch.length === 3) {
|
|
131
|
+
const _author = mentionMatch[2].replace('@', '')
|
|
132
|
+
if (!isValidUsername(_author)) return
|
|
133
|
+
const author = _author.toLowerCase()
|
|
134
|
+
|
|
132
135
|
el.setAttribute('class', 'markdown-author-link')
|
|
133
|
-
|
|
136
|
+
|
|
134
137
|
if (author.indexOf('/')===-1) {
|
|
135
138
|
if (el.textContent === href) {
|
|
136
139
|
el.textContent = `@${author}`
|
|
@@ -214,8 +217,11 @@ export function a(el: HTMLElement, forApp: boolean, webp: boolean): void {
|
|
|
214
217
|
// If a hive user with internal url
|
|
215
218
|
const imentionMatch = href.match(INTERNAL_MENTION_REGEX)
|
|
216
219
|
if (imentionMatch) {
|
|
220
|
+
const _author = imentionMatch[0].replace('/@', '')
|
|
221
|
+
if (!isValidUsername(_author)) return
|
|
222
|
+
const author = _author.toLowerCase()
|
|
223
|
+
|
|
217
224
|
el.setAttribute('class', 'markdown-author-link')
|
|
218
|
-
const author = imentionMatch[0].replace('/@', '').toLowerCase()
|
|
219
225
|
if (author.indexOf('/')===-1) {
|
|
220
226
|
if (el.textContent === href) {
|
|
221
227
|
el.textContent = `@${author}`
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { proxifyImageSrc } from "../proxify-image-src";
|
|
2
2
|
|
|
3
|
-
export function img(el: HTMLElement, webp: boolean): void {
|
|
4
|
-
el.removeAttribute("width");
|
|
5
|
-
el.removeAttribute("height");
|
|
6
|
-
|
|
3
|
+
export function img(el: HTMLElement, webp: boolean, state?: { firstImageFound: boolean }): void {
|
|
7
4
|
let src = el.getAttribute("src") || "";
|
|
8
5
|
|
|
9
6
|
// Normalize encoded characters
|
|
@@ -26,9 +23,20 @@ export function img(el: HTMLElement, webp: boolean): void {
|
|
|
26
23
|
}
|
|
27
24
|
|
|
28
25
|
// Sanitize any dynamic or low-res src-like attributes
|
|
29
|
-
["onerror", "dynsrc", "lowsrc"].forEach(attr => el.removeAttribute(attr));
|
|
26
|
+
["onerror", "dynsrc", "lowsrc", "width", "height"].forEach(attr => el.removeAttribute(attr));
|
|
30
27
|
|
|
31
28
|
el.setAttribute("itemprop", "image");
|
|
29
|
+
const isLCP = state && !state.firstImageFound;
|
|
30
|
+
|
|
31
|
+
if (isLCP) {
|
|
32
|
+
el.setAttribute("loading", "eager");
|
|
33
|
+
el.setAttribute("fetchpriority", "high");
|
|
34
|
+
state.firstImageFound = true;
|
|
35
|
+
} else {
|
|
36
|
+
el.setAttribute("loading", "lazy");
|
|
37
|
+
el.setAttribute("decoding", "async");
|
|
38
|
+
}
|
|
39
|
+
|
|
32
40
|
|
|
33
41
|
const cls = el.getAttribute("class") || "";
|
|
34
42
|
const shouldReplace = !cls.includes("no-replace");
|
|
@@ -39,3 +47,16 @@ export function img(el: HTMLElement, webp: boolean): void {
|
|
|
39
47
|
el.setAttribute("src", proxified);
|
|
40
48
|
}
|
|
41
49
|
}
|
|
50
|
+
|
|
51
|
+
export function createImageHTML(src: string, isLCP: boolean, webp: boolean): string {
|
|
52
|
+
const loading = isLCP ? 'eager' : 'lazy';
|
|
53
|
+
const fetch = isLCP ? 'fetchpriority="high"' : 'decoding="async"';
|
|
54
|
+
const proxified = proxifyImageSrc(src, 0, 0, webp ? 'webp' : 'match');
|
|
55
|
+
return `<img
|
|
56
|
+
class="markdown-img-link"
|
|
57
|
+
src="${proxified}"
|
|
58
|
+
loading="${loading}"
|
|
59
|
+
${fetch}
|
|
60
|
+
itemprop="image"
|
|
61
|
+
/>`;
|
|
62
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { IMG_REGEX, SECTION_LIST } from '../consts'
|
|
2
2
|
import { proxifyImageSrc } from '../proxify-image-src'
|
|
3
|
-
import {isValidPermlink} from "../helper";
|
|
3
|
+
import { isValidPermlink, isValidUsername } from "../helper";
|
|
4
|
+
import { createImageHTML } from "./img.method";
|
|
4
5
|
|
|
5
6
|
export function linkify(content: string, forApp: boolean, webp: boolean): string {
|
|
6
7
|
// Tags
|
|
@@ -21,7 +22,7 @@ export function linkify(content: string, forApp: boolean, webp: boolean): string
|
|
|
21
22
|
(match, preceeding1, preceeding2, user) => {
|
|
22
23
|
const userLower = user.toLowerCase()
|
|
23
24
|
const preceedings = (preceeding1 || '') + (preceeding2 || '')
|
|
24
|
-
if (userLower.indexOf('/') === -1) {
|
|
25
|
+
if (userLower.indexOf('/') === -1 && isValidUsername(user)) {
|
|
25
26
|
const attrs = forApp ? `data-author="${userLower}"` : `href="/@${userLower}"`
|
|
26
27
|
return `${preceedings}<a class="markdown-author-link" ${attrs}>@${user}</a>`
|
|
27
28
|
} else {
|
|
@@ -48,10 +49,13 @@ export function linkify(content: string, forApp: boolean, webp: boolean): string
|
|
|
48
49
|
)
|
|
49
50
|
|
|
50
51
|
// Image links
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
let firstImageUsed = false;
|
|
53
|
+
|
|
54
|
+
content = content.replace(IMG_REGEX, (imglink) => {
|
|
55
|
+
const isLCP = !firstImageUsed;
|
|
56
|
+
firstImageUsed = true;
|
|
57
|
+
return createImageHTML(imglink, isLCP, webp);
|
|
58
|
+
});
|
|
55
59
|
|
|
56
60
|
return content
|
|
57
61
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { IMG_REGEX, YOUTUBE_REGEX, WHITE_LIST, DOMParser, POST_REGEX
|
|
2
|
-
import { extractYtStartTime, isValidPermlink } from '../helper'
|
|
1
|
+
import { IMG_REGEX, YOUTUBE_REGEX, WHITE_LIST, DOMParser, POST_REGEX } from '../consts'
|
|
2
|
+
import { extractYtStartTime, isValidPermlink, isValidUsername } from '../helper'
|
|
3
3
|
import { proxifyImageSrc } from '../proxify-image-src'
|
|
4
4
|
import { linkify } from './linkify.method'
|
|
5
|
+
import {createImageHTML} from "./img.method";
|
|
5
6
|
|
|
6
7
|
export function text(node: HTMLElement, forApp: boolean, webp: boolean): void {
|
|
7
8
|
if (['a', 'code'].includes(node.parentNode.nodeName)) {
|
|
@@ -20,12 +21,10 @@ export function text(node: HTMLElement, forApp: boolean, webp: boolean): void {
|
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
if (node.nodeValue.match(IMG_REGEX)) {
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
node.parentNode.replaceChild(replaceNode, node)
|
|
24
|
+
const isLCP = false; // Traverse handles LCP; no need to double-count
|
|
25
|
+
const imageHTML = createImageHTML(node.nodeValue, isLCP, webp);
|
|
26
|
+
const replaceNode = DOMParser.parseFromString(imageHTML);
|
|
27
|
+
node.parentNode.replaceChild(replaceNode, node);
|
|
29
28
|
}
|
|
30
29
|
// If a youtube video
|
|
31
30
|
if (node.nodeValue.match(YOUTUBE_REGEX)) {
|
|
@@ -59,7 +58,9 @@ export function text(node: HTMLElement, forApp: boolean, webp: boolean): void {
|
|
|
59
58
|
const tag = postMatch[2]
|
|
60
59
|
const author = postMatch[3].replace('@', '')
|
|
61
60
|
const permlink = postMatch[4]
|
|
62
|
-
|
|
61
|
+
|
|
62
|
+
if (!isValidUsername(author)) return
|
|
63
|
+
if (!isValidPermlink(permlink)) return
|
|
63
64
|
|
|
64
65
|
const attrs = forApp ? `data-tag="${tag}" data-author="${author}" data-permlink="${permlink}" class="markdown-post-link"` : `class="markdown-post-link" href="/${tag}/@${author}/${permlink}"`
|
|
65
66
|
const replaceNode = DOMParser.parseFromString(
|
|
@@ -4,7 +4,7 @@ import { img } from './img.method'
|
|
|
4
4
|
import { p } from './p.method'
|
|
5
5
|
import { text } from './text.method'
|
|
6
6
|
|
|
7
|
-
export function traverse(node: Node, forApp: boolean, depth = 0, webp = false): void {
|
|
7
|
+
export function traverse(node: Node, forApp: boolean, depth = 0, webp = false, state = { firstImageFound: false }): void {
|
|
8
8
|
if (!node || !node.childNodes) {
|
|
9
9
|
return
|
|
10
10
|
}
|
|
@@ -22,12 +22,12 @@ export function traverse(node: Node, forApp: boolean, depth = 0, webp = false):
|
|
|
22
22
|
text(<HTMLElement>child, forApp, webp)
|
|
23
23
|
}
|
|
24
24
|
if (child.nodeName.toLowerCase() === 'img') {
|
|
25
|
-
img(<HTMLElement>child, webp)
|
|
25
|
+
img(<HTMLElement>child, webp, state)
|
|
26
26
|
}
|
|
27
27
|
if (child.nodeName.toLowerCase() === 'p') {
|
|
28
28
|
p(<HTMLElement>child)
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
traverse(child, forApp, depth + 1, webp)
|
|
31
|
+
traverse(child, forApp, depth + 1, webp, state)
|
|
32
32
|
})
|
|
33
33
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": 10,
|
|
3
3
|
"input": "Lorem ipsum dolor <img src=x onerror=alert(x)> sit amet.\n\n<a href=\"javascript:void(0)\">etiam ut sollicitudin neque</a>\n\n<a onclick=\"console.log('ss')\">Vivamus pulvinar semper porttitor</a>",
|
|
4
|
-
"result": "<p dir=\"auto\">Lorem ipsum dolor <img /> sit amet.</p>\n<p dir=\"auto\"><a>etiam ut sollicitudin neque</a></p>\n<p dir=\"auto\"><a>Vivamus pulvinar semper porttitor</a></p>"
|
|
4
|
+
"result": "<p dir=\"auto\">Lorem ipsum dolor <img itemprop=\"image\" loading=\"eager\" fetchpriority=\"high\" /> sit amet.</p>\n<p dir=\"auto\"><a>etiam ut sollicitudin neque</a></p>\n<p dir=\"auto\"><a>Vivamus pulvinar semper porttitor</a></p>"
|
|
5
5
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": 21,
|
|
3
3
|
"input": "javascript:/*--></title></style></textarea></script></xmp><svg/onload='+/\"/+/onmouseover=1/+/[*/[]/+alert(1)//'>\n\n<IMG SRC=\"javascript:alert('XSS');\"><img src=\"javascript:alert('XSS');\">\n\n<IMG SRC=javascript:alert('XSS')> <img src=javascript:alert('XSS')>",
|
|
4
|
-
"result": "<p dir=\"auto\">javascript:/<em>--><svg/onload='+/\"/+/onmouseover=1/+/[</em>/[]/+alert(1)//'></p>\n<p dir=\"auto\"><img /><img /></p>\n<p dir=\"auto\"><IMG SRC=javascript:alert('XSS')> <img src=javascript:alert('XSS')></p>"
|
|
4
|
+
"result": "<p dir=\"auto\">javascript:/<em>--><svg/onload='+/\"/+/onmouseover=1/+/[</em>/[]/+alert(1)//'></p>\n<p dir=\"auto\"><img itemprop=\"image\" loading=\"eager\" fetchpriority=\"high\" /><img itemprop=\"image\" loading=\"lazy\" decoding=\"async\" /></p>\n<p dir=\"auto\"><IMG SRC=javascript:alert('XSS')> <img src=javascript:alert('XSS')></p>"
|
|
5
5
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": 2112524,
|
|
3
3
|
"input": "What do you think ? With <3 Steemers\n\n\n\n<iframe src=\"https://player.vimeo.com/video/207439341\" width=\"640\" height=\"360\" frameborder=\"0\" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>\n<p dir=\"auto\"><a href=\"https://vimeo.com/207439341\">ShowReel 2017</a> from <a href=\"https://vimeo.com/freerubens\">Rubens Ben</a> on <a href=\"https://vimeo.com\">Vimeo</a>.</p>",
|
|
4
|
-
"result": "<p dir=\"auto\">What do you think ? With <3 Steemers</p>\n<p dir=\"auto\"><img src=\"https://images.ecency.com/p/6C2W1azD1rBrx31MBKd8RJWSbPhKuu863DJHXqo7MppEjTQsPhXD81k.png?format=match&mode=fit\" alt=\"GitHub Logo\" /></p>\n<iframe src=\"https://player.vimeo.com/video/207439341\" frameborder=\"0\" webkitallowfullscreen=\"webkitallowfullscreen\" mozallowfullscreen=\"mozallowfullscreen\" allowfullscreen=\"allowfullscreen\"></iframe>\n<p dir=\"auto\"><a class=\"markdown-external-link\" data-href=\"https://vimeo.com/207439341\">ShowReel 2017</a> from <a class=\"markdown-external-link\" data-href=\"https://vimeo.com/freerubens\">Rubens Ben</a> on <a class=\"markdown-external-link\" data-href=\"https://vimeo.com\">Vimeo</a>.</p>"
|
|
4
|
+
"result": "<p dir=\"auto\">What do you think ? With <3 Steemers</p>\n<p dir=\"auto\"><img src=\"https://images.ecency.com/p/6C2W1azD1rBrx31MBKd8RJWSbPhKuu863DJHXqo7MppEjTQsPhXD81k.png?format=match&mode=fit\" alt=\"GitHub Logo\" itemprop=\"image\" loading=\"eager\" fetchpriority=\"high\" /></p>\n<iframe src=\"https://player.vimeo.com/video/207439341\" frameborder=\"0\" webkitallowfullscreen=\"webkitallowfullscreen\" mozallowfullscreen=\"mozallowfullscreen\" allowfullscreen=\"allowfullscreen\"></iframe>\n<p dir=\"auto\"><a class=\"markdown-external-link\" data-href=\"https://vimeo.com/207439341\">ShowReel 2017</a> from <a class=\"markdown-external-link\" data-href=\"https://vimeo.com/freerubens\">Rubens Ben</a> on <a class=\"markdown-external-link\" data-href=\"https://vimeo.com\">Vimeo</a>.</p>"
|
|
5
5
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": 22,
|
|
3
3
|
"input": "<IMG SRC=JaVaScRiPt:alert('XSS')> <IMG SRC=javascript:alert("XSS")>\n<IMG SRC=`javascript:alert(\"RSnake says, 'XSS'\")`>\n<a onmouseover=\"alert(document.cookie)\">xxs link</a><a onmouseover=alert(document.cookie)>xxs link</a>\n<IMG \"\"\"><SCRIPT>alert(\"XSS\")</SCRIPT>\"><IMG SRC=javascript:alert(String.fromCharCode(88,83,83))>\n <IMG SRC= onmouseover=\"alert('xxs')\">",
|
|
4
|
-
"result": "<p dir=\"auto\"><IMG SRC=JaVaScRiPt:alert('XSS')> <IMG SRC=javascript:alert("XSS")><br />\n<IMG SRC=<code>javascript:alert(\"RSnake says, 'XSS'\")</code>><br />\n<a>xxs link</a><a>xxs link</a><br />\n<IMG \"\"\">alert("XSS")\"><img /><br />\n<IMG SRC= onmouseover=\"alert('xxs')\"></p>"
|
|
4
|
+
"result": "<p dir=\"auto\"><IMG SRC=JaVaScRiPt:alert('XSS')> <IMG SRC=javascript:alert("XSS")><br />\n<IMG SRC=<code>javascript:alert(\"RSnake says, 'XSS'\")</code>><br />\n<a>xxs link</a><a>xxs link</a><br />\n<IMG \"\"\">alert("XSS")\"><img itemprop=\"image\" loading=\"eager\" fetchpriority=\"high\" /><br />\n<IMG SRC= onmouseover=\"alert('xxs')\"></p>"
|
|
5
5
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": 23,
|
|
3
3
|
"input": "<IMG SRC=/ onerror=\"alert(String.fromCharCode(88,83,83))\"></img> <img src=x onerror=\"javascript:alert('XSS')\"> <IMG SRC=javascript:alert('XSS')> <IMG SRC=javascript:alert('XSS')> <IMG SRC=\"  javascript:alert('XSS');\">",
|
|
4
|
-
"result": "<p dir=\"auto\"><IMG SRC=/ onerror=\"alert(String.fromCharCode(88,83,83))\"> <img /> <IMG SRC=javascript:alert('XSS')> <img /> <img /></p>"
|
|
4
|
+
"result": "<p dir=\"auto\"><IMG SRC=/ onerror=\"alert(String.fromCharCode(88,83,83))\"> <img itemprop=\"image\" loading=\"eager\" fetchpriority=\"high\" /> <IMG SRC=javascript:alert('XSS')> <img itemprop=\"image\" loading=\"lazy\" decoding=\"async\" /> <img itemprop=\"image\" loading=\"lazy\" decoding=\"async\" /></p>"
|
|
5
5
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": 26,
|
|
3
3
|
"input": "<IMG DYNSRC=\"javascript:alert('XSS')\"> <IMG LOWSRC=\"javascript:alert('XSS')\"> <STYLE>li {list-style-image: url(\"javascript:alert('XSS')\");}</STYLE>",
|
|
4
|
-
"result": "<p dir=\"auto\"><img /> <img /> </p>"
|
|
4
|
+
"result": "<p dir=\"auto\"><img itemprop=\"image\" loading=\"eager\" fetchpriority=\"high\" /> <img itemprop=\"image\" loading=\"lazy\" decoding=\"async\" /> </p>"
|
|
5
5
|
}
|