123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- 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('')
- }
|