<commit_before>const { Transform } = require('stream');

const OGG_PAGE_HEADER_SIZE = 26;
const STREAM_STRUCTURE_VERSION = 0;

const charCode = x => x.charCodeAt(0);
const OGGS_HEADER = Buffer.from([...'OggS'].map(charCode));
const OPUS_HEAD = Buffer.from([...'OpusHead'].map(charCode));
const OPUS_TAGS = Buffer.from([...'OpusTags'].map(charCode));

/**
 * Demuxes an Ogg stream (containing Opus audio) to output an Opus stream.
 * @extends {TransformStream}
 */
class OggOpusDemuxer extends Transform {
  /**
   * Creates a new OggOpus demuxer.
   * @param {Object} [options] options that you would pass to a regular Transform stream.
   */
  constructor(options = {}) {
    super(Object.assign({ readableObjectMode: true }, options));
    this._remainder = null;
    this._head = null;
  }

  _transform(chunk, encoding, done) {
    if (this._remainder) {
      chunk = Buffer.concat([this._remainder, chunk]);
      this._remainder = null;
    }

    while (chunk) {
      const result = this._readPage(chunk);
      if (result) chunk = result;
      else break;
    }
    this._remainder = chunk;
    done();
  }

  /**
   * Reads a page from a buffer
   * @private
   * @param {Buffer} chunk the chunk containing the page
   * @returns {boolean|Buffer} if a buffer, it will be a slice of the excess data of the original, otherwise it will be
   * false and would indicate that there is not enough data to go ahead with reading this page.
   */
  _readPage(chunk) {
    if (chunk.length < OGG_PAGE_HEADER_SIZE) {
      return false;
    }
    if (!chunk.slice(0, 4).equals(OGGS_HEADER)) {
      throw Error(`capture_pattern is not ${OGGS_HEADER}`);
    }
    if (chunk.readUInt8(4) !== STREAM_STRUCTURE_VERSION) {
      throw Error(`stream_structure_version is not ${STREAM_STRUCTURE_VERSION}`);
    }

    const pageSegments = chunk.readUInt8(26), table = chunk.slice(27, 27 + pageSegments);
    let sizes = [], totalSize = 0;

    for (let i = 0; i < pageSegments;) {
      let size = 0, x = 255;
      while (x === 255) {
        if (i >= table.length) return false;
        x = table.readUInt8(i);
        i++;
        size += x;
      }
      sizes.push(size);
      totalSize += size;
    }

    if (chunk.length < 27 + pageSegments + totalSize) return false;

    let start = 27 + pageSegments;
    for (const size of sizes) {
      const segment = chunk.slice(start, start + size);
      const header = segment.slice(0, 8);
      if (this._head) {
        if (header.equals(OPUS_TAGS)) this.emit('tags', segment);
        else this.push(segment);
      } else if (header.equals(OPUS_HEAD)) {
        this.emit('head', segment);
        this._head = segment;
      } else {
        throw Error(`Invalid segment ${segment}`);
      }
      start += size;
    }
    return chunk.slice(start);
  }
}

/**
 * Emitted when the demuxer encounters the opus head.
 * @event OggOpusDemuxer#head
 * @param {Buffer} segment a buffer containing the opus head data.
 */

/**
 * Emitted when the demuxer encounters opus tags.
 * @event OggOpusDemuxer#tags
 * @param {Buffer} segment a buffer containing the opus tags.
 */

module.exports = OggOpusDemuxer;
<commit_msg>Fix OggOpusDemuxer - trying to read index out of bounds of the current buffer<commit_after>const { Transform } = require('stream');

const OGG_PAGE_HEADER_SIZE = 26;
const STREAM_STRUCTURE_VERSION = 0;

const charCode = x => x.charCodeAt(0);
const OGGS_HEADER = Buffer.from([...'OggS'].map(charCode));
const OPUS_HEAD = Buffer.from([...'OpusHead'].map(charCode));
const OPUS_TAGS = Buffer.from([...'OpusTags'].map(charCode));

/**
 * Demuxes an Ogg stream (containing Opus audio) to output an Opus stream.
 * @extends {TransformStream}
 */
class OggOpusDemuxer extends Transform {
  /**
   * Creates a new OggOpus demuxer.
   * @param {Object} [options] options that you would pass to a regular Transform stream.
   */
  constructor(options = {}) {
    super(Object.assign({ readableObjectMode: true }, options));
    this._remainder = null;
    this._head = null;
  }

  _transform(chunk, encoding, done) {
    if (this._remainder) {
      chunk = Buffer.concat([this._remainder, chunk]);
      this._remainder = null;
    }

    while (chunk) {
      const result = this._readPage(chunk);
      if (result) chunk = result;
      else break;
    }
    this._remainder = chunk;
    done();
  }

  /**
   * Reads a page from a buffer
   * @private
   * @param {Buffer} chunk the chunk containing the page
   * @returns {boolean|Buffer} if a buffer, it will be a slice of the excess data of the original, otherwise it will be
   * false and would indicate that there is not enough data to go ahead with reading this page.
   */
  _readPage(chunk) {
    if (chunk.length < OGG_PAGE_HEADER_SIZE) {
      return false;
    }
    if (!chunk.slice(0, 4).equals(OGGS_HEADER)) {
      throw Error(`capture_pattern is not ${OGGS_HEADER}`);
    }
    if (chunk.readUInt8(4) !== STREAM_STRUCTURE_VERSION) {
      throw Error(`stream_structure_version is not ${STREAM_STRUCTURE_VERSION}`);
    }

    if (chunk.length < 27) return false;
    const pageSegments = chunk.readUInt8(26);
    if (chunk.length < 27 + pageSegments) return false;
    const table = chunk.slice(27, 27 + pageSegments);

    let sizes = [], totalSize = 0;

    for (let i = 0; i < pageSegments;) {
      let size = 0, x = 255;
      while (x === 255) {
        if (i >= table.length) return false;
        x = table.readUInt8(i);
        i++;
        size += x;
      }
      sizes.push(size);
      totalSize += size;
    }

    if (chunk.length < 27 + pageSegments + totalSize) return false;

    let start = 27 + pageSegments;
    for (const size of sizes) {
      const segment = chunk.slice(start, start + size);
      const header = segment.slice(0, 8);
      if (this._head) {
        if (header.equals(OPUS_TAGS)) this.emit('tags', segment);
        else this.push(segment);
      } else if (header.equals(OPUS_HEAD)) {
        this.emit('head', segment);
        this._head = segment;
      } else {
        throw Error(`Invalid segment ${segment}`);
      }
      start += size;
    }
    return chunk.slice(start);
  }
}

/**
 * Emitted when the demuxer encounters the opus head.
 * @event OggOpusDemuxer#head
 * @param {Buffer} segment a buffer containing the opus head data.
 */

/**
 * Emitted when the demuxer encounters opus tags.
 * @event OggOpusDemuxer#tags
 * @param {Buffer} segment a buffer containing the opus tags.
 */

module.exports = OggOpusDemuxer;
<|endoftext|>


