├── .gitignore ├── .npmignore ├── test ├── _import.mjs ├── _require.cjs └── index.test.js ├── .editorconfig ├── lazy-example.js ├── .github └── workflows │ └── ci.yml ├── LICENSE ├── package.json ├── README.md └── src └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .github 2 | node_modules 3 | .DS_Store 4 | .editorconfig 5 | -------------------------------------------------------------------------------- /test/_import.mjs: -------------------------------------------------------------------------------- 1 | import assert from 'assert'; 2 | import plugin from 'markdown-it-image-figures'; 3 | 4 | assert.equal(typeof plugin, 'function', 'should return a function'); 5 | -------------------------------------------------------------------------------- /test/_require.cjs: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const plugin = require('markdown-it-image-figures'); 3 | 4 | assert.equal(typeof plugin, 'function', 'should return a function'); 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | indent_style = space 7 | indent_size = 2 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /lazy-example.js: -------------------------------------------------------------------------------- 1 | /* global lozad */ 2 | /* eslint-env browser */ 3 | (function (useNative, selector) { 4 | // Lazy Loading supported 5 | if (useNative && 'loading' in HTMLImageElement.prototype) { 6 | const lazyEls = document.querySelectorAll(`.${selector}`); 7 | 8 | lazyEls.forEach((lazyEl) => { 9 | lazyEl.setAttribute('src', lazyEl.getAttribute('data-src')); 10 | }); 11 | } else { 12 | const observer = lozad(`.${selector}`); 13 | observer.observe(); 14 | } 15 | }(true, 'lazy')); 16 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | 5 | concurrency: 6 | group: branch-node-${{ github.ref }} 7 | cancel-in-progress: true 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | node: [ '12', '14', '16' ] 15 | name: Node ${{ matrix.node }} sample 16 | steps: 17 | - uses: actions/checkout@v3 18 | - name: Setup node 19 | uses: actions/setup-node@v3 20 | with: 21 | node-version: ${{ matrix.node }} 22 | - run: npm ci --ignore-scripts 23 | - run: npm run build 24 | - run: npm test 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Antonio Laguna 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "markdown-it-image-figures", 3 | "version": "2.0.2", 4 | "description": "Render images occurring by itself in a paragraph as a figure with support for figcaptions.", 5 | "license": "MIT", 6 | "repository": "git+https://github.com/Antonio-Laguna/markdown-it-image-figures.git", 7 | "author": { 8 | "name": "Antonio Laguna", 9 | "email": "antonio@laguna.es", 10 | "url": "https://antonio.laguna.es" 11 | }, 12 | "engines": { 13 | "node": ">=12.0.0" 14 | }, 15 | "source": "src/index.js", 16 | "type": "module", 17 | "exports": { 18 | "import": "./dist/markdown-it-images-figures.mjs", 19 | "require": "./dist/markdown-it-images-figures.cjs", 20 | "default": "./dist/markdown-it-images-figures.module.js" 21 | }, 22 | "main": "./dist/markdown-it-images-figures.cjs", 23 | "module": "./dist/markdown-it-images-figures.module.js", 24 | "unpkg": "./dist/markdown-it-images-figures.umd.js", 25 | "scripts": { 26 | "build": "microbundle --target node --compress --sourcemap false", 27 | "clean": "node -e \"fs.rmSync('./dist', { recursive: true, force: true });\"", 28 | "dev": "microbundle watch", 29 | "test": "mocha ./test/index.test.js && npm run test:exports", 30 | "test:exports": "node ./test/_import.mjs && node ./test/_require.cjs", 31 | "lint": "eslint src/*.js lazy-example.js", 32 | "prepublishOnly": "npm run clean && npm run build && npm run lint && npm run test" 33 | }, 34 | "files": [ 35 | "src/index.js", 36 | "LICENSE", 37 | "README.md", 38 | "dist" 39 | ], 40 | "homepage": "https://github.com/Antonio-Laguna/markdown-it-image-figures", 41 | "keywords": [ 42 | "markdown-it", 43 | "markdown-it-plugin", 44 | "img", 45 | "figure", 46 | "lazy", 47 | "image" 48 | ], 49 | "eslintConfig": { 50 | "extends": [ 51 | "firstandthird", 52 | "plugin:mocha/recommended" 53 | ], 54 | "plugins": [ 55 | "mocha" 56 | ], 57 | "env": { 58 | "browser": false, 59 | "mocha": true, 60 | "node": true, 61 | "es6": true 62 | } 63 | }, 64 | "peerDependencies": { 65 | "markdown-it": "*" 66 | }, 67 | "devDependencies": { 68 | "babel-eslint": "^10.1.0", 69 | "eslint": "^8.12.0", 70 | "eslint-config-firstandthird": "^6.0.3", 71 | "eslint-plugin-import": "^2.25.4", 72 | "eslint-plugin-mocha": "^10.0.3", 73 | "markdown-it": "*", 74 | "markdown-it-attrs": "*", 75 | "microbundle": "^0.14.2", 76 | "mocha": "^9.2.2" 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Markdown IT Image Figures 2 | 3 | NPM version 4 | 5 | Render images occurring by itself in a paragraph as `
`, similar to [pandoc's implicit figures](http://pandoc.org/README.html#images). 6 | 7 | This module is a fork from [markdown-it-implicit-figures](https://github.com/arve0/markdown-it-implicit-figures) in which I wanted to introduce new features and make sure this was up to what the standard is today. 8 | 9 | Example input: 10 | ```md 11 | text with ![](img.png) 12 | 13 | ![](fig.png) 14 | 15 | works with links too: 16 | 17 | [![](fig.png)](page.html) 18 | ``` 19 | 20 | Output: 21 | ```html 22 |

text with

23 |
24 |

works with links too:

25 |
26 | ``` 27 | 28 | 29 | ## Install 30 | 31 | ``` 32 | $ npm i markdown-it-image-figures 33 | ``` 34 | 35 | ## Usage 36 | 37 | ```js 38 | const md = require('markdown-it')(); 39 | const implicitFigures = require('markdown-it-image-figures'); 40 | 41 | md.use(implicitFigures); 42 | 43 | const src = 'text with ![](img.png)\n\n![](fig.png)\n\nanother paragraph'; 44 | const res = md.render(src); 45 | 46 | console.log(res); 47 | 48 | /* 49 |

text with

50 |
51 |

another paragraph

52 | */ 53 | ``` 54 | 55 | ### Options 56 | 57 | - `dataType`: Set `dataType` to `true` to declare the `data-type` being wrapped, 58 | e.g.: `
`. This can be useful for applying a special 59 | styling for different kind of figures. 60 | 61 | - `figcaption`: Set `figcaption` to `true` to use the title as a `
` block after the image. E.g.: `![This is an alt](fig.png "This is a caption")` renders to 62 | 63 | ```html 64 |
65 | This is an alt 66 |
This is a caption
67 |
68 | ``` 69 | 70 | - `tabindex`: Set `tabindex` to `true` to add a `tabindex` property to each figure, beginning at `tabindex="1"` and incrementing for each figure encountered. Could be used with [this css-trick](https://css-tricks.com/expanding-images-html5/), which expands figures upon mouse-over. 71 | 72 | - `link`: Put a link around the image if there is none yet. 73 | 74 | - `copyAttrs`: Copy attributes matching (RegExp or string) `copyAttrs` to `figure` element. 75 | 76 | - `lazy`: Applies the `loading` attribute as `lazy`. 77 | 78 | - `removeSrc`: Removes the source from the image and saves it on `data-src`. 79 | 80 | Code like `![alt](fig.png)` renders to: 81 | 82 | ````html 83 |
84 | alt 85 |
86 | ```` 87 | 88 | You can override it for a single image with something like `![alt](fig.png){loading=eager}` which will generate the following markup: 89 | 90 | ````html 91 |
92 | alt 93 |
94 | ```` 95 | 96 | - `classes`: Adds the classes to the list of classes the image might have. 97 | 98 | - `async`: Adds the attribute `decoding="async"` to all images. As with `lazy` you should be able to undo this for singular images `![alt](fig.png){decoding=auto}` 99 | 100 | ## Web performance recommended settings 101 | 102 | Recommended settings for web performance is as follows 103 | 104 | ``` 105 | { 106 | lazy: true, 107 | async: true 108 | } 109 | ``` 110 | 111 | Which will add `loading="lazy"` and `decoding="async"` to all images. This can be changed per image as explained above so you can opt out for a image at the top if you'd like. This will work great for the majority of the browsers. 112 | 113 | However, if you need to broad your browser support and ensure that old browsers get lazy loaded images, you should probably use this setting: 114 | 115 | ```js 116 | md.use(implicitFigures, { 117 | lazy: true, 118 | removeSrc: true, 119 | async: true, 120 | classes: 'lazy' 121 | }); 122 | 123 | const src = '![alt](fig.png)'; 124 | const res = md.render(src); 125 | 126 | console.log(res); 127 | /* 128 |
129 | alt 130 |
131 | */ 132 | ``` 133 | 134 | Then you need to load something like [Lozad.js](https://github.com/ApoorvSaxena/lozad.js) and some script like [this](./lazy-example.js). You might want to customise the class on the attribute `classes` which get added to the `img` (for easy selector). 135 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function removeAttributeFromList(attrs, attribute) { 4 | const arr = Array.isArray(attrs) ? attrs : []; 5 | 6 | return arr.filter(([k]) => k !== attribute); 7 | } 8 | 9 | function removeAttributeFromImage(image, attribute) { 10 | if (image && image.attrs) { 11 | image.attrs = removeAttributeFromList(image.attrs, attribute); 12 | } 13 | } 14 | 15 | export default function imageFiguresPlugin(md, options) { 16 | options = options || {}; 17 | 18 | function imageFigures(state) { 19 | // reset tabIndex on md.render() 20 | let tabIndex = 1; 21 | 22 | // do not process first and last token 23 | for (let i = 1, l = state.tokens.length; i < (l - 1); ++i) { 24 | const token = state.tokens[i]; 25 | 26 | if (token.type !== 'inline') { 27 | continue; 28 | } 29 | // children: image alone, or link_open -> image -> link_close 30 | if (!token.children || (token.children.length !== 1 && token.children.length !== 3)) { 31 | continue; 32 | } 33 | // one child, should be img 34 | if (token.children.length === 1 && token.children[0].type !== 'image') { 35 | continue; 36 | } 37 | // three children, should be image enclosed in link 38 | if (token.children.length === 3) { 39 | const [childrenA, childrenB, childrenC] = token.children; 40 | const isEnclosed = childrenA.type !== 'link_open' || 41 | childrenB.type !== 'image' || 42 | childrenC.type !== 'link_close'; 43 | 44 | if (isEnclosed) { 45 | continue; 46 | } 47 | } 48 | // prev token is paragraph open 49 | if (i !== 0 && state.tokens[i - 1].type !== 'paragraph_open') { 50 | continue; 51 | } 52 | // next token is paragraph close 53 | if (i !== (l - 1) && state.tokens[i + 1].type !== 'paragraph_close') { 54 | continue; 55 | } 56 | 57 | // We have inline token containing an image only. 58 | // Previous token is paragraph open. 59 | // Next token is paragraph close. 60 | // Lets replace the paragraph tokens with figure tokens. 61 | const figure = state.tokens[i - 1]; 62 | figure.type = 'figure_open'; 63 | figure.tag = 'figure'; 64 | state.tokens[i + 1].type = 'figure_close'; 65 | state.tokens[i + 1].tag = 'figure'; 66 | 67 | if (options.dataType) { 68 | state.tokens[i - 1].attrPush(['data-type', 'image']); 69 | } 70 | let image; 71 | 72 | if (options.link && token.children.length === 1) { 73 | [image] = token.children; 74 | const link = new state.Token('link_open', 'a', 1); 75 | link.attrPush(['href', image.attrGet('src')]); 76 | 77 | token.children.unshift(link); 78 | token.children.push(new state.Token('link_close', 'a', -1)); 79 | } 80 | 81 | // for linked images, image is one off 82 | image = token.children.length === 1 ? token.children[0] : token.children[1]; 83 | 84 | if (options.figcaption) { 85 | let figCaption; 86 | const captionObj = image.attrs.find(([k]) => k === 'title'); 87 | 88 | if (Array.isArray(captionObj)) { 89 | figCaption = captionObj[1]; 90 | } 91 | 92 | if (figCaption) { 93 | const [captionContent] = md.parseInline(figCaption, state.env); 94 | token.children.push( 95 | new state.Token('figcaption_open', 'figcaption', 1) 96 | ); 97 | token.children.push(...captionContent.children); 98 | token.children.push( 99 | new state.Token('figcaption_close', 'figcaption', -1) 100 | ); 101 | 102 | if (image.attrs) { 103 | image.attrs = removeAttributeFromList(image.attrs, 'title'); 104 | } 105 | } 106 | } 107 | 108 | if (options.copyAttrs && image.attrs) { 109 | const f = options.copyAttrs === true ? '' : options.copyAttrs; 110 | // Copying so any further changes aren't duplicated 111 | figure.attrs = image.attrs 112 | .filter(([k]) => k.match(f)) 113 | .map(a => Array.from(a)); 114 | } 115 | 116 | if (options.tabindex) { 117 | // add a tabindex property 118 | // you could use this with css-tricks.com/expanding-images-html5 119 | state.tokens[i - 1].attrPush(['tabindex', tabIndex]); 120 | tabIndex++; 121 | } 122 | 123 | if (options.lazy) { 124 | const hasLoading = image.attrs.some(([attribute]) => attribute === 'loading'); 125 | 126 | if (!hasLoading) { 127 | image.attrs.push(['loading', 'lazy']); 128 | } 129 | } 130 | 131 | if (options.async) { 132 | const hasDecoding = image.attrs.some(([attribute]) => attribute === 'decoding'); 133 | 134 | if (!hasDecoding) { 135 | image.attrs.push(['decoding', 'async']); 136 | } 137 | } 138 | 139 | if (options.classes && typeof options.classes === 'string') { 140 | let hasClass = false; 141 | 142 | for (let j = 0, length = image.attrs.length; j < length && !hasClass; j++) { 143 | const attrPair = image.attrs[j]; 144 | 145 | if (attrPair[0] === 'class') { 146 | attrPair[1] = `${attrPair[1]} ${options.classes}`; 147 | hasClass = true; 148 | } 149 | } 150 | 151 | if (!hasClass) { 152 | image.attrs.push(['class', options.classes]); 153 | } 154 | } 155 | 156 | if (options.removeSrc) { 157 | const src = image.attrs.find(([k]) => k === 'src'); 158 | image.attrs.push(['data-src', src[1]]); 159 | removeAttributeFromImage(image, 'src'); 160 | } 161 | } 162 | } 163 | 164 | md.core.ruler.before('linkify', 'image_figures', imageFigures); 165 | } 166 | -------------------------------------------------------------------------------- /test/index.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable prefer-arrow-callback */ 2 | 'use strict'; 3 | import assert from 'assert'; 4 | import attrs from 'markdown-it-attrs'; 5 | import mdIT from 'markdown-it'; 6 | import implicitFigures from '../dist/markdown-it-images-figures.mjs'; 7 | 8 | describe('markdown-it-image-figures', function() { 9 | let md; 10 | beforeEach(function() { 11 | md = mdIT().use(implicitFigures); 12 | }); 13 | 14 | it('should add
when image is by itself in a paragraph', function() { 15 | const src = 'text with ![](img.png)\n\n![](fig.png)\n\nanother paragraph'; 16 | const expected = '

text with

\n
\n

another paragraph

\n'; 17 | const res = md.render(src); 18 | assert.strictEqual(res, expected); 19 | }); 20 | 21 | it('should add
when image is by itself in a paragraph and preceeded by a standalone link', function() { 22 | md = mdIT().use(implicitFigures, { dataType: true, figcaption: true }); 23 | const src = '[![](fig.png "Caption")](http://example.com)'; 24 | const expected = '
Caption
\n'; 25 | const res = md.render(src); 26 | assert.strictEqual(res, expected); 27 | }); 28 | 29 | it('should add data-type=image to figures when opts.dataType is set', function() { 30 | md = mdIT().use(implicitFigures, { dataType: true }); 31 | const src = '![](fig.png)\n'; 32 | const expected = '
\n'; 33 | const res = md.render(src); 34 | assert.strictEqual(res, expected); 35 | }); 36 | 37 | it('should add convert alt text into a figcaption when opts.figcaption is set', function() { 38 | md = mdIT().use(implicitFigures, { figcaption: true }); 39 | const src = '![This is an alt](fig.png "This is a caption")'; 40 | const expected = '
This is an alt
This is a caption
\n'; 41 | const res = md.render(src); 42 | assert.strictEqual(res, expected); 43 | }); 44 | 45 | it('should convert alt text for each image into a figcaption when opts.figcaption is set', function() { 46 | md = mdIT().use(implicitFigures, { figcaption: true }); 47 | const src = '![alt 1](fig.png "caption 1")\n\n![alt 2](fig2.png "caption 2")'; 48 | const expected = '
alt 1
caption 1
\n
alt 2
caption 2
\n'; 49 | const res = md.render(src); 50 | assert.strictEqual(res, expected); 51 | }); 52 | 53 | it('should add incremental tabindex to figures when opts.tabindex is set', function() { 54 | md = mdIT().use(implicitFigures, { tabindex: true }); 55 | const src = '![](fig.png)\n\n![](fig2.png)'; 56 | const expected = '
\n
\n'; 57 | const res = md.render(src); 58 | assert.strictEqual(res, expected); 59 | }); 60 | 61 | it('should reset tabindex on each md.render()', function() { 62 | md = mdIT().use(implicitFigures, { tabindex: true }); 63 | const src = '![](fig.png)\n\n![](fig2.png)'; 64 | const expected = '
\n
\n'; 65 | let res = md.render(src); 66 | assert.strictEqual(res, expected); 67 | // render again, should produce same if resetting 68 | res = md.render(src); 69 | assert.strictEqual(res, expected); 70 | }); 71 | 72 | it('should not make figures of paragraphs with text and inline code', function() { 73 | const src = 'Text.\n\nAnd `code`.'; 74 | const expected = '

Text.

\n

And code.

\n'; 75 | const res = md.render(src); 76 | assert.strictEqual(res, expected); 77 | }); 78 | 79 | it('should not make figures of paragraphs with links only', function() { 80 | const src = '[link](page.html)'; 81 | const expected = '

link

\n'; 82 | const res = md.render(src); 83 | assert.strictEqual(res, expected); 84 | }); 85 | 86 | it('should linkify captions', function() { 87 | md = mdIT({ linkify: true }).use(implicitFigures, { figcaption: true }); 88 | const src = '![](fig.png "www.google.com")'; 89 | const expected = '
www.google.com
\n'; 90 | const res = md.render(src); 91 | assert.strictEqual(res, expected); 92 | }); 93 | 94 | it('should work with markdown-it-attrs', function() { 95 | md = mdIT().use(attrs).use(implicitFigures); 96 | const src = '![](fig.png){.asdf}'; 97 | const expected = '
\n'; 98 | const res = md.render(src); 99 | assert.strictEqual(res, expected); 100 | }); 101 | 102 | it('should put the image inside a link to the image if it is not yet linked', function() { 103 | md = mdIT().use(implicitFigures, { link: true }); 104 | const src = '![www.google.com](fig.png)'; 105 | const expected = '
www.google.com
\n'; 106 | const res = md.render(src); 107 | assert.strictEqual(res, expected); 108 | }); 109 | 110 | it('should not mess up figcaption when linking', function() { 111 | md = mdIT().use(implicitFigures, { figcaption: true, link: true }); 112 | const src = '![](fig.png "www.google.com")'; 113 | const expected = '
www.google.com
\n'; 114 | const res = md.render(src); 115 | assert.strictEqual(res, expected); 116 | }); 117 | 118 | it('should leave the image inside a link (and not create an extra one) if it is already linked', function() { 119 | md = mdIT().use(implicitFigures, { link: true }); 120 | const src = '[![www.google.com](fig.png)](link.html)'; 121 | const expected = '
www.google.com
\n'; 122 | const res = md.render(src); 123 | assert.strictEqual(res, expected); 124 | }); 125 | 126 | it('should keep structured markup inside caption (event if not supported in "alt" attribute)', function() { 127 | md = mdIT().use(implicitFigures, { figcaption: true }); 128 | const src = '![](fig.png "Image from [source](to)")'; 129 | const expected = '
Image from source
\n'; 130 | const res = md.render(src); 131 | assert.strictEqual(res, expected); 132 | }); 133 | 134 | it('should copy attributes from img to figure tag', function() { 135 | md = mdIT().use(attrs).use(implicitFigures, { copyAttrs: '^class$' }); 136 | const src = '![alt](fig.png){.cls attr=val}'; 137 | const expected = '
alt
\n'; 138 | const res = md.render(src); 139 | assert.strictEqual(res, expected); 140 | }); 141 | 142 | it('should support lazy loading', function() { 143 | md = mdIT().use(attrs).use(implicitFigures, { lazy: true }); 144 | const src = '![alt](fig.png)'; 145 | const expected = '
alt
\n'; 146 | const res = md.render(src); 147 | assert.strictEqual(res, expected); 148 | }); 149 | 150 | it('should be possible to override lazy', function() { 151 | md = mdIT().use(attrs).use(implicitFigures, { lazy: true }); 152 | const src = '![alt](fig.png){loading=eager}'; 153 | const expected = '
alt
\n'; 154 | const res = md.render(src); 155 | assert.strictEqual(res, expected); 156 | }); 157 | 158 | it('should support async decoding', function() { 159 | md = mdIT().use(attrs).use(implicitFigures, { async: true }); 160 | const src = '![alt](fig.png)'; 161 | const expected = '
alt
\n'; 162 | const res = md.render(src); 163 | assert.strictEqual(res, expected); 164 | }); 165 | 166 | it('should be possible to override decoding', function() { 167 | md = mdIT().use(attrs).use(implicitFigures, { async: true }); 168 | const src = '![alt](fig.png){decoding=sync}'; 169 | const expected = '
alt
\n'; 170 | const res = md.render(src); 171 | assert.strictEqual(res, expected); 172 | }); 173 | 174 | it('should support removing source', function() { 175 | md = mdIT().use(attrs).use(implicitFigures, { removeSrc: true }); 176 | const src = '![alt](fig.png)'; 177 | const expected = '
alt
\n'; 178 | const res = md.render(src); 179 | assert.strictEqual(res, expected); 180 | }); 181 | 182 | it('should support adding classes', function() { 183 | md = mdIT().use(attrs).use(implicitFigures, { classes: 'one two' }); 184 | const src = '![alt](fig.png)'; 185 | const expected = '
alt
\n'; 186 | const res = md.render(src); 187 | assert.strictEqual(res, expected); 188 | }); 189 | 190 | it('should support copying classes', function() { 191 | md = mdIT().use(attrs).use(implicitFigures, { copyAttrs: '^class$', classes: 'one two' }); 192 | const src = '![alt](fig.png){.cls attr=val}'; 193 | const expected = '
alt
\n'; 194 | const res = md.render(src); 195 | assert.strictEqual(res, expected); 196 | }); 197 | }); 198 | --------------------------------------------------------------------------------