markdown-it-attrs
Advanced tools
Comparing version 0.0.3 to 0.1.0
172
index.js
'use strict'; | ||
var utils = require('./utils.js'); | ||
module.exports = function attributes(md) { | ||
// not tab, line feed, form feed, space, solidus, greater than sign, quotation mark, apostrophe and equals sign | ||
var allowedKeyChars = /[^\t\n\f \/>"'=]/; | ||
var pairSeparator = ' '; | ||
var keySeparator = '='; | ||
var classChar = '.'; | ||
var idChar = '#'; | ||
function curlyAttrs(state){ | ||
var l = state.tokens.length; | ||
var tokens = state.tokens; | ||
for (var i = 0; i < l; i++) { | ||
var l = tokens.length; | ||
for (var i = 0; i < l; ++i) { | ||
// block tokens contain markup | ||
// inline tokens contain the text | ||
if (tokens[i].type !== 'inline') { | ||
@@ -21,90 +18,49 @@ continue; | ||
var inlineTokens = tokens[i].children; | ||
if (inlineTokens.length <= 0) { | ||
if (!inlineTokens || inlineTokens.length <= 0) { | ||
continue; | ||
} | ||
var end = inlineTokens.length - 1; | ||
var content = inlineTokens[end].content; | ||
// should end in } | ||
if (content.charAt(content.length - 1) !== '}') { | ||
continue; | ||
} | ||
var curlyStart = content.indexOf('{'); | ||
// should start with { | ||
if (curlyStart === -1) { | ||
continue; | ||
} | ||
var key = ''; | ||
var value = ''; | ||
var parsingKey = true; | ||
var valueInsideQuotes = false; | ||
// read inside {}, excluding {, including } | ||
for (var ii = curlyStart + 1; ii < content.length; ii++) { | ||
var char = content.charAt(ii); | ||
// switch to reading value if equal sign | ||
if (char === keySeparator) { | ||
parsingKey = false; | ||
// attributes in inline tokens | ||
for (var j=0, k=inlineTokens.length; j<k; ++j) { | ||
// should be inline token of type text | ||
if (!inlineTokens[j] || inlineTokens[j].type !== 'text') { | ||
continue; | ||
} | ||
// {.class} | ||
if (char === classChar && key === '') { | ||
key = 'class'; | ||
parsingKey = false; | ||
// token before should not be opening | ||
if (!inlineTokens[j - 1] || inlineTokens[j - 1].nesting === 1) { | ||
continue; | ||
} | ||
// {#id} | ||
if (char === idChar && key === '') { | ||
key = 'id'; | ||
parsingKey = false; | ||
// token should contain { in begining | ||
if (inlineTokens[j].content[0] !== '{') { | ||
continue; | ||
} | ||
// {value="inside quotes"} | ||
if (char === '"' && value === '') { | ||
valueInsideQuotes = true; | ||
// } should be found | ||
var endChar = inlineTokens[j].content.indexOf('}'); | ||
if (endChar === -1) { | ||
continue; | ||
} | ||
if (char === '"' && valueInsideQuotes) { | ||
valueInsideQuotes = false; | ||
// which token to add attributes to | ||
var attrToken = matchingOpeningToken(inlineTokens, j - 1); | ||
if (!attrToken) { | ||
continue; | ||
} | ||
// read next key/value pair | ||
if ((char === pairSeparator && !valueInsideQuotes) || | ||
char === '}') { | ||
if (key === 'class' && | ||
tokens[i - 1].attrIndex(key) !== -1) { | ||
var classIdx = tokens[i - 1].attrIndex(key); | ||
tokens[i - 1].attrs[classIdx][1] += ' ' + value; | ||
} else { | ||
tokens[i - 1].attrPush([key, value]); | ||
} | ||
key = ''; | ||
value = ''; | ||
parsingKey = true; | ||
continue; | ||
var attrs = utils.getAttrs(inlineTokens[j].content, 1, endChar); | ||
if (attrs.length !== 0) { | ||
// remove {} | ||
inlineTokens[j].content = inlineTokens[j].content.substr(endChar + 1); | ||
// add attributes | ||
utils.addAttrs(attrs, attrToken); | ||
} | ||
} | ||
// continue if character not allowed | ||
if (parsingKey && char.search(allowedKeyChars) === -1) { | ||
continue; | ||
} | ||
// no other conditions met; append to key/value | ||
if (parsingKey) { | ||
key += char; | ||
continue; | ||
} | ||
value += char; | ||
// attributes for blocks | ||
if (hasCurlyEnd(tokens[i])) { | ||
var content = last(inlineTokens).content; | ||
var curlyStart = content.lastIndexOf('{'); | ||
var attrs = utils.getAttrs(content, curlyStart + 1, content.length - 1); | ||
// some blocks are hidden, example li > paragraph_open | ||
utils.addAttrs(attrs, firstTokenNotHidden(tokens, i - 1)); | ||
last(inlineTokens).content = content.slice(0, curlyStart).trim(); | ||
} | ||
inlineTokens[end].content = content.slice(0, curlyStart).trim(); | ||
} | ||
@@ -114,1 +70,57 @@ } | ||
}; | ||
/** | ||
* test if inline token has proper formated curly end | ||
*/ | ||
function hasCurlyEnd(token) { | ||
// we need minimum four chars, example {.b} | ||
if (!token.content || token.content.length < 4) { | ||
return false; | ||
} | ||
// should end in } | ||
var content = token.content; | ||
if (content.charAt(content.length - 1) !== '}') { | ||
return false; | ||
} | ||
// should start with { | ||
var curlyStart = content.indexOf('{'); | ||
if (curlyStart === -1) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
/** | ||
* some blocks are hidden (not rendered) | ||
*/ | ||
function firstTokenNotHidden(tokens, i) { | ||
if (tokens[i].hidden) { | ||
return firstTokenNotHidden(tokens, i - 1); | ||
} | ||
return tokens[i]; | ||
} | ||
/** | ||
* find corresponding opening block | ||
*/ | ||
function matchingOpeningToken(tokens, i) { | ||
if (tokens[i].type === 'softbreak') { | ||
return false; | ||
} | ||
// non closing blocks, example img | ||
if (tokens[i].nesting === 0) { | ||
return tokens[i]; | ||
} | ||
var type = tokens[i].type.replace('_close', '_open'); | ||
for (; i >= 0; --i) { | ||
if (tokens[i].type === type) { | ||
return tokens[i]; | ||
} | ||
} | ||
} | ||
function last(arr) { | ||
return arr.slice(-1)[0]; | ||
} |
{ | ||
"name": "markdown-it-attrs", | ||
"version": "0.0.3", | ||
"version": "0.1.0", | ||
"description": "Add classes, identifiers and attributes to your markdown with {} curly brackets, similar to pandoc's header attributes", | ||
@@ -18,3 +18,3 @@ "license": "MIT", | ||
"prepublish": "mocha", | ||
"postpublish": "git push; git push --tags" | ||
"postpublish": "git tag $npm_package_version; git push --tags; git push" | ||
}, | ||
@@ -21,0 +21,0 @@ "files": [ |
@@ -17,4 +17,31 @@ # markdown-it-attrs [![Build Status](https://travis-ci.org/arve0/markdown-it-attrs.svg?branch=master)](https://travis-ci.org/arve0/markdown-it-attrs) [![npm version](https://badge.fury.io/js/markdown-it-attrs.svg)](http://badge.fury.io/js/markdown-it-attrs) | ||
Works with inline elements too: | ||
```md | ||
paragraph *style me*{.red} more text | ||
``` | ||
Output: | ||
```html | ||
<p>paragraph <em class="red">style me</me> more text</p> | ||
``` | ||
**Note:** Plugin does not validate any input, so you should validate the attributes in your html output if security is a concern. | ||
## Ambiguity | ||
When class can be applied to both inline or block element, inline element will take precedence: | ||
```md | ||
- list item **bold**{.red} | ||
``` | ||
Output: | ||
```html | ||
<ul> | ||
<li>list item <strong class="red">bold</strong></li> | ||
<ul> | ||
``` | ||
If you need finer control, look into [decorate](https://github.com/rstacruz/markdown-it-decorate). | ||
## Install | ||
@@ -21,0 +48,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
7231
111
71