markdown-it-attrs
Advanced tools
Comparing version 2.0.0 to 2.1.0
14
index.js
'use strict'; | ||
const patterns = require('./patterns.js'); | ||
const patternsConfig = require('./patterns.js'); | ||
module.exports = function attributes(md) { | ||
const defaultOptions = { | ||
leftDelimiter: '{', | ||
rightDelimiter: '}' | ||
}; | ||
module.exports = function attributes(md, options) { | ||
if (!options) { | ||
options = defaultOptions; | ||
} | ||
const patterns = patternsConfig(options); | ||
function curlyAttrs(state) { | ||
@@ -8,0 +18,0 @@ let tokens = state.tokens; |
@@ -1,2 +0,2 @@ | ||
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.markdownItAttrs = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ | ||
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.markdownItAttrs = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ | ||
'use strict'; | ||
@@ -6,6 +6,16 @@ | ||
var patterns = require('./patterns.js'); | ||
var patternsConfig = require('./patterns.js'); | ||
module.exports = function attributes(md) { | ||
var defaultOptions = { | ||
leftDelimiter: '{', | ||
rightDelimiter: '}' | ||
}; | ||
module.exports = function attributes(md, options) { | ||
if (!options) { | ||
options = defaultOptions; | ||
} | ||
var patterns = patternsConfig(options); | ||
function curlyAttrs(state) { | ||
@@ -223,264 +233,266 @@ var tokens = state.tokens; | ||
module.exports = [{ | ||
/** | ||
* ```python {.cls} | ||
* for i in range(10): | ||
* print(i) | ||
* ``` | ||
*/ | ||
name: 'fenced code blocks', | ||
tests: [{ | ||
shift: 0, | ||
block: true, | ||
info: utils.hasCurly('end') | ||
}], | ||
transform: function transform(tokens, i) { | ||
var token = tokens[i]; | ||
var start = token.info.lastIndexOf('{'); | ||
var attrs = utils.getAttrs(token.info, start); | ||
utils.addAttrs(attrs, token); | ||
token.info = utils.removeCurly(token.info); | ||
} | ||
}, { | ||
/** | ||
* bla `click()`{.c} ![](img.png){.d} | ||
* | ||
* differs from 'inline attributes' as it does | ||
* not have a closing tag (nesting: -1) | ||
*/ | ||
name: 'inline nesting 0', | ||
tests: [{ | ||
shift: 0, | ||
type: 'inline', | ||
children: [{ | ||
shift: -1, | ||
type: function type(str) { | ||
return str === 'image' || str === 'code_inline'; | ||
} | ||
}, { | ||
module.exports = function (options) { | ||
return [{ | ||
/** | ||
* ```python {.cls} | ||
* for i in range(10): | ||
* print(i) | ||
* ``` | ||
*/ | ||
name: 'fenced code blocks', | ||
tests: [{ | ||
shift: 0, | ||
type: 'text', | ||
content: utils.hasCurly('start') | ||
}] | ||
}], | ||
transform: function transform(tokens, i, j) { | ||
var token = tokens[i].children[j]; | ||
var endChar = token.content.indexOf('}'); | ||
var attrToken = tokens[i].children[j - 1]; | ||
var attrs = utils.getAttrs(token.content, 0); | ||
utils.addAttrs(attrs, attrToken); | ||
if (token.content.length === endChar + 1) { | ||
tokens[i].children.splice(j, 1); | ||
} else { | ||
token.content = token.content.slice(endChar + 1); | ||
block: true, | ||
info: utils.hasDelimiters('end', options) | ||
}], | ||
transform: function transform(tokens, i) { | ||
var token = tokens[i]; | ||
var start = token.info.lastIndexOf(options.leftDelimiter); | ||
var attrs = utils.getAttrs(token.info, start, options); | ||
utils.addAttrs(attrs, token); | ||
token.info = utils.removeDelimiter(token.info, options); | ||
} | ||
} | ||
}, { | ||
/** | ||
* | h1 | | ||
* | -- | | ||
* | c1 | | ||
* {.c} | ||
*/ | ||
name: 'tables', | ||
tests: [{ | ||
// let this token be i, such that for-loop continues at | ||
// next token after tokens.splice | ||
shift: 0, | ||
type: 'table_close' | ||
}, { | ||
shift: 1, | ||
type: 'paragraph_open' | ||
/** | ||
* bla `click()`{.c} ![](img.png){.d} | ||
* | ||
* differs from 'inline attributes' as it does | ||
* not have a closing tag (nesting: -1) | ||
*/ | ||
name: 'inline nesting 0', | ||
tests: [{ | ||
shift: 0, | ||
type: 'inline', | ||
children: [{ | ||
shift: -1, | ||
type: function type(str) { | ||
return str === 'image' || str === 'code_inline'; | ||
} | ||
}, { | ||
shift: 0, | ||
type: 'text', | ||
content: utils.hasDelimiters('start', options) | ||
}] | ||
}], | ||
transform: function transform(tokens, i, j) { | ||
var token = tokens[i].children[j]; | ||
var endChar = token.content.indexOf(options.rightDelimiter); | ||
var attrToken = tokens[i].children[j - 1]; | ||
var attrs = utils.getAttrs(token.content, 0, options); | ||
utils.addAttrs(attrs, attrToken); | ||
if (token.content.length === endChar + 1) { | ||
tokens[i].children.splice(j, 1); | ||
} else { | ||
token.content = token.content.slice(endChar + 1); | ||
} | ||
} | ||
}, { | ||
shift: 2, | ||
type: 'inline', | ||
content: utils.hasCurly('only') | ||
}], | ||
transform: function transform(tokens, i) { | ||
var token = tokens[i + 2]; | ||
var tableOpen = utils.getMatchingOpeningToken(tokens, i); | ||
var attrs = utils.getAttrs(token.content, 0); | ||
// add attributes | ||
utils.addAttrs(attrs, tableOpen); | ||
// remove <p>{.c}</p> | ||
tokens.splice(i + 1, 3); | ||
} | ||
}, { | ||
/** | ||
* *emphasis*{.with attrs=1} | ||
*/ | ||
name: 'inline attributes', | ||
tests: [{ | ||
shift: 0, | ||
type: 'inline', | ||
children: [{ | ||
shift: -1, | ||
nesting: -1 // closing inline tag, </em>{.a} | ||
/** | ||
* | h1 | | ||
* | -- | | ||
* | c1 | | ||
* {.c} | ||
*/ | ||
name: 'tables', | ||
tests: [{ | ||
// let this token be i, such that for-loop continues at | ||
// next token after tokens.splice | ||
shift: 0, | ||
type: 'table_close' | ||
}, { | ||
shift: 1, | ||
type: 'paragraph_open' | ||
}, { | ||
shift: 2, | ||
type: 'inline', | ||
content: utils.hasDelimiters('only', options) | ||
}], | ||
transform: function transform(tokens, i) { | ||
var token = tokens[i + 2]; | ||
var tableOpen = utils.getMatchingOpeningToken(tokens, i); | ||
var attrs = utils.getAttrs(token.content, 0, options); | ||
// add attributes | ||
utils.addAttrs(attrs, tableOpen); | ||
// remove <p>{.c}</p> | ||
tokens.splice(i + 1, 3); | ||
} | ||
}, { | ||
/** | ||
* *emphasis*{.with attrs=1} | ||
*/ | ||
name: 'inline attributes', | ||
tests: [{ | ||
shift: 0, | ||
type: 'text', | ||
content: utils.hasCurly('start') | ||
}] | ||
}], | ||
transform: function transform(tokens, i, j) { | ||
var token = tokens[i].children[j]; | ||
var content = token.content; | ||
var attrs = utils.getAttrs(content, 0); | ||
var openingToken = utils.getMatchingOpeningToken(tokens[i].children, j - 1); | ||
utils.addAttrs(attrs, openingToken); | ||
token.content = content.slice(content.indexOf('}') + 1); | ||
} | ||
}, { | ||
/** | ||
* - item | ||
* {.a} | ||
*/ | ||
name: 'list softbreak', | ||
tests: [{ | ||
shift: -2, | ||
type: 'list_item_open' | ||
type: 'inline', | ||
children: [{ | ||
shift: -1, | ||
nesting: -1 // closing inline tag, </em>{.a} | ||
}, { | ||
shift: 0, | ||
type: 'text', | ||
content: utils.hasDelimiters('start', options) | ||
}] | ||
}], | ||
transform: function transform(tokens, i, j) { | ||
var token = tokens[i].children[j]; | ||
var content = token.content; | ||
var attrs = utils.getAttrs(content, 0, options); | ||
var openingToken = utils.getMatchingOpeningToken(tokens[i].children, j - 1); | ||
utils.addAttrs(attrs, openingToken); | ||
token.content = content.slice(content.indexOf(options.rightDelimiter) + 1); | ||
} | ||
}, { | ||
shift: 0, | ||
type: 'inline', | ||
children: [{ | ||
position: -2, | ||
type: 'softbreak' | ||
/** | ||
* - item | ||
* {.a} | ||
*/ | ||
name: 'list softbreak', | ||
tests: [{ | ||
shift: -2, | ||
type: 'list_item_open' | ||
}, { | ||
position: -1, | ||
content: utils.hasCurly('only') | ||
}] | ||
}], | ||
transform: function transform(tokens, i, j) { | ||
var token = tokens[i].children[j]; | ||
var content = token.content; | ||
var attrs = utils.getAttrs(content, 0); | ||
var ii = i - 2; | ||
while (tokens[ii - 1] && tokens[ii - 1].type !== 'ordered_list_open' && tokens[ii - 1].type !== 'bullet_list_open') { | ||
ii--; | ||
shift: 0, | ||
type: 'inline', | ||
children: [{ | ||
position: -2, | ||
type: 'softbreak' | ||
}, { | ||
position: -1, | ||
content: utils.hasDelimiters('only', options) | ||
}] | ||
}], | ||
transform: function transform(tokens, i, j) { | ||
var token = tokens[i].children[j]; | ||
var content = token.content; | ||
var attrs = utils.getAttrs(content, 0, options); | ||
var ii = i - 2; | ||
while (tokens[ii - 1] && tokens[ii - 1].type !== 'ordered_list_open' && tokens[ii - 1].type !== 'bullet_list_open') { | ||
ii--; | ||
} | ||
utils.addAttrs(attrs, tokens[ii - 1]); | ||
tokens[i].children = tokens[i].children.slice(0, -2); | ||
} | ||
utils.addAttrs(attrs, tokens[ii - 1]); | ||
tokens[i].children = tokens[i].children.slice(0, -2); | ||
} | ||
}, { | ||
/** | ||
* - nested list | ||
* - with double \n | ||
* {.a} <-- apply to nested ul | ||
* | ||
* {.b} <-- apply to root <ul> | ||
*/ | ||
name: 'list double softbreak', | ||
tests: [{ | ||
// let this token be i = 0 so that we can erase | ||
// the <p>{.a}</p> tokens below | ||
shift: 0, | ||
type: function type(str) { | ||
return str === 'bullet_list_close' || str === 'ordered_list_close'; | ||
} | ||
}, { | ||
shift: 1, | ||
type: 'paragraph_open' | ||
}, { | ||
shift: 2, | ||
type: 'inline', | ||
content: utils.hasCurly('only'), | ||
children: function children(arr) { | ||
return arr.length === 1; | ||
/** | ||
* - nested list | ||
* - with double \n | ||
* {.a} <-- apply to nested ul | ||
* | ||
* {.b} <-- apply to root <ul> | ||
*/ | ||
name: 'list double softbreak', | ||
tests: [{ | ||
// let this token be i = 0 so that we can erase | ||
// the <p>{.a}</p> tokens below | ||
shift: 0, | ||
type: function type(str) { | ||
return str === 'bullet_list_close' || str === 'ordered_list_close'; | ||
} | ||
}, { | ||
shift: 1, | ||
type: 'paragraph_open' | ||
}, { | ||
shift: 2, | ||
type: 'inline', | ||
content: utils.hasDelimiters('only', options), | ||
children: function children(arr) { | ||
return arr.length === 1; | ||
} | ||
}, { | ||
shift: 3, | ||
type: 'paragraph_close' | ||
}], | ||
transform: function transform(tokens, i) { | ||
var token = tokens[i + 2]; | ||
var content = token.content; | ||
var attrs = utils.getAttrs(content, 0, options); | ||
var openingToken = utils.getMatchingOpeningToken(tokens, i); | ||
utils.addAttrs(attrs, openingToken); | ||
tokens.splice(i + 1, 3); | ||
} | ||
}, { | ||
shift: 3, | ||
type: 'paragraph_close' | ||
}], | ||
transform: function transform(tokens, i) { | ||
var token = tokens[i + 2]; | ||
var content = token.content; | ||
var attrs = utils.getAttrs(content, 0); | ||
var openingToken = utils.getMatchingOpeningToken(tokens, i); | ||
utils.addAttrs(attrs, openingToken); | ||
tokens.splice(i + 1, 3); | ||
} | ||
}, { | ||
/** | ||
* - end of {.list-item} | ||
*/ | ||
name: 'list item end', | ||
tests: [{ | ||
shift: -2, | ||
type: 'list_item_open' | ||
}, { | ||
shift: 0, | ||
type: 'inline', | ||
children: [{ | ||
position: -1, | ||
content: utils.hasCurly('end') | ||
}] | ||
}], | ||
transform: function transform(tokens, i, j) { | ||
var token = tokens[i].children[j]; | ||
var content = token.content; | ||
var attrs = utils.getAttrs(content, content.lastIndexOf('{')); | ||
utils.addAttrs(attrs, tokens[i - 2]); | ||
var trimmed = content.slice(0, content.lastIndexOf('{')); | ||
token.content = last(trimmed) !== ' ' ? trimmed : trimmed.slice(0, -1); | ||
} | ||
}, { | ||
/** | ||
* something with softbreak | ||
* {.cls} | ||
*/ | ||
name: '\n{.a} softbreak then curly in start', | ||
tests: [{ | ||
shift: 0, | ||
type: 'inline', | ||
children: [{ | ||
position: -2, | ||
type: 'softbreak' | ||
/** | ||
* - end of {.list-item} | ||
*/ | ||
name: 'list item end', | ||
tests: [{ | ||
shift: -2, | ||
type: 'list_item_open' | ||
}, { | ||
position: -1, | ||
type: 'text', | ||
content: utils.hasCurly('only') | ||
}] | ||
}], | ||
transform: function transform(tokens, i, j) { | ||
var token = tokens[i].children[j]; | ||
var attrs = utils.getAttrs(token.content, 0); | ||
// find last closing tag | ||
var ii = i + 1; | ||
while (tokens[ii + 1] && tokens[ii + 1].nesting === -1) { | ||
ii++; | ||
shift: 0, | ||
type: 'inline', | ||
children: [{ | ||
position: -1, | ||
content: utils.hasDelimiters('end', options) | ||
}] | ||
}], | ||
transform: function transform(tokens, i, j) { | ||
var token = tokens[i].children[j]; | ||
var content = token.content; | ||
var attrs = utils.getAttrs(content, content.lastIndexOf(options.leftDelimiter), options); | ||
utils.addAttrs(attrs, tokens[i - 2]); | ||
var trimmed = content.slice(0, content.lastIndexOf(options.leftDelimiter)); | ||
token.content = last(trimmed) !== ' ' ? trimmed : trimmed.slice(0, -1); | ||
} | ||
var openingToken = utils.getMatchingOpeningToken(tokens, ii); | ||
utils.addAttrs(attrs, openingToken); | ||
tokens[i].children = tokens[i].children.slice(0, -2); | ||
} | ||
}, { | ||
/** | ||
* end of {.block} | ||
*/ | ||
name: 'end of block', | ||
tests: [{ | ||
shift: 0, | ||
type: 'inline', | ||
children: [{ | ||
position: -1, | ||
content: utils.hasCurly('end'), | ||
type: function type(t) { | ||
return t !== 'code_inline'; | ||
}, { | ||
/** | ||
* something with softbreak | ||
* {.cls} | ||
*/ | ||
name: '\n{.a} softbreak then curly in start', | ||
tests: [{ | ||
shift: 0, | ||
type: 'inline', | ||
children: [{ | ||
position: -2, | ||
type: 'softbreak' | ||
}, { | ||
position: -1, | ||
type: 'text', | ||
content: utils.hasDelimiters('only', options) | ||
}] | ||
}], | ||
transform: function transform(tokens, i, j) { | ||
var token = tokens[i].children[j]; | ||
var attrs = utils.getAttrs(token.content, 0, options); | ||
// find last closing tag | ||
var ii = i + 1; | ||
while (tokens[ii + 1] && tokens[ii + 1].nesting === -1) { | ||
ii++; | ||
} | ||
}] | ||
}], | ||
transform: function transform(tokens, i, j) { | ||
var token = tokens[i].children[j]; | ||
var content = token.content; | ||
var attrs = utils.getAttrs(content, content.lastIndexOf('{')); | ||
var ii = i + 1; | ||
while (tokens[ii + 1] && tokens[ii + 1].nesting === -1) { | ||
ii++; | ||
var openingToken = utils.getMatchingOpeningToken(tokens, ii); | ||
utils.addAttrs(attrs, openingToken); | ||
tokens[i].children = tokens[i].children.slice(0, -2); | ||
} | ||
var openingToken = utils.getMatchingOpeningToken(tokens, ii); | ||
utils.addAttrs(attrs, openingToken); | ||
var trimmed = content.slice(0, content.lastIndexOf('{')); | ||
token.content = last(trimmed) !== ' ' ? trimmed : trimmed.slice(0, -1); | ||
} | ||
}]; | ||
}, { | ||
/** | ||
* end of {.block} | ||
*/ | ||
name: 'end of block', | ||
tests: [{ | ||
shift: 0, | ||
type: 'inline', | ||
children: [{ | ||
position: -1, | ||
content: utils.hasDelimiters('end', options), | ||
type: function type(t) { | ||
return t !== 'code_inline'; | ||
} | ||
}] | ||
}], | ||
transform: function transform(tokens, i, j) { | ||
var token = tokens[i].children[j]; | ||
var content = token.content; | ||
var attrs = utils.getAttrs(content, content.lastIndexOf(options.leftDelimiter), options); | ||
var ii = i + 1; | ||
while (tokens[ii + 1] && tokens[ii + 1].nesting === -1) { | ||
ii++; | ||
} | ||
var openingToken = utils.getMatchingOpeningToken(tokens, ii); | ||
utils.addAttrs(attrs, openingToken); | ||
var trimmed = content.slice(0, content.lastIndexOf(options.leftDelimiter)); | ||
token.content = last(trimmed) !== ' ' ? trimmed : trimmed.slice(0, -1); | ||
} | ||
}]; | ||
}; | ||
@@ -501,4 +513,3 @@ // get last element of array or string | ||
exports.getAttrs = function (str, start, end) { | ||
// TODO: do not require `end`, stop when } is found | ||
exports.getAttrs = function (str, start, options) { | ||
// not tab, line feed, form feed, space, solidus, greater than sign, quotation mark, apostrophe and equals sign | ||
@@ -510,3 +521,3 @@ var allowedKeyChars = /[^\t\n\f />"'=]/; | ||
var idChar = '#'; | ||
var endChar = '}'; | ||
var endChar = options.rightDelimiter; | ||
@@ -567,3 +578,3 @@ var attrs = []; | ||
// read next key/value pair | ||
if (char_ === pairSeparator && !valueInsideQuotes || i === end) { | ||
if (char_ === pairSeparator && !valueInsideQuotes) { | ||
if (key === '') { | ||
@@ -626,3 +637,3 @@ // beginning or ending space: { .red } vs {.red} | ||
*/ | ||
exports.hasCurly = function (where) { | ||
exports.hasDelimiters = function (where, options) { | ||
@@ -655,4 +666,4 @@ if (!where) { | ||
// first char should be {, } found in char 2 or more | ||
start = str.charAt(0) === '{' ? 0 : -1; | ||
end = start === -1 ? -1 : str.indexOf('}', start + minCurlyLength - 1); | ||
start = str.charAt(0) === options.leftDelimiter ? 0 : -1; | ||
end = start === -1 ? -1 : str.indexOf(options.rightDelimiter, start + minCurlyLength - 1); | ||
break; | ||
@@ -662,4 +673,4 @@ | ||
// 'a{.b}' | ||
start = str.indexOf('{', 1); | ||
end = start === -1 ? -1 : str.indexOf('}', start + minCurlyLength - 1); | ||
start = str.indexOf(options.leftDelimiter, 1); | ||
end = start === -1 ? -1 : str.indexOf(options.rightDelimiter, start + minCurlyLength - 1); | ||
break; | ||
@@ -669,4 +680,4 @@ | ||
// last char should be } | ||
end = str.charAt(str.length - 1) === '}' ? str.length - 1 : -1; | ||
start = end === -1 ? -1 : str.lastIndexOf('{'); | ||
end = str.charAt(str.length - 1) === options.rightDelimiter ? str.length - 1 : -1; | ||
start = end === -1 ? -1 : str.lastIndexOf(options.leftDelimiter); | ||
break; | ||
@@ -676,4 +687,4 @@ | ||
// '{.a}' | ||
start = str.charAt(0) === '{' ? 0 : -1; | ||
end = str.charAt(str.length - 1) === '}' ? str.length - 1 : -1; | ||
start = str.charAt(0) === options.leftDelimiter ? 0 : -1; | ||
end = str.charAt(str.length - 1) === options.rightDelimiter ? str.length - 1 : -1; | ||
break; | ||
@@ -689,4 +700,7 @@ } | ||
*/ | ||
exports.removeCurly = function (str) { | ||
var curly = /[ \n]?{[^{}}]+}$/; | ||
exports.removeDelimiter = function (str, options) { | ||
var start = escapeRegExp(options.leftDelimiter); | ||
var end = escapeRegExp(options.rightDelimiter); | ||
var curly = new RegExp('[ \\n]?' + start + '[^' + start + end + ']+' + end + '$'); | ||
var pos = str.search(curly); | ||
@@ -698,2 +712,13 @@ | ||
/** | ||
* Escapes special characters in string s such that the string | ||
* can be used in `new RegExp`. For example "[" becomes "\\[". | ||
* | ||
* @param {string} s Regex string. | ||
* @return {string} Escaped string. | ||
*/ | ||
function escapeRegExp(s) { | ||
return s.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'); | ||
} | ||
/** | ||
* find corresponding opening block | ||
@@ -747,2 +772,2 @@ */ | ||
},{}]},{},[1])(1) | ||
}); | ||
}); |
{ | ||
"name": "markdown-it-attrs", | ||
"version": "2.0.0", | ||
"version": "2.1.0", | ||
"description": "Add classes, identifiers and attributes to your markdown with {} curly brackets, similar to pandoc's header attributes", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -9,3 +9,3 @@ 'use strict'; | ||
module.exports = [ | ||
module.exports = options => ([ | ||
{ | ||
@@ -23,3 +23,3 @@ /** | ||
block: true, | ||
info: utils.hasCurly('end') | ||
info: utils.hasDelimiters('end', options) | ||
} | ||
@@ -29,6 +29,6 @@ ], | ||
let token = tokens[i]; | ||
let start = token.info.lastIndexOf('{'); | ||
let attrs = utils.getAttrs(token.info, start); | ||
let start = token.info.lastIndexOf(options.leftDelimiter); | ||
let attrs = utils.getAttrs(token.info, start, options); | ||
utils.addAttrs(attrs, token); | ||
token.info = utils.removeCurly(token.info); | ||
token.info = utils.removeDelimiter(token.info, options); | ||
} | ||
@@ -54,3 +54,3 @@ }, { | ||
type: 'text', | ||
content: utils.hasCurly('start') | ||
content: utils.hasDelimiters('start', options) | ||
} | ||
@@ -62,5 +62,5 @@ ] | ||
let token = tokens[i].children[j]; | ||
let endChar = token.content.indexOf('}'); | ||
let endChar = token.content.indexOf(options.rightDelimiter); | ||
let attrToken = tokens[i].children[j - 1]; | ||
let attrs = utils.getAttrs(token.content, 0); | ||
let attrs = utils.getAttrs(token.content, 0, options); | ||
utils.addAttrs(attrs, attrToken); | ||
@@ -93,3 +93,3 @@ if (token.content.length === (endChar + 1)) { | ||
type: 'inline', | ||
content: utils.hasCurly('only') | ||
content: utils.hasDelimiters('only', options) | ||
} | ||
@@ -100,3 +100,3 @@ ], | ||
let tableOpen = utils.getMatchingOpeningToken(tokens, i); | ||
let attrs = utils.getAttrs(token.content, 0); | ||
let attrs = utils.getAttrs(token.content, 0, options); | ||
// add attributes | ||
@@ -123,3 +123,3 @@ utils.addAttrs(attrs, tableOpen); | ||
type: 'text', | ||
content: utils.hasCurly('start') | ||
content: utils.hasDelimiters('start', options) | ||
} | ||
@@ -132,6 +132,6 @@ ] | ||
let content = token.content; | ||
let attrs = utils.getAttrs(content, 0); | ||
let attrs = utils.getAttrs(content, 0, options); | ||
let openingToken = utils.getMatchingOpeningToken(tokens[i].children, j - 1); | ||
utils.addAttrs(attrs, openingToken); | ||
token.content = content.slice(content.indexOf('}') + 1); | ||
token.content = content.slice(content.indexOf(options.rightDelimiter) + 1); | ||
} | ||
@@ -157,3 +157,3 @@ }, { | ||
position: -1, | ||
content: utils.hasCurly('only') | ||
content: utils.hasDelimiters('only', options) | ||
} | ||
@@ -166,3 +166,3 @@ ] | ||
let content = token.content; | ||
let attrs = utils.getAttrs(content, 0); | ||
let attrs = utils.getAttrs(content, 0, options); | ||
let ii = i - 2; | ||
@@ -198,3 +198,3 @@ while (tokens[ii - 1] && | ||
type: 'inline', | ||
content: utils.hasCurly('only'), | ||
content: utils.hasDelimiters('only', options), | ||
children: (arr) => arr.length === 1 | ||
@@ -209,3 +209,3 @@ }, { | ||
let content = token.content; | ||
let attrs = utils.getAttrs(content, 0); | ||
let attrs = utils.getAttrs(content, 0, options); | ||
let openingToken = utils.getMatchingOpeningToken(tokens, i); | ||
@@ -230,3 +230,3 @@ utils.addAttrs(attrs, openingToken); | ||
position: -1, | ||
content: utils.hasCurly('end') | ||
content: utils.hasDelimiters('end', options) | ||
} | ||
@@ -239,5 +239,5 @@ ] | ||
let content = token.content; | ||
let attrs = utils.getAttrs(content, content.lastIndexOf('{')); | ||
let attrs = utils.getAttrs(content, content.lastIndexOf(options.leftDelimiter), options); | ||
utils.addAttrs(attrs, tokens[i - 2]); | ||
let trimmed = content.slice(0, content.lastIndexOf('{')); | ||
let trimmed = content.slice(0, content.lastIndexOf(options.leftDelimiter)); | ||
token.content = last(trimmed) !== ' ' ? | ||
@@ -263,3 +263,3 @@ trimmed : trimmed.slice(0, -1); | ||
type: 'text', | ||
content: utils.hasCurly('only') | ||
content: utils.hasDelimiters('only', options) | ||
} | ||
@@ -271,3 +271,3 @@ ] | ||
let token = tokens[i].children[j]; | ||
let attrs = utils.getAttrs(token.content, 0); | ||
let attrs = utils.getAttrs(token.content, 0, options); | ||
// find last closing tag | ||
@@ -292,3 +292,3 @@ let ii = i + 1; | ||
position: -1, | ||
content: utils.hasCurly('end'), | ||
content: utils.hasDelimiters('end', options), | ||
type: (t) => t !== 'code_inline' | ||
@@ -302,3 +302,3 @@ } | ||
let content = token.content; | ||
let attrs = utils.getAttrs(content, content.lastIndexOf('{')); | ||
let attrs = utils.getAttrs(content, content.lastIndexOf(options.leftDelimiter), options); | ||
let ii = i + 1; | ||
@@ -308,3 +308,3 @@ while (tokens[ii + 1] && tokens[ii + 1].nesting === -1) { ii++; } | ||
utils.addAttrs(attrs, openingToken); | ||
let trimmed = content.slice(0, content.lastIndexOf('{')); | ||
let trimmed = content.slice(0, content.lastIndexOf(options.leftDelimiter)); | ||
token.content = last(trimmed) !== ' ' ? | ||
@@ -314,3 +314,3 @@ trimmed : trimmed.slice(0, -1); | ||
} | ||
]; | ||
]); | ||
@@ -317,0 +317,0 @@ // get last element of array or string |
@@ -155,4 +155,19 @@ # 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) | ||
## Custom Delimiter | ||
You can configure `markdown-it-attrs` to use a different delimeter to declare attributes. | ||
```javascript | ||
md.use(attrs, { | ||
leftDelimiter: '[', | ||
rightDelimiter: ']' | ||
}); | ||
``` | ||
```markdown | ||
# Title [.large] | ||
``` | ||
## License | ||
MIT © [Arve Seljebu](http://arve0.github.io/) |
47
utils.js
@@ -8,4 +8,3 @@ 'use strict'; | ||
*/ | ||
exports.getAttrs = function (str, start, end) { | ||
// TODO: do not require `end`, stop when } is found | ||
exports.getAttrs = function (str, start, options) { | ||
// not tab, line feed, form feed, space, solidus, greater than sign, quotation mark, apostrophe and equals sign | ||
@@ -17,3 +16,3 @@ const allowedKeyChars = /[^\t\n\f />"'=]/; | ||
const idChar = '#'; | ||
const endChar = '}'; | ||
const endChar = options.rightDelimiter; | ||
@@ -72,3 +71,3 @@ const attrs = []; | ||
// read next key/value pair | ||
if ((char_ === pairSeparator && !valueInsideQuotes) || i === end) { | ||
if ((char_ === pairSeparator && !valueInsideQuotes)) { | ||
if (key === '') { | ||
@@ -131,3 +130,3 @@ // beginning or ending space: { .red } vs {.red} | ||
*/ | ||
exports.hasCurly = function (where) { | ||
exports.hasDelimiters = function (where, options) { | ||
@@ -149,3 +148,3 @@ if (!where) { | ||
function validCurlyLength(curly) { | ||
function validCurlyLength (curly) { | ||
let isClass = curly.charAt(1) === '.'; | ||
@@ -162,4 +161,4 @@ let isId = curly.charAt(1) === '#'; | ||
// first char should be {, } found in char 2 or more | ||
start = str.charAt(0) === '{' ? 0 : -1; | ||
end = start === -1 ? -1 : str.indexOf('}', start + minCurlyLength - 1); | ||
start = str.charAt(0) === options.leftDelimiter ? 0 : -1; | ||
end = start === -1 ? -1 : str.indexOf(options.rightDelimiter, start + minCurlyLength - 1); | ||
break; | ||
@@ -169,4 +168,4 @@ | ||
// 'a{.b}' | ||
start = str.indexOf('{', 1); | ||
end = start === -1 ? -1 : str.indexOf('}', start + minCurlyLength - 1); | ||
start = str.indexOf(options.leftDelimiter, 1); | ||
end = start === -1 ? -1 : str.indexOf(options.rightDelimiter, start + minCurlyLength - 1); | ||
break; | ||
@@ -176,4 +175,4 @@ | ||
// last char should be } | ||
end = str.charAt(str.length - 1) === '}' ? str.length - 1 : -1; | ||
start = end === -1 ? -1 : str.lastIndexOf('{'); | ||
end = str.charAt(str.length - 1) === options.rightDelimiter ? str.length - 1 : -1; | ||
start = end === -1 ? -1 : str.lastIndexOf(options.leftDelimiter); | ||
break; | ||
@@ -183,4 +182,4 @@ | ||
// '{.a}' | ||
start = str.charAt(0) === '{' ? 0 : -1; | ||
end = str.charAt(str.length - 1) === '}' ? str.length - 1 : -1; | ||
start = str.charAt(0) === options.leftDelimiter ? 0 : -1; | ||
end = str.charAt(str.length - 1) === options.rightDelimiter ? str.length - 1 : -1; | ||
break; | ||
@@ -196,4 +195,9 @@ } | ||
*/ | ||
exports.removeCurly = function (str) { | ||
let curly = /[ \n]?{[^{}}]+}$/; | ||
exports.removeDelimiter = function (str, options) { | ||
const start = escapeRegExp(options.leftDelimiter); | ||
const end = escapeRegExp(options.rightDelimiter); | ||
let curly = new RegExp( | ||
'[ \\n]?' + start + '[^' + start + end + ']+' + end + '$' | ||
); | ||
let pos = str.search(curly); | ||
@@ -205,2 +209,13 @@ | ||
/** | ||
* Escapes special characters in string s such that the string | ||
* can be used in `new RegExp`. For example "[" becomes "\\[". | ||
* | ||
* @param {string} s Regex string. | ||
* @return {string} Escaped string. | ||
*/ | ||
function escapeRegExp (s) { | ||
return s.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'); | ||
} | ||
/** | ||
* find corresponding opening block | ||
@@ -207,0 +222,0 @@ */ |
49140
1438
173