<commit_before>import * as PIXI from 'pixi.js';

const BASE_MIN_RES = 12;
const BASE_MAX_RES = 24;
const BASE_SCALE = 4;
const BASE_SCALE_UP = 1.25;

export default class Inset {
  constructor(
    uid,
    dataPos,
    remotePos,
    dataConfig,
    tilesetInfo,
    options,
    mouseHandler,
    isTransposed
  ) {
    this.uid = uid;
    this.dataX1 = dataPos[0];
    this.dataX2 = dataPos[1];
    this.dataY1 = dataPos[2];
    this.dataY2 = dataPos[3];
    this.remotePos = remotePos;
    this.dataConfig = dataConfig;
    this.tilesetInfo = tilesetInfo;
    this.options = options;
    this.mouseHandler = mouseHandler;
    this.t = isTransposed ? -1 : 1;

    this.gMain = new PIXI.Graphics();
    this.gBorder = new PIXI.Graphics();
    this.gLeaderLine = new PIXI.Graphics();

    this.gMain.addChild(this.gLeaderLine);
    this.gMain.addChild(this.gBorder);

    this.minRes = this.options.minRes || BASE_MIN_RES;
    this.maxRes = this.options.maxRes || BASE_MAX_RES;
    this.scaleBase = this.options.scale || BASE_SCALE;
    this.scaleExtra = 1;
    this.offsetX = 0;
    this.offsetY = 0;
    this.globalOffsetX = 0;
    this.globalOffsetY = 0;

    this.initGraphics(options);
  }

  /* --------------------------- Getter / Setter ---------------------------- */

  /**
   * Get main graphics.
   */
  get graphics() {
    return this.gMain;
  }

  /* ---------------------------- Custom Methods ---------------------------- */

  /**
   * Clear and initialize graphics.
   *
   * @param  {Object}  options  Custom line style for the border and leader line
   */
  clear(options = this.options) {
    this.gBorder.clear();
    this.gLeaderLine.clear();
    this.gMain.clear();
    this.initGraphics(options);
  }

  /**
   * Destroy graphics and unset data.
   */
  destroy() {
    if (this.sprite) this.sprite.removeAllListeners();

    this.gBorder.destroy();
    this.gLeaderLine.destroy();
    this.gMain.destroy();

    this.data = undefined;
    this.sprite = undefined;
  }

  /**
   * Draw inset border.
   *
   * @param  {Number}  x  X position of the inset to be drawn.
   * @param  {Number}  y  Y position of the inset to be drawn.
   * @param  {Number}  width  Width of the inset to be drawn.
   * @param  {Number}  height  Height of the inset to be drawn.
   */
  drawBorder(
    x = this.x, y = this.y, width = this.width, height = this.height
  ) {
    this.gBorder.drawRect(
      this.globalOffsetX + (-this.offsetX * this.t) + x - (width / 2 * this.t),
      this.globalOffsetY + (-this.offsetY * this.t) + y - (height / 2 * this.t),
      width * this.scaleExtra,
      height * this.scaleExtra
    );
  }

  /**
   * Draw leader line.
   */
  drawLeaderLine() {
    // Origin
    this.gLeaderLine.moveTo(
      this.globalOffsetX + this.originX,
      this.globalOffsetY + this.originY
    );

    // Inset position
    this.gLeaderLine.lineTo(
      this.globalOffsetX + this.x,
      this.globalOffsetY + this.y
    );
  }

  /**
   * Draw the image of the inset (i.e., a matrix snippet or image snippet)
   *
   * @param  {Function}  imgRenderer  Image renderer, i.e., function converting
   *   the data into canvas.
   * @param  {Boolean}  force  If `true` forces a rerendering of the image.
   */
  drawImage(imgRenderer, force = false) {
    if (!this.data) {
      if (!this.inFlight) {
        this.inFlight = this.fetchData()
          .then((data) => {
            this.data = data;
            this.inFlight = false;
            return this.drawImage(imgRenderer, force);
          })
          .catch((err) => {
            console.error(
              `Could not fetch the inset's image at [${this.remotePos.join(',')}]`,
              err
            );
          });
      }
      return this.inFlight;
    }

    return this.renderImage(this.data, imgRenderer, force)
      .then(() => {
        this.positionImage();
        return true;
      })
      .catch(err => console.error(err));
  }

  /**
   * Compute the closest zoom level providing enough resolution for displaying
   * the snippet.
   *
   * @return  {Number}  Closest zoom level.
   */
  computedZoom() {
    const absXLen = this.remotePos[1] - this.remotePos[0];
    const absYLen = this.remotePos[3] - this.remotePos[2];
    return Math.ceil(
      Math.log2((this.maxRes * (2 ** this.tilesetInfo.max_zoom)) / Math.max(absXLen, absYLen))
    );
  }

  /**
   * Fetch data for the image.
   *
   * @return  {Object}  Promise resolving to the JSON response
   */
  fetchData() {
    this.computedZoom();
    const loci = [
      [
        ...this.remotePos,
        this.dataConfig.tilesetUid,
        this.computedZoom()
      ]
    ];

    return fetch(
      `${this.dataConfig.server}/fragments_by_loci/?precision=2&dims=${this.minRes}`, {
        method: 'POST',
        headers: {
          accept: 'application/json; charset=UTF-8',
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(loci)
      })
      .then(response => response.json());
  }

  /**
   * Get or set global offset.
   *
   * @param  {Number}  x  Global X offset.
   * @param  {Number}  y  Global Y offset.
   * @return  {Array}   Tuple holding the global X and Y offset.
   */
  globalOffset(x = this.globalOffsetX, y = this.globalOffsetY) {
    this.globalOffsetX = x;
    this.globalOffsetY = y;
    return [x, y];
  }

  /**
   * Initialize line style of the border and leader line graphics.
   *
   * @param  {Object}  options  Line style for the border and leader line.
   */
  initGraphics(options = this.options) {
    this.gBorder.lineStyle(
      options.borderWidth || 1,
      options.borderColor || 0x000000,
      options.borderOpacity || 1
    );
    this.gLeaderLine.lineStyle(
      options.leaderLineWidth || 1,
      options.leaderLineColor || 0x000000,
      options.leaderLineOpacity || 1
    );
  }

  /**
   * Mouse click handler.
   *
   * @param  {Object}  event  Event object.
   */
  mouseclickHandler(event) {
    this.mouseHandler.click(event, this.gMain);
  }

  /**
   * Mouse over handler.
   *
   * @param  {Object}  event  Event object.
   */
  mouseOverHandler(event) {
    this.mouseHandler.mouseOver(event, this.gMain);
  }

  /**
   * Mouse out handler.
   *
   * @param  {Object}  event  Event object.
   */
  mouseOutHandler(event) {
    this.mouseHandler.mouseOut(event, this.gMain);
  }

  /**
   * Mouse down handler.
   *
   * @param  {Object}  event  Event object.
   */
  mouseDownHandler(event) {
    this.mouseDown = true;
    this.scale(BASE_SCALE_UP);
    this.mouseHandler.mouseDown(event, this.gMain);
    console.log(
      `Annotation: ${this.uid} |`,
      `Remote pos: ${this.remotePos.join(', ')} |`,
      `Ideal zoom level for snippet: ${this.computedZoom()}`
    );
  }

  /**
   * Mouse up handler.
   *
   * @param  {Object}  event  Event object.
   */
  mouseUpHandler(event) {
    if (this.mouseDown) this.mouseclickHandler(event);
    this.scale();
    this.mouseDown = false;
    this.mouseHandler.mouseUp(event, this.gMain);
  }

  /**
   * Get or set origin of the inset.
   *
   * @param  {Number}  x  X origin.
   * @param  {Number}  y  Y origin.
   * @return  {Array}   Tuple holding the X,Y origin.
   */
  origin(x = this.originX, y = this.originY) {
    this.originX = x;
    this.originY = y;
    return [x, y];
  }

  /**
   * Get or set the position of the inset.
   *
   * @param  {Number}  x  X position.
   * @param  {Number}  y  Y position.
   * @return  {Array}   Tuple holding the X,Y position.
   */
  position(x = this.x, y = this.y) {
    this.x = x;
    this.y = y;
    return [x, y];
  }

  /**
   * Position the image.
   *
   * @param  {Number}  x  X position of the inset to be drawn.
   * @param  {Number}  y  Y position of the inset to be drawn.
   * @param  {Number}  width  Width of the inset to be drawn.
   * @param  {Number}  height  Height of the inset to be drawn.
   */
  positionImage(
    x = this.x, y = this.y, width = this.width, height = this.height
  ) {
    this.sprite.x = this.globalOffsetX - this.offsetX + x + (width / -2 * this.t);
    this.sprite.y = this.globalOffsetY - this.offsetY + y + (height / -2 * this.t);

    this.sprite.scale.x = this.t * this.scaleBase * this.scaleExtra;
    this.sprite.scale.y = this.t * this.scaleBase * this.scaleExtra;
  }

  /**
   * Get or set the size of the inset
   *
   * @param  {Number}  width  Width of the inset.
   * @param  {Number}  height  Height of the inset.
   * @return  {Array}   Tuple holding `[width, height]`.
   */
  size(width = this.width, height = this.height) {
    this.width = width;
    this.height = height;
    return [width, height];
  }

  /**
   * Render the data to an image and assign event listeners.
   *
   * @param  {Array}  data  Data to be rendered
   * @param  {Function}  imgRenderer  Image renderer converting the data into
   *   canvas.
   */
  renderImage(data, imgRenderer, force) {
    if (this.sprite && !force) return Promise.resolve();

    return imgRenderer(data, this.width, this.height)
      .then((renderedData) => {
        this.data = renderedData;

        this.sprite = new PIXI.Sprite(
          PIXI.Texture.fromCanvas(
            this.data, PIXI.SCALE_MODES.NEAREST
          )
        );

        this.sprite.interactive = true;
        this.sprite
          .on('mousedown', this.mouseDownHandler.bind(this))
          .on('mouseover', this.mouseOverHandler.bind(this))
          .on('mouseout', this.mouseOutHandler.bind(this))
          .on('mouseup', this.mouseUpHandler.bind(this));

        this.gMain.addChild(this.sprite);
      });
  }

  /**
   * Scale the inset.
   *
   * @param  {Number}  amount  Amount by which to scale the inset
   */
  scale(amount = 1) {
    this.scaleExtra = amount;
    this.offsetX = this.width * this.scaleBase * (amount - 1) / 2 * this.t;
    this.offsetY = this.height * this.scaleBase * (amount - 1) / 2 * this.t;

    this.positionImage();
    this.gBorder.clear();
    this.initGraphics();
    this.drawBorder();
  }
}
<commit_msg>Avoid rendering sprites multiple times<commit_after>import * as PIXI from 'pixi.js';

const BASE_MIN_RES = 12;
const BASE_MAX_RES = 24;
const BASE_SCALE = 4;
const BASE_SCALE_UP = 1.25;

export default class Inset {
  constructor(
    uid,
    dataPos,
    remotePos,
    dataConfig,
    tilesetInfo,
    options,
    mouseHandler,
    isTransposed
  ) {
    this.uid = uid;
    this.dataX1 = dataPos[0];
    this.dataX2 = dataPos[1];
    this.dataY1 = dataPos[2];
    this.dataY2 = dataPos[3];
    this.remotePos = remotePos;
    this.dataConfig = dataConfig;
    this.tilesetInfo = tilesetInfo;
    this.options = options;
    this.mouseHandler = mouseHandler;
    this.t = isTransposed ? -1 : 1;

    this.gMain = new PIXI.Graphics();
    this.gBorder = new PIXI.Graphics();
    this.gLeaderLine = new PIXI.Graphics();

    this.gMain.addChild(this.gLeaderLine);
    this.gMain.addChild(this.gBorder);

    this.minRes = this.options.minRes || BASE_MIN_RES;
    this.maxRes = this.options.maxRes || BASE_MAX_RES;
    this.scaleBase = this.options.scale || BASE_SCALE;
    this.scaleExtra = 1;
    this.offsetX = 0;
    this.offsetY = 0;
    this.globalOffsetX = 0;
    this.globalOffsetY = 0;

    this.initGraphics(options);
  }

