Repository: zestedesavoir/zmarkdown
Pull request #344 (2019-07-28 13:58:01)

[remark-emoticons] Add a new locator for faster parsing and [zmarkdown] new emoticons
Hello!

# Context

New emoticons have been available for Zeste de Savoir for a long time, so I guess now is time to integrate these into ZMarkdown. This PR is the second one that aims to do so, but the first one with all tests running (only three snapshots updated). I'm reposting here the work done on @A-312 's fork, because he asked me to do so to resolve conflicts between the base master and his branch. This PR also includes a new `locator` as mentioned below.

# Work done

I also worked on a new locator for remark-emoticons, searching for chars instead of using Regex; it is way more clear, and should also be faster, to be able to search for all our new emojis. Tests are now up and running, and so we should be able to merge it after a quick review. By the way, I cleaned the AST IDL description to make it unambiguous, and it can be referenced on the related issue #126 .

**The tokenizer was left as is by now**, maybe we could rewrite it later, but for now, we're not parsing that much emoticons. By the way, I cleaned the AST IDL description to make it unambiguous, and it should be referenced on the reference issue #126 .

The previous work from @A-312 is left as it was made, because he knows better than me how the workflow is don on zds-site. For once, nothing left to do, except for a review (poke @vhf ), that I hope will not find too much mistakes .


Patch:
diff --git a/packages/remark-emoticons/README.md b/packages/remark-emoticons/README.md
index f2ede336..1db5f7c2 100644
--- a/packages/remark-emoticons/README.md
+++ b/packages/remark-emoticons/README.md
@@ -6,13 +6,7 @@ It introduces a new [MDAST][mdast] node type: "emoticon".
 
 This plugin is compatible with [rehype][rehype], turning the emoticons or shortcodes into HTML `<img>` tags with customizable classes.
 
-## Syntax
-
-```markdown
-:D
-```
-
-will yield an element of type:
+## AST node (see [mdast][mdast] specification)
 
 ```javascript
 interface emoticon <: Node {
@@ -29,7 +23,15 @@ interface emoticon <: Node {
 }
 ```
 
-if you configured the plugin with :
+## Syntax
+
+The following Markdown:
+
+```markdown
+:D
+```
+
+if you configured the plugin with:
 
 ```javascript
 {
@@ -40,7 +42,7 @@ if you configured the plugin with :
 }
 ```
 
-the produced AST node will contain :
+will produce this AST node:
 
 ```javascript
 {
@@ -57,7 +59,7 @@ the produced AST node will contain :
 }
 ```
 
