UNPKG

3.51 kBJavaScriptView Raw
1/*! https://mths.be/cssesc v3.0.0 by @mathias */
2'use strict';
3
4var object = {};
5var hasOwnProperty = object.hasOwnProperty;
6var merge = function merge(options, defaults) {
7 if (!options) {
8 return defaults;
9 }
10 var result = {};
11 for (var key in defaults) {
12 // `if (defaults.hasOwnProperty(key) { … }` is not needed here, since
13 // only recognized option names are used.
14 result[key] = hasOwnProperty.call(options, key) ? options[key] : defaults[key];
15 }
16 return result;
17};
18
19var regexAnySingleEscape = /[ -,\.\/:-@\[-\^`\{-~]/;
20var regexSingleEscape = /[ -,\.\/:-@\[\]\^`\{-~]/;
21var regexAlwaysEscape = /['"\\]/;
22var regexExcessiveSpaces = /(^|\\+)?(\\[A-F0-9]{1,6})\x20(?![a-fA-F0-9\x20])/g;
23
24// https://mathiasbynens.be/notes/css-escapes#css
25var cssesc = function cssesc(string, options) {
26 options = merge(options, cssesc.options);
27 if (options.quotes != 'single' && options.quotes != 'double') {
28 options.quotes = 'single';
29 }
30 var quote = options.quotes == 'double' ? '"' : '\'';
31 var isIdentifier = options.isIdentifier;
32
33 var firstChar = string.charAt(0);
34 var output = '';
35 var counter = 0;
36 var length = string.length;
37 while (counter < length) {
38 var character = string.charAt(counter++);
39 var codePoint = character.charCodeAt();
40 var value = void 0;
41 // If it’s not a printable ASCII character…
42 if (codePoint < 0x20 || codePoint > 0x7E) {
43 if (codePoint >= 0xD800 && codePoint <= 0xDBFF && counter < length) {
44 // It’s a high surrogate, and there is a next character.
45 var extra = string.charCodeAt(counter++);
46 if ((extra & 0xFC00) == 0xDC00) {
47 // next character is low surrogate
48 codePoint = ((codePoint & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000;
49 } else {
50 // It’s an unmatched surrogate; only append this code unit, in case
51 // the next code unit is the high surrogate of a surrogate pair.
52 counter--;
53 }
54 }
55 value = '\\' + codePoint.toString(16).toUpperCase() + ' ';
56 } else {
57 if (options.escapeEverything) {
58 if (regexAnySingleEscape.test(character)) {
59 value = '\\' + character;
60 } else {
61 value = '\\' + codePoint.toString(16).toUpperCase() + ' ';
62 }
63 } else if (/[\t\n\f\r\x0B]/.test(character)) {
64 value = '\\' + codePoint.toString(16).toUpperCase() + ' ';
65 } else if (character == '\\' || !isIdentifier && (character == '"' && quote == character || character == '\'' && quote == character) || isIdentifier && regexSingleEscape.test(character)) {
66 value = '\\' + character;
67 } else {
68 value = character;
69 }
70 }
71 output += value;
72 }
73
74 if (isIdentifier) {
75 if (/^-[-\d]/.test(output)) {
76 output = '\\-' + output.slice(1);
77 } else if (/\d/.test(firstChar)) {
78 output = '\\3' + firstChar + ' ' + output.slice(1);
79 }
80 }
81
82 // Remove spaces after `\HEX` escapes that are not followed by a hex digit,
83 // since they’re redundant. Note that this is only possible if the escape
84 // sequence isn’t preceded by an odd number of backslashes.
85 output = output.replace(regexExcessiveSpaces, function ($0, $1, $2) {
86 if ($1 && $1.length % 2) {
87 // It’s not safe to remove the space, so don’t.
88 return $0;
89 }
90 // Strip the space.
91 return ($1 || '') + $2;
92 });
93
94 if (!isIdentifier && options.wrap) {
95 return quote + output + quote;
96 }
97 return output;
98};
99
100// Expose default options (so they can be overridden globally).
101cssesc.options = {
102 'escapeEverything': false,
103 'isIdentifier': false,
104 'quotes': 'single',
105 'wrap': false
106};
107
108cssesc.version = '3.0.0';
109
110module.exports = cssesc;