  /* --------------------------- Getter / Setter ---------------------------- */

  /**
   * Get main graphics.
   */
  get graphics() {
    return this.gMain;
  }

  /* ---------------------------- Custom Methods ---------------------------- */

  /**
   * Clear and initialize graphics.
   *
   * @param  {Object}  options  Custom line style for the border and leader line
   */
  clear(options = this.options) {
    this.gBorder.clear();
    this.gLeaderLine.clear();
    this.gMain.clear();
    this.initGraphics(options);
    this.imageRendering
  }

  /**
   * Destroy graphics and unset data.
   */
  destroy() {
    if (this.sprite) this.sprite.removeAllListeners();

    this.gBorder.destroy();
    this.gLeaderLine.destroy();
    this.gMain.destroy();

    this.data = undefined;
    this.sprite = undefined;
  }

  /**
   * Draw inset border.
   *
   * @param  {Number}  x  X position of the inset to be drawn.
   * @param  {Number}  y  Y position of the inset to be drawn.
   * @param  {Number}  width  Width of the inset to be drawn.
   * @param  {Number}  height  Height of the inset to be drawn.
   */
  drawBorder(
    x = this.x, y = this.y, width = this.width, height = this.height
  ) {
    this.gBorder.drawRect(
      this.globalOffsetX + (-this.offsetX * this.t) + x - (width / 2 * this.t),
      this.globalOffsetY + (-this.offsetY * this.t) + y - (height / 2 * this.t),
      width * this.scaleExtra,
      height * this.scaleExtra
    );
  }

  /**
   * Draw leader line.
   */
  drawLeaderLine() {
    // Origin
    this.gLeaderLine.moveTo(
      this.globalOffsetX + this.originX,
      this.globalOffsetY + this.originY
    );

    // Inset position
    this.gLeaderLine.lineTo(
      this.globalOffsetX + this.x,
      this.globalOffsetY + this.y
    );
  }

  /**
   * Draw the image of the inset (i.e., a matrix snippet or image snippet)
   *
   * @param  {Function}  imgRenderer  Image renderer, i.e., function converting
   *   the data into canvas.
   * @param  {Boolean}  force  If `true` forces a rerendering of the image.
   */
  drawImage(imgRenderer, force = false) {
    if (!this.data) {
      if (!this.inFlight) {
        this.inFlight = this.fetchData()
          .then((data) => {
            this.data = data;
            this.inFlight = false;
            return this.drawImage(imgRenderer, force);
          })
          .catch((err) => {
            console.error(
              `Could not fetch the inset's image at [${this.remotePos.join(',')}]`,
              err
            );
          });
      }
      return this.inFlight;
    }

    return this.renderImage(this.data, imgRenderer, force)
      .then(() => {
        this.positionImage();
        return true;
      })
      .catch(err => console.error(err));
  }

  /**
   * Compute the closest zoom level providing enough resolution for displaying
   * the snippet.
   *
   * @return  {Number}  Closest zoom level.
   */
  computedZoom() {
    const absXLen = this.remotePos[1] - this.remotePos[0];
    const absYLen = this.remotePos[3] - this.remotePos[2];
    return Math.ceil(
      Math.log2((this.maxRes * (2 ** this.tilesetInfo.max_zoom)) / Math.max(absXLen, absYLen))
    );
  }

