You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
139 lines
3.2 KiB
139 lines
3.2 KiB
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('') |
|
}
|
|
|