All files / src/parser utils.js

100% Statements 32/32
100% Branches 20/20
100% Functions 5/5
100% Lines 31/31

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94        1x             835x 754x 754x 696x 696x     139x 139x                 21x 21x 21x                     51x   51x 135x 129x 129x 86x 2x   84x     43x                       31x   4x       27x     27x 662x   514x   148x   123x       25x 25x   25x          
// @ts-check
 
import d from 'debug';
 
const debug = d('ssvg:parser');
 
/**
 * @param  {ParseCtx} ctx
 * @return {string|null}
 */
export function skipSpaces(ctx) {
  for (var i = ctx.index; i < ctx.value.length; ++i) {
    const c = ctx.value[i];
    if (c !== ' ' && c !== '\t' && c !== '\n') {
      ctx.index = i;
      return c;
    }
  }
  ctx.index = ctx.value.length;
  return null;
}
 
/**
 * @param {string} message
 * @param {ParseCtx} ctx
 * @return {string}
 */
export function makeError(message, ctx) {
  var out = message + '\n' + ctx.value + '\n' + '-'.repeat(ctx.index) + '^\n';
  debug('SyntaxError: ' + out);
  return out;
}
 
/**
 * @template T
 * @param {ParseCtx} ctx
 * @param {(ctx: ParseCtx) => T} func
 * @return {T[]}
 */
export function parseList(ctx, func) {
  /** @type {T[]} */
  var values = [];
 
  while (ctx.index < ctx.value.length) {
    values.push(func(ctx));
    var c = skipSpaces(ctx);
    if (c !== null) {
      if (c !== ';') {
        throw new SyntaxError(makeError(`invalid list separator '${c}'`, ctx));
      }
      ++ctx.index;
    }
  }
  return values;
}
 
/**
 * @param {string} str
 * @param {string[]} args
 * @param {Window} win
 * @return {Function}
 * @details win may differ from global window when using iframes
 */
export function parseDomCallback(str, args, win = window) {
  // @ts-ignore
  if (win.hasOwnProperty(str) && typeof win[str] === 'function') {
    // @ts-ignore
    return win[str];
  }
 
  /* eslint-disable-next-line no-new-func */ // @ts-ignore
  var fun = new win.Function(...args, "return " + str);
  /** @type {boolean} */
  var direct;
  return function(/** @type {any[]} */ ...args) {
    if (direct === true) {
      // @ts-ignore
      return fun.call(this, ...args);
    }
    else if (direct === false) {
      // @ts-ignore
      return fun.call(this, ...args).call(this, ...args);
    }
    else { // first function call, let's check result
      // @ts-ignore
      const ret = fun.call(this, ...args);
      direct = ((typeof ret) !== 'function');
 
      return (direct === false) ? // @ts-ignore
        (ret.call(this, ...args)) : ret;
    }
  };
}