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