<commit_before>export default {
  name: 'Tooltip',
  desc: {
    'en-US': 'Tooltip is rendered by html nodes.',
    'zh-CN': 'Tooltip 是基于 HTML 实现的。',
  },
  props: [
    {
      name: 'separator',
      type: 'String',
      defaultVal: "' : '",
      isOptional: false,
      desc: {
        'en-US': 'The separator between name and value.',
        'zh-CN': 'name 和 value 之间的分隔符。',
      },
    },
    {
      name: 'offset',
      type: 'Number',
      defaultVal: '10',
      isOptional: false,
      desc: {
        'en-US': 'The offset size between the position of tooltip and the active position.',
        'zh-CN': 'Tooltip 与当前“游标”的偏移量。',
      },
    },
    {
      name: 'filterNull',
      type: 'Boolean',
      defaultVal: 'true',
      isOptional: false,
      desc: {
        'en-US': "When an item of the payload has value null or undefined, this item won't be displayed.",
        'zh-CN': '当tooltip内容中的某条数据的值为 null 或者 undefined 时，不展示该条数据',
      },
    },
    {
      name: 'itemStyle',
      type: 'Object',
      defaultVal: '{}',
      isOptional: false,
      desc: {
        'en-US': 'The style of default tooltip content item which is a li element.',
        'zh-CN': 'Tooltip 中每个项目（使用 li 节点渲染的）的样式。',
      },
      examples: [
        {
          name: 'React Inline style',
          url: 'https://reactjs.org/docs/dom-elements.html#style',
          isExternal: true,
        },
      ],
    },
    {
      name: 'wrapperStyle',
      type: 'Object',
      defaultVal: '{}',
      isOptional: false,
      desc: {
        'en-US': 'The style of tooltip wrapper which is a dom element.',
        'zh-CN': 'Tooltip 最外层节点（div）的样式。',
      },
      examples: [
        {
          name: 'React Inline style',
          url: 'https://reactjs.org/docs/dom-elements.html#style',
          isExternal: true,
        },
      ],
    },
    {
      name: 'contentStyle',
      type: 'Object',
      defaultVal: '{}',
      isOptional: false,
      desc: {
        'en-US': 'The style of tooltip content which is a dom element.',
        'zh-CN': 'Tooltip 内部展示的内容 (div) 的样式。',
      },
      examples: [
        {
          name: 'React Inline style',
          url: 'https://reactjs.org/docs/dom-elements.html#style',
          isExternal: true,
        },
      ],
    },
    {
      name: 'labelStyle',
      type: 'Object',
      defaultVal: '{}',
      isOptional: false,
      desc: {
        'en-US': 'The style of default tooltip label which is a p element.',
        'zh-CN': '类目文字标签（使用 <p /> 渲染）的样式。',
      },
      examples: [
        {
          name: 'React Inline style',
          url: 'https://reactjs.org/docs/dom-elements.html#style',
          isExternal: true,
        },
      ],
    },
    {
      name: 'cursor',
      type: 'Boolean | Object | ReactElement',
      defaultVal: 'true',
      isOptional: false,
      desc: {
        'en-US':
          'If set false, no cursor will be drawn when tooltip is active. If set a object, the option is the configuration of cursor. If set a React element, the option is the custom react element of drawing cursor.',
        'zh-CN':
          '游标相关配置，一般在类目图表展示 Tooltip 的时候，会渲染游标来标识相应的类目。如果值为 false，不渲染游标。当值为对象类型，会被解析为游标的属性，注意游标也是用 svg 元素渲染的。如果值为 React element，会克隆这个元素来展示自定义的游标。',
      },
      format: [
        '<Tooltip cursor={false} />',
        "<Tooltip cursor={{ stroke: 'red', strokeWidth: 2 }} />",
        '<Tooltip cursor={<CustomizedCursor />} />',
      ],
    },
    {
      name: 'viewBox',
      type: 'Object',
      defaultVal: 'null',
      isOptional: false,
      desc: {
        'en-US':
          'The box of viewing area, which has the shape of {x: someVal, y: someVal, width: someVal, height: someVal}, usually calculated internally.',
        'zh-CN': '图表的可视区域。通常通过 x、y、width、height 来描述。',
      },
      format: ['{ x: 0, y: 0, width: 400, height: 400 }'],
    },
    {
      name: 'allowEscapeViewBox',
      type: 'Object',
      defaultVal: '{ x: false, y: false }',
      isOptional: true,
      desc: {
        'en-US': 'This option allows the tooltip to extend beyond the viewBox of the chart itself.',
        'zh-CN': '此选项允许工具提示扩展到图表本身的viewBox之外。',
      },
      format: [{ x: true }, { y: true }, { x: true, y: true }],
    },
    {
      name: 'active',
      type: 'Boolean',
      defaultVal: 'false',
      isOptional: false,
      desc: {
        'en-US':
          'If set true, the tooltip is displayed. If set false, the tooltip is hidden, usually calculated internally.',
        'zh-CN':
          '是否处于激活状态。如果值为 true, tooltip 会显示出来。如果值为 false, tooltip 会被隐藏，这个值一般是图表内部控制的。',
      },
    },
    {
      name: 'position',
      type: 'Object',
      defaultVal: 'null',
      isOptional: true,
      desc: {
        'en-US': 'If this field is set, the tooltip position will be fixed and will not move anymore.',
        'zh-CN': '如果设置了此字段，则工具提示位置将固定不变，并且将不再移动。',
      },
      format: ['{ x: 100, y: 140 }'],
    },
    {
      name: 'coordinate',
      type: 'Object',
      defaultVal: '{ x: 0, y: 0 }',
      isOptional: false,
      desc: {
        'en-US': 'The coordinate of tooltip position, usually calculated internally.',
        'zh-CN': '用来描述位置的坐标，也是图表内部计算的值。',
      },
      format: ['{ x: 100, y: 140 }'],
    },
    {
      name: 'payload',
      type: 'Array',
      defaultVal: '[]',
      isOptional: false,
      desc: {
        'en-US': 'The source data of the content to be displayed in the tooltip, usually calculated internally.',
        'zh-CN': 'Tooltip 展示内容的源数据，通常是图表内部计算的。',
      },
      format: ["[{ name: '05-01', value: 12, unit: 'kg' }]"],
    },
    {
      name: 'label',
      type: 'String | Number',
      defaultVal: 'null',
      isOptional: true,
      desc: {
        'en-US': 'The label value which is active now, usually calculated internally.',
        'zh-CN': '当 Tooltip 显示出来的时候，表示类目文字标签。',
      },
    },
    {
      name: 'content',
      type: 'ReactElement | Function',
      defaultVal: 'null',
      isOptional: true,
      desc: {
        'en-US':
          'If set a React element, the option is the custom react element of rendering tooltip.  If set a function, the function will be called to render tooltip content.',
        'zh-CN':
          '定制 Tooltip 展示的内容。如果值为 React element，会克隆这个元素来渲染 Tooltip 的内容。如果值为函数，会调用这个函数来生成 Tooltip 的内容。',
      },
      format: [
        '<Tooltip content={<CustomizedTooltip external={external} />} />',
        '<Tooltip content={renderTooltip} />',
      ],
      examples: [
        {
          name: 'Customize tooltip content',
          url: '/examples/CustomContentOfTooltip',
        },
      ],
    },
    {
      name: 'formatter',
      type: 'Function',
      defaultVal: 'null',
      isOptional: true,
      desc: {
        'en-US':
          'The formatter function of value in tooltip. If you return an array, the first entry will be the formatted "value", and the second entry will be the formatted "name"',
        'zh-CN':
          '每个项目中“数值”的格式化函数。 如果函数返回值为数组格式，第一个元素是格式化后的“数值”，第二个元素是格式化后的“名称”',
      },
      format: [
        '(value, name, props) => ( return "formatted value" )',
        '(value, name, props) => ( return ["formatted value", "formatted name"， ] )',
      ],
    },
    {
      name: 'labelFormatter',
      type: 'Function',
      defaultVal: 'null',
      isOptional: true,
      desc: {
        'en-US': 'The formatter function of label in tooltip.',
        'zh-CN': '类目文字标签的格式化函数。',
      },
    },
    {
      name: 'itemSorter',
      type: 'Function',
      defaultVal: '() => -1',
      isOptional: false,
      desc: {
        'en-US': 'Sort function of payload',
        'zh-CN': '项目排序函数。',
      },
    },
    {
      name: 'isAnimationActive',
      type: 'Boolean',
      defaultVal: 'true in CSR, and false in SSR',
      isOptional: false,
      desc: {
        'en-US': 'If set false, animation of tooltip will be disabled.',
        'zh-CN': '当值为 false，不开启动画。',
      },
    },
    {
      name: 'animationBegin',
      type: 'Number',
      defaultVal: 0,
      isOptional: false,
      desc: {
        'en-US': 'Specifies when the animation should begin, the unit of this option is ms.',
        'zh-CN': '声明组件挂载或更新后，开始运行动画的间隔时长，单位为毫秒。',
      },
    },
    {
      name: 'animationDuration',
      type: 'Number',
      defaultVal: 1500,
      isOptional: false,
      desc: {
        'en-US': 'Specifies the duration of animation, the unit of this option is ms.',
        'zh-CN': '声明动画的运行时长，单位为毫秒。',
      },
    },
    {
      name: 'animationEasing',
      type: "'ease' | 'ease-in' | 'ease-out' | 'ease-in-out' | 'linear'",
      defaultVal: "'ease'",
      isOptional: false,
      desc: {
        'en-US': 'The type of easing function.',
        'zh-CN': '动画缓动函数的类型。',
      },
    },
  ],
  parentComponents: [
    'AreaChart',
    'BarChart',
    'LineChart',
    'ComposedChart',
    'PieChart',
    'RadarChart',
    'RadialBarChart',
    'ScatterChart',
    'Treemap',
  ],
};
<commit_msg>Put `format` values as a string<commit_after>export default {
  name: 'Tooltip',
  desc: {
    'en-US': 'Tooltip is rendered by html nodes.',
    'zh-CN': 'Tooltip 是基于 HTML 实现的。',
  },
  props: [
    {
      name: 'separator',
      type: 'String',
      defaultVal: "' : '",
      isOptional: false,
      desc: {
        'en-US': 'The separator between name and value.',
        'zh-CN': 'name 和 value 之间的分隔符。',
      },
    },
    {
      name: 'offset',
      type: 'Number',
      defaultVal: '10',
      isOptional: false,
      desc: {
        'en-US': 'The offset size between the position of tooltip and the active position.',
        'zh-CN': 'Tooltip 与当前“游标”的偏移量。',
      },
    },
    {
      name: 'filterNull',
      type: 'Boolean',
      defaultVal: 'true',
      isOptional: false,
      desc: {
        'en-US': "When an item of the payload has value null or undefined, this item won't be displayed.",
        'zh-CN': '当tooltip内容中的某条数据的值为 null 或者 undefined 时，不展示该条数据',
      },
    },
    {
      name: 'itemStyle',
      type: 'Object',
      defaultVal: '{}',
      isOptional: false,
      desc: {
        'en-US': 'The style of default tooltip content item which is a li element.',
        'zh-CN': 'Tooltip 中每个项目（使用 li 节点渲染的）的样式。',
      },
      examples: [
        {
          name: 'React Inline style',
          url: 'https://reactjs.org/docs/dom-elements.html#style',
          isExternal: true,
        },
      ],
    },
    {
      name: 'wrapperStyle',
      type: 'Object',
      defaultVal: '{}',
      isOptional: false,
      desc: {
        'en-US': 'The style of tooltip wrapper which is a dom element.',
        'zh-CN': 'Tooltip 最外层节点（div）的样式。',
      },
      examples: [
        {
          name: 'React Inline style',
          url: 'https://reactjs.org/docs/dom-elements.html#style',
          isExternal: true,
        },
      ],
    },
    {
      name: 'contentStyle',
      type: 'Object',
      defaultVal: '{}',
      isOptional: false,
      desc: {
        'en-US': 'The style of tooltip content which is a dom element.',
        'zh-CN': 'Tooltip 内部展示的内容 (div) 的样式。',
      },
      examples: [
        {
          name: 'React Inline style',
          url: 'https://reactjs.org/docs/dom-elements.html#style',
          isExternal: true,
        },
      ],
    },
    {
      name: 'labelStyle',
      type: 'Object',
      defaultVal: '{}',
      isOptional: false,
      desc: {
        'en-US': 'The style of default tooltip label which is a p element.',
        'zh-CN': '类目文字标签（使用 <p /> 渲染）的样式。',
      },
      examples: [
        {
          name: 'React Inline style',
          url: 'https://reactjs.org/docs/dom-elements.html#style',
          isExternal: true,
        },
      ],
    },
    {
      name: 'cursor',
      type: 'Boolean | Object | ReactElement',
      defaultVal: 'true',
      isOptional: false,
      desc: {
        'en-US':
          'If set false, no cursor will be drawn when tooltip is active. If set a object, the option is the configuration of cursor. If set a React element, the option is the custom react element of drawing cursor.',
        'zh-CN':
          '游标相关配置，一般在类目图表展示 Tooltip 的时候，会渲染游标来标识相应的类目。如果值为 false，不渲染游标。当值为对象类型，会被解析为游标的属性，注意游标也是用 svg 元素渲染的。如果值为 React element，会克隆这个元素来展示自定义的游标。',
      },
      format: [
        '<Tooltip cursor={false} />',
        "<Tooltip cursor={{ stroke: 'red', strokeWidth: 2 }} />",
        '<Tooltip cursor={<CustomizedCursor />} />',
      ],
    },
    {
      name: 'viewBox',
      type: 'Object',
      defaultVal: 'null',
      isOptional: false,
      desc: {
        'en-US':
          'The box of viewing area, which has the shape of {x: someVal, y: someVal, width: someVal, height: someVal}, usually calculated internally.',
        'zh-CN': '图表的可视区域。通常通过 x、y、width、height 来描述。',
      },
      format: ['{ x: 0, y: 0, width: 400, height: 400 }'],
    },
    {
      name: 'allowEscapeViewBox',
      type: 'Object',
      defaultVal: '{ x: false, y: false }',
      isOptional: true,
      desc: {
        'en-US': 'This option allows the tooltip to extend beyond the viewBox of the chart itself.',
        'zh-CN': '此选项允许工具提示扩展到图表本身的viewBox之外。',
      },
      format: ['{ x: true }', '{ y: true }', '{ x: true, y: true }'],
    },
    {
      name: 'active',
      type: 'Boolean',
      defaultVal: 'false',
      isOptional: false,
      desc: {
        'en-US':
          'If set true, the tooltip is displayed. If set false, the tooltip is hidden, usually calculated internally.',
        'zh-CN':
          '是否处于激活状态。如果值为 true, tooltip 会显示出来。如果值为 false, tooltip 会被隐藏，这个值一般是图表内部控制的。',
      },
    },
    {
      name: 'position',
      type: 'Object',
      defaultVal: 'null',
      isOptional: true,
      desc: {
        'en-US': 'If this field is set, the tooltip position will be fixed and will not move anymore.',
        'zh-CN': '如果设置了此字段，则工具提示位置将固定不变，并且将不再移动。',
      },
      format: ['{ x: 100, y: 140 }'],
    },
    {
      name: 'coordinate',
      type: 'Object',
      defaultVal: '{ x: 0, y: 0 }',
      isOptional: false,
      desc: {
        'en-US': 'The coordinate of tooltip position, usually calculated internally.',
        'zh-CN': '用来描述位置的坐标，也是图表内部计算的值。',
      },
      format: ['{ x: 100, y: 140 }'],
    },
    {
      name: 'payload',
      type: 'Array',
      defaultVal: '[]',
      isOptional: false,
      desc: {
        'en-US': 'The source data of the content to be displayed in the tooltip, usually calculated internally.',
        'zh-CN': 'Tooltip 展示内容的源数据，通常是图表内部计算的。',
      },
      format: ["[{ name: '05-01', value: 12, unit: 'kg' }]"],
    },
    {
      name: 'label',
      type: 'String | Number',
      defaultVal: 'null',
      isOptional: true,
      desc: {
        'en-US': 'The label value which is active now, usually calculated internally.',
        'zh-CN': '当 Tooltip 显示出来的时候，表示类目文字标签。',
      },
    },
    {
      name: 'content',
      type: 'ReactElement | Function',
      defaultVal: 'null',
      isOptional: true,
      desc: {
        'en-US':
          'If set a React element, the option is the custom react element of rendering tooltip.  If set a function, the function will be called to render tooltip content.',
        'zh-CN':
          '定制 Tooltip 展示的内容。如果值为 React element，会克隆这个元素来渲染 Tooltip 的内容。如果值为函数，会调用这个函数来生成 Tooltip 的内容。',
      },
      format: [
        '<Tooltip content={<CustomizedTooltip external={external} />} />',
        '<Tooltip content={renderTooltip} />',
      ],
      examples: [
        {
          name: 'Customize tooltip content',
          url: '/examples/CustomContentOfTooltip',
        },
      ],
    },
    {
      name: 'formatter',
      type: 'Function',
      defaultVal: 'null',
      isOptional: true,
      desc: {
        'en-US':
          'The formatter function of value in tooltip. If you return an array, the first entry will be the formatted "value", and the second entry will be the formatted "name"',
        'zh-CN':
          '每个项目中“数值”的格式化函数。 如果函数返回值为数组格式，第一个元素是格式化后的“数值”，第二个元素是格式化后的“名称”',
      },
      format: [
        '(value, name, props) => ( return "formatted value" )',
        '(value, name, props) => ( return ["formatted value", "formatted name"， ] )',
      ],
    },
    {
      name: 'labelFormatter',
      type: 'Function',
      defaultVal: 'null',
      isOptional: true,
      desc: {
        'en-US': 'The formatter function of label in tooltip.',
        'zh-CN': '类目文字标签的格式化函数。',
      },
    },
    {
      name: 'itemSorter',
      type: 'Function',
      defaultVal: '() => -1',
      isOptional: false,
      desc: {
        'en-US': 'Sort function of payload',
        'zh-CN': '项目排序函数。',
      },
    },
    {
      name: 'isAnimationActive',
      type: 'Boolean',
      defaultVal: 'true in CSR, and false in SSR',
      isOptional: false,
      desc: {
        'en-US': 'If set false, animation of tooltip will be disabled.',
        'zh-CN': '当值为 false，不开启动画。',
      },
    },
    {
      name: 'animationBegin',
      type: 'Number',
      defaultVal: 0,
      isOptional: false,
      desc: {
        'en-US': 'Specifies when the animation should begin, the unit of this option is ms.',
        'zh-CN': '声明组件挂载或更新后，开始运行动画的间隔时长，单位为毫秒。',
      },
    },
    {
      name: 'animationDuration',
      type: 'Number',
      defaultVal: 1500,
      isOptional: false,
      desc: {
        'en-US': 'Specifies the duration of animation, the unit of this option is ms.',
        'zh-CN': '声明动画的运行时长，单位为毫秒。',
      },
    },
    {
      name: 'animationEasing',
      type: "'ease' | 'ease-in' | 'ease-out' | 'ease-in-out' | 'linear'",
      defaultVal: "'ease'",
      isOptional: false,
      desc: {
        'en-US': 'The type of easing function.',
        'zh-CN': '动画缓动函数的类型。',
      },
    },
  ],
  parentComponents: [
    'AreaChart',
    'BarChart',
    'LineChart',
    'ComposedChart',
    'PieChart',
    'RadarChart',
    'RadialBarChart',
    'ScatterChart',
    'Treemap',
  ],
};
<|endoftext|>


