All checks were successful
Publish To Prod / deploy_and_publish (push) Successful in 35s
140 lines
3.2 KiB
JavaScript
140 lines
3.2 KiB
JavaScript
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('')
|
|
}
|