Преглед на файлове

feat(image dimensions): add support for setting image dimensions within markdown syntax

This feature allows users to define the image dimensions using markdown syntax:
```
![my image](img.jpg =100x80 "image title")
```
To enable this feature, use the option `parseImgDimensions`.

Closes #143
Estevão Soares dos Santos преди 10 години
родител
ревизия
af82c2b616

+ 10 - 2
README.md

@@ -139,7 +139,7 @@ var thisConverterSpecificOptions = conveter.getOptions();
 
 ### Valid Options
 
- * **omitExtraWLInCodeBlocks**: (boolean) Omits the trailing newline in a code block. Ex:
+ * **omitExtraWLInCodeBlocks**: (boolean) [default false] Omits the trailing newline in a code block. Ex:
    
     This:
     ```html
@@ -151,8 +151,16 @@ var thisConverterSpecificOptions = conveter.getOptions();
     <code><pre>var foo = 'bar';</pre></code>
     ```
 
- * **prefixHeaderId**: (string/boolean) Adds a prefix to the generated header ids. Passing a string will prefix that string to the header id. Setting to `true` will add a generic 'section' prefix.
+ * **noHeaderId**: (boolean) [default false] Disables the automatic generation of header ids. Setting to true overrides **prefixHeaderId**
 
+ * **prefixHeaderId**: (string/boolean) [default false] Adds a prefix to the generated header ids. Passing a string will prefix that string to the header id. Setting to `true` will add a generic 'section' prefix.
+ 
+ * **parseImgDimensions**: (boolean) [default false] Enables support for setting image dimensions from within markdown syntax.
+   Example:
+   ```
+   ![my image](foo.jpg =100x80)
+   ```
+   
 
 ## Integration with AngularJS
 

+ 48 - 64
dist/showdown.js

