| 1 | function getLocator(source, options) {
|
|---|
| 2 | if (options === void 0) { options = {}; }
|
|---|
| 3 | var offsetLine = options.offsetLine || 0;
|
|---|
| 4 | var offsetColumn = options.offsetColumn || 0;
|
|---|
| 5 | var originalLines = source.split('\n');
|
|---|
| 6 | var start = 0;
|
|---|
| 7 | var lineRanges = originalLines.map(function (line, i) {
|
|---|
| 8 | var end = start + line.length + 1;
|
|---|
| 9 | var range = { start: start, end: end, line: i };
|
|---|
| 10 | start = end;
|
|---|
| 11 | return range;
|
|---|
| 12 | });
|
|---|
| 13 | var i = 0;
|
|---|
| 14 | function rangeContains(range, index) {
|
|---|
| 15 | return range.start <= index && index < range.end;
|
|---|
| 16 | }
|
|---|
| 17 | function getLocation(range, index) {
|
|---|
| 18 | return { line: offsetLine + range.line, column: offsetColumn + index - range.start, character: index };
|
|---|
| 19 | }
|
|---|
| 20 | function locate(search, startIndex) {
|
|---|
| 21 | if (typeof search === 'string') {
|
|---|
| 22 | search = source.indexOf(search, startIndex || 0);
|
|---|
| 23 | }
|
|---|
| 24 | var range = lineRanges[i];
|
|---|
| 25 | var d = search >= range.end ? 1 : -1;
|
|---|
| 26 | while (range) {
|
|---|
| 27 | if (rangeContains(range, search))
|
|---|
| 28 | return getLocation(range, search);
|
|---|
| 29 | i += d;
|
|---|
| 30 | range = lineRanges[i];
|
|---|
| 31 | }
|
|---|
| 32 | }
|
|---|
| 33 | return locate;
|
|---|
| 34 | }
|
|---|
| 35 | function locate(source, search, options) {
|
|---|
| 36 | if (typeof options === 'number') {
|
|---|
| 37 | throw new Error('locate takes a { startIndex, offsetLine, offsetColumn } object as the third argument');
|
|---|
| 38 | }
|
|---|
| 39 | return getLocator(source, options)(search, options && options.startIndex);
|
|---|
| 40 | }
|
|---|
| 41 |
|
|---|
| 42 | var validNameCharacters = /[a-zA-Z0-9:_-]/;
|
|---|
| 43 | var whitespace = /[\s\t\r\n]/;
|
|---|
| 44 | var quotemark = /['"]/;
|
|---|
| 45 |
|
|---|
| 46 | function repeat(str, i) {
|
|---|
| 47 | var result = '';
|
|---|
| 48 | while (i--) { result += str; }
|
|---|
| 49 | return result;
|
|---|
| 50 | }
|
|---|
| 51 |
|
|---|
| 52 | function parse(source) {
|
|---|
| 53 | var header = '';
|
|---|
| 54 | var stack = [];
|
|---|
| 55 |
|
|---|
| 56 | var state = metadata;
|
|---|
| 57 | var currentElement = null;
|
|---|
| 58 | var root = null;
|
|---|
| 59 |
|
|---|
| 60 | function error(message) {
|
|---|
| 61 | var ref = locate(source, i);
|
|---|
| 62 | var line = ref.line;
|
|---|
| 63 | var column = ref.column;
|
|---|
| 64 | var before = source.slice(0, i);
|
|---|
| 65 | var beforeLine = /(^|\n).*$/.exec(before)[0].replace(/\t/g, ' ');
|
|---|
| 66 | var after = source.slice(i);
|
|---|
| 67 | var afterLine = /.*(\n|$)/.exec(after)[0];
|
|---|
| 68 |
|
|---|
| 69 | var snippet = "" + beforeLine + afterLine + "\n" + (repeat(' ', beforeLine.length)) + "^";
|
|---|
| 70 |
|
|---|
| 71 | throw new Error(
|
|---|
| 72 | (message + " (" + line + ":" + column + "). If this is valid SVG, it's probably a bug in svg-parser. Please raise an issue at https://github.com/Rich-Harris/svg-parser/issues – thanks!\n\n" + snippet)
|
|---|
| 73 | );
|
|---|
| 74 | }
|
|---|
| 75 |
|
|---|
| 76 | function metadata() {
|
|---|
| 77 | while ((i < source.length && source[i] !== '<') || !validNameCharacters.test(source[i + 1])) {
|
|---|
| 78 | header += source[i++];
|
|---|
| 79 | }
|
|---|
| 80 |
|
|---|
| 81 | return neutral();
|
|---|
| 82 | }
|
|---|
| 83 |
|
|---|
| 84 | function neutral() {
|
|---|
| 85 | var text = '';
|
|---|
| 86 | while (i < source.length && source[i] !== '<') { text += source[i++]; }
|
|---|
| 87 |
|
|---|
| 88 | if (/\S/.test(text)) {
|
|---|
| 89 | currentElement.children.push({ type: 'text', value: text });
|
|---|
| 90 | }
|
|---|
| 91 |
|
|---|
| 92 | if (source[i] === '<') {
|
|---|
| 93 | return tag;
|
|---|
| 94 | }
|
|---|
| 95 |
|
|---|
| 96 | return neutral;
|
|---|
| 97 | }
|
|---|
| 98 |
|
|---|
| 99 | function tag() {
|
|---|
| 100 | var char = source[i];
|
|---|
| 101 |
|
|---|
| 102 | if (char === '?') { return neutral; } // <?xml...
|
|---|
| 103 |
|
|---|
| 104 | if (char === '!') {
|
|---|
| 105 | if (source.slice(i + 1, i + 3) === '--') { return comment; }
|
|---|
| 106 | if (source.slice(i + 1, i + 8) === '[CDATA[') { return cdata; }
|
|---|
| 107 | if (/doctype/i.test(source.slice(i + 1, i + 8))) { return neutral; }
|
|---|
| 108 | }
|
|---|
| 109 |
|
|---|
| 110 | if (char === '/') { return closingTag; }
|
|---|
| 111 |
|
|---|
| 112 | var tagName = getName();
|
|---|
| 113 |
|
|---|
| 114 | var element = {
|
|---|
| 115 | type: 'element',
|
|---|
| 116 | tagName: tagName,
|
|---|
| 117 | properties: {},
|
|---|
| 118 | children: []
|
|---|
| 119 | };
|
|---|
| 120 |
|
|---|
| 121 | if (currentElement) {
|
|---|
| 122 | currentElement.children.push(element);
|
|---|
| 123 | } else {
|
|---|
| 124 | root = element;
|
|---|
| 125 | }
|
|---|
| 126 |
|
|---|
| 127 | var attribute;
|
|---|
| 128 | while (i < source.length && (attribute = getAttribute())) {
|
|---|
| 129 | element.properties[attribute.name] = attribute.value;
|
|---|
| 130 | }
|
|---|
| 131 |
|
|---|
| 132 | var selfClosing = false;
|
|---|
| 133 |
|
|---|
| 134 | if (source[i] === '/') {
|
|---|
| 135 | i += 1;
|
|---|
| 136 | selfClosing = true;
|
|---|
| 137 | }
|
|---|
| 138 |
|
|---|
| 139 | if (source[i] !== '>') {
|
|---|
| 140 | error('Expected >');
|
|---|
| 141 | }
|
|---|
| 142 |
|
|---|
| 143 | if (!selfClosing) {
|
|---|
| 144 | currentElement = element;
|
|---|
| 145 | stack.push(element);
|
|---|
| 146 | }
|
|---|
| 147 |
|
|---|
| 148 | return neutral;
|
|---|
| 149 | }
|
|---|
| 150 |
|
|---|
| 151 | function comment() {
|
|---|
| 152 | var index = source.indexOf('-->', i);
|
|---|
| 153 | if (!~index) { error('expected -->'); }
|
|---|
| 154 |
|
|---|
| 155 | i = index + 2;
|
|---|
| 156 | return neutral;
|
|---|
| 157 | }
|
|---|
| 158 |
|
|---|
| 159 | function cdata() {
|
|---|
| 160 | var index = source.indexOf(']]>', i);
|
|---|
| 161 | if (!~index) { error('expected ]]>'); }
|
|---|
| 162 |
|
|---|
| 163 | currentElement.children.push(source.slice(i + 7, index));
|
|---|
| 164 |
|
|---|
| 165 | i = index + 2;
|
|---|
| 166 | return neutral;
|
|---|
| 167 | }
|
|---|
| 168 |
|
|---|
| 169 | function closingTag() {
|
|---|
| 170 | var tagName = getName();
|
|---|
| 171 |
|
|---|
| 172 | if (!tagName) { error('Expected tag name'); }
|
|---|
| 173 |
|
|---|
| 174 | if (tagName !== currentElement.tagName) {
|
|---|
| 175 | error(("Expected closing tag </" + tagName + "> to match opening tag <" + (currentElement.tagName) + ">"));
|
|---|
| 176 | }
|
|---|
| 177 |
|
|---|
| 178 | allowSpaces();
|
|---|
| 179 |
|
|---|
| 180 | if (source[i] !== '>') {
|
|---|
| 181 | error('Expected >');
|
|---|
| 182 | }
|
|---|
| 183 |
|
|---|
| 184 | stack.pop();
|
|---|
| 185 | currentElement = stack[stack.length - 1];
|
|---|
| 186 |
|
|---|
| 187 | return neutral;
|
|---|
| 188 | }
|
|---|
| 189 |
|
|---|
| 190 | function getName() {
|
|---|
| 191 | var name = '';
|
|---|
| 192 | while (i < source.length && validNameCharacters.test(source[i])) { name += source[i++]; }
|
|---|
| 193 |
|
|---|
| 194 | return name;
|
|---|
| 195 | }
|
|---|
| 196 |
|
|---|
| 197 | function getAttribute() {
|
|---|
| 198 | if (!whitespace.test(source[i])) { return null; }
|
|---|
| 199 | allowSpaces();
|
|---|
| 200 |
|
|---|
| 201 | var name = getName();
|
|---|
| 202 | if (!name) { return null; }
|
|---|
| 203 |
|
|---|
| 204 | var value = true;
|
|---|
| 205 |
|
|---|
| 206 | allowSpaces();
|
|---|
| 207 | if (source[i] === '=') {
|
|---|
| 208 | i += 1;
|
|---|
| 209 | allowSpaces();
|
|---|
| 210 |
|
|---|
| 211 | value = getAttributeValue();
|
|---|
| 212 | if (!isNaN(value) && value.trim() !== '') { value = +value; } // TODO whitelist numeric attributes?
|
|---|
| 213 | }
|
|---|
| 214 |
|
|---|
| 215 | return { name: name, value: value };
|
|---|
| 216 | }
|
|---|
| 217 |
|
|---|
| 218 | function getAttributeValue() {
|
|---|
| 219 | return quotemark.test(source[i]) ? getQuotedAttributeValue() : getUnquotedAttributeValue();
|
|---|
| 220 | }
|
|---|
| 221 |
|
|---|
| 222 | function getUnquotedAttributeValue() {
|
|---|
| 223 | var value = '';
|
|---|
| 224 | do {
|
|---|
| 225 | var char = source[i];
|
|---|
| 226 | if (char === ' ' || char === '>' || char === '/') {
|
|---|
| 227 | return value;
|
|---|
| 228 | }
|
|---|
| 229 |
|
|---|
| 230 | value += char;
|
|---|
| 231 | i += 1;
|
|---|
| 232 | } while (i < source.length);
|
|---|
| 233 |
|
|---|
| 234 | return value;
|
|---|
| 235 | }
|
|---|
| 236 |
|
|---|
| 237 | function getQuotedAttributeValue() {
|
|---|
| 238 | var quotemark = source[i++];
|
|---|
| 239 |
|
|---|
| 240 | var value = '';
|
|---|
| 241 | var escaped = false;
|
|---|
| 242 |
|
|---|
| 243 | while (i < source.length) {
|
|---|
| 244 | var char = source[i++];
|
|---|
| 245 | if (char === quotemark && !escaped) {
|
|---|
| 246 | return value;
|
|---|
| 247 | }
|
|---|
| 248 |
|
|---|
| 249 | if (char === '\\' && !escaped) {
|
|---|
| 250 | escaped = true;
|
|---|
| 251 | }
|
|---|
| 252 |
|
|---|
| 253 | value += escaped ? ("\\" + char) : char;
|
|---|
| 254 | escaped = false;
|
|---|
| 255 | }
|
|---|
| 256 | }
|
|---|
| 257 |
|
|---|
| 258 | function allowSpaces() {
|
|---|
| 259 | while (i < source.length && whitespace.test(source[i])) { i += 1; }
|
|---|
| 260 | }
|
|---|
| 261 |
|
|---|
| 262 | var i = metadata.length;
|
|---|
| 263 | while (i < source.length) {
|
|---|
| 264 | if (!state) { error('Unexpected character'); }
|
|---|
| 265 | state = state();
|
|---|
| 266 | i += 1;
|
|---|
| 267 | }
|
|---|
| 268 |
|
|---|
| 269 | if (state !== neutral) {
|
|---|
| 270 | error('Unexpected end of input');
|
|---|
| 271 | }
|
|---|
| 272 |
|
|---|
| 273 | if (root.tagName === 'svg') { root.metadata = header; }
|
|---|
| 274 | return {
|
|---|
| 275 | type: 'root',
|
|---|
| 276 | children: [root]
|
|---|
| 277 | };
|
|---|
| 278 | }
|
|---|
| 279 |
|
|---|
| 280 | export { parse };
|
|---|
| 281 | //# sourceMappingURL=svg-parser.esm.js.map
|
|---|