Browse Source

feat(base64-wrapping): support for wrapping base64 strings

Wrapping base64 strings, which are usually extremely long lines of text, is now supported.
Newlines can be added arbitrarily, as long as they appear after the comma (,) character.

Closes #429
Estevao Soares dos Santos 8 years ago
parent
commit
8c593a4f11

+ 34 - 13
dist/showdown.js

@@ -1,4 +1,4 @@
-;/*! showdown v 1.7.3 - 23-08-2017 */
+;/*! showdown v 1.7.4 - 08-09-2017 */
 (function(){
 (function(){
 /**
 /**
  * Created by Tivie on 13-07-2015.
  * Created by Tivie on 13-07-2015.
@@ -586,7 +586,7 @@ showdown.helper.isFunction = function (a) {
  */
  */
 showdown.helper.isArray = function (a) {
 showdown.helper.isArray = function (a) {
   'use strict';
   'use strict';
-  return a.constructor === Array;
+  return Array.isArray(a);
 };
 };
 
 
 /**
 /**
@@ -2246,9 +2246,15 @@ showdown.subParser('images', function (text, options, globals) {
 
 
   var inlineRegExp      = /!\[([^\]]*?)][ \t]*()\([ \t]?<?([\S]+?(?:\([\S]*?\)[\S]*?)?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(["'])([^"]*?)\6)?[ \t]?\)/g,
   var inlineRegExp      = /!\[([^\]]*?)][ \t]*()\([ \t]?<?([\S]+?(?:\([\S]*?\)[\S]*?)?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(["'])([^"]*?)\6)?[ \t]?\)/g,
       crazyRegExp       = /!\[([^\]]*?)][ \t]*()\([ \t]?<([^>]*)>(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(?:(["'])([^"]*?)\6))?[ \t]?\)/g,
       crazyRegExp       = /!\[([^\]]*?)][ \t]*()\([ \t]?<([^>]*)>(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(?:(["'])([^"]*?)\6))?[ \t]?\)/g,
-      referenceRegExp   = /!\[([^\]]*?)] ?(?:\n *)?\[(.*?)]()()()()()/g,
+      base64RegExp      = /!\[([^\]]*?)][ \t]*()\([ \t]?<?(data:.+?\/.+?;base64,[A-Za-z0-9+/=\n]+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(["'])([^"]*?)\6)?[ \t]?\)/g,
+      referenceRegExp   = /!\[([^\]]*?)] ?(?:\n *)?\[([\s\S]*?)]()()()()()/g,
       refShortcutRegExp = /!\[([^\[\]]+)]()()()()()/g;
       refShortcutRegExp = /!\[([^\[\]]+)]()()()()()/g;
 
 
+  function writeImageTagBase64 (wholeMatch, altText, linkId, url, width, height, m5, title) {
+    url = url.replace(/\s/g, '');
+    return writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title);
+  }
+
   function writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title) {
   function writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title) {
 
 
     var gUrls   = globals.gUrls,
     var gUrls   = globals.gUrls,
@@ -2318,13 +2324,17 @@ showdown.subParser('images', function (text, options, globals) {
   text = text.replace(referenceRegExp, writeImageTag);
   text = text.replace(referenceRegExp, writeImageTag);
 
 
   // Next, handle inline images:  ![alt text](url =<width>x<height> "optional title")
   // Next, handle inline images:  ![alt text](url =<width>x<height> "optional title")
+
+  // base64 encoded images
+  text = text.replace(base64RegExp, writeImageTagBase64);
+
   // cases with crazy urls like ./image/cat1).png
   // cases with crazy urls like ./image/cat1).png
   text = text.replace(crazyRegExp, writeImageTag);
   text = text.replace(crazyRegExp, writeImageTag);
 
 
   // normal cases
   // normal cases
   text = text.replace(inlineRegExp, writeImageTag);
   text = text.replace(inlineRegExp, writeImageTag);
 
 
-  // handle reference-style shortcuts: |[img text]
+  // handle reference-style shortcuts: ![img text]
   text = text.replace(refShortcutRegExp, writeImageTag);
   text = text.replace(refShortcutRegExp, writeImageTag);
 
 
   text = globals.converter._dispatch('images.after', text, options, globals);
   text = globals.converter._dispatch('images.after', text, options, globals);
@@ -2774,14 +2784,20 @@ showdown.subParser('strikethrough', function (text, options, globals) {
 showdown.subParser('stripLinkDefinitions', function (text, options, globals) {
 showdown.subParser('stripLinkDefinitions', function (text, options, globals) {
   'use strict';
   'use strict';
 
 
-  var regex = /^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*<?([^>\s]+)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=¨0))/gm;
+  var regex       = /^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*<?([^>\s]+)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=¨0))/gm,
+      base64Regex = /^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*<?(data:.+?\/.+?;base64,[A-Za-z0-9+/=\n]+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n\n|(?=¨0)|(?=\n\[))/gm;
 
 
   // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
   // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
   text += '¨0';
   text += '¨0';
 
 
-  text = text.replace(regex, function (wholeMatch, linkId, url, width, height, blankLines, title) {
+  var replaceFunc = function (wholeMatch, linkId, url, width, height, blankLines, title) {
     linkId = linkId.toLowerCase();
     linkId = linkId.toLowerCase();
-    globals.gUrls[linkId] = showdown.subParser('encodeAmpsAndAngles')(url, options, globals);  // Link IDs are case-insensitive
+    if (url.match(/^data:.+?\/.+?;base64,/)) {
+      // remove newlines
+      globals.gUrls[linkId] = url.replace(/\s/g, '');
+    } else {
+      globals.gUrls[linkId] = showdown.subParser('encodeAmpsAndAngles')(url, options, globals);  // Link IDs are case-insensitive
+    }
 
 
     if (blankLines) {
     if (blankLines) {
       // Oops, found blank lines, so it's not a title.
       // Oops, found blank lines, so it's not a title.
@@ -2801,7 +2817,12 @@ showdown.subParser('stripLinkDefinitions', function (text, options, globals) {
     }
     }
     // Completely remove the definition from the text
     // Completely remove the definition from the text
     return '';
     return '';
-  });
+  };
+
+  // first we try to find base64 link references
+  text = text.replace(base64Regex, replaceFunc);
+
+  text = text.replace(regex, replaceFunc);
 
 
   // attacklab: strip sentinel
   // attacklab: strip sentinel
   text = text.replace(/¨0/, '');
   text = text.replace(/¨0/, '');
@@ -2968,17 +2989,17 @@ showdown.subParser('unescapeSpecialChars', function (text, options, globals) {
 
 
 var root = this;
 var root = this;
 
 
-// CommonJS/nodeJS Loader
-if (typeof module !== 'undefined' && module.exports) {
-  module.exports = showdown;
-
 // AMD Loader
 // AMD Loader
-} else if (typeof define === 'function' && define.amd) {
+if (typeof define === 'function' && define.amd) {
   define(function () {
   define(function () {
     'use strict';
     'use strict';
     return showdown;
     return showdown;
   });
   });
 
 
+// CommonJS/nodeJS Loader
+} else if (typeof module !== 'undefined' && module.exports) {
+  module.exports = showdown;
+
 // Regular Browser loader
 // Regular Browser loader
 } else {
 } else {
   root.showdown = showdown;
   root.showdown = showdown;

File diff suppressed because it is too large
+ 0 - 0
dist/showdown.js.map


File diff suppressed because it is too large
+ 0 - 1
dist/showdown.min.js


File diff suppressed because it is too large
+ 0 - 0
dist/showdown.min.js.map


+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "showdown",
   "name": "showdown",
-  "version": "1.7.3",
+  "version": "1.7.4",
   "description": "A Markdown to HTML converter written in Javascript",
   "description": "A Markdown to HTML converter written in Javascript",
   "author": "Estevão Santos",
   "author": "Estevão Santos",
   "homepage": "http://showdownjs.github.io/showdown/",
   "homepage": "http://showdownjs.github.io/showdown/",

+ 12 - 2
src/subParsers/images.js

@@ -8,9 +8,15 @@ showdown.subParser('images', function (text, options, globals) {
 
 
   var inlineRegExp      = /!\[([^\]]*?)][ \t]*()\([ \t]?<?([\S]+?(?:\([\S]*?\)[\S]*?)?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(["'])([^"]*?)\6)?[ \t]?\)/g,
   var inlineRegExp      = /!\[([^\]]*?)][ \t]*()\([ \t]?<?([\S]+?(?:\([\S]*?\)[\S]*?)?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(["'])([^"]*?)\6)?[ \t]?\)/g,
       crazyRegExp       = /!\[([^\]]*?)][ \t]*()\([ \t]?<([^>]*)>(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(?:(["'])([^"]*?)\6))?[ \t]?\)/g,
       crazyRegExp       = /!\[([^\]]*?)][ \t]*()\([ \t]?<([^>]*)>(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(?:(["'])([^"]*?)\6))?[ \t]?\)/g,
-      referenceRegExp   = /!\[([^\]]*?)] ?(?:\n *)?\[(.*?)]()()()()()/g,
+      base64RegExp      = /!\[([^\]]*?)][ \t]*()\([ \t]?<?(data:.+?\/.+?;base64,[A-Za-z0-9+/=\n]+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(["'])([^"]*?)\6)?[ \t]?\)/g,
+      referenceRegExp   = /!\[([^\]]*?)] ?(?:\n *)?\[([\s\S]*?)]()()()()()/g,
       refShortcutRegExp = /!\[([^\[\]]+)]()()()()()/g;
       refShortcutRegExp = /!\[([^\[\]]+)]()()()()()/g;
 
 
+  function writeImageTagBase64 (wholeMatch, altText, linkId, url, width, height, m5, title) {
+    url = url.replace(/\s/g, '');
+    return writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title);
+  }
+
   function writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title) {
   function writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title) {
 
 
     var gUrls   = globals.gUrls,
     var gUrls   = globals.gUrls,
@@ -80,13 +86,17 @@ showdown.subParser('images', function (text, options, globals) {
   text = text.replace(referenceRegExp, writeImageTag);
   text = text.replace(referenceRegExp, writeImageTag);
 
 
   // Next, handle inline images:  ![alt text](url =<width>x<height> "optional title")
   // Next, handle inline images:  ![alt text](url =<width>x<height> "optional title")
+
+  // base64 encoded images
+  text = text.replace(base64RegExp, writeImageTagBase64);
+
   // cases with crazy urls like ./image/cat1).png
   // cases with crazy urls like ./image/cat1).png
   text = text.replace(crazyRegExp, writeImageTag);
   text = text.replace(crazyRegExp, writeImageTag);
 
 
   // normal cases
   // normal cases
   text = text.replace(inlineRegExp, writeImageTag);
   text = text.replace(inlineRegExp, writeImageTag);
 
 
-  // handle reference-style shortcuts: |[img text]
+  // handle reference-style shortcuts: ![img text]
   text = text.replace(refShortcutRegExp, writeImageTag);
   text = text.replace(refShortcutRegExp, writeImageTag);
 
 
   text = globals.converter._dispatch('images.after', text, options, globals);
   text = globals.converter._dispatch('images.after', text, options, globals);

+ 15 - 4
src/subParsers/stripLinkDefinitions.js

@@ -6,14 +6,20 @@
 showdown.subParser('stripLinkDefinitions', function (text, options, globals) {
 showdown.subParser('stripLinkDefinitions', function (text, options, globals) {
   'use strict';
   'use strict';
 
 
-  var regex = /^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*<?([^>\s]+)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=¨0))/gm;
+  var regex       = /^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*<?([^>\s]+)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=¨0))/gm,
+      base64Regex = /^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*<?(data:.+?\/.+?;base64,[A-Za-z0-9+/=\n]+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n\n|(?=¨0)|(?=\n\[))/gm;
 
 
   // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
   // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
   text += '¨0';
   text += '¨0';
 
 
-  text = text.replace(regex, function (wholeMatch, linkId, url, width, height, blankLines, title) {
+  var replaceFunc = function (wholeMatch, linkId, url, width, height, blankLines, title) {
     linkId = linkId.toLowerCase();
     linkId = linkId.toLowerCase();
-    globals.gUrls[linkId] = showdown.subParser('encodeAmpsAndAngles')(url, options, globals);  // Link IDs are case-insensitive
+    if (url.match(/^data:.+?\/.+?;base64,/)) {
+      // remove newlines
+      globals.gUrls[linkId] = url.replace(/\s/g, '');
+    } else {
+      globals.gUrls[linkId] = showdown.subParser('encodeAmpsAndAngles')(url, options, globals);  // Link IDs are case-insensitive
+    }
 
 
     if (blankLines) {
     if (blankLines) {
       // Oops, found blank lines, so it's not a title.
       // Oops, found blank lines, so it's not a title.
@@ -33,7 +39,12 @@ showdown.subParser('stripLinkDefinitions', function (text, options, globals) {
     }
     }
     // Completely remove the definition from the text
     // Completely remove the definition from the text
     return '';
     return '';
-  });
+  };
+
+  // first we try to find base64 link references
+  text = text.replace(base64Regex, replaceFunc);
+
+  text = text.replace(regex, replaceFunc);
 
 
   // attacklab: strip sentinel
   // attacklab: strip sentinel
   text = text.replace(/¨0/, '');
   text = text.replace(/¨0/, '');

+ 3 - 0
test/issues/#429.multiline-base64-image-support.html

@@ -0,0 +1,3 @@
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYSURBVBhXYwCC/2AAZYEoOAMs8Z+BgQEAXdcR7/Q1gssAAAAASUVORK5CYII=" alt="foo" /></p>
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYSURBVBhXYwCC/2AAZYEoOAMs8Z+BgQEAXdcR7/Q1gssAAAAASUVORK5CYII=" alt="bar" /></p>
+

+ 9 - 0
test/issues/#429.multiline-base64-image-support.md

@@ -0,0 +1,9 @@
+![foo](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAAAXNSR0IArs4c6QAAAARnQU1BAA
+Cxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYSURBVBhXYwCC/2AAZYEoOAMs8Z+BgQEAXdcR7/Q1gssAAAAASUVORK5CYII=)
+
+![bar][]
+
+
+[bar]:
+data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAAAXNSR0IArs4c6QAAAARnQU1BAA
+Cxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYSURBVBhXYwCC/2AAZYEoOAMs8Z+BgQEAXdcR7/Q1gssAAAAASUVORK5CYII=

Some files were not shown because too many files changed in this diff