@@ -1,4 +1,4 @@
-;/*! showdown 15-06-2015 */
+;/*! showdown 16-06-2015 */
 (function(){
 /**
  * Created by Tivie on 06-01-2015.
@@ -11,7 +11,8 @@ var showdown = {},
     defaultOptions = {
       omitExtraWLInCodeBlocks: false,
       prefixHeaderId:          false,
-      noHeaderId:              false
+      noHeaderId:              false,
+      parseImgDimensions:      false
     },
     globalOptions = JSON.parse(JSON.stringify(defaultOptions)); //clone default options out of laziness =P
 
@@ -597,6 +598,7 @@ showdown.Converter = function (converterOptions) {
       gHtmlBlocks:     [],
       gUrls:           {},
       gTitles:         {},
+      gDimensions:     {},
       gListLevel:      0,
       hashLinkCounts:  {},
       langExtensions:  langExtensions,
@@ -1522,15 +1524,16 @@ showdown.subParser('headers', function (text, options, globals) {
 showdown.subParser('images', function (text, options, globals) {
   'use strict';
 
-  var writeImageTag = function (wholeMatch, m1, m2, m3, m4, m5, m6, m7) {
+  var inlineRegExp    = /!\[(.*?)]\s?\([ \t]*()<?(\S+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(['"])(.*?)\6[ \t]*)?\)/g,
+      referenceRegExp = /!\[(.*?)][ ]?(?:\n[ ]*)?\[(.*?)]()()()()()/g;
 
-    wholeMatch = m1;
-    var altText = m2,
-        linkId = m3.toLowerCase(),
-        url = m4,
-        title = m7,
-        gUrls = globals.gUrls,
-        gTitles = globals.gTitles;
+  function writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title) {
+
+    var gUrls   = globals.gUrls,
+        gTitles = globals.gTitles,
+        gDims   = globals.gDimensions;
+
+    linkId = linkId.toLowerCase();
 
     if (!title) {
       title = '';
@@ -1543,11 +1546,15 @@ showdown.subParser('images', function (text, options, globals) {
       }
       url = '#' + linkId;
 
-      if (typeof gUrls[linkId] !== 'undefined') {
+      if (!showdown.helper.isUndefined(gUrls[linkId])) {
         url = gUrls[linkId];
-        if (typeof gTitles[linkId] !== 'undefined') {
+        if (!showdown.helper.isUndefined(gTitles[linkId])) {
           title = gTitles[linkId];
         }
+        if (!showdown.helper.isUndefined(gDims[linkId])) {
+          width = gDims[linkId].width;
+          height = gDims[linkId].height;
+        }
       } else {
         return wholeMatch;
       }
@@ -1563,55 +1570,24 @@ showdown.subParser('images', function (text, options, globals) {
       result += ' title="' + title + '"';
     }
 
+    if (width && height) {
+      width  = (width === '*') ? 'auto' : width;
+      height = (height === '*') ? 'auto' : height;
+
+      result += ' width="' + width + '"';
+      result += ' height="' + height + '"';
+    }
+
     result += ' />';
 
     return result;
-  };
+  }
 
   // First, handle reference-style labeled images: ![alt text][id]
-  /*
-   text = text.replace(/
-   (						// wrap whole match in $1
-   !\[
-   (.*?)				// alt text = $2
-   \]
+  text = text.replace(referenceRegExp, writeImageTag);
 
-   [ ]?				// one optional space
-   (?:\n[ ]*)?			// one optional newline followed by spaces
-
-   \[
-   (.*?)				// id = $3
-   \]
-   )()()()()				// pad rest of backreferences
-   /g,writeImageTag);
-   */
-  text = text.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g, writeImageTag);
-
-  // Next, handle inline images:  ![alt text](url "optional title")
-  // Don't forget: encode * and _
-  /*
-   text = text.replace(/
-   (						// wrap whole match in $1
-   !\[
-   (.*?)				// alt text = $2
-   \]
-   \s?					// One optional whitespace character
-   \(					// literal paren
-   [ \t]*
-   ()					// no id, so leave $3 empty
-   <?(\S+?)>?			// src url = $4
-   [ \t]*
-   (					// $5
-   (['"])			// quote char = $6
-   (.*?)			// title = $7
-   \6				// matching quote
-   [ \t]*
-   )?					// title is optional
-   \)
-   )
-   /g,writeImageTag);
-   */
-  text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()<?(\S+?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g, writeImageTag);
+  // Next, handle inline images:  ![alt text](url =<width>x<height> "optional title")
+  text = text.replace(inlineRegExp, writeImageTag);
 
   return text;
 });
