| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259 | 
							- 'use strict';
 
- /**
 
-  * @typedef {import('./types').XastNode} XastNode
 
-  * @typedef {import('./types').XastInstruction} XastInstruction
 
-  * @typedef {import('./types').XastDoctype} XastDoctype
 
-  * @typedef {import('./types').XastComment} XastComment
 
-  * @typedef {import('./types').XastRoot} XastRoot
 
-  * @typedef {import('./types').XastElement} XastElement
 
-  * @typedef {import('./types').XastCdata} XastCdata
 
-  * @typedef {import('./types').XastText} XastText
 
-  * @typedef {import('./types').XastParent} XastParent
 
-  */
 
- // @ts-ignore sax will be replaced with something else later
 
- const SAX = require('@trysound/sax');
 
- const JSAPI = require('./svgo/jsAPI.js');
 
- const { textElems } = require('../plugins/_collections.js');
 
- class SvgoParserError extends Error {
 
-   /**
 
-    * @param message {string}
 
-    * @param line {number}
 
-    * @param column {number}
 
-    * @param source {string}
 
-    * @param file {void | string}
 
-    */
 
-   constructor(message, line, column, source, file) {
 
-     super(message);
 
-     this.name = 'SvgoParserError';
 
-     this.message = `${file || '<input>'}:${line}:${column}: ${message}`;
 
-     this.reason = message;
 
-     this.line = line;
 
-     this.column = column;
 
-     this.source = source;
 
-     if (Error.captureStackTrace) {
 
-       Error.captureStackTrace(this, SvgoParserError);
 
-     }
 
-   }
 
-   toString() {
 
-     const lines = this.source.split(/\r?\n/);
 
-     const startLine = Math.max(this.line - 3, 0);
 
-     const endLine = Math.min(this.line + 2, lines.length);
 
-     const lineNumberWidth = String(endLine).length;
 
-     const startColumn = Math.max(this.column - 54, 0);
 
-     const endColumn = Math.max(this.column + 20, 80);
 
-     const code = lines
 
-       .slice(startLine, endLine)
 
-       .map((line, index) => {
 
-         const lineSlice = line.slice(startColumn, endColumn);
 
-         let ellipsisPrefix = '';
 
-         let ellipsisSuffix = '';
 
-         if (startColumn !== 0) {
 
-           ellipsisPrefix = startColumn > line.length - 1 ? ' ' : '…';
 
-         }
 
-         if (endColumn < line.length - 1) {
 
-           ellipsisSuffix = '…';
 
-         }
 
-         const number = startLine + 1 + index;
 
-         const gutter = ` ${number.toString().padStart(lineNumberWidth)} | `;
 
-         if (number === this.line) {
 
-           const gutterSpacing = gutter.replace(/[^|]/g, ' ');
 
-           const lineSpacing = (
 
-             ellipsisPrefix + line.slice(startColumn, this.column - 1)
 
-           ).replace(/[^\t]/g, ' ');
 
-           const spacing = gutterSpacing + lineSpacing;
 
-           return `>${gutter}${ellipsisPrefix}${lineSlice}${ellipsisSuffix}\n ${spacing}^`;
 
-         }
 
-         return ` ${gutter}${ellipsisPrefix}${lineSlice}${ellipsisSuffix}`;
 
-       })
 
-       .join('\n');
 
-     return `${this.name}: ${this.message}\n\n${code}\n`;
 
-   }
 
- }
 
- const entityDeclaration = /<!ENTITY\s+(\S+)\s+(?:'([^']+)'|"([^"]+)")\s*>/g;
 
- const config = {
 
-   strict: true,
 
-   trim: false,
 
-   normalize: false,
 
-   lowercase: true,
 
-   xmlns: true,
 
-   position: true,
 
- };
 
- /**
 
-  * Convert SVG (XML) string to SVG-as-JS object.
 
-  *
 
-  * @type {(data: string, from?: string) => XastRoot}
 
-  */
 
