svg-parser.esm.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  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. var validNameCharacters = /[a-zA-Z0-9:_-]/;
  42. var whitespace = /[\s\t\r\n]/;
  43. var quotemark = /['"]/;
  44. function repeat(str, i) {
  45. var result = '';
  46. while (i--) { result += str; }
  47. return result;
  48. }
  49. function parse(source) {
  50. var header = '';
  51. var stack = [];
  52. var state = metadata;
  53. var currentElement = null;
  54. var root = null;
  55. function error(message) {
  56. var ref = locate(source, i);
  57. var line = ref.line;
  58. var column = ref.column;
  59. var before = source.slice(0, i);
  60. var beforeLine = /(^|\n).*$/.exec(before)[0].replace(/\t/g, ' ');
  61. var after = source.slice(i);
  62. var afterLine = /.*(\n|$)/.exec(after)[0];
  63. var snippet = "" + beforeLine + afterLine + "\n" + (repeat(' ', beforeLine.length)) + "^";
  64. throw new Error(
  65. (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)
  66. );
  67. }
  68. function metadata() {
  69. while ((i < source.length && source[i] !== '<') || !validNameCharacters.test(source[i + 1])) {
  70. header += source[i++];
  71. }
  72. return neutral();
  73. }
  74. function neutral() {
  75. var text = '';
  76. while (i < source.length && source[i] !== '<') { text += source[i++]; }
  77. if (/\S/.test(text)) {
  78. currentElement.children.push({ type: 'text', value: text });
  79. }
  80. if (source[i] === '<') {
  81. return tag;
  82. }
  83. return neutral;
  84. }
  85. function tag() {
  86. var char = source[i];
  87. if (char === '?') { return neutral; } // <?xml...
  88. if (char === '!') {
  89. if (source.slice(i + 1, i + 3) === '--') { return comment; }
  90. if (source.slice(i + 1, i + 8) === '[CDATA[') { return cdata; }
  91. if (/doctype/i.test(source.slice(i + 1, i + 8))) { return neutral; }
  92. }
  93. if (char === '/') { return closingTag; }
  94. var tagName = getName();
  95. var element = {
  96. type: 'element',
  97. tagName: tagName,
  98. properties: {},
  99. children: []
  100. };
  101. if (currentElement) {
  102. currentElement.children.push(element);
  103. } else {
  104. root = element;
  105. }
  106. var attribute;
  107. while (i < source.length && (attribute = getAttribute())) {
  108. element.properties[attribute.name] = attribute.value;
  109. }
  110. var selfClosing = false;
  111. if (source[i] === '/') {
  112. i += 1;
  113. selfClosing = true;
  114. }
  115. if (source[i] !== '>') {
  116. error('Expected >');
  117. }
  118. if (!selfClosing) {
  119. currentElement = element;
  120. stack.push(element);
  121. }
  122. return neutral;
  123. }
  124. function comment() {
  125. var index = source.indexOf('-->', i);
  126. if (!~index) { error('expected -->'); }
  127. i = index + 2;
  128. return neutral;
  129. }
  130. function cdata() {
  131. var index = source.indexOf(']]>', i);
  132. if (!~index) { error('expected ]]>'); }
  133. currentElement.children.push(source.slice(i + 7, index));
  134. i = index + 2;
  135. return neutral;
  136. }
  137. function closingTag() {
  138. var tagName = getName();
  139. if (!tagName) { error('Expected tag name'); }
  140. if (tagName !== currentElement.tagName) {
  141. error(("Expected closing tag </" + tagName + "> to match opening tag <" + (currentElement.tagName) + ">"));
  142. }
  143. allowSpaces();
  144. if (source[i] !== '>') {
  145. error('Expected >');
  146. }
  147. stack.pop();
  148. currentElement = stack[stack.length - 1];
  149. return neutral;
  150. }
  151. function getName() {
  152. var name = '';
  153. while (i < source.length && validNameCharacters.test(source[i])) { name += source[i++]; }
  154. return name;
  155. }
  156. function getAttribute() {
  157. if (!whitespace.test(source[i])) { return null; }
  158. allowSpaces();
  159. var name = getName();
  160. if (!name) { return null; }
  161. var value = true;
  162. allowSpaces();
  163. if (source[i] === '=') {
  164. i += 1;
  165. allowSpaces();
  166. value = getAttributeValue();
  167. if (!isNaN(value) && value.trim() !== '') { value = +value; } // TODO whitelist numeric attributes?
  168. }
  169. return { name: name, value: value };
  170. }
  171. function getAttributeValue() {
  172. return quotemark.test(source[i]) ? getQuotedAttributeValue() : getUnquotedAttributeValue();
  173. }
  174. function getUnquotedAttributeValue() {
  175. var value = '';
  176. do {
  177. var char = source[i];
  178. if (char === ' ' || char === '>' || char === '/') {
  179. return value;
  180. }
  181. value += char;
  182. i += 1;
  183. } while (i < source.length);
  184. return value;
  185. }
  186. function getQuotedAttributeValue() {
  187. var quotemark = source[i++];
  188. var value = '';
  189. var escaped = false;
  190. while (i < source.length) {
  191. var char = source[i++];
  192. if (char === quotemark && !escaped) {
  193. return value;
  194. }
  195. if (char === '\\' && !escaped) {
  196. escaped = true;
  197. }
  198. value += escaped ? ("\\" + char) : char;
  199. escaped = false;
  200. }
  201. }
  202. function allowSpaces() {
  203. while (i < source.length && whitespace.test(source[i])) { i += 1; }
  204. }
  205. var i = metadata.length;
  206. while (i < source.length) {
  207. if (!state) { error('Unexpected character'); }
  208. state = state();
  209. i += 1;
  210. }
  211. if (state !== neutral) {
  212. error('Unexpected end of input');
  213. }
  214. if (root.tagName === 'svg') { root.metadata = header; }
  215. return {
  216. type: 'root',
  217. children: [root]
  218. };
  219. }
  220. export { parse };
  221. //# sourceMappingURL=svg-parser.esm.js.map