@@ -1970,23 +1946,31 @@ showdown.subParser('stripBlankLines', function (text) {
 showdown.subParser('stripLinkDefinitions', function (text, options, globals) {
   'use strict';
 
-  var regex = /^[ ]{0,3}\[(.+)]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \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;
 
   // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
   text += '~0';
 
-  text = text.replace(regex, function (wholeMatch, m1, m2, m3, m4) {
-    m1 = m1.toLowerCase();
-    globals.gUrls[m1] = showdown.subParser('encodeAmpsAndAngles')(m2);  // Link IDs are case-insensitive
-    if (m3) {
+  text = text.replace(regex, function (wholeMatch, linkId, url, width, height, blankLines, title) {
+    linkId = linkId.toLowerCase();
+    globals.gUrls[linkId] = showdown.subParser('encodeAmpsAndAngles')(url);  // Link IDs are case-insensitive
+
+    if (blankLines) {
       // Oops, found blank lines, so it's not a title.
       // Put back the parenthetical statement we stole.
-      return m3 + m4;
+      return blankLines + title;
 
-    } else if (m4) {
-      globals.gTitles[m1] = m4.replace(/"|'/g, '&quot;');
+    } else {
+      if (title) {
+        globals.gTitles[linkId] = title.replace(/"|'/g, '&quot;');
+      }
+      if (options.parseImgDimensions && width && height) {
+        globals.gDimensions[linkId] = {
+          width:  width,
+          height: height
+        };
+      }
     }
-
     // Completely remove the definition from the text
     return '';
   });

Файловите разлики са ограничени, защото са твърде много
+ 0 - 0
dist/showdown.js.map


Файловите разлики са ограничени, защото са твърде много
+ 1 - 1
dist/showdown.min.js


Файловите разлики са ограничени, защото са твърде много
+ 0 - 0
dist/showdown.min.js.map


+ 1 - 0
src/converter.js

@@ -190,6 +190,7 @@ showdown.Converter = function (converterOptions) {
       gHtmlBlocks:     [],
       gUrls:           {},
       gTitles:         {},
+      gDimensions:     {},
       gListLevel:      0,
       hashLinkCounts:  {},
       langExtensions:  langExtensions,

+ 2 - 1
src/showdown.js

@@ -9,7 +9,8 @@ var showdown = {},
     defaultOptions = {
       omitExtraWLInCodeBlocks: false,
       prefixHeaderId:          false,
-      noHeaderId:              false
+      noHeaderId:              false,
+      parseImgDimensions:      false
     },
     globalOptions = JSON.parse(JSON.stringify(defaultOptions)); //clone default options out of laziness =P
 

+ 28 - 54
src/subParsers/images.js

@@ -4,15 +4,16 @@
 showdown.subParser('images', function (text, options, globals) {
   'use strict';
 
-  var writeImageTag = function (wholeMatch, m1, m2, m3, m4, m5, m6, m7) {
+  var inlineRegExp    = /!\[(.*?)]\s?\([ \t]*()<?(\S+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(['"])(.*?)\6[ \t]*)?\)/g,
+      referenceRegExp = /!\[(.*?)][ ]?(?:\n[ ]*)?\[(.*?)]()()()()()/g;
 
-    wholeMatch = m1;
-    var altText = m2,
-        linkId = m3.toLowerCase(),
-        url = m4,
-        title = m7,
-        gUrls = globals.gUrls,
-        gTitles = globals.gTitles;
+  function writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title) {
+
+    var gUrls   = globals.gUrls,
+        gTitles = globals.gTitles,
+        gDims   = globals.gDimensions;
+
+    linkId = linkId.toLowerCase();
 
     if (!title) {
       title = '';
@@ -25,11 +26,15 @@ showdown.subParser('images', function (text, options, globals) {
       }
       url = '#' + linkId;
 
-      if (typeof gUrls[linkId] !== 'undefined') {
+      if (!showdown.helper.isUndefined(gUrls[linkId])) {
         url = gUrls[linkId];
-        if (typeof gTitles[linkId] !== 'undefined') {
+        if (!showdown.helper.isUndefined(gTitles[linkId])) {
           title = gTitles[linkId];
         }
+        if (!showdown.helper.isUndefined(gDims[linkId])) {
+          width = gDims[linkId].width;
+          height = gDims[linkId].height;
+        }
       } else {
         return wholeMatch;
       }
@@ -45,55 +50,24 @@ showdown.subParser('images', function (text, options, globals) {
       result += ' title="' + title + '"';
     }
 
+    if (width && height) {
+      width  = (width === '*') ? 'auto' : width;
+      height = (height === '*') ? 'auto' : height;
+
+      result += ' width="' + width + '"';
+      result += ' height="' + height + '"';
+    }
+
     result += ' />';
 
     return result;
-  };
+  }
 
   // First, handle reference-style labeled images: ![alt text][id]
-  /*
-   text = text.replace(/
-   (						// wrap whole match in $1
-   !\[
-   (.*?)				// alt text = $2
-   \]
-
-   [ ]?				// one optional space
-   (?:\n[ ]*)?			// one optional newline followed by spaces
-
-   \[
-   (.*?)				// id = $3
-   \]
-   )()()()()				// pad rest of backreferences
-   /g,writeImageTag);
-   */
-  text = text.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g, writeImageTag);
-
-  // Next, handle inline images:  ![alt text](url "optional title")
-  // Don't forget: encode * and _
-  /*
-   text = text.replace(/
-   (						// wrap whole match in $1
-   !\[
-   (.*?)				// alt text = $2
-   \]
-   \s?					// One optional whitespace character
-   \(					// literal paren
-   [ \t]*
-   ()					// no id, so leave $3 empty
-   <?(\S+?)>?			// src url = $4
-   [ \t]*
-   (					// $5
-   (['"])			// quote char = $6
-   (.*?)			// title = $7
-   \6				// matching quote
-   [ \t]*
-   )?					// title is optional
-   \)
-   )
-   /g,writeImageTag);
-   */
-  text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()<?(\S+?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g, writeImageTag);
+  text = text.replace(referenceRegExp, writeImageTag);
+
+  // Next, handle inline images:  ![alt text](url =<width>x<height> "optional title")
+  text = text.replace(inlineRegExp, writeImageTag);
 
   return text;
 });

+ 17 - 9
src/subParsers/stripLinkDefinitions.js

@@ -26,23 +26,31 @@
 showdown.subParser('stripLinkDefinitions', function (text, options, globals) {
   'use strict';
 
-  var regex = /^[ ]{0,3}\[(.+)]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \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;
 
   // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
   text += '~0';
 
-  text = text.replace(regex, function (wholeMatch, m1, m2, m3, m4) {
-    m1 = m1.toLowerCase();
-    globals.gUrls[m1] = showdown.subParser('encodeAmpsAndAngles')(m2);  // Link IDs are case-insensitive
-    if (m3) {
+  text = text.replace(regex, function (wholeMatch, linkId, url, width, height, blankLines, title) {
+    linkId = linkId.toLowerCase();
+    globals.gUrls[linkId] = showdown.subParser('encodeAmpsAndAngles')(url);  // Link IDs are case-insensitive
+
+    if (blankLines) {
       // Oops, found blank lines, so it's not a title.
       // Put back the parenthetical statement we stole.
-      return m3 + m4;
+      return blankLines + title;
 
-    } else if (m4) {
-      globals.gTitles[m1] = m4.replace(/"|'/g, '&quot;');
+    } else {
+      if (title) {
+        globals.gTitles[linkId] = title.replace(/"|'/g, '&quot;');
+      }
+      if (options.parseImgDimensions && width && height) {
+        globals.gDimensions[linkId] = {
+          width:  width,
+          height: height
+        };
+      }
     }
-
     // Completely remove the definition from the text
     return '';
   });

+ 2 - 0
test/features/#143.support_image_dimensions.html

@@ -0,0 +1,2 @@
+<p><img src="./pic/pic1_50.png" alt="my image" width="100px" height="20px" /></p>
+<p><img src="./pic/pic1_50.png" alt="my image2" width="100px" height="20px" /></p>

+ 5 - 0
test/features/#143.support_image_dimensions.md

@@ -0,0 +1,5 @@
+![my image](./pic/pic1_50.png =100pxx20px)
+
+![my image2][1]
+
+[1]: ./pic/pic1_50.png =100pxx20px

+ 2 - 1
test/node/showdown.js

@@ -20,7 +20,8 @@ describe('showdown.options', function () {
       var opts = {
         omitExtraWLInCodeBlocks: false,
         prefixHeaderId:          false,
-        noHeaderId:              false
+        noHeaderId:              false,
+        parseImgDimensions:      false
       };
       expect(showdown.getDefaultOptions()).to.be.eql(opts);
     });

+ 20 - 0
test/node/testsuite.features.js

@@ -0,0 +1,20 @@
+/**
+ * Created by Estevao on 08-06-2015.
+ */
+var showdown = require('../../dist/showdown.js'),
+    bootstrap = require('../bootstrap.js'),
+    assertion = bootstrap.assertion,
+    testsuite = bootstrap.getTestSuite('test/features/');
+
+describe('makeHtml() features testsuite', function () {
+  'use strict';
+  for (var i = 0; i < testsuite.length; ++i) {
+    var converter;
+    if (testsuite[i].name === '#143.support_image_dimensions') {
+      converter = new showdown.Converter({parseImgDimensions: true});
+    } else {
+      converter = new showdown.Converter();
+    }
+    it(testsuite[i].name, assertion(testsuite[i], converter));
+  }
+});

Някои файлове не бяха показани, защото твърде много файлове са промени