<commit_before>/**
 * @file xsdwebform.parser.xsd.js
 * XSD Schema to HTML5 Web Form
 * @author George Bouris <gb@eworx.gr>
 * @copyright Copyright (C) 2017 EEA, Eworx, George Bouris. All rights reserved.
 */
'use strict';

import XSDWebFormParserError from './xsdwebform.parser.error.js';

/**
 * Class XSDWebFormParserTags
 * Parser for XSD Schema Tags
 *
 * import { XmlDocument } from 'xmldoc';
 * import XSDWebFormParserLog from './xsdwebform.parser.log.js';
 * import XSDWebFormParserTags from './xsdwebform.parser.xsd.js';
 *
 * var myXml = new XmlDocument(XML TEXT);
 * var myXmlParser = new XSDWebFormParserTags();
 * myXmlParser.xsdParse(myxml)
 */
class XSDWebFormParserTags {
	/**
	 * Class constructor
	 */
	constructor() {
		this.ELEMENT_TYPES = {
			"xs:element": this.parseElement,
			"xs:import": this.parseImport,
			"xs:simpleType": this.parseSimpleType,
			"xs:complexType": this.parseComplexType,
			"xs:sequence": this.parseSequence,
			"xs:all": this.parseAll,
			"xs:attribute": this.parseAttribute,
			"xs:restriction": this.parseRestriction,
			"xs:annotation": this.parseAnnotation,
			"xs:documentation": this.parseDocumentation,
			"xs:enumeration": this.parseEnumeration,
			"xs:minInclusive": this.parseMinInclusive,
			"xs:maxInclusive": this.parseMaxInclusive,
			"xs:union": this.parseUnion,
			"xs:pattern": this.parsePattern,
			"xs:whiteSpace": this.parseWhiteSpace,
			"xs:minLength": this.parseMinLength,
			"xs:maxLength": this.parseMaxLength,
			"xs:totalDigits": this.parseTotalDigits,
			"dd:multiValueDelim": this.parseMultiValueDelim,
			"dd2:Keyword": this.parseKeyword,
			"dd2:Definition": this.parseDefinition,
			"dd3:Methodology": this.parseMethodology
		};
		this.showLog = true;
		this.verbose = true;

		//this.logger = new XSDWebFormParserLog();
	}

	/**
	 * xsdParse - Parse inner XML Document
	 * @param xsdItem
	 */
	xsdParse(xsdItem) {
		if (this.showLog)
			this.logger.showItemLog(xsdItem, this.verbose);
		// Loop through Tag's childNodes
		if (xsdItem.children) {
			for (let i = 0, l = xsdItem.children.length; i < l; i++) {
				if (xsdItem.children[i].type === "element") {
					try {
						// Parse Tag Tag
						this.parseXSDItem(xsdItem.children[i].name);
						xsdItem.children[i].xparent = xsdItem;
						xsdItem.children[i].level = xsdItem.level + 1;
						// Recursive call - Send Children nodes to the same function(xsdParse) => Send their Children to xsdParse and so on
						/*
						XML
						 <xml>
							<item name="test1"/>  <-- child 1
							<item name="test2"/>  <-- child 2
							<item name="test3">  <-- child 3
								<subitem name="stest1"/> <-- child 1 of Child 3
							</item>
						</xml>

						Tree Representation:
							              [ XML ]
							           /       |       \
							Child 1	Child 2  Child 3
									   |
									child 1
						*/
						this.xsdParse(xsdItem.children[i]);
					} catch (err) {
						XSDWebFormParserError.reportError(err);
					}
				}
			}
		}
	}

	/**
	 * parseXSDItem - Parse XML Tag
	 * @param item
	 */
	parseXSDItem(item) {
		if ((item in this.ELEMENT_TYPES)) {
			/*
			 Execute hash value=>function

			 i.e. ELEMENT_TYPES["xs:element"] => (this.parseElement)(item, this)
			 Parse XML => Look for xs:element => Execute task this.parseElement(item<- xml node, this<-sender)
			 in this.parseElement():

			 Attributes: Find the attributes. e.g. <xs:element name="CountryCode"> | item.attr.name = CountryCode
			 Value: Find the value e.g. <xs:element name="CountryCode">My Value</xs:element> | item.val = My Value

			 Or

			 Attributes: Find the attributes. e.g. <xs:element name="CountryCode" value="My Value"> | item.attr.name = CountryCode, item.attr.value = My Value
			 Then, expand
			 this.CountryCodes =  {
				"My Value" : this.executeTaskMyValue,
				"Another Value" : this.executeTaskAnotherValue,
			 }

			 Or
			 this.executeTask(item.attr.value, sender)
			 executeTask(whatToExecute, sender)
			 */
			(this.ELEMENT_TYPES[item])(item, this);
		} else {
			console.log(`\n\x1b[1m\x1b[31m************* Unknown Tag {${item}} *************\x1b[0m\n`);
			process.stdout.write('\x07');
		}
	}

	/**
	 * parseElement- Parse Element Tag
	 * @param item
	 * @param sender
	 */

