'use strict' module.exports = createDirectiveHtmlExtension var decode = require('parse-entities/decode-entity') var own = {}.hasOwnProperty function createDirectiveHtmlExtension(options) { var extensions = options || {} return { enter: { directiveContainer: enterContainer, directiveContainerAttributes: enterAttributes, directiveContainerContent: enterContainerContent, directiveContainerLabel: enterLabel, directiveLeaf: enterLeaf, directiveLeafAttributes: enterAttributes, directiveLeafLabel: enterLabel, directiveText: enterText, directiveTextAttributes: enterAttributes, directiveTextLabel: enterLabel }, exit: { directiveContainer: exit, directiveContainerAttributeClassValue: exitAttributeClassValue, directiveContainerAttributeIdValue: exitAttributeIdValue, directiveContainerAttributeName: exitAttributeName, directiveContainerAttributeValue: exitAttributeValue, directiveContainerAttributes: exitAttributes, directiveContainerContent: exitContainerContent, directiveContainerFence: exitContainerFence, directiveContainerLabel: exitLabel, directiveContainerName: exitName, directiveLeaf: exit, directiveLeafAttributeClassValue: exitAttributeClassValue, directiveLeafAttributeIdValue: exitAttributeIdValue, directiveLeafAttributeName: exitAttributeName, directiveLeafAttributeValue: exitAttributeValue, directiveLeafAttributes: exitAttributes, directiveLeafLabel: exitLabel, directiveLeafName: exitName, directiveText: exit, directiveTextAttributeClassValue: exitAttributeClassValue, directiveTextAttributeIdValue: exitAttributeIdValue, directiveTextAttributeName: exitAttributeName, directiveTextAttributeValue: exitAttributeValue, directiveTextAttributes: exitAttributes, directiveTextLabel: exitLabel, directiveTextName: exitName } } function enterContainer() { return enter.call(this, 'containerDirective') } function enterLeaf() { return enter.call(this, 'leafDirective') } function enterText() { return enter.call(this, 'textDirective') } function enter(type) { var stack = this.getData('directiveStack') if (!stack) this.setData('directiveStack', (stack = [])) stack.push({type: type}) } function exitName(token) { var stack = this.getData('directiveStack') stack[stack.length - 1].name = this.sliceSerialize(token) } function enterLabel() { this.buffer() } function exitLabel() { var data = this.resume() var stack = this.getData('directiveStack') stack[stack.length - 1].label = data } function enterAttributes() { this.buffer() this.setData('directiveAttributes', []) } function exitAttributeIdValue(token) { this.getData('directiveAttributes').push([ 'id', decodeLight(this.sliceSerialize(token)) ]) } function exitAttributeClassValue(token) { this.getData('directiveAttributes').push([ 'class', decodeLight(this.sliceSerialize(token)) ]) } function exitAttributeName(token) { // Attribute names in CommonMark are significantly limited, so character // references can’t exist. this.getData('directiveAttributes').push([this.sliceSerialize(token), '']) } function exitAttributeValue(token) { var attributes = this.getData('directiveAttributes') attributes[attributes.length - 1][1] = decodeLight( this.sliceSerialize(token) ) } function exitAttributes() { var stack = this.getData('directiveStack') var attributes = this.getData('directiveAttributes') var cleaned = {} var index = -1 var attribute while (++index < attributes.length) { attribute = attributes[index] if (attribute[0] === 'class' && cleaned.class) { cleaned.class += ' ' + attribute[1] } else { cleaned[attribute[0]] = attribute[1] } } this.resume() this.setData('directiveAttributes') stack[stack.length - 1].attributes = cleaned } function enterContainerContent() { this.buffer() } function exitContainerContent() { var data = this.resume() var stack = this.getData('directiveStack') stack[stack.length - 1].content = data } function exitContainerFence() { var stack = this.getData('directiveStack') var directive = stack[stack.length - 1] if (!directive.fenceCount) directive.fenceCount = 0 directive.fenceCount++ if (directive.fenceCount === 1) this.setData('slurpOneLineEnding', true) } function exit() { var directive = this.getData('directiveStack').pop() var found var result if (own.call(extensions, directive.name)) { result = extensions[directive.name].call(this, directive) found = result !== false } if (!found && own.call(extensions, '*')) { result = extensions['*'].call(this, directive) found = result !== false } if (!found && directive.type !== 'textDirective') { this.setData('slurpOneLineEnding', true) } } } function decodeLight(value) { return value.replace( /&(#(\d{1,7}|x[\da-f]{1,6})|[\da-z]{1,31});/gi, decodeIfPossible ) } function decodeIfPossible($0, $1) { return decode($1) || $0 }