  /**
   * Fetch data for the image.
   *
   * @return  {Object}  Promise resolving to the JSON response
   */
  fetchData() {
    this.computedZoom();
    const loci = [
      [
        ...this.remotePos,
        this.dataConfig.tilesetUid,
        this.computedZoom()
      ]
    ];

    return fetch(
      `${this.dataConfig.server}/fragments_by_loci/?precision=2&dims=${this.minRes}`, {
        method: 'POST',
        headers: {
          accept: 'application/json; charset=UTF-8',
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(loci)
      })
      .then(response => response.json());
  }

  /**
   * Get or set global offset.
   *
   * @param  {Number}  x  Global X offset.
   * @param  {Number}  y  Global Y offset.
   * @return  {Array}   Tuple holding the global X and Y offset.
   */
  globalOffset(x = this.globalOffsetX, y = this.globalOffsetY) {
    this.globalOffsetX = x;
    this.globalOffsetY = y;
    return [x, y];
  }

  /**
   * Initialize line style of the border and leader line graphics.
   *
   * @param  {Object}  options  Line style for the border and leader line.
   */
  initGraphics(options = this.options) {
    this.gBorder.lineStyle(
      options.borderWidth || 1,
      options.borderColor || 0x000000,
      options.borderOpacity || 1
    );
    this.gLeaderLine.lineStyle(
      options.leaderLineWidth || 1,
      options.leaderLineColor || 0x000000,
      options.leaderLineOpacity || 1
    );
  }

  /**
   * Mouse click handler.
   *
   * @param  {Object}  event  Event object.
   */
  mouseclickHandler(event) {
    this.mouseHandler.click(event, this.gMain);
  }

  /**
   * Mouse over handler.
   *
   * @param  {Object}  event  Event object.
   */
  mouseOverHandler(event) {
    this.mouseHandler.mouseOver(event, this.gMain);
  }

  /**
   * Mouse out handler.
   *
   * @param  {Object}  event  Event object.
   */
  mouseOutHandler(event) {
    this.mouseHandler.mouseOut(event, this.gMain);
  }

  /**
   * Mouse down handler.
   *
   * @param  {Object}  event  Event object.
   */
  mouseDownHandler(event) {
    this.mouseDown = true;
    this.scale(BASE_SCALE_UP);
    this.mouseHandler.mouseDown(event, this.gMain);
    console.log(
      `Annotation: ${this.uid} |`,
      `Remote pos: ${this.remotePos.join(', ')} |`,
      `Ideal zoom level for snippet: ${this.computedZoom()}`
    );
  }

  /**
   * Mouse up handler.
   *
   * @param  {Object}  event  Event object.
   */
  mouseUpHandler(event) {
    if (this.mouseDown) this.mouseclickHandler(event);
    this.scale();
    this.mouseDown = false;
    this.mouseHandler.mouseUp(event, this.gMain);
  }

  /**
   * Get or set origin of the inset.
   *
   * @param  {Number}  x  X origin.
   * @param  {Number}  y  Y origin.
   * @return  {Array}   Tuple holding the X,Y origin.
   */
  origin(x = this.originX, y = this.originY) {
    this.originX = x;
    this.originY = y;
    return [x, y];
  }

  /**
   * Get or set the position of the inset.
   *
   * @param  {Number}  x  X position.
   * @param  {Number}  y  Y position.
   * @return  {Array}   Tuple holding the X,Y position.
   */
  position(x = this.x, y = this.y) {
    this.x = x;
    this.y = y;
    return [x, y];
  }

  /**
   * Position the image.
   *
   * @param  {Number}  x  X position of the inset to be drawn.
   * @param  {Number}  y  Y position of the inset to be drawn.
   * @param  {Number}  width  Width of the inset to be drawn.
   * @param  {Number}  height  Height of the inset to be drawn.
   */
  positionImage(
    x = this.x, y = this.y, width = this.width, height = this.height
  ) {
    this.sprite.x = this.globalOffsetX - this.offsetX + x + (width / -2 * this.t);
    this.sprite.y = this.globalOffsetY - this.offsetY + y + (height / -2 * this.t);

    this.sprite.scale.x = this.t * this.scaleBase * this.scaleExtra;
    this.sprite.scale.y = this.t * this.scaleBase * this.scaleExtra;
  }

  /**
   * Get or set the size of the inset
   *
   * @param  {Number}  width  Width of the inset.
   * @param  {Number}  height  Height of the inset.
   * @return  {Array}   Tuple holding `[width, height]`.
   */
  size(width = this.width, height = this.height) {
    this.width = width;
    this.height = height;
    return [width, height];
  }

  /**
   * Render the data to an image and assign event listeners.
   *
   * @param  {Array}  data  Data to be rendered
   * @param  {Function}  imgRenderer  Image renderer converting the data into
   *   canvas.
   */
  renderImage(data, imgRenderer, force) {
    if (this.sprite && !force) return Promise.resolve();

    if (this.imageRendering) return this.imageRendering;

    this.imageRendering = imgRenderer(data, this.width, this.height)
      .then((renderedData) => {
        this.data = renderedData;

        this.sprite = new PIXI.Sprite(
          PIXI.Texture.fromCanvas(
            this.data, PIXI.SCALE_MODES.NEAREST
          )
        );

        this.sprite.interactive = true;
        this.sprite
          .on('mousedown', this.mouseDownHandler.bind(this))
          .on('mouseover', this.mouseOverHandler.bind(this))
          .on('mouseout', this.mouseOutHandler.bind(this))
          .on('mouseup', this.mouseUpHandler.bind(this));

        this.gMain.addChild(this.sprite);
      });

    return this.imageRendering;
  }

  /**
   * Scale the inset.
   *
   * @param  {Number}  amount  Amount by which to scale the inset
   */
  scale(amount = 1) {
    this.scaleExtra = amount;
    this.offsetX = this.width * this.scaleBase * (amount - 1) / 2 * this.t;
    this.offsetY = this.height * this.scaleBase * (amount - 1) / 2 * this.t;

    this.positionImage();
    this.gBorder.clear();
    this.initGraphics();
    this.drawBorder();
  }
}
<|endoftext|>


<commit_before>import Ember from 'ember';
import DS from 'ember-data';
import { Projection } from 'ember-flexberry-data';
export let Model = Ember.Mixin.create({
  /**
    Non-stored property.

    @property name
  */
  name: DS.attr('string'),
  /**
    Method to set non-stored property.
    Please, use code below in model class (outside of this mixin) otherwise it will be replaced during regeneration of models.
    Please, implement 'nameCompute' method in model class (outside of this mixin) if you want to compute value of 'name' property.

    @method _nameCompute
    @private
    @example
      ```javascript
      _nameChanged: Ember.on('init', Ember.observer('name', function() {
        Ember.run.once(this, '_nameCompute');
      }))
      ```
  */
  _nameCompute: function() {
    let result = (this.nameCompute && typeof this.nameCompute === 'function') ? this.nameCompute() : null;
    this.set('name', result);
  },
  /**
    Non-stored property.

    @property primitiveTypes
  */
  primitiveTypes: DS.attr('string'),
  /**
    Method to set non-stored property.
    Please, use code below in model class (outside of this mixin) otherwise it will be replaced during regeneration of models.
    Please, implement 'primitiveTypesCompute' method in model class (outside of this mixin) if you want to compute value of 'primitiveTypes' property.

    @method _primitiveTypesCompute
    @private
    @example
      ```javascript
      _primitiveTypesChanged: Ember.on('init', Ember.observer('primitiveTypes', function() {
        Ember.run.once(this, '_primitiveTypesCompute');
      }))
      ```
  */
  _primitiveTypesCompute: function() {
    let result = (this.primitiveTypesCompute && typeof this.primitiveTypesCompute === 'function') ? this.primitiveTypesCompute() : null;
    this.set('primitiveTypes', result);
  },
  /**
    Non-stored property.

    @property primitives
  */
  primitives: DS.attr('string'),
  /**
    Method to set non-stored property.
    Please, use code below in model class (outside of this mixin) otherwise it will be replaced during regeneration of models.
    Please, implement 'primitivesCompute' method in model class (outside of this mixin) if you want to compute value of 'primitives' property.

    @method _primitivesCompute
    @private
    @example
      ```javascript
      _primitivesChanged: Ember.on('init', Ember.observer('primitives', function() {
        Ember.run.once(this, '_primitivesCompute');
      }))
      ```
  */
  _primitivesCompute: function() {
    let result = (this.primitivesCompute && typeof this.primitivesCompute === 'function') ? this.primitivesCompute() : null;
    this.set('primitives', result);
  },
  /**
    Non-stored property.

    @property helpKeyword
  */
  helpKeyword: DS.attr('string'),
  /**
    Method to set non-stored property.
    Please, use code below in model class (outside of this mixin) otherwise it will be replaced during regeneration of models.
    Please, implement 'helpKeywordCompute' method in model class (outside of this mixin) if you want to compute value of 'helpKeyword' property.

    @method _helpKeywordCompute
    @private
    @example
      ```javascript
      _helpKeywordChanged: Ember.on('init', Ember.observer('helpKeyword', function() {
        Ember.run.once(this, '_helpKeywordCompute');
      }))
      ```
  */
  _helpKeywordCompute: function() {
    let result = (this.helpKeywordCompute && typeof this.helpKeywordCompute === 'function') ? this.helpKeywordCompute() : null;
    this.set('helpKeyword', result);
  },
  getValidations: function () {
    let parentValidations = this._super();
    let thisValidations = {
    };
    return Ember.$.extend(true, {}, parentValidations, thisValidations);
  },
  init: function () {
    this.set('validations', this.getValidations());
    this._super.apply(this, arguments);
  }
});
export let defineBaseModel = function (modelClass) {
  modelClass.reopenClass({
    _parentModelName: 'fd-cad'
  });
};

export let defineProjections = function (modelClass) {
  modelClass.defineProjection('EditFormView', 'fd-dev-uml-cad', {
    name: Projection.attr('Name'),
    description: Projection.attr('Description')
  });
  modelClass.defineProjection('ListFormView', 'fd-dev-uml-cad', {
    name: Projection.attr('Name'),
    description: Projection.attr('Description'),
    changeUser: Projection.attr('Change user'),
    changeDate: Projection.attr('Change date'),
    createUser: Projection.attr('Create user'),
    createDate: Projection.attr('Create date')
  });
  modelClass.defineProjection('SearchDiagram', 'fd-dev-uml-cad', {
    name: Projection.attr(''),
    subsystem: Projection.belongsTo('fd-subsystem', '', {
      stage: Projection.belongsTo('fd-stage', '', {

      }),
    }),
  });
  modelClass.defineProjection('FdUmlCad', 'fd-dev-uml-cad', {
    name: Projection.attr(''),
    primitivesJsonString: Projection.attr(''),
    primitivesStreamString: Projection.attr(''),
    caseObjectsString: Projection.attr(''),
    subsystem: Projection.belongsTo('fd-subsystem', '', {

    })
  });
};
<commit_msg>Chores fd-dev-uml-cad model add property in projection<commit_after>import Ember from 'ember';
import DS from 'ember-data';
import { Projection } from 'ember-flexberry-data';
export let Model = Ember.Mixin.create({
  /**
    Non-stored property.

    @property name
  */
  name: DS.attr('string'),
  /**
    Method to set non-stored property.
    Please, use code below in model class (outside of this mixin) otherwise it will be replaced during regeneration of models.
    Please, implement 'nameCompute' method in model class (outside of this mixin) if you want to compute value of 'name' property.

    @method _nameCompute
    @private
    @example
      ```javascript
      _nameChanged: Ember.on('init', Ember.observer('name', function() {
        Ember.run.once(this, '_nameCompute');
      }))
      ```
  */
  _nameCompute: function() {
    let result = (this.nameCompute && typeof this.nameCompute === 'function') ? this.nameCompute() : null;
    this.set('name', result);
  },
  /**
    Non-stored property.

    @property primitiveTypes
  */
  primitiveTypes: DS.attr('string'),
  /**
    Method to set non-stored property.
    Please, use code below in model class (outside of this mixin) otherwise it will be replaced during regeneration of models.
    Please, implement 'primitiveTypesCompute' method in model class (outside of this mixin) if you want to compute value of 'primitiveTypes' property.

    @method _primitiveTypesCompute
    @private
    @example
      ```javascript
      _primitiveTypesChanged: Ember.on('init', Ember.observer('primitiveTypes', function() {
        Ember.run.once(this, '_primitiveTypesCompute');
      }))
      ```
  */
  _primitiveTypesCompute: function() {
    let result = (this.primitiveTypesCompute && typeof this.primitiveTypesCompute === 'function') ? this.primitiveTypesCompute() : null;
    this.set('primitiveTypes', result);
  },
  /**
    Non-stored property.

    @property primitives
  */
  primitives: DS.attr('string'),
  /**
    Method to set non-stored property.
    Please, use code below in model class (outside of this mixin) otherwise it will be replaced during regeneration of models.
    Please, implement 'primitivesCompute' method in model class (outside of this mixin) if you want to compute value of 'primitives' property.

    @method _primitivesCompute
    @private
    @example
      ```javascript
      _primitivesChanged: Ember.on('init', Ember.observer('primitives', function() {
        Ember.run.once(this, '_primitivesCompute');
      }))
      ```
  */
  _primitivesCompute: function() {
    let result = (this.primitivesCompute && typeof this.primitivesCompute === 'function') ? this.primitivesCompute() : null;
    this.set('primitives', result);
  },
  /**
    Non-stored property.

    @property helpKeyword
  */
  helpKeyword: DS.attr('string'),
  /**
    Method to set non-stored property.
    Please, use code below in model class (outside of this mixin) otherwise it will be replaced during regeneration of models.
    Please, implement 'helpKeywordCompute' method in model class (outside of this mixin) if you want to compute value of 'helpKeyword' property.

    @method _helpKeywordCompute
    @private
    @example
      ```javascript
      _helpKeywordChanged: Ember.on('init', Ember.observer('helpKeyword', function() {
        Ember.run.once(this, '_helpKeywordCompute');
      }))
      ```
  */
  _helpKeywordCompute: function() {
    let result = (this.helpKeywordCompute && typeof this.helpKeywordCompute === 'function') ? this.helpKeywordCompute() : null;
    this.set('helpKeyword', result);
  },
  getValidations: function () {
    let parentValidations = this._super();
    let thisValidations = {
    };
    return Ember.$.extend(true, {}, parentValidations, thisValidations);
  },
  init: function () {
    this.set('validations', this.getValidations());
    this._super.apply(this, arguments);
  }
});
export let defineBaseModel = function (modelClass) {
  modelClass.reopenClass({
    _parentModelName: 'fd-cad'
  });
};

export let defineProjections = function (modelClass) {
  modelClass.defineProjection('EditFormView', 'fd-dev-uml-cad', {
    name: Projection.attr('Name'),
    description: Projection.attr('Description')
  });
  modelClass.defineProjection('ListFormView', 'fd-dev-uml-cad', {
    name: Projection.attr('Name'),
    description: Projection.attr('Description'),
    changeUser: Projection.attr('Change user'),
    changeDate: Projection.attr('Change date'),
    createUser: Projection.attr('Create user'),
    createDate: Projection.attr('Create date'),
    subsystem: Projection.belongsTo('fd-subsystem', '', {
      stage: Projection.belongsTo('fd-stage', '', {
      }, { hidden: true }),
    }, { hidden: true }),
  });
  modelClass.defineProjection('SearchDiagram', 'fd-dev-uml-cad', {
    name: Projection.attr(''),
    subsystem: Projection.belongsTo('fd-subsystem', '', {
      stage: Projection.belongsTo('fd-stage', '', {

      }),
    }),
  });
  modelClass.defineProjection('FdUmlCad', 'fd-dev-uml-cad', {
    name: Projection.attr(''),
    primitivesJsonString: Projection.attr(''),
    primitivesStreamString: Projection.attr(''),
    caseObjectsString: Projection.attr(''),
    subsystem: Projection.belongsTo('fd-subsystem', '', {

    })
  });
};
<|endoftext|>


<commit_before>/* Web Worker Attractor Finder */

// Make sure we load all component types
require('./chaos/Components');

const Context = require('./chaos/Context'),
      Bounds = require('./chaos/Bounds'),
      Point = require('./chaos/Point'),
      Time = require('./chaos/Time'),
      Configuration = require('./chaos/Configuration'),
      AttractorSnapshot = require('./chaos/AttractorSnapshot');

const SETTLE_ITERATIONS = 1000;
const SEARCH_ITERATIONS = 1000;

let lastStatus;

self.addEventListener('message', (e) => {
    const data = e.data;
    switch (data.action) {
        case 'start':
            doWork(
                Configuration.decode(data.configuration),
                data.viewport,
                data.snapshot && AttractorSnapshot.decode(data.snapshot)
            );
            break;
    }
});

function sendStatus(newStatus) {
    if (lastStatus === newStatus) {
        return;
    }
    lastStatus = newStatus;
    self.postMessage({event: 'status', status: newStatus});
}

function doWork(configuration, viewport, snapshot) {
    sendStatus('');
    let map = (snapshot || configuration).map;
    let rng = (snapshot || configuration).rng;
    map.initialize(!!snapshot);

    let projection = configuration.projection;
    let colorizer = configuration.colorizer;
    let dimensions = map.dimensions;
    let numCoefficients = map.coefficients;
    let criteria = configuration.criteria;
    let totalIterations = viewport.width * viewport.height * configuration.density / viewport.devicePixelRatio;

    let work = () => {
        let coefficients = [];
        let initialValue = [];
        let value;
        let values = [];

        rng.reset(snapshot ? rng.seed : Time.now());
        map.reset();

        for (let i = 0; i < numCoefficients; i++) {
            coefficients.push(rng.next() * 2 - 1);
        }

        for (let i = 0; i < dimensions; i++) {
            initialValue.push(rng.next() * 2 - 1);
        }

        value = initialValue.slice();

        let bounds = new Bounds(dimensions);

        /* set up our context with our computed bounds and other state */
        let context = new Context(map, rng, criteria, initialValue, coefficients, bounds);

        /* reset search criteria state */
        for (let criterion of criteria) {
            criterion.reset(context, initialValue);
        }

        /* settle */
        sendStatus(snapshot ? 'Settling attractor' : 'Settling potential attractor');

        for (let i = 0; i < SETTLE_ITERATIONS; i++) {
            value = map.apply(value, coefficients);
            if (!Point.isValid(value)) {
                return false;
            }

            for (let criterion of criteria) {
                if (criterion.requiresSettling) {
                    continue;
                }

                let result = criterion.test(context, value);
                if (!snapshot && !result) {
                    criterion.test(context, value);
                    return false;
                }
            }
        }

        /* reset our 'initial value' to the settled one */
        initialValue = value.slice();

        /* Compute bounds */
        sendStatus('Computing Bounds');
        for (let i = 0; i < totalIterations; i++) {
            value = map.apply(value, coefficients);
            if (!Point.isValid(value)) {
                return false;
            }

            bounds.update(value);
        }

        if (!bounds.isValid()) {
            return false;
        }

        /* reset our value to the settled one */
        value = initialValue.slice();

        /* set up our context with our computed bounds and other state */
        context = new Context(map, rng, criteria, initialValue, coefficients, bounds);

        /* reset search criteria state */
        for (let criterion of criteria) {
            criterion.reset(context, initialValue);
        }

        /* reset projection and colorizer */
        projection.reset();
        colorizer.reset();

        /* search + generate */
        sendStatus(snapshot || criteria.length === 0 ? "Generating remaining points" : "Applying search criteria");

        for (let i = 0; i < totalIterations; i++) {
            value = map.apply(value, coefficients);
            if (!Point.isValid(value)) {
                return false;
            }

            let normalized = bounds.normalize(value);
            let projected = projection.apply(context, normalized);

            if (projected.length > 0 && projected[0].constructor !== Array) {
                projected = [ projected ];
            }

            for (let vertex of projected) {
                let colorized = colorizer.apply(context, vertex);
                values.push(colorized);
            }

            if (i < SEARCH_ITERATIONS) {
                for (let criterion of criteria) {
                    if (!criterion.requiresSettling) {
                        continue;
                    }

                    let result = criterion.test(context, value);
                    if (!snapshot && !result) {
                        return false;
                    }
                }
            }

            if (!snapshot && i === SEARCH_ITERATIONS) {
                sendStatus('Generating remaining points');
            }
        }

        snapshot = new AttractorSnapshot(map, rng);

        const stats = [];
        stats.push('C = ' + numCoefficients);
        for (let criterion of criteria) {
            const stat = criterion.renderStats();
            if (stat) {
                stats.push(stat);
            }
        }

        self.postMessage({
            event: 'complete',
            snapshot: snapshot.encode(),
            values: values,
            stats: stats
        });

        return true;
    };

    while (!work()) {
        // if snapshot, work should've succeed
        if (snapshot) {
            break;
        }
    }
}<commit_msg>Use devicePixelRatio for density<commit_after>/* Web Worker Attractor Finder */

// Make sure we load all component types
require('./chaos/Components');

const Context = require('./chaos/Context'),
      Bounds = require('./chaos/Bounds'),
      Point = require('./chaos/Point'),
      Time = require('./chaos/Time'),
      Configuration = require('./chaos/Configuration'),
      AttractorSnapshot = require('./chaos/AttractorSnapshot');

const SETTLE_ITERATIONS = 1000;
const SEARCH_ITERATIONS = 1000;

let lastStatus;

self.addEventListener('message', (e) => {
    const data = e.data;
    switch (data.action) {
        case 'start':
            doWork(
                Configuration.decode(data.configuration),
                data.viewport,
                data.snapshot && AttractorSnapshot.decode(data.snapshot)
            );
            break;
    }
});

function sendStatus(newStatus) {
    if (lastStatus === newStatus) {
        return;
    }
    lastStatus = newStatus;
    self.postMessage({event: 'status', status: newStatus});
}

function doWork(configuration, viewport, snapshot) {
    sendStatus('');
    let map = (snapshot || configuration).map;
    let rng = (snapshot || configuration).rng;
    map.initialize(!!snapshot);

    let projection = configuration.projection;
    let colorizer = configuration.colorizer;
    let dimensions = map.dimensions;
    let numCoefficients = map.coefficients;
    let criteria = configuration.criteria;
    let totalIterations = viewport.width * viewport.height * configuration.density * viewport.devicePixelRatio;

    let work = () => {
        let coefficients = [];
        let initialValue = [];
        let value;
        let values = [];

        rng.reset(snapshot ? rng.seed : Time.now());
        map.reset();

        for (let i = 0; i < numCoefficients; i++) {
            coefficients.push(rng.next() * 2 - 1);
        }

        for (let i = 0; i < dimensions; i++) {
            initialValue.push(rng.next() * 2 - 1);
        }

        value = initialValue.slice();

        let bounds = new Bounds(dimensions);

        /* set up our context with our computed bounds and other state */
        let context = new Context(map, rng, criteria, initialValue, coefficients, bounds);

        /* reset search criteria state */
        for (let criterion of criteria) {
            criterion.reset(context, initialValue);
        }

        /* settle */
        sendStatus(snapshot ? 'Settling attractor' : 'Settling potential attractor');

        for (let i = 0; i < SETTLE_ITERATIONS; i++) {
            value = map.apply(value, coefficients);
            if (!Point.isValid(value)) {
                return false;
            }

            for (let criterion of criteria) {
                if (criterion.requiresSettling) {
                    continue;
                }

                let result = criterion.test(context, value);
                if (!snapshot && !result) {
                    criterion.test(context, value);
                    return false;
                }
            }
        }

        /* reset our 'initial value' to the settled one */
        initialValue = value.slice();

        /* Compute bounds */
        sendStatus('Computing Bounds');
        for (let i = 0; i < totalIterations; i++) {
            value = map.apply(value, coefficients);
            if (!Point.isValid(value)) {
                return false;
            }

            bounds.update(value);
        }

        if (!bounds.isValid()) {
            return false;
        }

        /* reset our value to the settled one */
        value = initialValue.slice();

        /* set up our context with our computed bounds and other state */
        context = new Context(map, rng, criteria, initialValue, coefficients, bounds);

        /* reset search criteria state */
        for (let criterion of criteria) {
            criterion.reset(context, initialValue);
        }

        /* reset projection and colorizer */
        projection.reset();
        colorizer.reset();

        /* search + generate */
        sendStatus(snapshot || criteria.length === 0 ? "Generating remaining points" : "Applying search criteria");

        for (let i = 0; i < totalIterations; i++) {
            value = map.apply(value, coefficients);
            if (!Point.isValid(value)) {
                return false;
            }

            let normalized = bounds.normalize(value);
            let projected = projection.apply(context, normalized);

            if (projected.length > 0 && projected[0].constructor !== Array) {
                projected = [ projected ];
            }

            for (let vertex of projected) {
                let colorized = colorizer.apply(context, vertex);
                values.push(colorized);
            }

            if (i < SEARCH_ITERATIONS) {
                for (let criterion of criteria) {
                    if (!criterion.requiresSettling) {
                        continue;
                    }

                    let result = criterion.test(context, value);
                    if (!snapshot && !result) {
                        return false;
                    }
                }
            }

            if (!snapshot && i === SEARCH_ITERATIONS) {
                sendStatus('Generating remaining points');
            }
        }

        snapshot = new AttractorSnapshot(map, rng);

        const stats = [];
        stats.push('C = ' + numCoefficients);
        for (let criterion of criteria) {
            const stat = criterion.renderStats();
            if (stat) {
                stats.push(stat);
            }
        }

        self.postMessage({
            event: 'complete',
            snapshot: snapshot.encode(),
            values: values,
            stats: stats
        });

        return true;
    };

    while (!work()) {
        // if snapshot, work should've succeed
        if (snapshot) {
            break;
        }
    }
}<|endoftext|>


<commit_before>function getElementValue(elementId) {
	var elementValue = $(elementId).val();
	return elementValue;
};

function createSearchString(searchUrl, searchBoxId) {
	var searchBoxContent = getElementValue(searchBoxId);
	var query_string = '?q=' + searchBoxContent;
	var fullSearchUrl = searchUrl + query_string;
	return fullSearchUrl;
};

$(document).ready(function() {
	navBarId = '#mysearch2';
	$(navBarId).bind("enterKey", function(e) {
		var fullSearchUrl = createSearchString(BASE_SEARCH_PAGE_URL, navBarId);
		//console.log(fullSearchUrl);
		window.open(fullSearchUrl, '_self');
	});
	$(navBarId).keyup(function(e) {
		if(e.which == 13) {
			$(this).trigger("enterKey");
		}
	});
});<commit_msg>minor js change<commit_after>function getElementValue(elementId) {
	var elementValue = $(elementId).val();
	return elementValue;
};

function createSearchString(searchUrl, searchBoxId) {
	var searchBoxContent = getElementValue(searchBoxId);
	var query_string = '?q=' + searchBoxContent;
	var fullSearchUrl = searchUrl + query_string;
	return fullSearchUrl;
};

$(document).ready(function() {
	var navBarId = '#mysearch2';
	$(navBarId).bind("enterKey", function(e) {
		var fullSearchUrl = createSearchString(BASE_SEARCH_PAGE_URL, navBarId);
		//console.log(fullSearchUrl);
		window.open(fullSearchUrl, '_self');
	});
	$(navBarId).keyup(function(e) {
		if(e.which == 13) {
			$(this).trigger("enterKey");
		}
	});
});<|endoftext|>


<commit_before>var init = true;
var MAX_RETRIES = 3;
var retries = MAX_RETRIES;
var g = false;
var time_range = false;
var levelData = [];

var layout = {
    drawPoints: true,
    showRoller: true,
    animatedZooms: true,
    title: "Galileo Sensor Chart",
    labels: ['Time', 'Light Level', 'Sound level', 'Temperature level', 'Humidity level'],
    interactionModel: interactionModel,
    width: $(document.body).width(),
    height: 540
}

var compare = function (filter) {
    return function (a,b) { //closure
        var a = a[filter],
            b = b[filter];

        if (a < b) {
            return -1;
        }else if (a > b) {
            return 1;
        } else {
            return 0;
        }
    };
};

function initialiseGraph() {
    g = new Dygraph(document.getElementById("sensorgraph"), levelData, layout);
}

function updateGraph() {
    g.updateOptions({'file': levelData});
}

function send() {
    setTimeout(function() {
        var output = {
            "init": init,
            "time": init ? 0 : g.xAxisRange()[1]
        };

        if(!init && g.xAxisRange()[0] < g.xAxisExtremes()[0]) {
            output.startTime = parseInt(g.xAxisRange()[0]/1000);
            output.endTime = parseInt(g.xAxisExtremes()[0]/1000);
            console.log("loading earlier data");
        } else if(!init && g.xAxisRange()[1] > g.xAxisExtremes()[1]) {
            output.startTime = parseInt(g.xAxisExtremes()[1]/1000)
            output.endTime = parseInt(g.xAxisRange()[1]/1000);
            console.log("loading later data");
        }

        if(isConnected(ws)) {
            retries = MAX_RETRIES;
            ws.send(JSON.stringify(output));
        } else if(retries-- > 0) {
            ws = connect();
        } else {
            alert("Unable to connect to server after " + MAX_RETRIES + " retries");
            clearInterval(c);
        }
    }, 1000);
}

function isConnected(ws) {
    return ws.readyState === 1;
}

function connect() {
    var socket = new WebSocket("ws://" + document.location.host + "/websocket");
    socket.onmessage = receive;
    return socket;
}

function disconnect() {
    ws.onclose = function () {}; // disable onclose handler first
    ws.close();
}

function parseRow(ele) {
    // (timestamp, light, sound, temperature, humidity) -> (int 4, int 4 int 4, real, real).
    return [
        new Date(parseInt(ele[0])*1000),
        parseInt(ele[1]),
        parseInt(ele[2]),
        parseFloat(ele[3]),
        parseFloat(ele[4])
    ];
}

function receive(msg) {
    var data = JSON.parse(msg.data);
    var rows = data;
    console.log(rows.length);
    if(rows.length) {
        levelData = levelData.concat(rows.map(parseRow)).sort(function(a, b) {
            return a[0] - b[0];
        }).filter(function(ele, pos, arr) { return arr.indexOf(ele) == pos; });

        if(init) {
            initialiseGraph();
            init = false;
        } else updateGraph();
    }

    send();
}

window.addEventListener('resize', function() {
    g.resize($(document.body).width());
});

window.addEventListener("load", function() {
    if ("WebSocket" in window) {
        ws = connect();
    };

    send();

    window.onbeforeunload = disconnect;
});
<commit_msg>first attempt at auto-resizing height<commit_after>var init = true;
var MAX_RETRIES = 3;
var retries = MAX_RETRIES;
var g = false;
var time_range = false;
var levelData = [];

var layout = {
    drawPoints: true,
    showRoller: true,
    animatedZooms: true,
    title: "Galileo Sensor Chart",
    labels: ['Time', 'Light Level', 'Sound level', 'Temperature level', 'Humidity level'],
    interactionModel: interactionModel,
    width: $(document.body).width(),
    height: $(window).height() - $(document).height() - 50
}

var compare = function (filter) {
    return function (a,b) { //closure
        var a = a[filter],
            b = b[filter];

        if (a < b) {
            return -1;
        }else if (a > b) {
            return 1;
        } else {
            return 0;
        }
    };
};

function initialiseGraph() {
    g = new Dygraph(document.getElementById("sensorgraph"), levelData, layout);
}

function updateGraph() {
    g.updateOptions({'file': levelData});
}

function send() {
    setTimeout(function() {
        var output = {
            "init": init,
            "time": init ? 0 : g.xAxisRange()[1]
        };

        if(!init && g.xAxisRange()[0] < g.xAxisExtremes()[0]) {
            output.startTime = parseInt(g.xAxisRange()[0]/1000);
            output.endTime = parseInt(g.xAxisExtremes()[0]/1000);
            console.log("loading earlier data");
        } else if(!init && g.xAxisRange()[1] > g.xAxisExtremes()[1]) {
            output.startTime = parseInt(g.xAxisExtremes()[1]/1000)
            output.endTime = parseInt(g.xAxisRange()[1]/1000);
            console.log("loading later data");
        }

        if(isConnected(ws)) {
            retries = MAX_RETRIES;
            ws.send(JSON.stringify(output));
        } else if(retries-- > 0) {
            ws = connect();
        } else {
            alert("Unable to connect to server after " + MAX_RETRIES + " retries");
            clearInterval(c);
        }
    }, 1000);
}

function isConnected(ws) {
    return ws.readyState === 1;
}

function connect() {
    var socket = new WebSocket("ws://" + document.location.host + "/websocket");
    socket.onmessage = receive;
    return socket;
}

function disconnect() {
    ws.onclose = function () {}; // disable onclose handler first
    ws.close();
}

function parseRow(ele) {
    // (timestamp, light, sound, temperature, humidity) -> (int 4, int 4 int 4, real, real).
    return [
        new Date(parseInt(ele[0])*1000),
        parseInt(ele[1]),
        parseInt(ele[2]),
        parseFloat(ele[3]),
        parseFloat(ele[4])
    ];
}

function receive(msg) {
    var data = JSON.parse(msg.data);
    var rows = data;
    console.log(rows.length);
    if(rows.length) {
        levelData = levelData.concat(rows.map(parseRow)).sort(function(a, b) {
            return a[0] - b[0];
        }).filter(function(ele, pos, arr) { return arr.indexOf(ele) == pos; });

        if(init) {
            initialiseGraph();
            init = false;
        } else updateGraph();
    }

    send();
}

window.addEventListener('resize', function() {
    g.resize($(document.body).width(), $(window).height() - $(document).height() - 50);
});

window.addEventListener("load", function() {
    if ("WebSocket" in window) {
        ws = connect();
    };

    send();

    window.onbeforeunload = disconnect;
});
<|endoftext|>


<commit_before>/* global window, document, jQuery */

// ==UserScript==
// @name         Pixiv hover zoom
// @namespace    https://github.com/rplus
// @version      0.4
// @description  hover zoom for Pixiv
// @author       Rplus
// @include      http://www.pixiv.net/*
// @grant        none
// ==/UserScript==

;(function(window, document, $) {
  'use strict';

  var phzw = $('#pixiv-hz-wrap');
  var phzwCache = {};
  var phzwPos = [];
  var phzwPattern = /member_illust\.php\?mode=medium&illust_id=(\d+)/;
  var phzwURL;
  var phzwOriginImg;
  var body = document.body;

  var phzwToggle = function(_switch) {
    if (_switch) {
      phzw.css({
        'top': phzwPos[0],
        'left': phzwPos[1],
        'opacity': 1,
        'visibility': 'visible',
        'transform': 'translateX(' + phzwPos[2] + '%)'
      });
    } else {
      if ('none' !== phzw[0].style.display) {
        phzw.css({
          'visibility': 'hidden',
          'opacity': 0
        });
        // phzw.fadeOut();
      }
    }
  };

  var phzwUpdate = function(_html, _url) {
    if (_url !== phzwURL) {
      phzwURL = _url;
      phzw.attr('href', _url).html(_html);
    }
    phzwToggle(true);
  };

  var phzwPull = function(_url, _preload) {
    var _html;
    var _urlID = _url.match(phzwPattern).pop();

    if (phzwCache[_urlID]) {
      _html = phzwCache[_urlID];
      if (!_preload) { phzwUpdate(_html, _url); }
    } else {
      $.get(_url, function(data) {
        var _html;
        if (phzwOriginImg && -1 !== data.indexOf('original-image')) {
          // large image
          _html = data.match(/_illust_modal.+?()/).pop();
          _html = _html.replace(/(alt=(".+?"))/, '$1 title=$2').replace(/data-src/, 'src');
        } else {
          _html = data.match(/works_display.+?()/).pop();
          _html = _html.replace(/(alt=(".+?"))/, '$1 title=$2');
        }

        phzwCache[_urlID] = _html;
        if (!_preload) {
          phzwUpdate(_html, _url);
        } else {
          $('
').html(_html);
        }
      });
    }
  };

  if (!phzw.length) {
    $(body).append('');
    phzw = $('#pixiv-hz-wrap').css({
      'position': 'absolute',
      'visibility': 'hidden',
      'opacity': 0,
      'box-shadow': '0 0 0 3px #fff, 0 0 3px 3px',
      'transition': 'all .3s',
      'z-index': '1000'
    });
  }

  $('._unit').on('mouseenter.phzw', 'a', function(e) {
    if ($(this).attr('href').match(phzwPattern)) {
      e.stopPropagation();
      var thisRect = this.getBoundingClientRect();
      phzwPos = [thisRect.top + body.scrollTop + thisRect.height, thisRect.left, -100 * thisRect.left / body.scrollWidth];
      phzwPull(this.href);
    }
  });

  $(document).on('click.phzw', function() {
    phzwToggle(false);
  });

  $('.link-item').eq(0).prepend('<button> ♥ preload all ♥ </button>').find('button').on('click', function(event) {
    event.preventDefault();
    $('._unit').find('a').filter(function() {
      return $(this).attr('href').match(phzwPattern);
    }).each(function() {
      phzwPull(this.href, true);
    });

  });

})(window, document, jQuery, undefined);
<commit_msg>update version<commit_after>/* global window, document, jQuery */

// ==UserScript==
// @name         Pixiv hover zoom
// @namespace    https://github.com/rplus
// @version      0.5
// @description  hover zoom for Pixiv
// @author       Rplus
// @include      http://www.pixiv.net/*
// @grant        none
// ==/UserScript==

;(function(window, document, $) {
  'use strict';

  var phzw = $('#pixiv-hz-wrap');
  var phzwCache = {};
  var phzwPos = [];
  var phzwPattern = /member_illust\.php\?mode=medium&illust_id=(\d+)/;
  var phzwURL;
  var phzwOriginImg;
  var body = document.body;

  var phzwToggle = function(_switch) {
    if (_switch) {
      phzw.css({
        'top': phzwPos[0],
        'left': phzwPos[1],
        'opacity': 1,
        'visibility': 'visible',
        'transform': 'translateX(' + phzwPos[2] + '%)'
      });
    } else {
      if ('none' !== phzw[0].style.display) {
        phzw.css({
          'visibility': 'hidden',
          'opacity': 0
        });
        // phzw.fadeOut();
      }
    }
  };

  var phzwUpdate = function(_html, _url) {
    if (_url !== phzwURL) {
      phzwURL = _url;
      phzw.attr('href', _url).html(_html);
    }
    phzwToggle(true);
  };

  var phzwPull = function(_url, _preload) {
    var _html;
    var _urlID = _url.match(phzwPattern).pop();

    if (phzwCache[_urlID]) {
      _html = phzwCache[_urlID];
      if (!_preload) { phzwUpdate(_html, _url); }
    } else {
      $.get(_url, function(data) {
        var _html;
        if (phzwOriginImg && -1 !== data.indexOf('original-image')) {
          // large image
          _html = data.match(/_illust_modal.+?()/).pop();
          _html = _html.replace(/(alt=(".+?"))/, '$1 title=$2').replace(/data-src/, 'src');
        } else {
          _html = data.match(/works_display.+?()/).pop();
          _html = _html.replace(/(alt=(".+?"))/, '$1 title=$2');
        }

        phzwCache[_urlID] = _html;
        if (!_preload) {
          phzwUpdate(_html, _url);
        } else {
          $('
').html(_html);
        }
      });
    }
  };

  if (!phzw.length) {
    $(body).append('');
    phzw = $('#pixiv-hz-wrap').css({
      'position': 'absolute',
      'visibility': 'hidden',
      'opacity': 0,
      'box-shadow': '0 0 0 3px #fff, 0 0 3px 3px',
      'transition': 'all .3s',
      'z-index': '1000'
    });
  }

  $('._unit').on('mouseenter.phzw', 'a', function(e) {
    if ($(this).attr('href').match(phzwPattern)) {
      e.stopPropagation();
      var thisRect = this.getBoundingClientRect();
      phzwPos = [thisRect.top + body.scrollTop + thisRect.height, thisRect.left, -100 * thisRect.left / body.scrollWidth];
      phzwPull(this.href);
    }
  });

  $(document).on('click.phzw', function() {
    phzwToggle(false);
  });

  $('.link-item').eq(0).prepend('<button> ♥ preload all ♥ </button>').find('button').on('click', function(event) {
    event.preventDefault();
    $('._unit').find('a').filter(function() {
      return $(this).attr('href').match(phzwPattern);
    }).each(function() {
      phzwPull(this.href, true);
    });

  });

})(window, document, jQuery, undefined);
<|endoftext|>


<commit_before>import PrimeFactors from '../../../src/functions/prime_factors/prime_factors.js';

describe('Prime Factors', () => {

    it('should calculate prime factor of an int', () => {
        expect(PrimeFactors.of(1)).to.deep.equal([]);
        expect(PrimeFactors.of(2)).to.deep.equal([2]);
        expect(PrimeFactors.of(3)).to.deep.equal([3]);
        expect(PrimeFactors.of(4)).to.deep.equal([2, 2]);
        expect(PrimeFactors.of(5)).to.deep.equal([5]);
        expect(PrimeFactors.of(6)).to.deep.equal([2, 3]);
        expect(PrimeFactors.of(7)).to.deep.equal([7]);
        expect(PrimeFactors.of(8)).to.deep.equal([2, 2, 2]);
    });

});<commit_msg>added test for input of 9, test fails<commit_after>import PrimeFactors from '../../../src/functions/prime_factors/prime_factors.js';

describe('Prime Factors', () => {

    it('should calculate prime factor of an int', () => {
        expect(PrimeFactors.of(1)).to.deep.equal([]);
        expect(PrimeFactors.of(2)).to.deep.equal([2]);
        expect(PrimeFactors.of(3)).to.deep.equal([3]);
        expect(PrimeFactors.of(4)).to.deep.equal([2, 2]);
        expect(PrimeFactors.of(5)).to.deep.equal([5]);
        expect(PrimeFactors.of(6)).to.deep.equal([2, 3]);
        expect(PrimeFactors.of(7)).to.deep.equal([7]);
        expect(PrimeFactors.of(8)).to.deep.equal([2, 2, 2]);
        expect(PrimeFactors.of(9)).to.deep.equal([3, 3, 3]);
    });

});<|endoftext|>


<commit_before>import hbs from 'htmlbars-inline-precompile';
import sinon from 'sinon';
import {click, find, findAll, render} from '@ember/test-helpers';
import {describe, it} from 'mocha';
import {expect} from 'chai';
import {setupRenderingTest} from 'ember-mocha';

describe('Integration: Component: gh-theme-table', function () {
    setupRenderingTest();

    it('renders', async function () {
        this.set('themes', [
            {name: 'Daring', package: {name: 'Daring', version: '0.1.4'}, active: true},
            {name: 'casper', package: {name: 'Casper', version: '1.3.1'}},
            {name: 'oscar-ghost-1.1.0', package: {name: 'Lanyon', version: '1.1.0'}},
            {name: 'foo'}
        ]);
        this.set('actionHandler', sinon.spy());

        await render(hbs`{{gh-theme-table
            themes=themes
            activateTheme=(action actionHandler)
            downloadTheme=(action actionHandler)
            deleteTheme=(action actionHandler)
        }}`);

        expect(findAll('[data-test-themes-list]').length, 'themes list is present').to.equal(1);
        expect(findAll('[data-test-theme-id]').length, 'number of rows').to.equal(4);

        let packageNames = findAll('[data-test-theme-title]').map(name => name.textContent.trim());

        expect(
            packageNames,
            'themes are ordered by label, casper has "default"'
        ).to.deep.equal([
            'Casper (default)',
            'Daring',
            'foo',
            'Lanyon'
        ]);

        expect(
            find('[data-test-theme-active="true"]').querySelector('[data-test-theme-title]'),
            'active theme is highlighted'
        ).to.have.trimmed.text('Daring');

        expect(
            findAll('[data-test-theme-activate-button]').length,
            'non-active themes have an activate link'
        ).to.equal(3);

        expect(
            find('[data-test-theme-active="true"]').querySelector('[data-test-theme-activate-button]'),
            'active theme doesn\'t have an activate link'
        ).to.not.exist;

        expect(
            findAll('[data-test-theme-download-button]').length,
            'all themes have a download link'
        ).to.equal(4);

        expect(
            find('[data-test-theme-id="foo"]').querySelector('[data-test-theme-delete-button]'),
            'non-active, non-casper theme has delete link'
        ).to.exist;

        expect(
            find('[data-test-theme-id="casper"]').querySelector('[data-test-theme-delete-button]'),
            'casper doesn\'t have delete link'
        ).to.not.exist;

        expect(
            find('[data-test-theme-active="true"]').querySelector('[data-test-theme-delete-button]'),
            'active theme doesn\'t have delete link'
        ).to.not.exist;
    });

    it('delete link triggers passed in action', async function () {
        let deleteAction = sinon.spy();
        let actionHandler = sinon.spy();

        this.set('themes', [
            {name: 'Foo', active: true},
            {name: 'Bar'}
        ]);
        this.set('deleteAction', deleteAction);
        this.set('actionHandler', actionHandler);

        await render(hbs`{{gh-theme-table
            themes=themes
            activateTheme=(action actionHandler)
            downloadTheme=(action actionHandler)
            deleteTheme=(action deleteAction)
        }}`);

        await click('[data-test-theme-id="Bar"] [data-test-theme-delete-button]');

        expect(deleteAction.calledOnce).to.be.true;
        expect(deleteAction.firstCall.args[0].name).to.equal('Bar');
    });

    it('download link triggers passed in action', async function () {
        let downloadAction = sinon.spy();
        let actionHandler = sinon.spy();

        this.set('themes', [
            {name: 'Foo', active: true},
            {name: 'Bar'}
        ]);
        this.set('downloadAction', downloadAction);
        this.set('actionHandler', actionHandler);

        await render(hbs`{{gh-theme-table
            themes=themes
            activateTheme=(action actionHandler)
            downloadTheme=(action downloadAction)
            deleteTheme=(action actionHandler)
        }}`);

        await click('[data-test-theme-id="Foo"] [data-test-theme-download-button]');

        expect(downloadAction.calledOnce).to.be.true;
        expect(downloadAction.firstCall.args[0].name).to.equal('Foo');
    });

    it('activate link triggers passed in action', async function () {
        let activateAction = sinon.spy();
        let actionHandler = sinon.spy();

        this.set('themes', [
            {name: 'Foo', active: true},
            {name: 'Bar'}
        ]);
        this.set('activateAction', activateAction);
        this.set('actionHandler', actionHandler);

        await render(hbs`{{gh-theme-table
            themes=themes
            activateTheme=(action activateAction)
            downloadTheme=(action actionHandler)
            deleteTheme=(action actionHandler)
        }}`);

        await click('[data-test-theme-id="Bar"] [data-test-theme-activate-button]');

        expect(activateAction.calledOnce).to.be.true;
        expect(activateAction.firstCall.args[0].name).to.equal('Bar');
    });

    it('displays folder names if there are duplicate package names', async function () {
        this.set('themes', [
            {name: 'daring', package: {name: 'Daring', version: '0.1.4'}, active: true},
            {name: 'daring-0.1.5', package: {name: 'Daring', version: '0.1.4'}},
            {name: 'casper', package: {name: 'Casper', version: '1.3.1'}},
            {name: 'another', package: {name: 'Casper', version: '1.3.1'}},
            {name: 'mine', package: {name: 'Casper', version: '1.3.1'}},
            {name: 'foo'}
        ]);
        this.set('actionHandler', sinon.spy());

        await render(hbs`{{gh-theme-table
            themes=themes
            activateTheme=(action actionHandler)
            downloadTheme=(action actionHandler)
            deleteTheme=(action actionHandler)
        }}`);

        let packageNames = findAll('[data-test-theme-title]').map(name => name.textContent.trim());

        expect(
            packageNames,
            'themes are ordered by label, folder names shown for duplicates'
        ).to.deep.equal([
            'Casper (another)',
            'Casper (default)',
            'Casper (mine)',
            'Daring (daring)',
            'Daring (daring-0.1.5)',
            'foo'
        ]);
    });
});
<commit_msg>Updated <GhThemeTable> test<commit_after>import hbs from 'htmlbars-inline-precompile';
import sinon from 'sinon';
import {click, find, findAll, render} from '@ember/test-helpers';
import {describe, it} from 'mocha';
import {expect} from 'chai';
import {setupRenderingTest} from 'ember-mocha';

describe('Integration: Component: gh-theme-table', function () {
    setupRenderingTest();

    it('renders', async function () {
        this.set('themes', [
            {name: 'Daring', package: {name: 'Daring', version: '0.1.4'}, active: true},
            {name: 'casper', package: {name: 'Casper', version: '1.3.1'}},
            {name: 'oscar-ghost-1.1.0', package: {name: 'Lanyon', version: '1.1.0'}},
            {name: 'foo'}
        ]);

        await render(hbs`<GhThemeTable @themes={{themes}} />`);

        expect(findAll('[data-test-themes-list]').length, 'themes list is present').to.equal(1);
        expect(findAll('[data-test-theme-id]').length, 'number of rows').to.equal(4);

        let packageNames = findAll('[data-test-theme-title]').map(name => name.textContent.trim());

        expect(packageNames[0]).to.match(/Casper \(default\)/);
        expect(packageNames[1]).to.match(/Daring\s+Active/);
        expect(packageNames[2]).to.match(/foo/);
        expect(packageNames[3]).to.match(/Lanyon/);

        expect(
            find('[data-test-theme-active="true"]').querySelector('[data-test-theme-title]'),
            'active theme is highlighted'
        ).to.contain.text('Daring');

        expect(
            findAll('[data-test-button="activate"]').length,
            'non-active themes have an activate link'
        ).to.equal(3);

        expect(
            find('[data-test-theme-active="true"]').querySelector('[data-test-button="activate"]'),
            'active theme doesn\'t have an activate link'
        ).to.not.exist;
    });

    it('has download button in actions dropdown for all themes', async function () {
        const themes = [
            {name: 'Daring', package: {name: 'Daring', version: '0.1.4'}, active: true},
            {name: 'casper', package: {name: 'Casper', version: '1.3.1'}},
            {name: 'oscar-ghost-1.1.0', package: {name: 'Lanyon', version: '1.1.0'}},
            {name: 'foo'}
        ];
        this.set('themes', themes);

        await render(hbs`<GhThemeTable @themes={{themes}} />`);

        for (const theme of themes) {
            await click(`[data-test-theme-id="${theme.name}"] [data-test-button="actions"]`);
            expect(
                find(`[data-test-actions-for="${theme.name}"] [data-test-button="download"]`)
            ).to.exist;
        }
    });

    it('has delete button for non-active, non-default, themes', async function () {
        const themes = [
            {name: 'Daring', package: {name: 'Daring', version: '0.1.4'}},
            {name: 'oscar-ghost-1.1.0', package: {name: 'Lanyon', version: '1.1.0'}},
            {name: 'foo'}
        ];
        this.set('themes', themes);

        await render(hbs`<GhThemeTable @themes={{themes}} />`);

        for (const theme of themes) {
            await click(`[data-test-theme-id="${theme.name}"] [data-test-button="actions"]`);
            expect(
                find(`[data-test-actions-for="${theme.name}"] [data-test-button="delete"]`)
            ).to.exist;
        }
    });

    it('does not show delete action for casper', async function () {
        const themes = [
            {name: 'Daring', package: {name: 'Daring', version: '0.1.4'}, active: true},
            {name: 'casper', package: {name: 'Casper', version: '1.3.1'}},
            {name: 'oscar-ghost-1.1.0', package: {name: 'Lanyon', version: '1.1.0'}},
            {name: 'foo'}
        ];
        this.set('themes', themes);

        await render(hbs`<GhThemeTable @themes={{themes}} />`);

        await click(`[data-test-theme-id="casper"] [data-test-button="actions"]`);
        expect(find('[data-test-actions-for="casper"]')).to.exist;
        expect(
            find(`[data-test-actions-for="casper"] [data-test-button="delete"]`)
        ).to.not.exist;
    });

    it('does not show delete action for active theme', async function () {
        const themes = [
            {name: 'Daring', package: {name: 'Daring', version: '0.1.4'}, active: true},
            {name: 'casper', package: {name: 'Casper', version: '1.3.1'}},
            {name: 'oscar-ghost-1.1.0', package: {name: 'Lanyon', version: '1.1.0'}},
            {name: 'foo'}
        ];
        this.set('themes', themes);

        await render(hbs`<GhThemeTable @themes={{themes}} />`);

        await click(`[data-test-theme-id="Daring"] [data-test-button="actions"]`);
        expect(find('[data-test-actions-for="Daring"]')).to.exist;
        expect(
            find(`[data-test-actions-for="Daring"] [data-test-button="delete"]`)
        ).to.not.exist;
    });

    it('displays folder names if there are duplicate package names', async function () {
        this.set('themes', [
            {name: 'daring', package: {name: 'Daring', version: '0.1.4'}},
            {name: 'daring-0.1.5', package: {name: 'Daring', version: '0.1.4'}},
            {name: 'casper', package: {name: 'Casper', version: '1.3.1'}},
            {name: 'another', package: {name: 'Casper', version: '1.3.1'}},
            {name: 'mine', package: {name: 'Casper', version: '1.3.1'}},
            {name: 'foo'}
        ]);

        await render(hbs`<GhThemeTable @themes={{themes}} />`);

        let packageNames = findAll('[data-test-theme-title]').map(name => name.textContent.trim());

        expect(
            packageNames,
            'themes are ordered by label, folder names shown for duplicates'
        ).to.deep.equal([
            'Casper (another)',
            'Casper (default)',
            'Casper (mine)',
            'Daring (daring)',
            'Daring (daring-0.1.5)',
            'foo'
        ]);
    });
});
<|endoftext|>


<commit_before>const vega = require('vega'),
      path = require('path'),
      args = require('./args'),
      read = require('./read');

function load(file) {
  return require(path.resolve(file));
}

const Levels = {
  error: vega.Error,
  warn:  vega.Warn,
  info:  vega.Info,
  debug: vega.Debug
};

module.exports = function(type, callback, opt) {
  // parse command line arguments
  const arg = args(type);

  // set baseURL, if specified. default to input spec directory
  const base = arg.base || (arg._[0] ? path.dirname(arg._[0]) : null);

  // set log level, defaults to logging warning messages
  const loglevel = Levels[String(arg.loglevel).toLowerCase()] || vega.Warn;

  // load config file, if specified
  const config = arg.config ? load(arg.config) : null;

  // set output image scale factor
  const scale = arg.scale || undefined;

  // use a seeded random number generator, if specified
  if (typeof arg.seed !== 'undefined') {
    if (isNaN(arg.seed)) throw 'Illegal seed value: must be a valid number.';
    vega.setRandom(vega.randomLCG(arg.seed));
  }

  // load custom number format locale, if specified
  if (arg.format) vega.formatLocale(load(arg.format));

  // load custom date/time format locale, if specified
  if (arg.timeFormat) vega.formatTimeLocale(load(arg.timeFormat));

  // instantiate view and invoke headless render method
  function render(spec) {
    const view = new vega.View(vega.parse(spec, config), {
        loader: vega.loader({baseURL: base}),   // load files from base path
        logger: vega.logger(loglevel, 'error'), // route all logging to stderr
        renderer: 'none'                        // no primary renderer needed
      }).finalize();                            // clear any timers, etc

    return (type === 'svg'
        ? view.toSVG(scale)
        : view.toCanvas(scale, opt)
      ).then(_ => callback(_, arg));
  }

  // read input from file or stdin
  read(arg._[0] || null)
    .then(text => render(JSON.parse(text)))
    .catch(err => console.error(err)); // eslint-disable-line no-console
}
<commit_msg>Fix incorrect function name formatTimeLocale<commit_after>const vega = require('vega'),
      path = require('path'),
      args = require('./args'),
      read = require('./read');

function load(file) {
  return require(path.resolve(file));
}

const Levels = {
  error: vega.Error,
  warn:  vega.Warn,
  info:  vega.Info,
  debug: vega.Debug
};

module.exports = function(type, callback, opt) {
  // parse command line arguments
  const arg = args(type);

  // set baseURL, if specified. default to input spec directory
  const base = arg.base || (arg._[0] ? path.dirname(arg._[0]) : null);

  // set log level, defaults to logging warning messages
  const loglevel = Levels[String(arg.loglevel).toLowerCase()] || vega.Warn;

  // load config file, if specified
  const config = arg.config ? load(arg.config) : null;

  // set output image scale factor
  const scale = arg.scale || undefined;

  // use a seeded random number generator, if specified
  if (typeof arg.seed !== 'undefined') {
    if (isNaN(arg.seed)) throw 'Illegal seed value: must be a valid number.';
    vega.setRandom(vega.randomLCG(arg.seed));
  }

  // load custom number format locale, if specified
  if (arg.format) vega.formatLocale(load(arg.format));

  // load custom date/time format locale, if specified
  if (arg.timeFormat) vega.timeFormatLocale(load(arg.timeFormat));

  // instantiate view and invoke headless render method
  function render(spec) {
    const view = new vega.View(vega.parse(spec, config), {
        loader: vega.loader({baseURL: base}),   // load files from base path
        logger: vega.logger(loglevel, 'error'), // route all logging to stderr
        renderer: 'none'                        // no primary renderer needed
      }).finalize();                            // clear any timers, etc

    return (type === 'svg'
        ? view.toSVG(scale)
        : view.toCanvas(scale, opt)
      ).then(_ => callback(_, arg));
  }

  // read input from file or stdin
  read(arg._[0] || null)
    .then(text => render(JSON.parse(text)))
    .catch(err => console.error(err)); // eslint-disable-line no-console
}
<|endoftext|>


<commit_before>"use strict";

var path = require('path'),
  fs = require('fs-extra'),
  JSON5 = require('json5'),
  _ = require('lodash'),
  mp = require('./markdown_parser');

var annotations_exporter = function (pl) {

  var paths = pl.config.paths;

  /*
  Returns the array of comments that used to be wrapped in raw JS.
   */
  function parseAnnotationsJS() {
    //attempt to read the file
    try {
      var oldAnnotations = fs.readFileSync(path.resolve(paths.source.annotations, 'annotations.js'), 'utf8');
    } catch (ex) {
      if (pl.config.debug) {
        console.log('annotations.js file missing from ' + paths.source.annotations + '. This may be expected.');
      }
      return [];
    }

    //parse as JSON by removing the old wrapping js syntax. comments and the trailing semi-colon
    oldAnnotations = oldAnnotations.replace('var comments = ', '');
    try {
      var oldAnnotationsJSON = JSON5.parse(oldAnnotations.trim().slice(0, -1));
    } catch (ex) {
      console.log('There was an error parsing JSON for ' + paths.source.annotations + 'annotations.js');
      console.log(ex);
      return [];
    }
    return oldAnnotationsJSON.comments;
  }

  /*
   Converts the annotations.md file yaml list into an array of annotations
   */
  function parseAnnotationsMD() {
    var markdown_parser = new mp();
    var annotations = [];

    //attempt to read the file
    var annotationsMD = '';
    try {
      annotationsMD = fs.readFileSync(path.resolve(paths.source.annotations, 'annotations.md'), 'utf8');
    } catch (ex) {
      if (pl.config.debug) {
        console.log('annotations.md file missing from ' + paths.source.annotations + '. This may be expected.');
      }
      return [];
    }

    //take the annotation snippets and split them on our custom delimiter
    var annotationsYAML = annotationsMD.split('~*~');

    for (var i = 0; i < annotationsYAML.length; i++) {
      var annotation = {};

      var markdownObj = markdown_parser.parse(annotationsYAML[i]);

      annotation.el = markdownObj.el || markdownObj.selector;
      annotation.title = markdownObj.title;
      annotation.comment = markdownObj.markdown;

      annotations.push(annotation);
    }
    return annotations;
  }

  function gatherAnnotations() {
    var annotationsJS = parseAnnotationsJS();
    var annotationsMD = parseAnnotationsMD();
    return _.unionBy(annotationsJS, annotationsMD, 'el');
  }

  return {
    gather: function () {
      return gatherAnnotations();
    },
    gatherJS: function () {
      return parseAnnotationsJS();
    },
    gatherMD: function () {
      return parseAnnotationsMD();
    }
  };

};

module.exports = annotations_exporter;
<commit_msg>fix issue with annotations not parsing correctly if already valid json<commit_after>"use strict";

var path = require('path'),
  fs = require('fs-extra'),
  JSON5 = require('json5'),
  _ = require('lodash'),
  mp = require('./markdown_parser');

var annotations_exporter = function (pl) {

  var paths = pl.config.paths;

  /*
  Returns the array of comments that used to be wrapped in raw JS.
   */
  function parseAnnotationsJS() {
    //attempt to read the file
    try {
      var oldAnnotations = fs.readFileSync(path.resolve(paths.source.annotations, 'annotations.js'), 'utf8');
    } catch (ex) {
      if (pl.config.debug) {
        console.log('annotations.js file missing from ' + paths.source.annotations + '. This may be expected.');
      }
      return [];
    }

    //parse as JSON by removing the old wrapping js syntax. comments and the trailing semi-colon
    oldAnnotations = oldAnnotations.replace('var comments = ', '');
    if (oldAnnotations.trim().substr(oldAnnotations.length - 1)  === ';') {
      oldAnnotations = oldAnnotations.trim().slice(0, -1);
    }

    try {
      var oldAnnotationsJSON = JSON5.parse(oldAnnotations);
    } catch (ex) {
      console.log('There was an error parsing JSON for ' + paths.source.annotations + 'annotations.js');
      console.log(ex);
      return [];
    }
    return oldAnnotationsJSON.comments;
  }

  /*
   Converts the annotations.md file yaml list into an array of annotations
   */
  function parseAnnotationsMD() {
    var markdown_parser = new mp();
    var annotations = [];

    //attempt to read the file
    var annotationsMD = '';
    try {
      annotationsMD = fs.readFileSync(path.resolve(paths.source.annotations, 'annotations.md'), 'utf8');
    } catch (ex) {
      if (pl.config.debug) {
        console.log('annotations.md file missing from ' + paths.source.annotations + '. This may be expected.');
      }
      return [];
    }

    //take the annotation snippets and split them on our custom delimiter
    var annotationsYAML = annotationsMD.split('~*~');

    for (var i = 0; i < annotationsYAML.length; i++) {
      var annotation = {};

      var markdownObj = markdown_parser.parse(annotationsYAML[i]);

      annotation.el = markdownObj.el || markdownObj.selector;
      annotation.title = markdownObj.title;
      annotation.comment = markdownObj.markdown;

      annotations.push(annotation);
    }
    return annotations;
  }

  function gatherAnnotations() {
    var annotationsJS = parseAnnotationsJS();
    var annotationsMD = parseAnnotationsMD();
    return _.unionBy(annotationsJS, annotationsMD, 'el');
  }

  return {
    gather: function () {
      return gatherAnnotations();
    },
    gatherJS: function () {
      return parseAnnotationsJS();
    },
    gatherMD: function () {
      return parseAnnotationsMD();
    }
  };

};

module.exports = annotations_exporter;
<|endoftext|>