- const parseSvg = (data, from) => {
 
-   const sax = SAX.parser(config.strict, config);
 
-   /**
 
-    * @type {XastRoot}
 
-    */
 
-   const root = new JSAPI({ type: 'root', children: [] });
 
-   /**
 
-    * @type {XastParent}
 
-    */
 
-   let current = root;
 
-   /**
 
-    * @type {Array<XastParent>}
 
-    */
 
-   const stack = [root];
 
-   /**
 
-    * @type {<T extends XastNode>(node: T) => T}
 
-    */
 
-   const pushToContent = (node) => {
 
-     const wrapped = new JSAPI(node, current);
 
-     current.children.push(wrapped);
 
-     return wrapped;
 
-   };
 
-   /**
 
-    * @type {(doctype: string) => void}
 
-    */
 
-   sax.ondoctype = (doctype) => {
 
-     /**
 
-      * @type {XastDoctype}
 
-      */
 
-     const node = {
 
-       type: 'doctype',
 
-       // TODO parse doctype for name, public and system to match xast
 
-       name: 'svg',
 
-       data: {
 
-         doctype,
 
-       },
 
-     };
 
-     pushToContent(node);
 
-     const subsetStart = doctype.indexOf('[');
 
-     if (subsetStart >= 0) {
 
-       entityDeclaration.lastIndex = subsetStart;
 
-       let entityMatch = entityDeclaration.exec(data);
 
-       while (entityMatch != null) {
 
-         sax.ENTITIES[entityMatch[1]] = entityMatch[2] || entityMatch[3];
 
-         entityMatch = entityDeclaration.exec(data);
 
-       }
 
-     }
 
-   };
 
-   /**
 
-    * @type {(data: { name: string, body: string }) => void}
 
-    */
 
-   sax.onprocessinginstruction = (data) => {
 
-     /**
 
-      * @type {XastInstruction}
 
-      */
 
-     const node = {
 
-       type: 'instruction',
 
-       name: data.name,
 
-       value: data.body,
 
-     };
 
-     pushToContent(node);
 
-   };
 
-   /**
 
-    * @type {(comment: string) => void}
 
-    */
 
-   sax.oncomment = (comment) => {
 
-     /**
 
-      * @type {XastComment}
 
-      */
 
-     const node = {
 
-       type: 'comment',
 
-       value: comment.trim(),
 
-     };
 
-     pushToContent(node);
 
-   };
 
-   /**
 
-    * @type {(cdata: string) => void}
 
-    */
 
-   sax.oncdata = (cdata) => {
 
-     /**
 
-      * @type {XastCdata}
 
-      */
 
-     const node = {
 
-       type: 'cdata',
 
-       value: cdata,
 
-     };
 
-     pushToContent(node);
 
-   };
 
-   /**
 
-    * @type {(data: { name: string, attributes: Record<string, { value: string }>}) => void}
 
-    */
 
-   sax.onopentag = (data) => {
 
-     /**
 
-      * @type {XastElement}
 
-      */
 
-     let element = {
 
-       type: 'element',
 
-       name: data.name,
 
-       attributes: {},
 
-       children: [],
 
-     };
 
-     for (const [name, attr] of Object.entries(data.attributes)) {
 
-       element.attributes[name] = attr.value;
 
-     }
 
-     element = pushToContent(element);
 
-     current = element;
 
-     stack.push(element);
 
-   };
 
-   /**
 
-    * @type {(text: string) => void}
 
-    */
 
-   sax.ontext = (text) => {
 
-     if (current.type === 'element') {
 
-       // prevent trimming of meaningful whitespace inside textual tags
 
-       if (textElems.includes(current.name)) {
 
-         /**
 
-          * @type {XastText}
 
-          */
 
-         const node = {
 
-           type: 'text',
 
-           value: text,
 
-         };
 
-         pushToContent(node);
 
-       } else if (/\S/.test(text)) {
 
-         /**
 
-          * @type {XastText}
 
-          */
 
-         const node = {
 
-           type: 'text',
 
-           value: text.trim(),
 
-         };
 
-         pushToContent(node);
 
-       }
 
-     }
 
-   };
 
-   sax.onclosetag = () => {
 
-     stack.pop();
 
-     current = stack[stack.length - 1];
 
-   };
 
-   /**
 
-    * @type {(e: any) => void}
 
-    */
 
-   sax.onerror = (e) => {
 
-     const error = new SvgoParserError(
 
-       e.reason,
 
-       e.line + 1,
 
-       e.column,
 
-       data,
 
-       from
 
-     );
 
-     if (e.message.indexOf('Unexpected end') === -1) {
 
-       throw error;
 
-     }
 
-   };
 
-   sax.write(data).close();
 
-   return root;
 
- };
 
- exports.parseSvg = parseSvg;
 
 
  |