-If you use [rehype][rehype] stringifier it will output :
+If you use [rehype][rehype] stringifier it will output:
 
 ```html
 <img src="/static/smileys/happy.png" alt=":D" class="some-smiley foo">
diff --git a/packages/remark-emoticons/dist/index.js b/packages/remark-emoticons/dist/index.js
index e0a03bb9..fd916a7a 100644
--- a/packages/remark-emoticons/dist/index.js
+++ b/packages/remark-emoticons/dist/index.js
@@ -1,50 +1,53 @@
 "use strict";
 
-function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }
-
-function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); }
-
-function _iterableToArrayLimit(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
-
-function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
+var whitespace = require('is-whitespace-character');
 
 function escapeRegExp(str) {
-  return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); // eslint-disable-line no-useless-escape
+  return str.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&');
 }
 
 module.exports = function inlinePlugin(ctx) {
   var emoticonClasses = ctx && ctx.classes;
-  var emoticons = ctx && ctx.emoticons;
+  var emoticonsRaw = ctx && ctx.emoticons;
 
-  if (!emoticons) {
+  if (!emoticonsRaw) {
     throw new Error('remark-emoticons needs to be passed a configuration object as option');
-  }
+  } // Convert emoticons to lowercase
 
-  for (var _i = 0, _Object$entries = Object.entries(emoticons); _i < _Object$entries.length; _i++) {
-    var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
-        key = _Object$entries$_i[0],
-        val = _Object$entries$_i[1];
 
-    emoticons[key.toLowerCase()] = val;
-  }
+  var emoticons = Object.keys(emoticonsRaw).reduce(function (acc, key) {
+    acc[key.toLowerCase()] = emoticonsRaw[key];
+    return acc;
+  }, {}); // Create a list composed of the first character of each emoticon
 
+  var firstChars = Object.keys(emoticons).reduce(function (acc, key) {
+    var firstChar = key.charAt(0);
+    if (acc.indexOf(firstChar) === -1) acc.push(firstChar);
+    return acc;
+  }, []);
   var pattern = Object.keys(emoticons).map(escapeRegExp).join('|');
   var regex = new RegExp("(?:\\s|^)(".concat(pattern, ")(?:\\s|$)"), 'i');
 
   function locator(value, fromIndex) {
-    var keep = regex.exec(value);
+    var lowestMatch = -1;
 
-    if (keep) {
-      var index = keep.index;
+    for (var c = 0; c < firstChars.length; c++) {
+      var _char = firstChars[c];
+      var match = value.indexOf(_char, fromIndex);
 
-      while (/^\s/.test(value.charAt(index))) {
-        index++;
+      if (match === -1 && _char !== _char.toUpperCase()) {
+        match = value.indexOf(_char.toUpperCase(), fromIndex);
       }
 
-      return index;
+      if (match !== -1) {
+        // A smiley should be precedeed by at least one whitespace
+        if (whitespace(value[match - 1]) && (lowestMatch === -1 || match > lowestMatch)) {
+          lowestMatch = match;
+        }
+      }
     }
 
-    return -1;
+    return lowestMatch;
   }
 
   function inlineTokenizer(eat, value, silent) {
diff --git a/packages/remark-emoticons/package.json b/packages/remark-emoticons/package.json
index a56d7c38..4d96feb9 100644
--- a/packages/remark-emoticons/package.json
+++ b/packages/remark-emoticons/package.json
@@ -30,5 +30,8 @@
   "keywords": [
     "remark"
   ],
+  "dependencies": {
+    "is-whitespace-character": "^1.0.0"
+  },
   "license": "MIT"
 }
diff --git a/packages/remark-emoticons/src/index.js b/packages/remark-emoticons/src/index.js
index 9938c1d0..4e52dc49 100644
--- a/packages/remark-emoticons/src/index.js
+++ b/packages/remark-emoticons/src/index.js
@@ -1,33 +1,53 @@
+const whitespace = require('is-whitespace-character')
+
 function escapeRegExp (str) {
-  return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&') // eslint-disable-line no-useless-escape
+  return str.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&')
 }
 
 module.exports = function inlinePlugin (ctx) {
   const emoticonClasses = ctx && ctx.classes
-  const emoticons = ctx && ctx.emoticons
+  const emoticonsRaw = ctx && ctx.emoticons
 
-  if (!emoticons) {
+  if (!emoticonsRaw) {
     throw new Error('remark-emoticons needs to be passed a configuration object as option')
   }
 
-  for (const [key, val] of Object.entries(emoticons)) {
-    emoticons[key.toLowerCase()] = val
-  }
+  // Convert emoticons to lowercase
+  const emoticons = Object.keys(emoticonsRaw).reduce((acc, key) => {
+    acc[key.toLowerCase()] = emoticonsRaw[key]
+    return acc
+  }, {})
 
-  const pattern = Object.keys(emoticons).map(escapeRegExp).join('|')
+  // Create a list composed of the first character of each emoticon
+  const firstChars = Object.keys(emoticons).reduce((acc, key) => {
+    const firstChar = key.charAt(0)
+    if (acc.indexOf(firstChar) === -1) acc.push(firstChar)
+    return acc
+  }, [])
 
+  const pattern = Object.keys(emoticons).map(escapeRegExp).join('|')
   const regex = new RegExp(`(?:\\s|^)(${pattern})(?:\\s|$)`, 'i')
 
   function locator (value, fromIndex) {
-    const keep = regex.exec(value)
-    if (keep) {
-      let index = keep.index
-      while (/^\s/.test(value.charAt(index))) {
-        index++
+    let lowestMatch = -1
+
+    for (let c = 0; c < firstChars.length; c++) {
+      const char = firstChars[c]
+      let match = value.indexOf(char, fromIndex)
+
+      if (match === -1 && char !== char.toUpperCase()) {
+        match = value.indexOf(char.toUpperCase(), fromIndex)
+      }
+
+      if (match !== -1) {
+        // A smiley should be precedeed by at least one whitespace
+        if (whitespace(value[match - 1]) && (lowestMatch === -1 || match > lowestMatch)) {
+          lowestMatch = match
+        }
       }
-      return index
     }
-    return -1
+
+    return lowestMatch
   }
 
   function inlineTokenizer (eat, value, silent) {
diff --git a/packages/zmarkdown/config/remark.js b/packages/zmarkdown/config/remark.js
index fb28f315..2f28c327 100644
--- a/packages/zmarkdown/config/remark.js
+++ b/packages/zmarkdown/config/remark.js
@@ -162,35 +162,153 @@ const remarkConfig = {
 
   emoticons: {
     emoticons: {
-      ':ange:': '/static/smileys/ange.png',
-      ':colere:': '/static/smileys/angry.gif',
-      'o_O': '/static/smileys/blink.gif',
-      ';)': '/static/smileys/clin.png',
-      ':B': '/static/smileys/b.png',
-      ':diable:': '/static/smileys/diable.png',
-      ':D': '/static/smileys/heureux.png',
-      '^^': '/static/smileys/hihi.png',
-      ':o': '/static/smileys/huh.png',
-      ':p': '/static/smileys/langue.png',
-      ':magicien:': '/static/smileys/magicien.png',
-      ':colere2:': '/static/smileys/mechant.png',
-      ':ninja:': '/static/smileys/ninja.png',
-      'x(': '/static/smileys/pinch.png',
-      '>_<': '/static/smileys/pinch.png',
-      'X/': '/static/smileys/pinch.png',
-      ':pirate:': '/static/smileys/pirate.png',
-      ":'(": '/static/smileys/pleure.png',
-      ':lol:': '/static/smileys/rire.gif',
-      ':honte:': '/static/smileys/rouge.png',
-      ':-°': '/static/smileys/siffle.png',
-      ':)': '/static/smileys/smile.png',
-      ':soleil:': '/static/smileys/soleil.png',
-      ':(': '/static/smileys/triste.png',
-      ':euh:': '/static/smileys/unsure.gif',
-      ':waw:': '/static/smileys/waw.png',
-      ':zorro:': '/static/smileys/zorro.png',
-      '^(;,;)^': '/static/smileys/cthulhu.png',
-      ':popcorn:': '/static/smileys/popcorn.png',
+      ':ange:': '/static/smileys/svg/ange.svg',
+      ':colere:': '/static/smileys/svg/angry.svg',
+      'o_O': '/static/smileys/svg/blink.svg',
+      ';)': '/static/smileys/svg/clin.svg',
+      ':B': '/static/smileys/svg/b.svg',
+      ':diable:': '/static/smileys/svg/diable.svg',
+      ':D': '/static/smileys/svg/heureux.svg',
+      '^^': '/static/smileys/svg/hihi.svg',
+      ':o': '/static/smileys/svg/huh.svg',
+      ':p': '/static/smileys/svg/langue.svg',
+      ':magicien:': '/static/smileys/svg/magicien.svg',
+      ':colere2:': '/static/smileys/svg/mechant.svg',
+      ':ninja:': '/static/smileys/svg/ninja.svg',
+      '>_<': '/static/smileys/svg/pinch.svg',
+      'X/': '/static/smileys/svg/pinch.svg',
+      ':pirate:': '/static/smileys/svg/pirate.svg',
+      ":'(": '/static/smileys/svg/pleure.svg',
+      ':lol:': '/static/smileys/svg/rire.svg',
+      ':honte:': '/static/smileys/svg/rouge.svg',
+      ':-°': '/static/smileys/svg/siffle.svg',
+      ':)': '/static/smileys/svg/smile.svg',
+      ':soleil:': '/static/smileys/svg/soleil.svg',
+      ':(': '/static/smileys/svg/triste.svg',
+      ':euh:': '/static/smileys/svg/unsure.svg',
+      ':waw:': '/static/smileys/svg/waw.svg',
+      ':zorro:': '/static/smileys/svg/zorro.svg',
+      '^(;,;)^': '/static/smileys/svg/cthulhu.svg',
+      ':bounce:': '/static/smileys/svg/bounce.svg',
+      ':popcorn:': '/static/smileys/svg/popcorn.svg',
+      ':démon:': '/static/smileys/svg/f47f.svg',
+      ':demon:': '/static/smileys/svg/f47f.svg',
+      ':content:': '/static/smileys/svg/f600.svg',
+      ':joyeux:': '/static/smileys/svg/f601.svg',
+      ':mortderire:': '/static/smileys/svg/f602.svg',
+      ':daccord:': '/static/smileys/svg/f603.svg',
+      ':eneffet:': '/static/smileys/svg/f604.svg',
+      ':eneffetgené:': '/static/smileys/svg/f605.svg',
+      ':eneffetgene:': '/static/smileys/svg/f605.svg',
+      'x)': '/static/smileys/svg/f606.svg',
+      ':innocent:': '/static/smileys/svg/f607.svg',
+      ':démonjoyeux:': '/static/smileys/svg/f608.svg',
+      ':demonjoyeux:': '/static/smileys/svg/f608.svg',
+      ':clindoeil:': '/static/smileys/svg/f609.svg',
+      ':rejouis:': '/static/smileys/svg/f60a.svg',
+      ':yum:': '/static/smileys/svg/f60b.svg',
+      ':soulagé:': '/static/smileys/svg/f60c.svg',
+      ':soulage:': '/static/smileys/svg/f60c.svg',
+      '<3': '/static/smileys/svg/f60d.svg',
+      ':confiant:': '/static/smileys/svg/f60e.svg',
+      ':malicieux:': '/static/smileys/svg/f60f.svg',
+      ':indifférent:': '/static/smileys/svg/f610.svg',
+      ':indifferent:': '/static/smileys/svg/f610.svg',
+      ':détaché:': '/static/smileys/svg/f611.svg',
+      ':detache:': '/static/smileys/svg/f611.svg',
+      ':lassé:': '/static/smileys/svg/f612.svg',
+      ':lasse:': '/static/smileys/svg/f612.svg',
+      ':sueurfroide:': '/static/smileys/svg/f613.svg',
+      ':insatisfait:': '/static/smileys/svg/f614.svg',
+      ':-/': '/static/smileys/svg/f615.svg',
+      ':contrarié:': '/static/smileys/svg/f616.svg',
+      ':contrarie:': '/static/smileys/svg/f616.svg',
+      ':bisou:': '/static/smileys/svg/f617.svg',
+      ':bisoucoeur:': '/static/smileys/svg/f618.svg',
+      ':bisousourire:': '/static/smileys/svg/f619.svg',
+      ':bisourougir:': '/static/smileys/svg/f61a.svg',
+      ':-P': '/static/smileys/svg/f61b.svg',
+      ';-P': '/static/smileys/svg/f61c.svg',
+      'x-P': '/static/smileys/svg/f61d.svg',
+      ':déçuinquiet:': '/static/smileys/svg/f61e.svg',
+      ':decuinquiet:': '/static/smileys/svg/f61e.svg',
+      ':inquiet:': '/static/smileys/svg/f61f.svg',
+      ':fâché:': '/static/smileys/svg/f620.svg',
+      ':fache:': '/static/smileys/svg/f620.svg',
+      ':fâchérouge:': '/static/smileys/svg/f621.svg',
+      ':facherouge:': '/static/smileys/svg/f621.svg',
+      ':tristelarme:': '/static/smileys/svg/f622.svg',
+      'x(': '/static/smileys/svg/f623.svg',
+      ':fulminant:': '/static/smileys/svg/f624.svg',
+      ':deçularme:': '/static/smileys/svg/f625.svg',
+      ':decularme:': '/static/smileys/svg/f625.svg',
+      ':déçu:': '/static/smileys/svg/f626.svg',
+      ':decu:': '/static/smileys/svg/f626.svg',
+      ':déçutriste:': '/static/smileys/svg/f627.svg',
+      ':decutriste:': '/static/smileys/svg/f627.svg',
+      ':déçuangoissé:': '/static/smileys/svg/f628.svg',
+      ':decuangoisse:': '/static/smileys/svg/f628.svg',
+      ':éreinté:': '/static/smileys/svg/f629.svg',
+      ':ereinte:': '/static/smileys/svg/f629.svg',
+      ':somnole:': '/static/smileys/svg/f62a.svg',
+      ':fatigué:': '/static/smileys/svg/f62b.svg',
+      ':fatigue:': '/static/smileys/svg/f62b.svg',
+      ':grimace:': '/static/smileys/svg/f62c.svg',
+      ':pleure:': '/static/smileys/svg/f62d.svg',
+      ':ébahi:': '/static/smileys/svg/f62e.svg',
+      ':ebahi:': '/static/smileys/svg/f62e.svg',
+      ':étonné:': '/static/smileys/svg/f62f.svg',
+      ':etonne:': '/static/smileys/svg/f62f.svg',
+      ':angoissé:': '/static/smileys/svg/f630.svg',
+      ':angoisse:': '/static/smileys/svg/f630.svg',
+      ':hurlantdepeur:': '/static/smileys/svg/f631.svg',
+      ':abasourdi:': '/static/smileys/svg/f632.svg',
+      ':surprisrougi:': '/static/smileys/svg/f633.svg',
+      ':dort:': '/static/smileys/svg/f634.svg',
+      ':vertige:': '/static/smileys/svg/f635.svg',
+      ':muet:': '/static/smileys/svg/f636.svg',
+      ':masquetissu:': '/static/smileys/svg/f637.svg',
+      ':nonsatisfait:': '/static/smileys/svg/f641.svg',
+      ':satisfait:': '/static/smileys/svg/f642.svg',
+      ':inversé:': '/static/smileys/svg/f643.svg',
+      ':inverse:': '/static/smileys/svg/f643.svg',
+      ':regardauciel:': '/static/smileys/svg/f644.svg',
+      ':bouchezipper:': '/static/smileys/svg/f910.svg',
+      ':appâtdugain:': '/static/smileys/svg/f911.svg',
+      ':appatdugain:': '/static/smileys/svg/f911.svg',
+      ':thermomètre:': '/static/smileys/svg/f912.svg',
+      ':intello4yeux:': '/static/smileys/svg/f913.svg',
+      ':pensif:': '/static/smileys/svg/f914.svg',
+      ':blessé:': '/static/smileys/svg/f915.svg',
+      ':blesse:': '/static/smileys/svg/f915.svg',
+      ':bienveillant:': '/static/smileys/svg/f917.svg',
+      ':nausée:': '/static/smileys/svg/f922.svg',
+      ':nausee:': '/static/smileys/svg/f922.svg',
+      ':pliéderire:': '/static/smileys/svg/f923.svg',
+      ':pliederire:': '/static/smileys/svg/f923.svg',
+      ':baver:': '/static/smileys/svg/f924.svg',
+      ':pinocchio:': '/static/smileys/svg/f925.svg',
+      ':eternuer:': '/static/smileys/svg/f927.svg',
+      ':quésaco:': '/static/smileys/svg/f928.svg',
+      ':quesaco:': '/static/smileys/svg/f928.svg',
+      ':regardfan:': '/static/smileys/svg/f929.svg',
+      ':hébété:': '/static/smileys/svg/f92a.svg',
+      ':hebete:': '/static/smileys/svg/f92a.svg',
+      ':chut:': '/static/smileys/svg/f92b.svg',
+      ':fureur:': '/static/smileys/svg/f92c.svg',
+      ':bailler:': '/static/smileys/svg/f92d.svg',
+      ':vomir:': '/static/smileys/svg/f92e.svg',
+      ':tropcogiter:': '/static/smileys/svg/f92f.svg',
+      ':amoureux:': '/static/smileys/svg/f970.svg',
+      ':fêter:': '/static/smileys/svg/f973.svg',
+      ':feter:': '/static/smileys/svg/f973.svg',
+      ':maldecoeur:': '/static/smileys/svg/f974.svg',
+      ':avoirchaud:': '/static/smileys/svg/f975.svg',
+      ':avoirfroid:': '/static/smileys/svg/f976.svg',
+      ':désolé:': '/static/smileys/svg/f97a.svg',
+      ':desole:': '/static/smileys/svg/f97a.svg',
+      ':triste:': '/static/smileys/svg/2639.svg',
+      ':rougir:': '/static/smileys/svg/263a.svg',
     },
     classes: 'smiley',
   },


Test patch:
diff --git a/packages/zmarkdown/__tests__/__snapshots__/new-html-suite.test.js.snap b/packages/zmarkdown/__tests__/__snapshots__/new-html-suite.test.js.snap
index a6edda84..b5bcb3a9 100644
--- a/packages/zmarkdown/__tests__/__snapshots__/new-html-suite.test.js.snap
+++ b/packages/zmarkdown/__tests__/__snapshots__/new-html-suite.test.js.snap
@@ -36,8 +36,8 @@ exports[`pedantic mode disabled unordered lists markers 1`] = `
 </ul>"
 `;
 
-exports[`smileys translates >_< 1`] = `"<p>This is funny <img src=\\"/static/smileys/pinch.png\\" alt=\\">_<\\" class=\\"smiley\\"></p>"`;
+exports[`smileys translates >_< 1`] = `"<p>This is funny <img src=\\"/static/smileys/svg/pinch.svg\\" alt=\\">_<\\" class=\\"smiley\\"></p>"`;
 
-exports[`smileys translates X/ 1`] = `"<p>This is funny <img src=\\"/static/smileys/pinch.png\\" alt=\\"X/\\" class=\\"smiley\\"></p>"`;
+exports[`smileys translates X/ 1`] = `"<p>This is funny <img src=\\"/static/smileys/svg/pinch.svg\\" alt=\\"X/\\" class=\\"smiley\\"></p>"`;
 
-exports[`smileys translates cthulhu 1`] = `"<p><img src=\\"/static/smileys/cthulhu.png\\" alt=\\"^(;,;)^\\" class=\\"smiley\\"></p>"`;
+exports[`smileys translates cthulhu 1`] = `"<p><img src=\\"/static/smileys/svg/cthulhu.svg\\" alt=\\"^(;,;)^\\" class=\\"smiley\\"></p>"`;
diff --git a/packages/zmarkdown/__tests__/fixtures/zds/extensions/emoticons.html b/packages/zmarkdown/__tests__/fixtures/zds/extensions/emoticons.html
index 25015f8a..75ab50b4 100644
--- a/packages/zmarkdown/__tests__/fixtures/zds/extensions/emoticons.html
+++ b/packages/zmarkdown/__tests__/fixtures/zds/extensions/emoticons.html
@@ -1,8 +1,8 @@
-<p>Lolilol <img alt=":)" src="/static/smileys/smile.png" class="smiley"> Hey <img alt=":D" src="/static/smileys/heureux.png" class="smiley"></p>
-<p><img alt=":)" src="/static/smileys/smile.png" class="smiley"></p>
+<p>Lolilol <img alt=":)" src="/static/smileys/svg/smile.svg" class="smiley"> Hey <img alt=":D" src="/static/smileys/svg/heureux.svg" class="smiley"></p>
+<p><img alt=":)" src="/static/smileys/svg/smile.svg" class="smiley"></p>
 <blockquote>
 <p>Citation</p>
 </blockquote>
-<p><img alt=":D" src="/static/smileys/heureux.png" class="smiley"> Ce n'est pas une légende</p>
+<p><img alt=":D" src="/static/smileys/svg/heureux.svg" class="smiley"> Ce n'est pas une légende</p>
 <figure><img src="https://zestedesavoir.com/media/galleries/3014/bee33fae-2216-463a-8b85-d1d9efe2c374.png" alt="toto"><figcaption>toto</figcaption></figure>
-<p><img alt=":D" src="/static/smileys/heureux.png" class="smiley"> ce n'est pas une légende non plus</p>
+<p><img alt=":D" src="/static/smileys/svg/heureux.svg" class="smiley"> ce n'est pas une légende non plus</p>