module.exports = safe var patternCompile = require('./pattern-compile') var patternInScope = require('./pattern-in-scope') function safe(context, input, config) { var value = (config.before || '') + (input || '') + (config.after || '') var positions = [] var result = [] var infos = {} var index = -1 var before var after var position var pattern var expression var match var start var end while (++index < context.unsafe.length) { pattern = context.unsafe[index] if (!patternInScope(context.stack, pattern)) { continue } expression = patternCompile(pattern) while ((match = expression.exec(value))) { before = 'before' in pattern || pattern.atBreak after = 'after' in pattern position = match.index + (before ? match[1].length : 0) if (positions.indexOf(position) === -1) { positions.push(position) infos[position] = {before: before, after: after} } else { if (infos[position].before && !before) { infos[position].before = false } if (infos[position].after && !after) { infos[position].after = false } } } } positions.sort(numerical) start = config.before ? config.before.length : 0 end = value.length - (config.after ? config.after.length : 0) index = -1 while (++index < positions.length) { position = positions[index] if ( // Character before or after matched: position < start || position >= end ) { continue } // If this character is supposed to be escaped because it has a condition on // the next character, and the next character is definitly being escaped, // then skip this escape. if ( position + 1 < end && positions[index + 1] === position + 1 && infos[position].after && !infos[position + 1].before && !infos[position + 1].after ) { continue } if (start !== position) { // If we have to use a character reference, an ampersand would be more // correct, but as backslashes only care about punctuation, either will // do the trick result.push(escapeBackslashes(value.slice(start, position), '\\')) } start = position if ( /[!-/:-@[-`{-~]/.test(value.charAt(position)) && (!config.encode || config.encode.indexOf(value.charAt(position)) === -1) ) { // Character escape. result.push('\\') } else { // Character reference. result.push( '&#x' + value.charCodeAt(position).toString(16).toUpperCase() + ';' ) start++ } } result.push(escapeBackslashes(value.slice(start, end), config.after)) return result.join('') } function numerical(a, b) { return a - b } function escapeBackslashes(value, after) { var expression = /\\(?=[!-/:-@[-`{-~])/g var positions = [] var results = [] var index = -1 var start = 0 var whole = value + after var match while ((match = expression.exec(whole))) { positions.push(match.index) } while (++index < positions.length) { if (start !== positions[index]) { results.push(value.slice(start, positions[index])) } results.push('\\') start = positions[index] } results.push(value.slice(start)) return results.join('') }