	parseElement(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseImport- Parse Import Tag
	 * @param item
	 * @param sender
	 */
	parseImport(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseSimpleType - Parse SimpleType Tag
	 * @param item
	 * @param sender
	 */
	parseSimpleType(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseComplexType - Parse ComplexType Tag
	 * @param item
	 * @param sender
	 */
	parseComplexType(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseSequence - Parse Sequence Tag
	 * @param item
	 * @param sender
	 */
	parseSequence(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseAll - Parse All Tag
	 * @param item
	 * @param sender
	 */
	parseAll(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseAttribute - Parse Attribute Tag
	 * @param item
	 * @param sender
	 */
	parseAttribute(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseRestriction - Parse Restriction Tag
	 * @param item
	 * @param sender
	 */
	parseRestriction(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}
	/**
	 * parseAnnotation - Parse Annotation Tag
	 * @param item
	 * @param sender
	 */

	parseAnnotation(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}
	/**
	 * parseDocumentation - Parse Documentation Tag
	 * @param item
	 * @param sender
	 */

	parseDocumentation(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseEnumeration - Parse Enumeration Tag
	 * @param item
	 * @param sender
	 */
	parseEnumeration(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseMinInclusive - Parse MinInclusive Tag
	 * @param item
	 * @param sender
	 */
	parseMinInclusive(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseMaxInclusive - Parse MaxInclusive Tag
	 * @param item
	 * @param sender
	 */
	parseMaxInclusive(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseUnion - Parse Union Tag
	 * @param item
	 * @param sender
	 */
	parseUnion(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parsePattern - Parse Pattern Tag
	 * @param item
	 * @param sender
	 */
	parsePattern(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseWhiteSpace - Parse WhiteSpace Tag
	 * @param item
	 * @param sender
	 */
	parseWhiteSpace(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseMinLength - Parse MinLength Tag
	 * @param item
	 * @param sender
	 */
	parseMinLength(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseMaxLength - Parse MaxLength Tag
	 * @param item
	 * @param sender
	 */
	parseMaxLength(item, sender) {

		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseTotalDigits - Parse TotalDigits Tag
	 * @param item
	 * @param sender
	 */
	parseTotalDigits(item, sender) {

		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseMultiValueDelim - Parse MultivalueDelim Tag
	 * @param item
	 * @param sender
	 */
	parseMultiValueDelim(item, sender) {

		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseKeyword - Parse Keyword Tag
	 * @param item
	 * @param sender
	 */
	parseKeyword(item, sender) {

		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseDefinition - Parse Definition Tag
	 * @param item
	 * @param sender
	 */
	parseDefinition(item, sender) {

		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseMethodology - Parse Methodology Tag
	 * @param item
	 * @param sender
	 */
	parseMethodology(item, sender) {

		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * setLogger
	 * @param logger
	 */
	setLogger(logger) {
		this.logger = logger;
	}

	/**
	 * setLog  - Show Log
	 * @param bool
	 */
	setLog(bool) {
		this.showLog = bool;
	}

	/**
	 * setVerbose - Show Log verbose
	 * @param bool
	 */
	setVerbose(bool) {
		this.verbose = bool;
	}

	/**
	 * getLog - Show Log
	 */
	getLog() {
		return this.showLog;
	}

	/**
	 * getLog - Show Log
	 */
	getVerbose() {
		return this.verbose;
	}

}

module.exports = XSDWebFormParserTags;<commit_msg>Updated XSDWebFormParserTags<commit_after>/**
 * @file xsdwebform.parser.xsd.js
 * XSD Schema to HTML5 Web Form
 * @author George Bouris <gb@eworx.gr>
 * @copyright Copyright (C) 2017 EEA, Eworx, George Bouris. All rights reserved.
 */
'use strict';

import XSDWebFormParserError from './xsdwebform.parser.error.js';

/**
 * Class XSDWebFormParserTags
 * Parser for XSD Schema Tags
 *
 * import { XmlDocument } from 'xmldoc';
 * import XSDWebFormParserLog from './xsdwebform.parser.log.js';
 * import XSDWebFormParserTags from './xsdwebform.parser.xsd.js';
 *
 * var myXml = new XmlDocument(XML TEXT);
 * var myXmlParser = new XSDWebFormParserTags();
 * myXmlParser.xsdParse(myxml)
 */
class XSDWebFormParserTags {
	/**
	 * Class constructor
	 */
	constructor() {
		this.ELEMENT_TYPES = {
			"xs:element": this.parseElement,
			"xs:import": this.parseImport,
			"xs:simpleType": this.parseSimpleType,
			"xs:complexType": this.parseComplexType,
			"xs:sequence": this.parseSequence,
			"xs:all": this.parseAll,
			"xs:attribute": this.parseAttribute,
			"xs:restriction": this.parseRestriction,
			"xs:annotation": this.parseAnnotation,
			"xs:documentation": this.parseDocumentation,
			"xs:enumeration": this.parseEnumeration,
			"xs:minInclusive": this.parseMinInclusive,
			"xs:maxInclusive": this.parseMaxInclusive,
			"xs:union": this.parseUnion,
			"xs:pattern": this.parsePattern,
			"xs:whiteSpace": this.parseWhiteSpace,
			"xs:minLength": this.parseMinLength,
			"xs:maxLength": this.parseMaxLength,
			"xs:totalDigits": this.parseTotalDigits,
			"dd:multiValueDelim": this.parseMultiValueDelim,
			"dd2:Keyword": this.parseKeyword,
			"dd2:Definition": this.parseDefinition,
			"dd3:Methodology": this.parseMethodology
		};
		this.showLog = true;
		this.verbose = true;

		//this.logger = new XSDWebFormParserLog();
	}

	/**
	 * xsdParse - Parse inner XML Document
	 * @param xsdItem
	 */
	xsdParse(xsdItem) {
		if (this.showLog)
			this.logger.showItemLog(xsdItem, this.verbose);
		// Loop through Tag's childNodes
		if (xsdItem.children) {
			for (let i = 0, l = xsdItem.children.length; i < l; i++) {
				if (xsdItem.children[i].type === "element") {
					try {
						// Parse Tag Tag
						this.parseXSDItem(xsdItem.children[i].name);
						xsdItem.children[i].xparent = xsdItem;
						xsdItem.children[i].level = xsdItem.level + 1;
						// Recursive call - Send Children nodes to the same function(xsdParse) => Send their Children to xsdParse and so on
						/*
						XML ( Markup Language : [boxes] [box1/][box2/][/boxes] )
						 <xml>
							<item name="test1"/>  <-- child 1
							<item name="test2"/>  <-- child 2
							<item name="test3">  <-- child 3
								<subitem name="stest1"/> <-- child 1 of Child 3
							</item>
						</xml>

						Tree Representation:
							              [ XML ]
							           /       |       \
							Child 1	Child 2  Child 3
									   |
									child 1
						*/
						this.xsdParse(xsdItem.children[i]);
					} catch (err) {
						XSDWebFormParserError.reportError(err);
					}
				}
			}
		}
	}

	/**
	 * parseXSDItem - Parse XML Tag
	 * @param item
	 */
	parseXSDItem(item) {
		if ((item in this.ELEMENT_TYPES)) {
			/*
			 Execute hash value=>function

			 i.e. ELEMENT_TYPES["xs:element"] => (this.parseElement)(item, this)
			 Parse XML => Look for xs:element => Execute task this.parseElement(item<- xml node, this<-sender)
			 in this.parseElement():

			 Attributes: Find the attributes. e.g. <xs:element name="CountryCode"> | item.attr.name = CountryCode
			 Value: Find the value e.g. <xs:element name="CountryCode">My Value</xs:element> | item.val = My Value

			 Or

			 Attributes: Find the attributes. e.g. <xs:element name="CountryCode" value="My Value"> | item.attr.name = CountryCode, item.attr.value = My Value
			 Then, expand
			 this.CountryCodes =  {
				"My Value" : this.executeTaskMyValue,
				"Another Value" : this.executeTaskAnotherValue,
			 }

			 Or
			 this.executeTask(item.attr.value, sender)
			 executeTask(whatToExecute, sender)
			 */
			(this.ELEMENT_TYPES[item])(item, this);
		} else {
			console.log(`\n\x1b[1m\x1b[31m************* Unknown Tag {${item}} *************\x1b[0m\n`);
			process.stdout.write('\x07');
		}
	}

	/**
	 * parseElement- Parse Element Tag
	 * @param item
	 * @param sender
	 */

	parseElement(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseImport- Parse Import Tag
	 * @param item
	 * @param sender
	 */
	parseImport(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseSimpleType - Parse SimpleType Tag
	 * @param item
	 * @param sender
	 */
	parseSimpleType(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseComplexType - Parse ComplexType Tag
	 * @param item
	 * @param sender
	 */
	parseComplexType(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseSequence - Parse Sequence Tag
	 * @param item
	 * @param sender
	 */
	parseSequence(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseAll - Parse All Tag
	 * @param item
	 * @param sender
	 */
	parseAll(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseAttribute - Parse Attribute Tag
	 * @param item
	 * @param sender
	 */
	parseAttribute(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseRestriction - Parse Restriction Tag
	 * @param item
	 * @param sender
	 */
	parseRestriction(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}
	/**
	 * parseAnnotation - Parse Annotation Tag
	 * @param item
	 * @param sender
	 */

	parseAnnotation(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}
	/**
	 * parseDocumentation - Parse Documentation Tag
	 * @param item
	 * @param sender
	 */

	parseDocumentation(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseEnumeration - Parse Enumeration Tag
	 * @param item
	 * @param sender
	 */
	parseEnumeration(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseMinInclusive - Parse MinInclusive Tag
	 * @param item
	 * @param sender
	 */
	parseMinInclusive(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseMaxInclusive - Parse MaxInclusive Tag
	 * @param item
	 * @param sender
	 */
	parseMaxInclusive(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseUnion - Parse Union Tag
	 * @param item
	 * @param sender
	 */
	parseUnion(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parsePattern - Parse Pattern Tag
	 * @param item
	 * @param sender
	 */
	parsePattern(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseWhiteSpace - Parse WhiteSpace Tag
	 * @param item
	 * @param sender
	 */
	parseWhiteSpace(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseMinLength - Parse MinLength Tag
	 * @param item
	 * @param sender
	 */
	parseMinLength(item, sender) {
		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseMaxLength - Parse MaxLength Tag
	 * @param item
	 * @param sender
	 */
	parseMaxLength(item, sender) {

		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseTotalDigits - Parse TotalDigits Tag
	 * @param item
	 * @param sender
	 */
	parseTotalDigits(item, sender) {

		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseMultiValueDelim - Parse MultivalueDelim Tag
	 * @param item
	 * @param sender
	 */
	parseMultiValueDelim(item, sender) {

		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseKeyword - Parse Keyword Tag
	 * @param item
	 * @param sender
	 */
	parseKeyword(item, sender) {

		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseDefinition - Parse Definition Tag
	 * @param item
	 * @param sender
	 */
	parseDefinition(item, sender) {

		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * parseMethodology - Parse Methodology Tag
	 * @param item
	 * @param sender
	 */
	parseMethodology(item, sender) {

		if (sender.showLog)
			sender.logger.logXsdTag(item);
	}

	/**
	 * setLogger
	 * @param logger
	 */
	setLogger(logger) {
		this.logger = logger;
	}

	/**
	 * setLog  - Show Log
	 * @param bool
	 */
	setLog(bool) {
		this.showLog = bool;
	}

	/**
	 * setVerbose - Show Log verbose
	 * @param bool
	 */
	setVerbose(bool) {
		this.verbose = bool;
	}

	/**
	 * getLog - Show Log
	 */
	getLog() {
		return this.showLog;
	}

	/**
	 * getLog - Show Log
	 */
	getVerbose() {
		return this.verbose;
	}

}

module.exports = XSDWebFormParserTags;<|endoftext|>


<commit_before>/*
 * L.PosAnimation is used by Leaflet internally for pan animations.
 */

L.PosAnimation = L.Evented.extend({

	run: function (el, newPos, duration, easeLinearity) { // (HTMLElement, Point[, Number, Number])
		this.stop();

		this._el = el;
		this._inProgress = true;
		this._newPos = newPos;

		this.fire('start');

		el.style[L.DomUtil.TRANSITION] = 'all ' + (duration || 0.25) +
		        's cubic-bezier(0,0,' + (easeLinearity || 0.5) + ',1)';

		L.DomEvent.on(el, L.DomUtil.TRANSITION_END, this._onTransitionEnd, this);
		L.DomUtil.setPosition(el, newPos);

		// toggle reflow, Chrome flickers for some reason if you don't do this
		L.Util.falseFn(el.offsetWidth);

		// there's no native way to track value updates of transitioned properties, so we imitate this
		this._stepTimer = setInterval(L.bind(this._onStep, this), 50);
	},

	stop: function () {
		if (!this._inProgress) { return; }

		// if we just removed the transition property, the element would jump to its final position,
		// so we need to make it stay at the current position

                // Only setPosition if _getPos actually returns a valid position.
		this._newPos = this._getPos();
		if (this._newPos) {
		    L.DomUtil.setPosition(this._el, this._newPos);
		}

		this._onTransitionEnd();
		L.Util.falseFn(this._el.offsetWidth); // force reflow in case we are about to start a new animation
	},

	_onStep: function () {
		var stepPos = this._getPos();
		if (!stepPos) {
			this._onTransitionEnd();
			return;
		}
		// jshint camelcase: false
		// make L.DomUtil.getPosition return intermediate position value during animation
		this._el._leaflet_pos = stepPos;

		this.fire('step');
	},

	// you can't easily get intermediate values of properties animated with CSS3 Transitions,
	// we need to parse computed style (in case of transform it returns matrix string)

	_transformRe: /([-+]?(?:\d*\.)?\d+)\D*, ([-+]?(?:\d*\.)?\d+)\D*\)/,

	_getPos: function () {
		var left, top, matches,
		    el = this._el,
		    style = window.getComputedStyle(el);

		if (L.Browser.any3d) {
			matches = style[L.DomUtil.TRANSFORM].match(this._transformRe);
			if (!matches) { return; }
			left = parseFloat(matches[1]);
			top  = parseFloat(matches[2]);
		} else {
			left = parseFloat(style.left);
			top  = parseFloat(style.top);
		}

		return new L.Point(left, top, true);
	},

	_onTransitionEnd: function () {
		L.DomEvent.off(this._el, L.DomUtil.TRANSITION_END, this._onTransitionEnd, this);

		if (!this._inProgress) { return; }
		this._inProgress = false;

		this._el.style[L.DomUtil.TRANSITION] = '';

		// jshint camelcase: false
		// make sure L.DomUtil.getPosition returns the final position value after animation
		this._el._leaflet_pos = this._newPos;

		clearInterval(this._stepTimer);

		this.fire('step').fire('end');
	}

});
<commit_msg>Performance: this triggers a reflow, but newest Chrome doesn’t flicker anymore.<commit_after>/*
 * L.PosAnimation is used by Leaflet internally for pan animations.
 */

L.PosAnimation = L.Evented.extend({

	run: function (el, newPos, duration, easeLinearity) { // (HTMLElement, Point[, Number, Number])
		this.stop();

		this._el = el;
		this._inProgress = true;
		this._newPos = newPos;

		this.fire('start');

		el.style[L.DomUtil.TRANSITION] = 'all ' + (duration || 0.25) +
		        's cubic-bezier(0,0,' + (easeLinearity || 0.5) + ',1)';

		L.DomEvent.on(el, L.DomUtil.TRANSITION_END, this._onTransitionEnd, this);
		L.DomUtil.setPosition(el, newPos);

		// there's no native way to track value updates of transitioned properties, so we imitate this
		this._stepTimer = setInterval(L.bind(this._onStep, this), 50);
	},

	stop: function () {
		if (!this._inProgress) { return; }

		// if we just removed the transition property, the element would jump to its final position,
		// so we need to make it stay at the current position

                // Only setPosition if _getPos actually returns a valid position.
		this._newPos = this._getPos();
		if (this._newPos) {
		    L.DomUtil.setPosition(this._el, this._newPos);
		}

		this._onTransitionEnd();
		L.Util.falseFn(this._el.offsetWidth); // force reflow in case we are about to start a new animation
	},

	_onStep: function () {
		var stepPos = this._getPos();
		if (!stepPos) {
			this._onTransitionEnd();
			return;
		}
		// jshint camelcase: false
		// make L.DomUtil.getPosition return intermediate position value during animation
		this._el._leaflet_pos = stepPos;

		this.fire('step');
	},

	// you can't easily get intermediate values of properties animated with CSS3 Transitions,
	// we need to parse computed style (in case of transform it returns matrix string)

	_transformRe: /([-+]?(?:\d*\.)?\d+)\D*, ([-+]?(?:\d*\.)?\d+)\D*\)/,

	_getPos: function () {
		var left, top, matches,
		    el = this._el,
		    style = window.getComputedStyle(el);

		if (L.Browser.any3d) {
			matches = style[L.DomUtil.TRANSFORM].match(this._transformRe);
			if (!matches) { return; }
			left = parseFloat(matches[1]);
			top  = parseFloat(matches[2]);
		} else {
			left = parseFloat(style.left);
			top  = parseFloat(style.top);
		}

		return new L.Point(left, top, true);
	},

	_onTransitionEnd: function () {
		L.DomEvent.off(this._el, L.DomUtil.TRANSITION_END, this._onTransitionEnd, this);

		if (!this._inProgress) { return; }
		this._inProgress = false;

		this._el.style[L.DomUtil.TRANSITION] = '';

		// jshint camelcase: false
		// make sure L.DomUtil.getPosition returns the final position value after animation
		this._el._leaflet_pos = this._newPos;

		clearInterval(this._stepTimer);

		this.fire('step').fire('end');
	}

});
<|endoftext|>


<commit_before>/*
 * Copyright © 2016-2020 The Thingsboard Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import './event.scss';

/* eslint-disable import/no-unresolved, import/default */

import eventTableTemplate from './event-table.tpl.html';

/* eslint-enable import/no-unresolved, import/default */

/*@ngInject*/
export default function EventTableDirective($compile, $templateCache, $rootScope, types,
                                            eventService, edgeService, attributeService) {

    var linker = function (scope, element, attrs) {

        var template = $templateCache.get(eventTableTemplate);

        element.html(template);

        scope.eventTypeScope = angular.copy(types.eventType);
        if (scope.entityType !== types.entityType.edge) {
            delete scope.eventTypeScope.edgeEvent;
        }

        if (attrs.disabledEventTypes) {
            var disabledEventTypes = attrs.disabledEventTypes.split(',');
            scope.eventTypes = {};
            for (var type in scope.eventTypeScope) {
                var eventType = scope.eventTypeScope[type];
                var enabled = true;
                for (var i=0;i<disabledEventTypes.length;i++) {
                    if (eventType.value === disabledEventTypes[i]) {
                        enabled = false;
                        break;
                    }
                }
                if (enabled) {
                    scope.eventTypes[type] = eventType;
                }
            }
        } else {
            scope.eventTypes = angular.copy(scope.eventTypeScope);
        }

        if (attrs.debugEventTypes) {
            var debugEventTypes = attrs.debugEventTypes.split(',');
            for (i=0;i<debugEventTypes.length;i++) {
                for (type in types.debugEventType) {
                    eventType = types.debugEventType[type];
                    if (eventType.value === debugEventTypes[i]) {
                        scope.eventTypes[type] = eventType;
                    }
                }
            }
        }

        scope.eventType = attrs.defaultEventType;

        var pageSize = 20;
        var startTime = 0;
        var endTime = 0;

        scope.timewindow = {
            history: {
                timewindowMs: 24 * 60 * 60 * 1000 // 1 day
            }
        }

        scope.topIndex = 0;

        scope.theEvents = {
            getItemAtIndex: function (index) {
                if (index > scope.events.data.length) {
                    scope.theEvents.fetchMoreItems_(index);
                    return null;
                }
                var item = scope.events.data[index];
                if (item) {
                    item.indexNumber = index + 1;
                }
                return item;
            },

            getLength: function () {
                if (scope.events.hasNext) {
                    return scope.events.data.length + scope.events.nextPageLink.limit;
                } else {
                    return scope.events.data.length;
                }
            },

            fetchMoreItems_: function () {
                if (scope.events.hasNext && !scope.events.pending) {
                    if (scope.entityType && scope.entityId && scope.eventType && scope.tenantId) {
                        var promise = '';
                        if (scope.eventType !== types.eventType.edgeEvent.value) {
                            promise = eventService.getEvents(scope.entityType, scope.entityId,
                                scope.eventType, scope.tenantId, scope.events.nextPageLink);
                        } else {
                            promise = edgeService.getEdgeEvents(scope.entityId, scope.events.nextPageLink);
                            scope.loadEdgeInfo();
                        }
                        if (promise) {
                            scope.events.pending = true;
                            promise.then(
                                function success(events) {
                                    scope.events.data = scope.events.data.concat(events.data);
                                    scope.events.nextPageLink = events.nextPageLink;
                                    scope.events.hasNext = events.hasNext;
                                    if (scope.events.hasNext) {
                                        scope.events.nextPageLink.limit = pageSize;
                                    }
                                    scope.events.pending = false;
                                },
                                function fail() {
                                    scope.events.hasNext = false;
                                    scope.events.pending = false;
                                });
                        } else {
                            scope.events.hasNext = false;
                        }
                    } else {
                        scope.events.hasNext = false;
                    }
                }
            }
        };

        scope.$watch("entityId", function(newVal, prevVal) {
            if (newVal && !angular.equals(newVal, prevVal)) {
                scope.loadEdgeInfo();
                scope.resetFilter();
                scope.reload();
            }
        });

        scope.$watch("eventType", function(newVal, prevVal) {
            if (newVal && !angular.equals(newVal, prevVal)) {
                scope.reload();
            }
        });

        scope.$watch("timewindow", function(newVal, prevVal) {
            if (newVal && !angular.equals(newVal, prevVal)) {
                scope.reload();
            }
        }, true);

        scope.resetFilter = function() {
            scope.timewindow = {
                history: {
                    timewindowMs: 24 * 60 * 60 * 1000 // 1 day
                }
            };
            scope.eventType = attrs.defaultEventType;
        }

        scope.updateTimeWindowRange = function() {
            if (scope.timewindow.history.timewindowMs) {
                var currentTime = (new Date).getTime();
                startTime = currentTime - scope.timewindow.history.timewindowMs;
                endTime = currentTime;
            } else {
                startTime = scope.timewindow.history.fixedTimewindow.startTimeMs;
                endTime = scope.timewindow.history.fixedTimewindow.endTimeMs;
            }
        }

        scope.reload = function() {
            scope.topIndex = 0;
            scope.selected = [];
            scope.updateTimeWindowRange();
            scope.events = {
                data: [],
                nextPageLink: {
                    limit: pageSize,
                    startTime: startTime,
                    endTime: endTime
                },
                hasNext: true,
                pending: false
            };
            scope.theEvents.getItemAtIndex(pageSize);
        }

        scope.noData = function() {
            return scope.events.data.length == 0 && !scope.events.hasNext;
        }

        scope.hasData = function() {
            return scope.events.data.length > 0;
        }

        scope.loading = function() {
            return $rootScope.loading;
        }

        scope.hasScroll = function() {
            var repeatContainer = scope.repeatContainer[0];
            if (repeatContainer) {
                var scrollElement = repeatContainer.children[0];
                if (scrollElement) {
                    return scrollElement.scrollHeight > scrollElement.clientHeight;
                }
            }
            return false;
        }

        scope.subscriptionId = null;
        scope.queueStartTs;

        scope.loadEdgeInfo = function() {
            attributeService.getEntityAttributesValues(scope.entityType, scope.entityId, types.attributesScope.server.value,
                types.edgeAttributeKeys.queueStartTs, {})
                .then(function success(attributes) {
                    scope.onUpdate(attributes);
                });

            scope.checkSubscription();

            attributeService.getEntityAttributes(scope.entityType, scope.entityId, types.attributesScope.server.value, {order: '', limit: 1, page: 1, search: ''},
                function (attributes) {
                    if (attributes && attributes.data) {
                        scope.onUpdate(attributes.data);
                    }
                });
        }

        scope.onUpdate = function(attributes) {
            let edge = attributes.reduce(function (map, attribute) {
                map[attribute.key] = attribute;
                return map;
            }, {});
            if (edge.queueStartTs) {
                scope.queueStartTs = edge.queueStartTs.lastUpdateTs;
            }
        }

        scope.checkSubscription = function() {
            var newSubscriptionId = null;
            if (scope.entityId && scope.entityType && types.attributesScope.server.value) {
                newSubscriptionId = attributeService.subscribeForEntityAttributes(scope.entityType, scope.entityId, types.attributesScope.server.value);
            }
            if (scope.subscriptionId && scope.subscriptionId != newSubscriptionId) {
                attributeService.unsubscribeForEntityAttributes(scope.subscriptionId);
            }
            scope.subscriptionId = newSubscriptionId;
        }

        scope.$on('$destroy', function () {
            if (scope.subscriptionId) {
                attributeService.unsubscribeForEntityAttributes(scope.subscriptionId);
            }
        });

        scope.reload();

        $compile(element.contents())(scope);
    }

    return {
        restrict: "E",
        link: linker,
        scope: {
            entityType: '=',
            entityId: '=',
            tenantId: '='
        }
    };
}
<commit_msg>Removed 2 scope.loadEdgeInfo() lines<commit_after>/*
 * Copyright © 2016-2020 The Thingsboard Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import './event.scss';

/* eslint-disable import/no-unresolved, import/default */

import eventTableTemplate from './event-table.tpl.html';

/* eslint-enable import/no-unresolved, import/default */

/*@ngInject*/
export default function EventTableDirective($compile, $templateCache, $rootScope, types,
                                            eventService, edgeService, attributeService) {

    var linker = function (scope, element, attrs) {

        var template = $templateCache.get(eventTableTemplate);

        element.html(template);

        scope.eventTypeScope = angular.copy(types.eventType);
        if (scope.entityType !== types.entityType.edge) {
            delete scope.eventTypeScope.edgeEvent;
        }

        if (attrs.disabledEventTypes) {
            var disabledEventTypes = attrs.disabledEventTypes.split(',');
            scope.eventTypes = {};
            for (var type in scope.eventTypeScope) {
                var eventType = scope.eventTypeScope[type];
                var enabled = true;
                for (var i=0;i<disabledEventTypes.length;i++) {
                    if (eventType.value === disabledEventTypes[i]) {
                        enabled = false;
                        break;
                    }
                }
                if (enabled) {
                    scope.eventTypes[type] = eventType;
                }
            }
        } else {
            scope.eventTypes = angular.copy(scope.eventTypeScope);
        }

        if (attrs.debugEventTypes) {
            var debugEventTypes = attrs.debugEventTypes.split(',');
            for (i=0;i<debugEventTypes.length;i++) {
                for (type in types.debugEventType) {
                    eventType = types.debugEventType[type];
                    if (eventType.value === debugEventTypes[i]) {
                        scope.eventTypes[type] = eventType;
                    }
                }
            }
        }

        scope.eventType = attrs.defaultEventType;

        var pageSize = 20;
        var startTime = 0;
        var endTime = 0;

        scope.timewindow = {
            history: {
                timewindowMs: 24 * 60 * 60 * 1000 // 1 day
            }
        }

        scope.topIndex = 0;

        scope.theEvents = {
            getItemAtIndex: function (index) {
                if (index > scope.events.data.length) {
                    scope.theEvents.fetchMoreItems_(index);
                    return null;
                }
                var item = scope.events.data[index];
                if (item) {
                    item.indexNumber = index + 1;
                }
                return item;
            },

            getLength: function () {
                if (scope.events.hasNext) {
                    return scope.events.data.length + scope.events.nextPageLink.limit;
                } else {
                    return scope.events.data.length;
                }
            },

            fetchMoreItems_: function () {
                if (scope.events.hasNext && !scope.events.pending) {
                    if (scope.entityType && scope.entityId && scope.eventType && scope.tenantId) {
                        var promise = '';
                        if (scope.eventType !== types.eventType.edgeEvent.value) {
                            promise = eventService.getEvents(scope.entityType, scope.entityId,
                                scope.eventType, scope.tenantId, scope.events.nextPageLink);
                        } else {
                            promise = edgeService.getEdgeEvents(scope.entityId, scope.events.nextPageLink);
                        }
                        if (promise) {
                            scope.events.pending = true;
                            promise.then(
                                function success(events) {
                                    scope.events.data = scope.events.data.concat(events.data);
                                    scope.events.nextPageLink = events.nextPageLink;
                                    scope.events.hasNext = events.hasNext;
                                    if (scope.events.hasNext) {
                                        scope.events.nextPageLink.limit = pageSize;
                                    }
                                    scope.events.pending = false;
                                },
                                function fail() {
                                    scope.events.hasNext = false;
                                    scope.events.pending = false;
                                });
                        } else {
                            scope.events.hasNext = false;
                        }
                    } else {
                        scope.events.hasNext = false;
                    }
                }
            }
        };

        scope.$watch("entityId", function(newVal, prevVal) {
            if (newVal && !angular.equals(newVal, prevVal)) {
                scope.resetFilter();
                scope.reload();
            }
        });

        scope.$watch("eventType", function(newVal, prevVal) {
            if (newVal && !angular.equals(newVal, prevVal)) {
                scope.reload();
            }
        });

        scope.$watch("timewindow", function(newVal, prevVal) {
            if (newVal && !angular.equals(newVal, prevVal)) {
                scope.reload();
            }
        }, true);

        scope.resetFilter = function() {
            scope.timewindow = {
                history: {
                    timewindowMs: 24 * 60 * 60 * 1000 // 1 day
                }
            };
            scope.eventType = attrs.defaultEventType;
        }

        scope.updateTimeWindowRange = function() {
            if (scope.timewindow.history.timewindowMs) {
                var currentTime = (new Date).getTime();
                startTime = currentTime - scope.timewindow.history.timewindowMs;
                endTime = currentTime;
            } else {
                startTime = scope.timewindow.history.fixedTimewindow.startTimeMs;
                endTime = scope.timewindow.history.fixedTimewindow.endTimeMs;
            }
        }

        scope.reload = function() {
            scope.topIndex = 0;
            scope.selected = [];
            scope.updateTimeWindowRange();
            scope.events = {
                data: [],
                nextPageLink: {
                    limit: pageSize,
                    startTime: startTime,
                    endTime: endTime
                },
                hasNext: true,
                pending: false
            };
            scope.theEvents.getItemAtIndex(pageSize);
        }

        scope.noData = function() {
            return scope.events.data.length == 0 && !scope.events.hasNext;
        }

        scope.hasData = function() {
            return scope.events.data.length > 0;
        }

        scope.loading = function() {
            return $rootScope.loading;
        }

        scope.hasScroll = function() {
            var repeatContainer = scope.repeatContainer[0];
            if (repeatContainer) {
                var scrollElement = repeatContainer.children[0];
                if (scrollElement) {
                    return scrollElement.scrollHeight > scrollElement.clientHeight;
                }
            }
            return false;
        }

        scope.subscriptionId = null;
        scope.queueStartTs;

        scope.loadEdgeInfo = function() {
            attributeService.getEntityAttributesValues(scope.entityType, scope.entityId, types.attributesScope.server.value,
                types.edgeAttributeKeys.queueStartTs, {})
                .then(function success(attributes) {
                    scope.onUpdate(attributes);
                });

            scope.checkSubscription();

            attributeService.getEntityAttributes(scope.entityType, scope.entityId, types.attributesScope.server.value, {order: '', limit: 1, page: 1, search: ''},
                function (attributes) {
                    if (attributes && attributes.data) {
                        scope.onUpdate(attributes.data);
                    }
                });
        }

        scope.onUpdate = function(attributes) {
            let edge = attributes.reduce(function (map, attribute) {
                map[attribute.key] = attribute;
                return map;
            }, {});
            if (edge.queueStartTs) {
                scope.queueStartTs = edge.queueStartTs.lastUpdateTs;
            }
        }

        scope.checkSubscription = function() {
            var newSubscriptionId = null;
            if (scope.entityId && scope.entityType && types.attributesScope.server.value) {
                newSubscriptionId = attributeService.subscribeForEntityAttributes(scope.entityType, scope.entityId, types.attributesScope.server.value);
            }
            if (scope.subscriptionId && scope.subscriptionId != newSubscriptionId) {
                attributeService.unsubscribeForEntityAttributes(scope.subscriptionId);
            }
            scope.subscriptionId = newSubscriptionId;
        }

        scope.$on('$destroy', function () {
            if (scope.subscriptionId) {
                attributeService.unsubscribeForEntityAttributes(scope.subscriptionId);
            }
        });

        scope.reload();

        $compile(element.contents())(scope);
    }

    return {
        restrict: "E",
        link: linker,
        scope: {
            entityType: '=',
            entityId: '=',
            tenantId: '='
        }
    };
}
<|endoftext|>


<commit_before>	"use strict";

var request = require("request"),
	util = require("util"),
	async = require("async"),
	mongoose = require("mongoose"),
	XmlDocument = require("xmldoc").XmlDocument,
	JournalModel = require("../models/JournalModel"),//can only have one model with a connection
	ArticleModel = require("../models/ArticleModelNoConnection");

var DocumentController = function(){
 	//any constructor needed?
 	//what about initialising queue of pmids
 	var pmidQueue = [];

 	return {
 		pmidQueue : pmidQueue,
 		getPmid: this.getPmid,
 		initQueue : this.initQueue,
 		getQueue: this.getQueue,
 		fetchArticleData: this.fetchArticleData,
 		toHash: this.toHash,
 		createDocument: this.createDocument,
 		checkJournalTitle: this.checkJournalTitle,
 		updateDocument: this.updateDocument,
 		validate : this.validate
 	}

};

DocumentController.prototype.initQueue = function(callback){
	console.log("hitting initQue")
		//queue consists of docs that do not have title fields.
		//ignore pmid = 0 because it means no data in pubmed.
		ArticleModel.find({year: {$exists: false}}, "pmid", function(err, arrayResult){
				if(err){
					callback(err);
				}else{
					//arrayResult contains objects with pmid and _id keys.
					callback(null, arrayResult);
				}
		});

}

DocumentController.prototype.getQueue = function(callback) {
	console.log("hitting getQue")
	//needs some initalisation so can be emptied
	//what will be in the queue? Should the pmid's ObjectId be there too?
	//how am I linking these pmids as references or citations?
	var pmidArray = [];

	this.initQueue(function(nullObj, pmidObjectArray){//I don't like this nullObj business being passed.

		for(var i = 0, l = pmidObjectArray.length; i < l; i++){
				var pmidObj = pmidObjectArray.pop()
				pmidArray.push(pmidObj["pmid"]);
		}
		callback(null, pmidArray);
	});

};

DocumentController.prototype.getPmid = function(callback){

	ArticleModel.findOne({year:{$exists:false}}, "pmid", function(err, result){
		if(err) console.log(err);
		console.log("pmid is: "+ result["pmid"]);
			callback(null, result["pmid"])
	});

	console.log("hitting getPmid")

		//var activePmidArray = [];//everytime getPmid is called this array goes down one
		//when array is empty, more pmids are sought from getQueue -> initQueue
		//

		// this.getQueue(function(nullObj, pmids){
		// 	console.log("remaining pmids to gather data for: "+ pmids.length)
		// 	var num = pmids.pop();

		// 	callback(null, num);
		// });
};

DocumentController.prototype.fetchArticleData = function(pmid, callback){

	this.pmid = pmid;

	var options = {
		url: "http://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?",
		qs: {
			db: "pubmed",
			id: this.pmid,
			retmode: "xml",
			email: "robertjsullivan.esq@gmail.com",
			project:"ccbrowser"

		}
	};

	request(options, function(err, response, body){
		if(!err){
		callback(null, response);//adding null in here because of async.waterfall sig - don't fully understand.
	}
	});

};

DocumentController.prototype.createDocument = function(callback){

	//get first item from queue.
	//send off request
	//"hash" the response
	//provide the doc to the callback
	//this.pmid = pmid;
	//var data;
	var self = this;
	var getQueue = this.getQueue;
	//var fetchArticleData = this.fetchArticleData;

	//async waterfall of functions
	async.waterfall([
		function(cb){
			self.getPmid(cb)// instead of getting the whole queue, how about a getPmid()?
		},
		function(pmid, cb){
			//var pmid = array.pop()
			console.log("pmid: " + pmid )
			self.fetchArticleData(pmid, cb);
		}, function(data, cb){
			self.toHash(data, cb);
		}], callback);

};
//rename
DocumentController.prototype.toHash = function(data, callback){

	this.data = data.body;
	var xmlResponse = new XmlDocument(this.data);
	var	doc = {};
	//journal ObjectId biz
	var ObjectId = mongoose.Types.ObjectId;
	var journalId;
	var journalObjectId;

	//extract sections
	var pubMedArticle = xmlResponse.childNamed("PubmedArticle");

	var medlineCitation = pubMedArticle.childNamed("MedlineCitation");

	var pubMedData = pubMedArticle.childNamed("PubmedData");

	var articleIdList = pubMedData.childNamed("ArticleIdList");

	//article fields - just assign. Assumes all these values are present
	//in XML. how to handle it is they are not?
	var title = medlineCitation.valueWithPath("Article.ArticleTitle")

	if(articleIdList.childWithAttribute("IdType", "doi") === undefined){
		var doi = undefined;
	}else{
		doi = articleIdList.childWithAttribute("IdType", "doi").val;
	}

	var pmid = articleIdList.childWithAttribute("IdType", "pubmed").val;
	if(articleIdList.childWithAttribute("IdType", "pmc") === undefined){
		var pmcid = undefined;
	}else{
		pmcid = pmcid = articleIdList.childWithAttribute("IdType", "pmc").val;
	}

	var abstract = medlineCitation.valueWithPath("Article.Abstract.AbstractText");

	var articleDate = medlineCitation.valueWithPath("Article.ArticleDate.Year"); //not alwasy present either.
	var author = medlineCitation.valueWithPath("Article.AuthorList.Author.LastName"); //surname only. first author in list taken
	var pubDate = medlineCitation.valueWithPath("Article.Journal.JournalIssue.PubDate.Year") || "0000";

	//now to add journal title
	var journalTitle = medlineCitation.valueWithPath("Article.Journal.ISOAbbreviation")

	//checkJournalTitle
	this.checkJournalTitle(journalTitle, function(result){
			//if any of these fields are undefined...?

			journalId = result["_id"].toString();
			journalObjectId = ObjectId(journalId);

			doc["title"] = title;
			doc["doi"] = doi;
			doc["pmid"] = pmid; //already have this because taking pmid from queue
			doc["pmcid"] = pmcid;

			doc["abstract"] = abstract;//if not undefined

			doc["year"] = pubDate;
			doc["author"] = author;
			doc["journal"] = journalObjectId;
			//remove any undefined fields after the fact?

			callback(doc);

	})

};

DocumentController.prototype.checkJournalTitle = function(journalTitle, callback){

	JournalModel.find({title: journalTitle})
					.select("ObjectId title")
					.exec(function(err, journal){
						if(err){
							console.log(err);
						}else if (journal.length === 0){//results array is empty

							JournalModel.addJournal(journalTitle, function(err, result){
								if(err){
									callback(err);
								}else{

									callback(result);
								}
							});
						}else{
							//passback the journalObj because a search result is an array.
							var journalObj = journal.pop();

							callback(journalObj);
						}
					});

};

DocumentController.prototype.validate = function(doc, callback){
	//not mongoose validate because that appears to be done in findOneAndUpdate op.
	//just want to check each required value is defined.
	//if not it needs to be removed from doc.
	//also set boolean value if pmcid exists.
	var vDoc;

	for(var i in doc){
		if(doc.hasOwnProperty(i)){
			if(doc[i] === undefined){
				delete doc[i];

			}

		}

	};
	console.log(doc)
	callback(doc)

}

DocumentController.prototype.updateDocument = function(doc, callback){
	//works and doesn't overwrite existing pmid but be careful
	//findONeAndUpdate calls mongo's findAndModify which returns the doc to app

	ArticleModel.findOneAndUpdate({pmid: doc["pmid"]}, doc, function(err, result){
		if(err){
			callback(err);
		}else{
			callback(result)
		}
	});

};

module.exports = DocumentController;<commit_msg>Changed query in getPmid method to find articles without a title field.<commit_after>	"use strict";

var request = require("request"),
	util = require("util"),
	async = require("async"),
	mongoose = require("mongoose"),
	XmlDocument = require("xmldoc").XmlDocument,
	JournalModel = require("../models/JournalModel"),//can only have one model with a connection
	ArticleModel = require("../models/ArticleModelNoConnection");

var DocumentController = function(){
 	//any constructor needed?
 	//what about initialising queue of pmids
 	var pmidQueue = [];

 	return {
 		pmidQueue : pmidQueue,
 		getPmid: this.getPmid,
 		initQueue : this.initQueue,
 		getQueue: this.getQueue,
 		fetchArticleData: this.fetchArticleData,
 		toHash: this.toHash,
 		createDocument: this.createDocument,
 		checkJournalTitle: this.checkJournalTitle,
 		updateDocument: this.updateDocument,
 		validate : this.validate
 	}

};

DocumentController.prototype.initQueue = function(callback){
	console.log("hitting initQue")
		//queue consists of docs that do not have title fields.
		//ignore pmid = 0 because it means no data in pubmed.
		ArticleModel.find({year: {$exists: false}}, "pmid", function(err, arrayResult){
				if(err){
					callback(err);
				}else{
					//arrayResult contains objects with pmid and _id keys.
					callback(null, arrayResult);
				}
		});

}

DocumentController.prototype.getQueue = function(callback) {
	console.log("hitting getQue")
	//needs some initalisation so can be emptied
	//what will be in the queue? Should the pmid's ObjectId be there too?
	//how am I linking these pmids as references or citations?
	var pmidArray = [];

	this.initQueue(function(nullObj, pmidObjectArray){//I don't like this nullObj business being passed.

		for(var i = 0, l = pmidObjectArray.length; i < l; i++){
				var pmidObj = pmidObjectArray.pop()
				pmidArray.push(pmidObj["pmid"]);
		}
		callback(null, pmidArray);
	});

};

DocumentController.prototype.getPmid = function(callback){

	ArticleModel.findOne({title:{$exists:false}}, "pmid", function(err, result){
		if(err) console.log(err);
		console.log("pmid is: "+ result["pmid"]);
			callback(null, result["pmid"])
	});

	console.log("hitting getPmid")

		//var activePmidArray = [];//everytime getPmid is called this array goes down one
		//when array is empty, more pmids are sought from getQueue -> initQueue
		//

		// this.getQueue(function(nullObj, pmids){
		// 	console.log("remaining pmids to gather data for: "+ pmids.length)
		// 	var num = pmids.pop();

		// 	callback(null, num);
		// });
};

DocumentController.prototype.fetchArticleData = function(pmid, callback){

	this.pmid = pmid;

	var options = {
		url: "http://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?",
		qs: {
			db: "pubmed",
			id: this.pmid,
			retmode: "xml",
			email: "robertjsullivan.esq@gmail.com",
			project:"ccbrowser"

		}
	};

	request(options, function(err, response, body){
		if(!err){
		callback(null, response);//adding null in here because of async.waterfall sig - don't fully understand.
	}
	});

};

DocumentController.prototype.createDocument = function(callback){

	//get first item from queue.
	//send off request
	//"hash" the response
	//provide the doc to the callback
	//this.pmid = pmid;
	//var data;
	var self = this;
	var getQueue = this.getQueue;
	//var fetchArticleData = this.fetchArticleData;

	//async waterfall of functions
	async.waterfall([
		function(cb){
			self.getPmid(cb)// instead of getting the whole queue, how about a getPmid()?
		},
		function(pmid, cb){
			//var pmid = array.pop()
			console.log("pmid: " + pmid )
			self.fetchArticleData(pmid, cb);
		}, function(data, cb){
			self.toHash(data, cb);
		}], callback);

};
//rename
DocumentController.prototype.toHash = function(data, callback){

	this.data = data.body;
	var xmlResponse = new XmlDocument(this.data);
	var	doc = {};
	//journal ObjectId biz
	var ObjectId = mongoose.Types.ObjectId;
	var journalId;
	var journalObjectId;

	//extract sections
	var pubMedArticle = xmlResponse.childNamed("PubmedArticle");

	var medlineCitation = pubMedArticle.childNamed("MedlineCitation");

	var pubMedData = pubMedArticle.childNamed("PubmedData");

	var articleIdList = pubMedData.childNamed("ArticleIdList");

	//article fields - just assign. Assumes all these values are present
	//in XML. how to handle it is they are not?
	var title = medlineCitation.valueWithPath("Article.ArticleTitle")

	if(articleIdList.childWithAttribute("IdType", "doi") === undefined){
		var doi = undefined;
	}else{
		doi = articleIdList.childWithAttribute("IdType", "doi").val;
	}

	var pmid = articleIdList.childWithAttribute("IdType", "pubmed").val;
	if(articleIdList.childWithAttribute("IdType", "pmc") === undefined){
		var pmcid = undefined;
	}else{
		pmcid = pmcid = articleIdList.childWithAttribute("IdType", "pmc").val;
	}

	var abstract = medlineCitation.valueWithPath("Article.Abstract.AbstractText");

	var articleDate = medlineCitation.valueWithPath("Article.ArticleDate.Year"); //not alwasy present either.
	var author = medlineCitation.valueWithPath("Article.AuthorList.Author.LastName"); //surname only. first author in list taken
	var pubDate = medlineCitation.valueWithPath("Article.Journal.JournalIssue.PubDate.Year") || "0000";

	//now to add journal title
	var journalTitle = medlineCitation.valueWithPath("Article.Journal.ISOAbbreviation")

	//checkJournalTitle
	this.checkJournalTitle(journalTitle, function(result){
			//if any of these fields are undefined...?

			journalId = result["_id"].toString();
			journalObjectId = ObjectId(journalId);

			doc["title"] = title;
			doc["doi"] = doi;
			doc["pmid"] = pmid; //already have this because taking pmid from queue
			doc["pmcid"] = pmcid;

			doc["abstract"] = abstract;//if not undefined

			doc["year"] = pubDate;
			doc["author"] = author;
			doc["journal"] = journalObjectId;
			//remove any undefined fields after the fact?

			callback(doc);

	})

};

DocumentController.prototype.checkJournalTitle = function(journalTitle, callback){

	JournalModel.find({title: journalTitle})
					.select("ObjectId title")
					.exec(function(err, journal){
						if(err){
							console.log(err);
						}else if (journal.length === 0){//results array is empty

							JournalModel.addJournal(journalTitle, function(err, result){
								if(err){
									callback(err);
								}else{

									callback(result);
								}
							});
						}else{
							//passback the journalObj because a search result is an array.
							var journalObj = journal.pop();

							callback(journalObj);
						}
					});

};

DocumentController.prototype.validate = function(doc, callback){
	//not mongoose validate because that appears to be done in findOneAndUpdate op.
	//just want to check each required value is defined.
	//if not it needs to be removed from doc.
	//also set boolean value if pmcid exists.
	var vDoc;

	for(var i in doc){
		if(doc.hasOwnProperty(i)){
			if(doc[i] === undefined){
				delete doc[i];

			}

		}

	};
	console.log(doc)
	callback(doc)

}

DocumentController.prototype.updateDocument = function(doc, callback){
	//works and doesn't overwrite existing pmid but be careful
	//findONeAndUpdate calls mongo's findAndModify which returns the doc to app

	ArticleModel.findOneAndUpdate({pmid: doc["pmid"]}, doc, function(err, result){
		if(err){
			callback(err);
		}else{
			callback(result)
		}
	});

};

module.exports = DocumentController;<|endoftext|>


<commit_before>import * as pickler from '../common/pickler';

const range = (from, to) => [...Array(to).keys()].slice(from);

class DSymbol {
  constructor(dim, sData, vData) {
    this._s = new Int32Array(sData);
    this._v = new Int32Array(vData);
    this._dim = dim;
    this._size = this._v.length / dim;
  }

  get dim() {
    return this._dim;
  }

  get size() {
    return this._size;
  }

  isElement(D) {
    return Number.isInteger(D) && D >= 1 && D <= this.size;
  }

  elements() {
    return range(1, this.size + 1);
  }

  isIndex(i) {
    return Number.isInteger(i) && i >= 0 && i <= this.dim;
  }

  indices() {
    return range(0, this.dim + 1);
  }

  s(i, D) {
    if (this.isElement(D) && this.isIndex(i))
      return this._s[i * this.size + D - 1];
  }

  v(i, j, D) {
    if (this.isElement(D) && this.isIndex(i) && this.isIndex(j)) {
      if (j == i+1)
        return this._v[i * this.size + D - 1];
      else if (j == i-1)
        return this._v[j * this.size + D - 1];
      else if (this.s(i, D) == this.s(j, D))
        return 2;
      else
        return 1;
    }
  }

  orbit2(i, j, D) {
    const seen = new Int8Array(this.size + 1);
    const result = [];

    let E = D;
    do {
      for (const k of [i, j]) {
        E = this.s(k, E) || E;
        if (!seen[E]) {
          result.push(E);
          seen[E] = true;
        }
      }
    }
    while (E != D);

    return result;
  }

  orbitReps2(i, j) {
    const seen = new Int8Array(this.size + 1);
    const result = [];

    for (let D = 1; D <= this.size; ++D) {
      if (!seen[D]) {
        let E = D;

        do {
          E = this.s(i, E) || E;
          seen[E] = true;
          E = this.s(j, E) || E;
          seen[E] = true;
        }
        while (E != D);

        result.push(D);
      }
    }

    return result;
  }

  r(i, j, D) {
    let k = 0;
    let E = D;

    do {
      E = this.s(i, E) || E;
      E = this.s(j, E) || E;
      ++k;
    }
    while (E != D);

    return k;
  }

  m(i, j, D) {
    return this.r(i, j, D) * this.v(i, j, D);
  }

  toString() {
    const out = [
      '<1.1:',
      (this.dim == 2 ? [this.size] : [this.size, this.dim]).join(' '),
      ':'
    ];

    for (let i = 0; i <= this.dim; ++i) {
      if (i > 0)
        out.push(',');
      const reps = this.elements().filter(D => (this.s(i, D) || D) >= D);
      out.push(reps.map(D => this.s(i, D) || 0).join(' '));
    }

    out.push(':');

    for (let i = 0; i < this.dim; ++i) {
      if (i > 0)
        out.push(',');
      const reps = this.orbitReps2(i, i+1);
      out.push(reps.map(D => this.m(i, i+1, D) || 0).join(' '));
    }

    out.push('>');

    return out.join('');
  }

  get __typeName() { return 'DelaneySymbol'; }
};

pickler.register(
  'DelaneySymbol',
  ({ _s, _v, _dim }) => ({ _s, _v, _dim }),
  ({ _s, _v, _dim }) => new DSymbol(_dim, _s, _v)
);

export const makeDSymbol = (dim, s, v) =>
  Object.freeze(new DSymbol(dim, s, v));

export const isElement = (ds, D) => ds.isElement(D);
export const elements = ds => ds.elements();
export const size = ds => ds.size;

export const isIndex = (ds, i) => ds.isIndex(i);
export const indices = ds => ds.indices();
export const dim = ds => ds.dim;

export const s = (ds, i, D) => ds.s(i, D);
export const v = (ds, i, j, D) => ds.v(i, j, D);
export const r = (ds, i, j, D) => ds.r(i, j, D);
export const m = (ds, i, j, D) => ds.m(i, j, D);

export const orbit2 = (ds, i, j, D) => ds.orbit2(i, j, D);
export const orbitReps2 = (ds, i, j) => ds.orbitReps2(i, j);

const _assert = (condition, message) => {
  if (!condition)
    throw new Error(message || 'assertion error');
};

const _assertIndex = (ds, i) =>
  _assert(ds.isIndex(i), `need integer between 0 and ${ds.dim}, got ${i}`);

const _assertElement = (ds, D) =>
  _assert(ds.isElement(D), `need integer between 1 and ${ds.size}, got ${D}`);

const _assertNonNegative = v =>
  _assert(Number.isInteger(v) && v >= 0, `need non-negative integer, got ${v}`);

export const withPairings = (ds, i, specs) => {
  _assertIndex(ds, i);

  const sNew = ds._s.slice();
  const get = D => sNew[i * ds.size + D - 1];
  const set = (D, x) => { sNew[i * ds.size + D - 1] = x; };

  const dangling = [];

  for (const [D, E] of specs) {
    _assertElement(ds, D);
    _assertElement(ds, E);

    dangling.push(get(D));
    dangling.push(get(E));

    set(D, E);
    set(E, D);
  }

  for (const D of dangling) {
    if (D && get(get(D)) != D)
      set(D, 0);
  }

  return makeDSymbol(ds.dim, sNew, ds._v);
};

export const withBranchings = (ds, i, specs) => {
  _assertIndex(ds, i);

  const vNew = ds._v.slice();
  const set = (D, x) => { vNew[i * ds.size + D - 1] = x; };

  for (const [D, v] of specs) {
    _assertElement(ds, D);
    _assertNonNegative(v);

    let E = D;
    do {
      E = ds.s(i, E) || E;
      set(E, v);
      E = ds.s(i+1, E) || E;
      set(E, v);
    }
    while (E != D);
  }

  return makeDSymbol(ds.dim, ds._s, vNew);
};

export const build = (dim, size, pairingsFn, branchingsFn) => {
  let ds = makeDSymbol(
    dim,
    new Int32Array((dim+1) * size),
    new Int32Array(dim * size)
  );

  const ds0 = ds;
  for (let i = 0; i <= dim; ++i)
    ds = withPairings(ds, i, pairingsFn(ds0, i));

  const ds1 = ds;
  for (let i = 0; i < dim; i++)
    ds = withBranchings(ds, i, branchingsFn(ds1, i));

  return ds;
};

export const parse = str => {
  const _parseInts = str => str.trim().split(/\s+/).map(s => parseInt(s));

  const parts = str.trim().replace(/^</, '').replace(/>$/, '').split(/:/);
  if (parts[0].match(/\d+\.\d+/))
    parts.shift();

  const dims = _parseInts(parts[0]);
  const size = dims[0];
  const dim  = dims[1] || 2;

  const gluings = parts[1].split(/,/).map(_parseInts);
  const degrees = parts[2].split(/,/).map(_parseInts);

  const s = new Int32Array((dim+1) * size);
  const v = new Int32Array(dim * size);

  const get = (a, i, D)    => a[i * size + D - 1];
  const set = (a, i, D, x) => { a[i * size + D - 1] = x; };

  for (let i = 0; i <= dim; ++i) {
    let k = -1;
    for (let D = 1; D <= size; ++D) {
      if (!get(s, i, D)) {
        const E = gluings[i][++k];
        set(s, i, D, E);
        set(s, i, E, D);
      }
    }
  }

  for (let i = 0; i < dim; ++i) {
    let k = -1;
    for (let D = 1; D <= size; ++D) {
      if (!get(v, i, D)) {
        const m = degrees[i][++k];
        let E = D;
        let r = 0;

        do {
          E = get(s, i, E) || E;
          E = get(s, i+1, E) || E;
          ++r;
        }
        while (E != D);

        const b = m / r;

        do {
          E = get(s, i, E) || E;
          set(v, i, E, b);
          E = get(s, i+1, E) || E;
          set(v, i, E, b);
        }
        while (E != D);
      }
    }
  }

  return makeDSymbol(dim, s, v);
};

export const parseSymbols = text => text
  .split('\n')
  .filter(line => line.match(/^\s*</))
  .map(parse);

if (require.main == module) {
  const ds = parse('<1.1:3:1 2 3,1 3,2 3:4 8,3>');

  console.log(`${ds}`);
  console.log(`pickled: ${JSON.stringify(pickler.pickle(ds))}`);
  console.log(`unpickled: ${pickler.unpickle(pickler.pickle(ds))}`);

  console.log(`${withPairings(ds, 1, [[2,1]])}`);
  console.log(`${withBranchings(ds, 0, [[2,3],[1,5]])}`);
}
<commit_msg>delaney: simplify withBranching()<commit_after>import * as pickler from '../common/pickler';

const range = (from, to) => [...Array(to).keys()].slice(from);

class DSymbol {
  constructor(dim, sData, vData) {
    this._s = new Int32Array(sData);
    this._v = new Int32Array(vData);
    this._dim = dim;
    this._size = this._v.length / dim;
  }

  get dim() {
    return this._dim;
  }

  get size() {
    return this._size;
  }

  isElement(D) {
    return Number.isInteger(D) && D >= 1 && D <= this.size;
  }

  elements() {
    return range(1, this.size + 1);
  }

  isIndex(i) {
    return Number.isInteger(i) && i >= 0 && i <= this.dim;
  }

  indices() {
    return range(0, this.dim + 1);
  }

  s(i, D) {
    if (this.isElement(D) && this.isIndex(i))
      return this._s[i * this.size + D - 1];
  }

  v(i, j, D) {
    if (this.isElement(D) && this.isIndex(i) && this.isIndex(j)) {
      if (j == i+1)
        return this._v[i * this.size + D - 1];
      else if (j == i-1)
        return this._v[j * this.size + D - 1];
      else if (this.s(i, D) == this.s(j, D))
        return 2;
      else
        return 1;
    }
  }

  orbit2(i, j, D) {
    const seen = new Int8Array(this.size + 1);
    const result = [];

    let E = D;
    do {
      for (const k of [i, j]) {
        E = this.s(k, E) || E;
        if (!seen[E]) {
          result.push(E);
          seen[E] = true;
        }
      }
    }
    while (E != D);

    return result;
  }

  orbitReps2(i, j) {
    const seen = new Int8Array(this.size + 1);
    const result = [];

    for (let D = 1; D <= this.size; ++D) {
      if (!seen[D]) {
        let E = D;

        do {
          E = this.s(i, E) || E;
          seen[E] = true;
          E = this.s(j, E) || E;
          seen[E] = true;
        }
        while (E != D);

        result.push(D);
      }
    }

    return result;
  }

  r(i, j, D) {
    let k = 0;
    let E = D;

    do {
      E = this.s(i, E) || E;
      E = this.s(j, E) || E;
      ++k;
    }
    while (E != D);

    return k;
  }

  m(i, j, D) {
    return this.r(i, j, D) * this.v(i, j, D);
  }

  toString() {
    const out = [
      '<1.1:',
      (this.dim == 2 ? [this.size] : [this.size, this.dim]).join(' '),
      ':'
    ];

    for (let i = 0; i <= this.dim; ++i) {
      if (i > 0)
        out.push(',');
      const reps = this.elements().filter(D => (this.s(i, D) || D) >= D);
      out.push(reps.map(D => this.s(i, D) || 0).join(' '));
    }

    out.push(':');

    for (let i = 0; i < this.dim; ++i) {
      if (i > 0)
        out.push(',');
      const reps = this.orbitReps2(i, i+1);
      out.push(reps.map(D => this.m(i, i+1, D) || 0).join(' '));
    }

    out.push('>');

    return out.join('');
  }

  get __typeName() { return 'DelaneySymbol'; }
};

pickler.register(
  'DelaneySymbol',
  ({ _s, _v, _dim }) => ({ _s, _v, _dim }),
  ({ _s, _v, _dim }) => new DSymbol(_dim, _s, _v)
);

export const makeDSymbol = (dim, s, v) =>
  Object.freeze(new DSymbol(dim, s, v));

export const isElement = (ds, D) => ds.isElement(D);
export const elements = ds => ds.elements();
export const size = ds => ds.size;

export const isIndex = (ds, i) => ds.isIndex(i);
export const indices = ds => ds.indices();
export const dim = ds => ds.dim;

export const s = (ds, i, D) => ds.s(i, D);
export const v = (ds, i, j, D) => ds.v(i, j, D);
export const r = (ds, i, j, D) => ds.r(i, j, D);
export const m = (ds, i, j, D) => ds.m(i, j, D);

export const orbit2 = (ds, i, j, D) => ds.orbit2(i, j, D);
export const orbitReps2 = (ds, i, j) => ds.orbitReps2(i, j);

const _assert = (condition, message) => {
  if (!condition)
    throw new Error(message || 'assertion error');
};

const _assertIndex = (ds, i) =>
  _assert(ds.isIndex(i), `need integer between 0 and ${ds.dim}, got ${i}`);

const _assertElement = (ds, D) =>
  _assert(ds.isElement(D), `need integer between 1 and ${ds.size}, got ${D}`);

const _assertNonNegative = v =>
  _assert(Number.isInteger(v) && v >= 0, `need non-negative integer, got ${v}`);

export const withPairings = (ds, i, specs) => {
  _assertIndex(ds, i);

  const sNew = ds._s.slice();
  const get = D => sNew[i * ds.size + D - 1];
  const set = (D, x) => { sNew[i * ds.size + D - 1] = x; };

  const dangling = [];

  for (const [D, E] of specs) {
    _assertElement(ds, D);
    _assertElement(ds, E);

    dangling.push(get(D));
    dangling.push(get(E));

    set(D, E);
    set(E, D);
  }

  for (const D of dangling) {
    if (D && get(get(D)) != D)
      set(D, 0);
  }

  return makeDSymbol(ds.dim, sNew, ds._v);
};

export const withBranchings = (ds, i, specs) => {
  _assertIndex(ds, i);

  const vNew = ds._v.slice();

  for (const [D, v] of specs) {
    _assertElement(ds, D);
    _assertNonNegative(v);

    for (const E of ds.orbit2(i, i+1, D))
      vNew[i * ds.size + E - 1] = v;
  }

  return makeDSymbol(ds.dim, ds._s, vNew);
};

export const build = (dim, size, pairingsFn, branchingsFn) => {
  let ds = makeDSymbol(
    dim,
    new Int32Array((dim+1) * size),
    new Int32Array(dim * size)
  );

  const ds0 = ds;
  for (let i = 0; i <= dim; ++i)
    ds = withPairings(ds, i, pairingsFn(ds0, i));

  const ds1 = ds;
  for (let i = 0; i < dim; i++)
    ds = withBranchings(ds, i, branchingsFn(ds1, i));

  return ds;
};

export const parse = str => {
  const _parseInts = str => str.trim().split(/\s+/).map(s => parseInt(s));

  const parts = str.trim().replace(/^</, '').replace(/>$/, '').split(/:/);
  if (parts[0].match(/\d+\.\d+/))
    parts.shift();

  const dims = _parseInts(parts[0]);
  const size = dims[0];
  const dim  = dims[1] || 2;

  const gluings = parts[1].split(/,/).map(_parseInts);
  const degrees = parts[2].split(/,/).map(_parseInts);

  const s = new Int32Array((dim+1) * size);
  const v = new Int32Array(dim * size);

  const get = (a, i, D)    => a[i * size + D - 1];
  const set = (a, i, D, x) => { a[i * size + D - 1] = x; };

  for (let i = 0; i <= dim; ++i) {
    let k = -1;
    for (let D = 1; D <= size; ++D) {
      if (!get(s, i, D)) {
        const E = gluings[i][++k];
        set(s, i, D, E);
        set(s, i, E, D);
      }
    }
  }

  for (let i = 0; i < dim; ++i) {
    let k = -1;
    for (let D = 1; D <= size; ++D) {
      if (!get(v, i, D)) {
        const m = degrees[i][++k];
        let E = D;
        let r = 0;

        do {
          E = get(s, i, E) || E;
          E = get(s, i+1, E) || E;
          ++r;
        }
        while (E != D);

        const b = m / r;

        do {
          E = get(s, i, E) || E;
          set(v, i, E, b);
          E = get(s, i+1, E) || E;
          set(v, i, E, b);
        }
        while (E != D);
      }
    }
  }

  return makeDSymbol(dim, s, v);
};

export const parseSymbols = text => text
  .split('\n')
  .filter(line => line.match(/^\s*</))
  .map(parse);

if (require.main == module) {
  const ds = parse('<1.1:3:1 2 3,1 3,2 3:4 8,3>');

  console.log(`${ds}`);
  console.log(`pickled: ${JSON.stringify(pickler.pickle(ds))}`);
  console.log(`unpickled: ${pickler.unpickle(pickler.pickle(ds))}`);

  console.log(`${withPairings(ds, 1, [[2,1]])}`);
  console.log(`${withBranchings(ds, 0, [[2,3],[1,5]])}`);
}
<|endoftext|>