<commit_before>import $ from 'jquery';
import DEFAULTS from './defaults';
import TEMPLATE from './template';
import render from './render';
import preview from './preview';
import events from './events';
import handlers from './handlers';
import change from './change';
import methods from './methods';
import * as utils from './utilities';

const CLASS_HIDDEN = 'cropper-hidden';
const REGEXP_DATA_URL = /^data:/;
const REGEXP_DATA_URL_JPEG = /^data:image\/jpeg;base64,/;

class Cropper {
  constructor(element, options) {
    const self = this;

    self.$element = $(element);
    self.options = $.extend({}, DEFAULTS, $.isPlainObject(options) && options);
    self.loaded = false;
    self.ready = false;
    self.completed = false;
    self.rotated = false;
    self.cropped = false;
    self.disabled = false;
    self.replaced = false;
    self.limited = false;
    self.wheeling = false;
    self.isImg = false;
    self.originalUrl = '';
    self.canvas = null;
    self.cropBox = null;
    self.pointers = {};
    self.init();
  }

  init() {
    const self = this;
    const $this = self.$element;
    let url;

    if ($this.is('img')) {
      self.isImg = true;

      // Should use `$.fn.attr` here. e.g.: "img/picture.jpg"
      self.originalUrl = url = $this.attr('src');

      // Stop when it's a blank image
      if (!url) {
        return;
      }

      // Should use `$.fn.prop` here. e.g.: "http://example.com/img/picture.jpg"
      url = $this.prop('src');
    } else if ($this.is('canvas') && window.HTMLCanvasElement) {
      url = $this[0].toDataURL();
    }

    self.load(url);
  }

  // A shortcut for triggering custom events
  trigger(type, data) {
    const e = $.Event(type, data);

    this.$element.trigger(e);

    return e;
  }

  load(url) {
    const self = this;
    const options = self.options;
    const $this = self.$element;

    if (!url) {
      return;
    }

    self.url = url;
    self.image = {};

    if (!options.checkOrientation || !ArrayBuffer) {
      self.clone();
      return;
    }

    // XMLHttpRequest disallows to open a Data URL in some browsers like IE11 and Safari
    if (REGEXP_DATA_URL.test(url)) {
      if (REGEXP_DATA_URL_JPEG.test(url)) {
        self.read(utils.dataURLToArrayBuffer(url));
      } else {
        self.clone();
      }
      return;
    }

    const xhr = new XMLHttpRequest();

    xhr.onerror = xhr.onabort = $.proxy(() => {
      self.clone();
    }, this);

    xhr.onload = function load() {
      self.read(this.response);
    };

    if (options.checkCrossOrigin && utils.isCrossOriginURL(url) && $this.prop('crossOrigin')) {
      url = utils.addTimestamp(url);
    }

    xhr.open('get', url);
    xhr.responseType = 'arraybuffer';
    xhr.withCredentials = $this.prop('crossOrigin') === 'use-credentials';
    xhr.send();
  }

  read(arrayBuffer) {
    const self = this;
    const options = self.options;
    const orientation = utils.getOrientation(arrayBuffer);
    const image = self.image;
    let rotate = 0;
    let scaleX = 1;
    let scaleY = 1;

    if (orientation > 1) {
      self.url = utils.arrayBufferToDataURL(arrayBuffer);

      switch (orientation) {

        // flip horizontal
        case 2:
          scaleX = -1;
          break;

        // rotate left 180°
        case 3:
          rotate = -180;
          break;

        // flip vertical
        case 4:
          scaleY = -1;
          break;

        // flip vertical + rotate right 90°
        case 5:
          rotate = 90;
          scaleY = -1;
          break;

        // rotate right 90°
        case 6:
          rotate = 90;
          break;

        // flip horizontal + rotate right 90°
        case 7:
          rotate = 90;
          scaleX = -1;
          break;

        // rotate left 90°
        case 8:
          rotate = -90;
          break;
      }
    }

    if (options.rotatable) {
      image.rotate = rotate;
    }

    if (options.scalable) {
      image.scaleX = scaleX;
      image.scaleY = scaleY;
    }

    self.clone();
  }

  clone() {
    const self = this;
    const options = self.options;
    const $this = self.$element;
    const url = self.url;
    let crossOrigin = '';
    let crossOriginUrl;

    if (options.checkCrossOrigin && utils.isCrossOriginURL(url)) {
      crossOrigin = $this.prop('crossOrigin');

      if (crossOrigin) {
        crossOriginUrl = url;
      } else {
        crossOrigin = 'anonymous';

        // Bust cache (#148) when there is not a "crossOrigin" property
        crossOriginUrl = utils.addTimestamp(url);
      }
    }

    self.crossOrigin = crossOrigin;
    self.crossOriginUrl = crossOriginUrl;

    const $clone = $(`<img ${utils.getCrossOrigin(crossOrigin)} src="${crossOriginUrl || url}">`);

    self.$clone = $clone;

    if (self.isImg) {
      if ($this[0].complete) {
        self.start();
      } else {
        $this.one('load', $.proxy(self.start, this));
      }
    } else {
      $clone.one('load', $.proxy(self.start, this))
        .one('error', $.proxy(self.stop, this))
        .addClass('cropper-hide')
        .insertAfter($this);
    }
  }

  start() {
    const self = this;
    const $clone = self.$clone;
    let $image = self.$element;

    if (!self.isImg) {
      $clone.off('error', self.stop);
      $image = $clone;
    }

    utils.getImageSize($image[0], (naturalWidth, naturalHeight) => {
      $.extend(self.image, {
        naturalWidth,
        naturalHeight,
        aspectRatio: naturalWidth / naturalHeight
      });

      self.loaded = true;
      self.build();
    });
  }

  stop() {
    const self = this;

    self.$clone.remove();
    self.$clone = null;
  }

  build() {
    const self = this;
    const options = self.options;
    const $this = self.$element;
    const $clone = self.$clone;
    let $cropper;
    let $cropBox;
    let $face;

    if (!self.loaded) {
      return;
    }

    // Unbuild first when replace
    if (self.ready) {
      self.unbuild();
    }

    // Create cropper elements
    self.$container = $this.parent();
    self.$cropper = $cropper = $(TEMPLATE);
    self.$canvas = $cropper.find('.cropper-canvas').append($clone);
    self.$dragBox = $cropper.find('.cropper-drag-box');
    self.$cropBox = $cropBox = $cropper.find('.cropper-crop-box');
    self.$viewBox = $cropper.find('.cropper-view-box');
    self.$face = $face = $cropBox.find('.cropper-face');

    // Hide the original image
    $this.addClass(CLASS_HIDDEN).after($cropper);

    // Show the clone image if is hidden
    if (!self.isImg) {
      $clone.removeClass('cropper-hide');
    }

    self.initPreview();
    self.bind();

    options.aspectRatio = Math.max(0, options.aspectRatio) || NaN;
    options.viewMode = Math.max(0, Math.min(3, Math.round(options.viewMode))) || 0;

    self.cropped = options.autoCrop;

    if (options.autoCrop) {
      if (options.modal) {
        self.$dragBox.addClass('cropper-modal');
      }
    } else {
      $cropBox.addClass(CLASS_HIDDEN);
    }

    if (!options.guides) {
      $cropBox.find('.cropper-dashed').addClass(CLASS_HIDDEN);
    }

    if (!options.center) {
      $cropBox.find('.cropper-center').addClass(CLASS_HIDDEN);
    }

    if (options.cropBoxMovable) {
      $face.addClass('cropper-move').data('action', 'all');
    }

    if (!options.highlight) {
      $face.addClass('cropper-invisible');
    }

    if (options.background) {
      $cropper.addClass('cropper-bg');
    }

    if (!options.cropBoxResizable) {
      $cropBox.find('.cropper-line, .cropper-point').addClass(CLASS_HIDDEN);
    }

    self.setDragMode(options.dragMode);
    self.render();
    self.ready = true;
    self.setData(options.data);

    // Trigger the ready event asynchronously to keep `data('cropper')` is defined
    self.completing = setTimeout(() => {
      if ($.isFunction(options.ready)) {
        $this.one('ready', options.ready);
      }

      self.trigger('ready');
      self.trigger('crop', self.getData());
      self.completed = true;
    }, 0);
  }

  unbuild() {
    const self = this;

    if (!self.ready) {
      return;
    }

    if (!self.completed) {
      clearTimeout(self.completing);
    }

    self.ready = false;
    self.completed = false;
    self.initialImage = null;

    // Clear `initialCanvas` is necessary when replace
    self.initialCanvas = null;
    self.initialCropBox = null;
    self.container = null;
    self.canvas = null;

    // Clear `cropBox` is necessary when replace
    self.cropBox = null;
    self.unbind();

    self.resetPreview();
    self.$preview = null;

    self.$viewBox = null;
    self.$cropBox = null;
    self.$dragBox = null;
    self.$canvas = null;
    self.$container = null;

    self.$cropper.remove();
    self.$cropper = null;
  }

  static setDefaults(options) {
    $.extend(DEFAULTS, $.isPlainObject(options) && options);
  }
}

$.extend(Cropper.prototype, render);
$.extend(Cropper.prototype, preview);
$.extend(Cropper.prototype, events);
$.extend(Cropper.prototype, handlers);
$.extend(Cropper.prototype, change);
$.extend(Cropper.prototype, methods);

export default Cropper;
<commit_msg>Fixed #885<commit_after>import $ from 'jquery';
import DEFAULTS from './defaults';
import TEMPLATE from './template';
import render from './render';
import preview from './preview';
import events from './events';
import handlers from './handlers';
import change from './change';
import methods from './methods';
import * as utils from './utilities';

const CLASS_HIDDEN = 'cropper-hidden';
const REGEXP_DATA_URL = /^data:/;
const REGEXP_DATA_URL_JPEG = /^data:image\/jpeg;base64,/;

class Cropper {
  constructor(element, options) {
    const self = this;

    self.$element = $(element);
    self.options = $.extend({}, DEFAULTS, $.isPlainObject(options) && options);
    self.loaded = false;
    self.ready = false;
    self.completed = false;
    self.rotated = false;
    self.cropped = false;
    self.disabled = false;
    self.replaced = false;
    self.limited = false;
    self.wheeling = false;
    self.isImg = false;
    self.originalUrl = '';
    self.canvas = null;
    self.cropBox = null;
    self.pointers = {};
    self.init();
  }

  init() {
    const self = this;
    const $this = self.$element;
    let url;

    if ($this.is('img')) {
      self.isImg = true;

      // Should use `$.fn.attr` here. e.g.: "img/picture.jpg"
      self.originalUrl = url = $this.attr('src');

      // Stop when it's a blank image
      if (!url) {
        return;
      }

      // Should use `$.fn.prop` here. e.g.: "http://example.com/img/picture.jpg"
      url = $this.prop('src');
    } else if ($this.is('canvas') && window.HTMLCanvasElement) {
      url = $this[0].toDataURL();
    }

    self.load(url);
  }

  // A shortcut for triggering custom events
  trigger(type, data) {
    const e = $.Event(type, data);

    this.$element.trigger(e);

    return e;
  }

  load(url) {
    const self = this;
    const options = self.options;
    const $this = self.$element;

    if (!url) {
      return;
    }

    self.url = url;
    self.image = {};

    if (!options.checkOrientation || !window.ArrayBuffer) {
      self.clone();
      return;
    }

    // XMLHttpRequest disallows to open a Data URL in some browsers like IE11 and Safari
    if (REGEXP_DATA_URL.test(url)) {
      if (REGEXP_DATA_URL_JPEG.test(url)) {
        self.read(utils.dataURLToArrayBuffer(url));
      } else {
        self.clone();
      }
      return;
    }

    const xhr = new XMLHttpRequest();

    xhr.onerror = xhr.onabort = $.proxy(() => {
      self.clone();
    }, this);

    xhr.onload = function load() {
      self.read(this.response);
    };

    if (options.checkCrossOrigin && utils.isCrossOriginURL(url) && $this.prop('crossOrigin')) {
      url = utils.addTimestamp(url);
    }

    xhr.open('get', url);
    xhr.responseType = 'arraybuffer';
    xhr.withCredentials = $this.prop('crossOrigin') === 'use-credentials';
    xhr.send();
  }

  read(arrayBuffer) {
    const self = this;
    const options = self.options;
    const orientation = utils.getOrientation(arrayBuffer);
    const image = self.image;
    let rotate = 0;
    let scaleX = 1;
    let scaleY = 1;

    if (orientation > 1) {
      self.url = utils.arrayBufferToDataURL(arrayBuffer);

      switch (orientation) {

        // flip horizontal
        case 2:
          scaleX = -1;
          break;

        // rotate left 180°
        case 3:
          rotate = -180;
          break;

        // flip vertical
        case 4:
          scaleY = -1;
          break;

        // flip vertical + rotate right 90°
        case 5:
          rotate = 90;
          scaleY = -1;
          break;

        // rotate right 90°
        case 6:
          rotate = 90;
          break;

        // flip horizontal + rotate right 90°
        case 7:
          rotate = 90;
          scaleX = -1;
          break;

        // rotate left 90°
        case 8:
          rotate = -90;
          break;
      }
    }

    if (options.rotatable) {
      image.rotate = rotate;
    }

    if (options.scalable) {
      image.scaleX = scaleX;
      image.scaleY = scaleY;
    }

    self.clone();
  }

  clone() {
    const self = this;
    const options = self.options;
    const $this = self.$element;
    const url = self.url;
    let crossOrigin = '';
    let crossOriginUrl;

    if (options.checkCrossOrigin && utils.isCrossOriginURL(url)) {
      crossOrigin = $this.prop('crossOrigin');

      if (crossOrigin) {
        crossOriginUrl = url;
      } else {
        crossOrigin = 'anonymous';

        // Bust cache (#148) when there is not a "crossOrigin" property
        crossOriginUrl = utils.addTimestamp(url);
      }
    }

    self.crossOrigin = crossOrigin;
    self.crossOriginUrl = crossOriginUrl;

    const $clone = $(`<img ${utils.getCrossOrigin(crossOrigin)} src="${crossOriginUrl || url}">`);

    self.$clone = $clone;

    if (self.isImg) {
      if ($this[0].complete) {
        self.start();
      } else {
        $this.one('load', $.proxy(self.start, this));
      }
    } else {
      $clone.one('load', $.proxy(self.start, this))
        .one('error', $.proxy(self.stop, this))
        .addClass('cropper-hide')
        .insertAfter($this);
    }
  }

  start() {
    const self = this;
    const $clone = self.$clone;
    let $image = self.$element;

    if (!self.isImg) {
      $clone.off('error', self.stop);
      $image = $clone;
    }

    utils.getImageSize($image[0], (naturalWidth, naturalHeight) => {
      $.extend(self.image, {
        naturalWidth,
        naturalHeight,
        aspectRatio: naturalWidth / naturalHeight
      });

      self.loaded = true;
      self.build();
    });
  }

  stop() {
    const self = this;

    self.$clone.remove();
    self.$clone = null;
  }

  build() {
    const self = this;
    const options = self.options;
    const $this = self.$element;
    const $clone = self.$clone;
    let $cropper;
    let $cropBox;
    let $face;

    if (!self.loaded) {
      return;
    }

    // Unbuild first when replace
    if (self.ready) {
      self.unbuild();
    }

    // Create cropper elements
    self.$container = $this.parent();
    self.$cropper = $cropper = $(TEMPLATE);
    self.$canvas = $cropper.find('.cropper-canvas').append($clone);
    self.$dragBox = $cropper.find('.cropper-drag-box');
    self.$cropBox = $cropBox = $cropper.find('.cropper-crop-box');
    self.$viewBox = $cropper.find('.cropper-view-box');
    self.$face = $face = $cropBox.find('.cropper-face');

    // Hide the original image
    $this.addClass(CLASS_HIDDEN).after($cropper);

    // Show the clone image if is hidden
    if (!self.isImg) {
      $clone.removeClass('cropper-hide');
    }

    self.initPreview();
    self.bind();

    options.aspectRatio = Math.max(0, options.aspectRatio) || NaN;
    options.viewMode = Math.max(0, Math.min(3, Math.round(options.viewMode))) || 0;

    self.cropped = options.autoCrop;

    if (options.autoCrop) {
      if (options.modal) {
        self.$dragBox.addClass('cropper-modal');
      }
    } else {
      $cropBox.addClass(CLASS_HIDDEN);
    }

    if (!options.guides) {
      $cropBox.find('.cropper-dashed').addClass(CLASS_HIDDEN);
    }

    if (!options.center) {
      $cropBox.find('.cropper-center').addClass(CLASS_HIDDEN);
    }

    if (options.cropBoxMovable) {
      $face.addClass('cropper-move').data('action', 'all');
    }

    if (!options.highlight) {
      $face.addClass('cropper-invisible');
    }

    if (options.background) {
      $cropper.addClass('cropper-bg');
    }

    if (!options.cropBoxResizable) {
      $cropBox.find('.cropper-line, .cropper-point').addClass(CLASS_HIDDEN);
    }

    self.setDragMode(options.dragMode);
    self.render();
    self.ready = true;
    self.setData(options.data);

    // Trigger the ready event asynchronously to keep `data('cropper')` is defined
    self.completing = setTimeout(() => {
      if ($.isFunction(options.ready)) {
        $this.one('ready', options.ready);
      }

      self.trigger('ready');
      self.trigger('crop', self.getData());
      self.completed = true;
    }, 0);
  }

  unbuild() {
    const self = this;

    if (!self.ready) {
      return;
    }

    if (!self.completed) {
      clearTimeout(self.completing);
    }

    self.ready = false;
    self.completed = false;
    self.initialImage = null;

    // Clear `initialCanvas` is necessary when replace
    self.initialCanvas = null;
    self.initialCropBox = null;
    self.container = null;
    self.canvas = null;

    // Clear `cropBox` is necessary when replace
    self.cropBox = null;
    self.unbind();

    self.resetPreview();
    self.$preview = null;

    self.$viewBox = null;
    self.$cropBox = null;
    self.$dragBox = null;
    self.$canvas = null;
    self.$container = null;

    self.$cropper.remove();
    self.$cropper = null;
  }

  static setDefaults(options) {
    $.extend(DEFAULTS, $.isPlainObject(options) && options);
  }
}

$.extend(Cropper.prototype, render);
$.extend(Cropper.prototype, preview);
$.extend(Cropper.prototype, events);
$.extend(Cropper.prototype, handlers);
$.extend(Cropper.prototype, change);
$.extend(Cropper.prototype, methods);

export default Cropper;
<|endoftext|>


<commit_before>export class Evented {
  constructor(/* options = {}*/) {
    // TODO: do we need this empty constructor?
  }

  on(event, handler, ctx) {
    const once = arguments.length <= 3 || arguments[3] === undefined ? false : arguments[3];

    if (typeof this.bindings === 'undefined') {
      this.bindings = {};
    }
    if (typeof this.bindings[event] === 'undefined') {
      this.bindings[event] = [];
    }
    this.bindings[event].push({ handler, ctx, once });
  }

  once(event, handler, ctx) {
    this.on(event, handler, ctx, true);
  }

  off(event, handler) {
    if (typeof this.bindings === 'undefined' || typeof this.bindings[event] === 'undefined') {
      return false;
    }

    if (typeof handler === 'undefined') {
      delete this.bindings[event];
    } else {
      let i = 0;
      while (i < this.bindings[event].length) {
        if (this.bindings[event][i].handler === handler) {
          this.bindings[event].splice(i, 1);
        } else {
          ++i;
        }
      }
    }
  }

  trigger(event) {
    if (typeof this.bindings !== 'undefined' && this.bindings[event]) {
      const _len = arguments.length;
      const args = Array(_len > 1 ? _len - 1 : 0);
      let i = 0;

      for (let _key = 1; _key < _len; _key++) {
        args[_key - 1] = arguments[_key];
      }

      while (i < this.bindings[event].length) {
        const _bindings$event$i = this.bindings[event][i];
        const { ctx, handler, once } = _bindings$event$i;

        let context = ctx;
        if (typeof context === 'undefined') {
          context = this;
        }

        handler.apply(context, args);

        if (once) {
          this.bindings[event].splice(i, 1);
        } else {
          ++i;
        }
      }
    }
  }

}
<commit_msg>Reduce Evented complexity (#217)<commit_after>import _ from 'lodash';

export class Evented {
  on(event, handler, ctx) {
    const once = arguments.length <= 3 || arguments[3] === undefined ? false : arguments[3];

    if (_.isUndefined(this.bindings)) {
      this.bindings = {};
    }
    if (_.isUndefined(this.bindings[event])) {
      this.bindings[event] = [];
    }
    this.bindings[event].push({ handler, ctx, once });
  }

  once(event, handler, ctx) {
    this.on(event, handler, ctx, true);
  }

  off(event, handler) {
    if (_.isUndefined(this.bindings) || _.isUndefined(this.bindings[event])) {
      return false;
    }

    if (_.isUndefined(handler)) {
      delete this.bindings[event];
    } else {
      this.bindings[event].forEach((binding, index) => {
        if (binding.handler === handler) {
          this.bindings[event].splice(index, 1);
        }
      });
    }
  }

  trigger(event) {
    if (!_.isUndefined(this.bindings) && this.bindings[event]) {
      const args = _.drop(arguments);

      this.bindings[event].forEach((binding, index) => {
        const { ctx, handler, once } = binding;

        const context = ctx || this;

        handler.apply(context, args);

        if (once) {
          this.bindings[event].splice(index, 1);
        }
      });
    }
  }

}
<|endoftext|>


<commit_before>'use strict';

Darwinator.Helpers = {

  /**
  * Given a layer in a tileMap, returns the index of the tile in every
  * position of the layer.
  * @method Darwinator.Helpers#convertTileMap
  * @param {Array} - [layer] The layer to convert
  * @return {Array} - The converted map
  */
  convertTileMap: function(layer) {
    var indexes = new Array(layer.length);

    for (var i = 0; i < indexes.length; i++) {
      indexes[i] = new Array(layer[i].length);
      for(var l = 0; l < indexes[i].length; l++) {
        indexes[i][l] = layer[i][l].index;
      }
    }
    return indexes;
  },

  /**
  * Calculate what tile a specific pixel is in.
  * @method Darwinator.Helpers#pixelsToTile
  * @param {Number} - [x] The x-value of the pixel
  * @param {Number} - [y] The y-value of the pixel
  * @return {Array} - The indexes of the tile, given as [x,y]
  */
  pixelsToTile: function(x, y) {
    if (x < 0) {
      x = 0;
    }
    if (y < 0) {
      y = 0;
    }
    var pos = {
      x: Math.floor(x / Darwinator.TILE_WIDTH),
      y: Math.floor(y / Darwinator.TILE_HEIGHT)
    }

    return pos;
  },

  /**
  * Calculate the position in pixels of a tile.
  * @method Darwinator.Helpers#tileToPixels
  * @param {Number} - [x] The x-index of the tile
  * @param {Number} - [y] The y-index of the tile
  * @return {Array} - The pixel in the center of the tile, returned as [X,Y]
  */
  tileToPixels: function(x, y) {
    var pos;
    var tileWidth = Darwinator.TILE_WIDTH;
    var tileHeight = Darwinator.TILE_HEIGHT;
    pos.x = Math.floor(x * tileWidth + (tileWidth / 2));
    pos.y = Math.floor(y * tileHeight + (tileHeight / 2));
    return pos;
  },

  /**
  * Calculates distance between two points. Works for both distance in pixels and distance in tiles.
  * @method Darwinator.Helpers#calculateDistance
  * @param {Array/Tuple} - [pos1] The first point, given as [x,y] or as an object with properties x,y
  * @param {Array/Tple} - [pos2] The second point, given as [x,y] or as an object with properties x,y
  * @return {Number} The distance between the two points.
  */
  calculateDistance: function(pos1, pos2) {
    var x1 = pos1.hasOwnProperty('x') ? pos1.x : pos1[0];
    var y1 = pos1.hasOwnProperty('y') ? pos1.y : pos1[1];
    var x2 = pos2.hasOwnProperty('x') ? pos2.x : pos2[0];
    var y2 = pos2.hasOwnProperty('y') ? pos2.y : pos2[1];
    var dx = x1 - x2;
    var dy = y1 - y2;
    return Math.sqrt(dx*dx + dy*dy);
  }
};
<commit_msg>Cleanup code and update documentation<commit_after>'use strict';

Darwinator.Helpers = {

  /**
  * Given a layer in a tileMap, returns the index of the tile in every
  * position of the layer.
  * @method Darwinator.Helpers#convertTileMap
  * @param {Array} - [layer] The layer to convert
  * @return {Array} - The converted map
  */
  convertTileMap: function(layer) {
    var indexes = new Array(layer.length);

    for (var i = 0; i < indexes.length; i++) {
      indexes[i] = new Array(layer[i].length);
      for(var l = 0; l < indexes[i].length; l++) {
        indexes[i][l] = layer[i][l].index;
      }
    }
    return indexes;
  },

  /**
  * Calculate what tile a specific pixel is in.
  * @method Darwinator.Helpers#pixelsToTile
  * @param {Number} - [x] The x-value of the pixel
  * @param {Number} - [y] The y-value of the pixel
  * @return {Array} - The indexes of the tile, given as [x,y]
  */
  pixelsToTile: function(x, y) {
    if (x < 0) {
      x = 0;
    }
    if (y < 0) {
      y = 0;
    }
    var pos = {
      x: Math.floor(x / Darwinator.TILE_WIDTH),
      y: Math.floor(y / Darwinator.TILE_HEIGHT)
    };

    return pos;
  },

  /**
  * Calculate the position in pixels of a tile.
  * @method Darwinator.Helpers#tileToPixels
  * @param {Number} - [x] The x-index of the tile
  * @param {Number} - [y] The y-index of the tile
  * @return {Tuple} - The pixel in the center of the tile, returned as an object with properties x,y
  */
  tileToPixels: function(xCor, yCor) {
    var tileWidth = Darwinator.TILE_WIDTH;
    var tileHeight = Darwinator.TILE_HEIGHT;
    var pos = {
      x: Math.floor(xCor * tileWidth + (tileWidth / 2)),
      y: Math.floor(yCor * tileHeight + (tileHeight / 2))
    };

    return pos;
  },

  /**
  * Calculates distance between two points. Works for both distance in pixels and distance in tiles.
  * @method Darwinator.Helpers#calculateDistance
  * @param {Array/Tuple} - [pos1] The first point, given as [x,y] or as an object with properties x,y
  * @param {Array/Tple} - [pos2] The second point, given as [x,y] or as an object with properties x,y
  * @return {Number} The distance between the two points.
  */
  calculateDistance: function(pos1, pos2) {
    var x1 = pos1.hasOwnProperty('x') ? pos1.x : pos1[0];
    var y1 = pos1.hasOwnProperty('y') ? pos1.y : pos1[1];
    var x2 = pos2.hasOwnProperty('x') ? pos2.x : pos2[0];
    var y2 = pos2.hasOwnProperty('y') ? pos2.y : pos2[1];
    var dx = x1 - x2;
    var dy = y1 - y2;
    return Math.sqrt(dx*dx + dy*dy);
  }
};
<|endoftext|>


<commit_before>
import {
	MATERIAL_LINE,
	SHADING_CURSOR, SHADING_DEPTH, SHADING_DEPTH_CURSOR, SHADING_HEIGHT, SHADING_INCLINATION, SHADING_LENGTH, SHADING_DISTANCE
} from '../core/constants';

import { Cfg } from '../core/lib';

import { AHI } from './AHI';
import { AHIControl } from './AHIControl';
import { AngleScale } from './AngleScale';
import { Compass } from './Compass';
import { CompassControl } from './CompassControl';
import { CursorScale } from './CursorScale';
import { CursorControl } from './CursorControl';
import { LinearScale } from './LinearScale';
import { ProgressDial } from './ProgressDial';
import { PositionRing } from './PositionRing';
import { ScaleBar } from './ScaleBar';
import { HudObject } from './HudObject';

import { Materials } from '../materials/Materials';

import {
	Scene, Group,
	AmbientLight, DirectionalLight,
	OrthographicCamera
} from '../Three';

// THREE objects

var renderer;
var camera;
var scene;

var hScale = 0;

var attitudeGroup;

var linearScale = null;
var angleScale  = null;
var cursorScale = null;
var scaleBar    = null;
var cursorControl = null;
var positionRing = null;

var compass;
var ahi;

var progressDials;
var progressDial;

// viewer state

var viewer;
var controls;
var isVisible = true;
var caveLoaded = false;

function init ( viewerIn, viewRenderer ) {

	viewer = viewerIn;
	renderer = viewRenderer;

	const container = viewer.container;

	const hHeight = container.clientHeight / 2;
	const hWidth  = container.clientWidth / 2;

	// create GL scene and camera for overlay
	camera = new OrthographicCamera( -hWidth, hWidth, hHeight, -hHeight, 1, 1000 );
	camera.position.z = 600;

	scene = new Scene();

	// group to simplyfy resize handling
	attitudeGroup = new Group();
	attitudeGroup.position.set( hWidth, -hHeight, 0 );

	scene.addStatic( attitudeGroup );

	scene.name = 'HUD';

	HudObject.init();

	const aLight = new AmbientLight( 0x888888 );
	const dLight = new DirectionalLight( 0xFFFFFF );

	dLight.position.set( -1, 1, 1 );

	scene.addStatic( aLight );
	scene.addStatic( dLight );

	progressDials = [ new ProgressDial( true, 0, viewer ), new ProgressDial( false, 1, viewer ) ];

	progressDial = progressDials [ 0 ];

	ahi = new AHI();
	compass = new Compass();
	angleScale = new AngleScale( i18n( 'inclination' ) );

	attitudeGroup.addStatic( ahi );
	attitudeGroup.addStatic( compass );
	attitudeGroup.addStatic( angleScale );

	attitudeGroup.addStatic( progressDials[ 0 ] );
	attitudeGroup.addStatic( progressDials[ 1 ] );

	positionRing = new PositionRing();

	scene.add( positionRing );

	viewer.addEventListener( 'newCave', caveChanged );
	viewer.addEventListener( 'change', viewChanged );
	viewer.addEventListener( 'resized', resize );

	Cfg.addEventListener( 'change', cfgChanged );

	controls = viewer.getControls();

	new CompassControl( viewer );
	new AHIControl( viewer );

}

function i18n ( text ) {

	const tr = Cfg.i18n( 'hud.' + text );

	return ( tr === undefined ) ? text : tr;

}

function setVisibility ( visible ) {

	compass.visible = visible;
	ahi.visible = visible;
	progressDial.setVisibility( visible );

	if ( scaleBar ) scaleBar.visible = visible;

	isVisible = visible;

	// reset correct disposition of colour keys etc.
	if ( linearScale ) {

		if ( visible ) {

			viewChanged ( { type: 'change', name: 'shadingMode' } );

		} else {

			linearScale.visible = false;
			cursorScale.visible = false;
			angleScale.visible = false;

		}

	}

	viewer.renderView();

}

function getVisibility() {

	return isVisible;

}

function getProgressDial( ring ) {

	return progressDials[ ring ];

}

function setScale( scale ) {

	hScale = scale;

}

function resize () {

	const container = viewer.container;

	const hWidth  = container.clientWidth / 2;
	const hHeight = container.clientHeight / 2;

	// adjust cameras to new aspect ratio etc.
	camera.left   = -hWidth;
	camera.right  =  hWidth;
	camera.top    =  hHeight;
	camera.bottom = -hHeight;

	camera.updateProjectionMatrix();

	attitudeGroup.position.set( hWidth, -hHeight, 0 );
	attitudeGroup.updateMatrix();

	newScales();

}

function renderHUD () {

	// update HUD components

	const currentCamera = controls.cameraManager.activeCamera;

	compass.set( currentCamera );
	ahi.set( currentCamera );

	updateScaleBar( currentCamera );

	// render on screen
	renderer.clearDepth();
	renderer.render( scene, camera );

}

function cfgChanged ( /* event */ ) {

	// only change controls when a cave has been loaded already
	// prevents flicker when racing with i18n resource loading
	if ( caveLoaded ) caveChanged();

}

function caveChanged ( /* event */ ) {

	caveLoaded = true;

	newScales();

	viewChanged ( { type: 'change', name: 'shadingMode' } );

}

function newScales () {

	const container = viewer.container;
	const hasLegs = viewer.minHeight !== Infinity && viewer.maxHeight !== -Infinity;

	if ( linearScale ) {

		linearScale.dispose();
		scene.remove( linearScale );

	}

	if ( hasLegs ) {

		linearScale = new LinearScale( container, viewer );
		scene.addStatic( linearScale );

	}

	if ( cursorScale ) {

		cursorScale.dispose();
		scene.remove( cursorScale );

	}

	if ( hasLegs ) {

		cursorScale = new CursorScale( container );

		if ( cursorControl ) cursorControl.dispose();

		cursorControl = new CursorControl( viewer, cursorScale );

		scene.addStatic( cursorScale );

	}

	if ( scaleBar ) {

		scene.remove( scaleBar );
		scaleBar = null;

	}

	updateScaleBar( controls.object );

	setVisibility( isVisible );

}

function viewChanged ( event ) {

	if ( event.name !== 'shadingMode' || ! isVisible || ! caveLoaded ) return;

	// hide all - and only make required elements visible

	var useAngleScale = false;
	var useLinearScale = false;
	var useCursorScale = false;

	switch ( viewer.shadingMode ) {

	case SHADING_HEIGHT:

		useLinearScale = true;

		linearScale.setRange( viewer.minHeight, viewer.maxHeight, i18n( 'height' ) ).setMaterial( Materials.getHeightMaterial( MATERIAL_LINE ) );

		break;

	case SHADING_DEPTH:

		useLinearScale = true;

		linearScale.setRange( viewer.maxHeight - viewer.minHeight, 0, i18n( 'depth' ) ).setMaterial( Materials.getHeightMaterial( MATERIAL_LINE ) );

		break;

	case SHADING_DISTANCE:

		useLinearScale = true;

		linearScale.setRange( 0, viewer.maxDistance, i18n( 'distance' ) ).setMaterial( Materials.getHeightMaterial( MATERIAL_LINE ) );

		break;

	case SHADING_CURSOR:

		useCursorScale = true;

		cursorScale.setRange( viewer.minHeight, viewer.maxHeight, i18n( 'height' ) );

		cursorChanged();

		break;

	case SHADING_DEPTH_CURSOR:

		useCursorScale = true;

		cursorScale.setRange( viewer.maxHeight - viewer.minHeight, 0, i18n( 'depth' ) );

		cursorChanged();

		break;

	case SHADING_LENGTH:

		useLinearScale = true;

		linearScale.setRange( viewer.minLegLength, viewer.maxLegLength, i18n( 'leg_length' ) ).setMaterial( Materials.getHeightMaterial( MATERIAL_LINE, true ) );

		break;

	case SHADING_INCLINATION:

		useAngleScale = true;

		break;

	}

	angleScale.visible = useAngleScale;
	linearScale.visible= useLinearScale;
	cursorScale.visible = useCursorScale;

	if ( useCursorScale ) {

		viewer.addEventListener( 'cursorChange', cursorChanged );

	} else {

		viewer.removeEventListener( 'cursorChange', cursorChanged );

	}

	viewer.renderView();

}

function cursorChanged ( /* event */ ) {

	const cursorHeight = viewer.cursorHeight;
	const range = viewer.maxHeight - viewer.minHeight;

	var scaledHeight = 0;
	var realHeight = 0;

	if ( viewer.shadingMode === SHADING_CURSOR ) {

		scaledHeight = ( viewer.cursorHeight + range / 2 ) / range;
		realHeight = cursorHeight + range / 2 + viewer.minHeight;

	} else {

		scaledHeight = 1 - cursorHeight / range;
		realHeight = cursorHeight;

	}

	scaledHeight = Math.max( Math.min( scaledHeight, 1 ), 0 );

	cursorScale.setCursor( scaledHeight, Math.round( realHeight ) );

}

function updateScaleBar ( camera ) {

	if ( camera instanceof OrthographicCamera ) {

		if ( scaleBar === null ) {

			scaleBar = new ScaleBar( viewer.container, hScale, ( HudObject.stdWidth + HudObject.stdMargin ) * 4 );
			scene.addStatic( scaleBar );

		}

		if ( isVisible !== scaleBar.visible ) scaleBar.visible = isVisible;

		scaleBar.setScale( camera.zoom );

		if ( viewer.trackGPS ) {

			positionRing.update( hScale, camera.zoom, 10 );

		} else {

			positionRing.visible = false;

		}

	} else {

		if ( scaleBar !== null && scaleBar.visible ) scaleBar.visible = false;

		positionRing.visible = false;

	}

}

export const HUD = {
	init:            init,
	renderHUD:       renderHUD,
	setVisibility:   setVisibility,
	getVisibility:   getVisibility,
	getProgressDial: getProgressDial,
	setScale:        setScale
};

// EOF<commit_msg>fix missed rename<commit_after>
import {
	MATERIAL_LINE,
	SHADING_CURSOR, SHADING_DEPTH, SHADING_DEPTH_CURSOR, SHADING_HEIGHT, SHADING_INCLINATION, SHADING_LENGTH, SHADING_DISTANCE
} from '../core/constants';

import { Cfg } from '../core/lib';

import { AHI } from './AHI';
import { AHIControl } from './AHIControl';
import { AngleScale } from './AngleScale';
import { Compass } from './Compass';
import { CompassControl } from './CompassControl';
import { CursorScale } from './CursorScale';
import { CursorControl } from './CursorControl';
import { LinearScale } from './LinearScale';
import { ProgressDial } from './ProgressDial';
import { PositionRing } from './PositionRing';
import { ScaleBar } from './ScaleBar';
import { HudObject } from './HudObject';

import { Materials } from '../materials/Materials';

import {
	Scene, Group,
	AmbientLight, DirectionalLight,
	OrthographicCamera
} from '../Three';

// THREE objects

var renderer;
var camera;
var scene;

var hScale = 0;

var attitudeGroup;

var linearScale = null;
var angleScale  = null;
var cursorScale = null;
var scaleBar    = null;
var cursorControl = null;
var positionRing = null;

var compass;
var ahi;

var progressDials;
var progressDial;

// viewer state

var viewer;
var controls;
var isVisible = true;
var caveLoaded = false;

function init ( viewerIn, viewRenderer ) {

	viewer = viewerIn;
	renderer = viewRenderer;

	const container = viewer.container;

	const hHeight = container.clientHeight / 2;
	const hWidth  = container.clientWidth / 2;

	// create GL scene and camera for overlay
	camera = new OrthographicCamera( -hWidth, hWidth, hHeight, -hHeight, 1, 1000 );
	camera.position.z = 600;

	scene = new Scene();

	// group to simplyfy resize handling
	attitudeGroup = new Group();
	attitudeGroup.position.set( hWidth, -hHeight, 0 );

	scene.addStatic( attitudeGroup );

	scene.name = 'HUD';

	HudObject.init();

	const aLight = new AmbientLight( 0x888888 );
	const dLight = new DirectionalLight( 0xFFFFFF );

	dLight.position.set( -1, 1, 1 );

	scene.addStatic( aLight );
	scene.addStatic( dLight );

	progressDials = [ new ProgressDial( true, 0, viewer ), new ProgressDial( false, 1, viewer ) ];

	progressDial = progressDials [ 0 ];

	ahi = new AHI();
	compass = new Compass();
	angleScale = new AngleScale( i18n( 'inclination' ) );

	attitudeGroup.addStatic( ahi );
	attitudeGroup.addStatic( compass );
	attitudeGroup.addStatic( angleScale );

	attitudeGroup.addStatic( progressDials[ 0 ] );
	attitudeGroup.addStatic( progressDials[ 1 ] );

	positionRing = new PositionRing();

	scene.add( positionRing );

	viewer.addEventListener( 'newCave', caveChanged );
	viewer.addEventListener( 'change', viewChanged );
	viewer.addEventListener( 'resized', resize );

	Cfg.addEventListener( 'change', cfgChanged );

	controls = viewer.getControls();

	new CompassControl( viewer );
	new AHIControl( viewer );

}

function i18n ( text ) {

	const tr = Cfg.i18n( 'hud.' + text );

	return ( tr === undefined ) ? text : tr;

}

function setVisibility ( visible ) {

	compass.visible = visible;
	ahi.visible = visible;
	progressDial.setVisibility( visible );

	if ( scaleBar ) scaleBar.visible = visible;

	isVisible = visible;

	// reset correct disposition of colour keys etc.
	if ( linearScale ) {

		if ( visible ) {

			viewChanged ( { type: 'change', name: 'shadingMode' } );

		} else {

			linearScale.visible = false;
			cursorScale.visible = false;
			angleScale.visible = false;

		}

	}

	viewer.renderView();

}

function getVisibility() {

	return isVisible;

}

function getProgressDial( ring ) {

	return progressDials[ ring ];

}

function setScale( scale ) {

	hScale = scale;

}

function resize () {

	const container = viewer.container;

	const hWidth  = container.clientWidth / 2;
	const hHeight = container.clientHeight / 2;

	// adjust cameras to new aspect ratio etc.
	camera.left   = -hWidth;
	camera.right  =  hWidth;
	camera.top    =  hHeight;
	camera.bottom = -hHeight;

	camera.updateProjectionMatrix();

	attitudeGroup.position.set( hWidth, -hHeight, 0 );
	attitudeGroup.updateMatrix();

	newScales();

}

function renderHUD () {

	// update HUD components

	const currentCamera = controls.cameraManager.activeCamera;

	compass.set( currentCamera );
	ahi.set( currentCamera );

	updateScaleBar( currentCamera );

	// render on screen
	renderer.clearDepth();
	renderer.render( scene, camera );

}

function cfgChanged ( /* event */ ) {

	// only change controls when a cave has been loaded already
	// prevents flicker when racing with i18n resource loading
	if ( caveLoaded ) caveChanged();

}

function caveChanged ( /* event */ ) {

	caveLoaded = true;

	newScales();

	viewChanged ( { type: 'change', name: 'shadingMode' } );

}

function newScales () {

	const container = viewer.container;
	const hasLegs = viewer.minHeight !== Infinity && viewer.maxHeight !== -Infinity;

	if ( linearScale ) {

		linearScale.dispose();
		scene.remove( linearScale );

	}

	if ( hasLegs ) {

		linearScale = new LinearScale( container, viewer );
		scene.addStatic( linearScale );

	}

	if ( cursorScale ) {

		cursorScale.dispose();
		scene.remove( cursorScale );

	}

	if ( hasLegs ) {

		cursorScale = new CursorScale( container );

		if ( cursorControl ) cursorControl.dispose();

		cursorControl = new CursorControl( viewer, cursorScale );

		scene.addStatic( cursorScale );

	}

	if ( scaleBar ) {

		scene.remove( scaleBar );
		scaleBar = null;

	}

	updateScaleBar( controls.object );

	setVisibility( isVisible );

}

function viewChanged ( event ) {

	if ( event.name !== 'shadingMode' || ! isVisible || ! caveLoaded ) return;

	// hide all - and only make required elements visible

	var useAngleScale = false;
	var useLinearScale = false;
	var useCursorScale = false;

	switch ( viewer.shadingMode ) {

	case SHADING_HEIGHT:

		useLinearScale = true;

		linearScale.setRange( viewer.minHeight, viewer.maxHeight, i18n( 'height' ) ).setMaterial( Materials.getHeightMaterial( MATERIAL_LINE ) );

		break;

	case SHADING_DEPTH:

		useLinearScale = true;

		linearScale.setRange( viewer.maxHeight - viewer.minHeight, 0, i18n( 'depth' ) ).setMaterial( Materials.getHeightMaterial( MATERIAL_LINE ) );

		break;

	case SHADING_DISTANCE:

		useLinearScale = true;

		linearScale.setRange( 0, viewer.maxDistance, i18n( 'distance' ) ).setMaterial( Materials.getHeightMaterial( MATERIAL_LINE ) );

		break;

	case SHADING_CURSOR:

		useCursorScale = true;

		cursorScale.setRange( viewer.minHeight, viewer.maxHeight, i18n( 'height' ) );

		cursorChanged();

		break;

	case SHADING_DEPTH_CURSOR:

		useCursorScale = true;

		cursorScale.setRange( viewer.maxHeight - viewer.minHeight, 0, i18n( 'depth' ) );

		cursorChanged();

		break;

	case SHADING_LENGTH:

		useLinearScale = true;

		linearScale.setRange( viewer.minLegLength, viewer.maxLegLength, i18n( 'leg_length' ) ).setMaterial( Materials.getHeightMaterial( MATERIAL_LINE, true ) );

		break;

	case SHADING_INCLINATION:

		useAngleScale = true;

		break;

	}

	angleScale.visible = useAngleScale;
	linearScale.visible= useLinearScale;
	cursorScale.visible = useCursorScale;

	if ( useCursorScale ) {

		viewer.addEventListener( 'cursorChange', cursorChanged );

	} else {

		viewer.removeEventListener( 'cursorChange', cursorChanged );

	}

	viewer.renderView();

}

function cursorChanged ( /* event */ ) {

	const cursorHeight = viewer.cursorHeight;
	const range = viewer.maxHeight - viewer.minHeight;

	var scaledHeight = 0;
	var realHeight = 0;

	if ( viewer.shadingMode === SHADING_CURSOR ) {

		scaledHeight = ( viewer.cursorHeight + range / 2 ) / range;
		realHeight = cursorHeight + range / 2 + viewer.minHeight;

	} else {

		scaledHeight = 1 - cursorHeight / range;
		realHeight = cursorHeight;

	}

	scaledHeight = Math.max( Math.min( scaledHeight, 1 ), 0 );

	cursorScale.setCursor( scaledHeight, Math.round( realHeight ) );

}

function updateScaleBar ( camera ) {

	if ( camera instanceof OrthographicCamera ) {

		if ( scaleBar === null ) {

			scaleBar = new ScaleBar( viewer.container, hScale, ( HudObject.stdWidth + HudObject.stdMargin ) * 4 );
			scene.addStatic( scaleBar );

		}

		if ( isVisible !== scaleBar.visible ) scaleBar.visible = isVisible;

		scaleBar.setScale( camera.zoom );

		if ( viewer.trackLocation ) {

			positionRing.update( hScale, camera.zoom, 10 );

		} else {

			positionRing.visible = false;

		}

	} else {

		if ( scaleBar !== null && scaleBar.visible ) scaleBar.visible = false;

		positionRing.visible = false;

	}

}

export const HUD = {
	init:            init,
	renderHUD:       renderHUD,
	setVisibility:   setVisibility,
	getVisibility:   getVisibility,
	getProgressDial: getProgressDial,
	setScale:        setScale
};

// EOF<|endoftext|>


<commit_before>var MapBuilder = function(args){

  this.init = function(constructorParams) {
    // Dynamically add meta tags if they don't already exist
    const currentMetaTags = document.getElementsByTagName('meta');
    let currentCharsetTag, currentMobileTag;

    for (var i = 0; i < currentMetaTags.length; i++) {
      const content = currentMetaTags[i].getAttribute('content');
      if (content === 'text/html; charset=utf-8') {
        currentCharsetTag = true;
      } else if (content === 'width=device-width, initial-scale=1.0') {
        currentMobileTag = true;
      }
    }
    if (!currentCharsetTag) {
      const metaCharset = document.createElement('meta');
      metaCharset.httpEquiv = 'Content-Type';
      metaCharset.content = 'text/html; charset=utf-8';
      document.getElementsByTagName('body')[0].appendChild(metaCharset);
    }

    if (!currentMobileTag) {
      const metaMobileDevice = document.createElement('meta');
      metaMobileDevice.name = 'viewport';
      metaMobileDevice.content = 'width=device-width, initial-scale=1.0';
      document.getElementsByTagName('body')[0].appendChild(metaMobileDevice);
    }

    var scripts = document.getElementsByTagName('script');
    var version;
    var newBase;
    for (var j = 0; j < scripts.length; j++) {
      if (scripts[j].id === 'library-load') {
        newBase = scripts[j].src;
        version = scripts[j].getAttribute('version');
      }
    }
    newBase = newBase.split(version)[0] + version;
    window._app = {
      cache: constructorParams.version,
      esri: '#{esriVersion}',
      base: newBase
    };

    function makePath (base, path) {
      var position = base.length - 1;
      return !path ?
        (base.indexOf('/', position) !== position ? base + '/' : base) :
        (base.indexOf('/', position) !== position ?
          base + '/' + path :
          base + path
        );
    }
    constructorParams.cssPath = makePath(newBase, 'css');

    var base = makePath(newBase);

    window.dojoConfig = {
      parseOnLoad: false,
      async: true,
      packages: [
        { name: 'js', location: makePath(base, 'js') }
      ],
      deps: ['dojo/ready'],
      callback: function () {
        require(['js/libraryMain'], function(libraryMain) {
          libraryMain.default.startup();
          libraryMain.default.configureApp(constructorParams);
          libraryMain.default.lazyloadAssets(constructorParams);
          libraryMain.default.initializeApp(constructorParams);
        });
      }
    };

    function loadRequiredCSS() {
      const css = `
        @font-face {
          font-family: 'Fira Sans';
          src: url('https://my.gfw-mapbuilder.org/fonts/firasans-regular-webfont.woff2') format('woff2'),
              url('https://my.gfw-mapbuilder.org/fonts/firasans-regular-webfont.ttf') format('ttf'),
              url('https://my.gfw-mapbuilder.org/fonts/firasans-regular-webfont.woff') format('woff'),
              url('https://my.gfw-mapbuilder.org/fonts/firasans-bold-webfont.woff2') format('woff2'),
              url('https://my.gfw-mapbuilder.org/fonts/firasans-bold-webfont.ttf') format('ttf'),
              url('https://my.gfw-mapbuilder.org/fonts/firasans-bold-webfont.woff') format('woff');
          font-weight: normal;
          font-style: normal;
        }

        .layer-transparency input[type=range]::-webkit-slider-thumb {
          background: #fff url('https://my.gfw-mapbuilder.org/img/dot.svg') no-repeat 50% 50%;
        }

        .layer-transparency input[type=range]::-moz-range-thumb {
          background: #fff url('https://my.gfw-mapbuilder.org/img/dot.svg') no-repeat 50% 50%;
        }

        .layer-transparency input[type=range]::-ms-thumb {
          background: #fff url('https://my.gfw-mapbuilder.org/img/dot.svg') no-repeat 50% 50%;
        }

        .tree-icon {
          background: url('https://my.gfw-mapbuilder.org/img/tree.png') no-repeat center;
        }

        .forest-icon {
          background: url('https://my.gfw-mapbuilder.org/img/forest.png') no-repeat center;
        }

        .chart-icon {
          background: url('https://my.gfw-mapbuilder.org/img/chart-icon.svg') no-repeat center;
        }

        .print-icon {
          background: url('https://my.gfw-mapbuilder.org/img/print-icon.svg') no-repeat center;
        }

        .subscribe-icon {
          background: url('https://my.gfw-mapbuilder.org/img/subscribe-icon.svg') no-repeat center;
        }
      `;

      const styleTag = document.createElement('style');
      styleTag.type = 'text/css';
      styleTag.appendChild(document.createTextNode(css));
      document.getElementsByTagName('head')[0].appendChild(styleTag);
    }

    function loadjsfile(filename, options) {
      const script = document.createElement('script');
      script.src = filename;
      if (options) {
        Object.entries(options).forEach((item) => {
          const [ attribute, attributeValue ] = item;
          script[attribute] = attributeValue;
        });
      }
      document.getElementsByTagName('head')[0].appendChild(script);
    }

    loadjsfile('https://my.gfw-mapbuilder.org/js/arcgis-api-mapbuilder-1.2/dojo/dojo.js');
    loadjsfile('https://cdnjs.cloudflare.com/ajax/libs/vega/4.2.0/vega.min.js', { charset: 'UTF-8' });
    loadRequiredCSS();

    /*eslint-disable */
    (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
    (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
    m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
    })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
    /*eslint-enable */
  };

  window.customApp = {
    ...args
  };

  this.constructorArgs = args;
  this.init(args);
};

window.MapBuilder = MapBuilder;
<commit_msg>Change version logic to make it backwards compatible<commit_after>var MapBuilder = function(args){

  this.init = function(constructorParams) {
    // Dynamically add meta tags if they don't already exist
    const currentMetaTags = document.getElementsByTagName('meta');
    let currentCharsetTag, currentMobileTag;

    for (var i = 0; i < currentMetaTags.length; i++) {
      const content = currentMetaTags[i].getAttribute('content');
      if (content === 'text/html; charset=utf-8') {
        currentCharsetTag = true;
      } else if (content === 'width=device-width, initial-scale=1.0') {
        currentMobileTag = true;
      }
    }
    if (!currentCharsetTag) {
      const metaCharset = document.createElement('meta');
      metaCharset.httpEquiv = 'Content-Type';
      metaCharset.content = 'text/html; charset=utf-8';
      document.getElementsByTagName('body')[0].appendChild(metaCharset);
    }

    if (!currentMobileTag) {
      const metaMobileDevice = document.createElement('meta');
      metaMobileDevice.name = 'viewport';
      metaMobileDevice.content = 'width=device-width, initial-scale=1.0';
      document.getElementsByTagName('body')[0].appendChild(metaMobileDevice);
    }

    var scripts = document.getElementsByTagName('script');
    var version;
    var newBase;
    for (var j = 0; j < scripts.length; j++) {
      if (scripts[j].id === 'library-load') {
        newBase = scripts[j].src;
        version = scripts[j].getAttribute('version') || constructorParams.version;
      }
    }
    newBase = newBase.split(version)[0] + version;
    window._app = {
      cache: version,
      esri: '#{esriVersion}',
      base: newBase
    };

    function makePath (base, path) {
      var position = base.length - 1;
      return !path ?
        (base.indexOf('/', position) !== position ? base + '/' : base) :
        (base.indexOf('/', position) !== position ?
          base + '/' + path :
          base + path
        );
    }
    constructorParams.cssPath = makePath(newBase, 'css');

    var base = makePath(newBase);

    window.dojoConfig = {
      parseOnLoad: false,
      async: true,
      packages: [
        { name: 'js', location: makePath(base, 'js') }
      ],
      deps: ['dojo/ready'],
      callback: function () {
        require(['js/libraryMain'], function(libraryMain) {
          libraryMain.default.startup();
          libraryMain.default.configureApp(constructorParams);
          libraryMain.default.lazyloadAssets(constructorParams);
          libraryMain.default.initializeApp(constructorParams);
        });
      }
    };

    function loadRequiredCSS() {
      const css = `
        @font-face {
          font-family: 'Fira Sans';
          src: url('https://my.gfw-mapbuilder.org/fonts/firasans-regular-webfont.woff2') format('woff2'),
              url('https://my.gfw-mapbuilder.org/fonts/firasans-regular-webfont.ttf') format('ttf'),
              url('https://my.gfw-mapbuilder.org/fonts/firasans-regular-webfont.woff') format('woff'),
              url('https://my.gfw-mapbuilder.org/fonts/firasans-bold-webfont.woff2') format('woff2'),
              url('https://my.gfw-mapbuilder.org/fonts/firasans-bold-webfont.ttf') format('ttf'),
              url('https://my.gfw-mapbuilder.org/fonts/firasans-bold-webfont.woff') format('woff');
          font-weight: normal;
          font-style: normal;
        }

        .layer-transparency input[type=range]::-webkit-slider-thumb {
          background: #fff url('https://my.gfw-mapbuilder.org/img/dot.svg') no-repeat 50% 50%;
        }

        .layer-transparency input[type=range]::-moz-range-thumb {
          background: #fff url('https://my.gfw-mapbuilder.org/img/dot.svg') no-repeat 50% 50%;
        }

        .layer-transparency input[type=range]::-ms-thumb {
          background: #fff url('https://my.gfw-mapbuilder.org/img/dot.svg') no-repeat 50% 50%;
        }

        .tree-icon {
          background: url('https://my.gfw-mapbuilder.org/img/tree.png') no-repeat center;
        }

        .forest-icon {
          background: url('https://my.gfw-mapbuilder.org/img/forest.png') no-repeat center;
        }

        .chart-icon {
          background: url('https://my.gfw-mapbuilder.org/img/chart-icon.svg') no-repeat center;
        }

        .print-icon {
          background: url('https://my.gfw-mapbuilder.org/img/print-icon.svg') no-repeat center;
        }

        .subscribe-icon {
          background: url('https://my.gfw-mapbuilder.org/img/subscribe-icon.svg') no-repeat center;
        }
      `;

      const styleTag = document.createElement('style');
      styleTag.type = 'text/css';
      styleTag.appendChild(document.createTextNode(css));
      document.getElementsByTagName('head')[0].appendChild(styleTag);
    }

    function loadjsfile(filename, options) {
      const script = document.createElement('script');
      script.src = filename;
      if (options) {
        Object.entries(options).forEach((item) => {
          const [ attribute, attributeValue ] = item;
          script[attribute] = attributeValue;
        });
      }
      document.getElementsByTagName('head')[0].appendChild(script);
    }

    loadjsfile('https://my.gfw-mapbuilder.org/js/arcgis-api-mapbuilder-1.2/dojo/dojo.js');
    loadjsfile('https://cdnjs.cloudflare.com/ajax/libs/vega/4.2.0/vega.min.js', { charset: 'UTF-8' });
    loadRequiredCSS();

    /*eslint-disable */
    (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
    (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
    m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
    })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
    /*eslint-enable */
  };

  window.customApp = {
    ...args
  };

  this.constructorArgs = args;
  this.init(args);
};

window.MapBuilder = MapBuilder;
<|endoftext|>


<commit_before>import { throttle } from 'lodash';
import localforage from 'localforage';
import L from 'leaflet';
import Tangram from 'tangram'; // eslint-disable-line import/no-extraneous-dependencies, import/no-unresolved
import LeafletHash from './leaflet-hash';
import EventEmitter from '../components/event-emitter';

import { hideSceneLoadingIndicator } from './MapLoading';
import { handleInspectionHoverEvent, handleInspectionClickEvent } from './inspection';

// We need to manually set the image path when Leaflet is bundled.
// See https://github.com/Leaflet/Leaflet/issues/766
L.Icon.Default.imagePath = './data/imgs';

/* eslint-disable import/no-mutable-exports */
// ^ Not ideal, but this is what it is right now
export let map;

// Declare these exports now, but Tangram is set up later.
// See initTangram() and loadScene().
export let tangramLayer = null;
export let tangramScene = null;

/**
 * Initializes Tangram
 * Tangram must be initialized with a scene file. Only initialize Tangram when
 * Tangram Play knows what scene to load, not before. See loadScene().
 *
 * @param {string} pathToSceneFile - url of scene file to load. Can be a BlobURL.
 * @param {string} sceneBasePath - scene base path, if different from a base path
 *          parsed from `pathToSceneFile`. Requires Tangram v0.10.5+.
 */
function initTangram(pathToSceneFile, sceneBasePath) {
  // Add Tangram Layer
  tangramLayer = Tangram.leafletLayer({
    scene: pathToSceneFile,
    sceneBasePath,
    events: {
      hover: handleInspectionHoverEvent,
      click: handleInspectionClickEvent,
    },
  });

  tangramLayer.addTo(map);

  tangramLayer.scene.subscribe({
    load(args) {
      EventEmitter.dispatch('tangram:sceneupdate', args);
    },

    // Hides loading indicator after vector tiles have downloaded and rendered
    // Plus a short delay to ease the transition
    /* eslint-disable camelcase */
    view_complete() {
      window.setTimeout(() => {
        hideSceneLoadingIndicator();
      }, 250);
    },
    /* eslint-enable camelcase */
  });

  // Attach scene to export
  tangramScene = tangramLayer.scene;

  // Export to window for debugging.
  window.layer = tangramLayer;
  window.scene = tangramLayer.scene;
}

// Sends a scene path and base path to Tangram.
export function loadScene(pathToSceneFile, { reset = false, basePath = null } = {}) {
  // Initialize Tangram if the layer does not exist, or if the layer
  // exists but had been previously removed from the map.
  // Tangrm cannot be initialized at app start-up because it needs to
  // know what scene file to use. So only initialize Tangram when Tangram Play
  // knows what scene to load, not before.
  if (!tangramLayer || tangramLayer.getContainer() === null) {
    return initTangram(pathToSceneFile, basePath);
  }

  // If `reset` is `false`, we are updating content from an already open scene.
  // (`reset` is `true` if a new scene is loaded.) We need to preserve the
  // scene base path, which allows Tangram to continue accessing resources
  // relative to it, even though the updated scene exists only in memory.
  // If the original scene already has this `config_path` set, we re-use it.
  const path = basePath || tangramLayer.scene.config_path;

  return tangramLayer.scene.load(pathToSceneFile, path);
}

export function destroyScene() {
  if (tangramLayer) {
    // Removing the layer from Leaflet calls scene.destroy() internally,
    // freeing up the canvas and GL resources
    tangramLayer.remove();
  }
}

function getMapStartLocation() {
  // Set default location
  const defaultStartLocation = {
    latlng: [0.0, 0.0],
    zoom: 3,
  };

  // URL Parsing
  // Leaflet-style URL hash pattern: ?scene.yaml#[zoom],[lat],[lng]
  let urlHash = window.location.hash.slice(1).split('/');
  if (urlHash.length === 3) {
    // Convert from strings
    urlHash = urlHash.map(Number);

    return Promise.resolve({
      latlng: [urlHash[1], urlHash[2]],
      zoom: urlHash[0],
    });
  }

  // If no valid URL hash is provided, check local storage to see if
  // lat & lng & zoom have been saved from a previous session
  return localforage.getItem('last-map-view')
    .then((view) => {
      if (view && view.lat && view.lng && view.zoom) {
        return {
          latlng: [view.lat, view.lng],
          zoom: view.zoom,
        };
      }

      return defaultStartLocation;
    });
}

/* New section to handle React components */

// Need to setup dispatch services to let the React component MapPanel
// know when map has changed. These are throttled to prevent expensive
// React operations to be performed in rapid succession.
function setupEventListeners() {
  // Make sure that map zoom label changes when the map is done zooming
  map.on('zoomend', throttle((e) => {
    EventEmitter.dispatch('leaflet:zoomend', {});
  }), 500);
  // Any other time the map moves: drag, bookmark select, tangram play edit
  map.on('moveend', throttle((e) => {
    EventEmitter.dispatch('leaflet:moveend', {});
  }), 1000);
}

// Initializes Leaflet-based map
export function initMap() {
  // Initalize Leaflet
  map = L.map('map', {
    zoomControl: false,
    attributionControl: false,
    maxZoom: 20,
    keyboardZoomOffset: 0.05,
    zoomSnap: 0, // Enables fractional zoom.
  });

  // Get map start position
  getMapStartLocation()
    .then((mapStartLocation) => {
      // Create Leaflet map
      map.setView(mapStartLocation.latlng, mapStartLocation.zoom);

      // Add leaflet-hash (forked version)
      const hash = new LeafletHash(map, { refreshInterval: 250 }); // eslint-disable-line no-unused-vars

      // Report ready to other things that depend on map state.
      // The problem is that the other map-based sub-components like MapPanel
      // cannot assume that the map exists already when they're mounted,
      // because the children of this component will be mounted before
      // the parent's componentDidMount() is called. So, like all other
      // inter-component communication outside of the React framework, we
      // currently use an EventEmitter to report a ready state, which then
      // populates the sub-components' state. I'd imagine this situation to
      // improve when we look into a react-leaflet implementation.
      EventEmitter.dispatch('map:init');
    })
    .catch((error) => {
      console.error(error);
    });

  // Force Leaflet to update itself.
  // This resolves an issue where the map may sometimes not appear
  // or only partially appear when this app is first loaded.
  window.setTimeout(() => {
    map.invalidateSize(false);
  }, 0);

  // Set up a listener to record current map view settings when user leaves
  window.addEventListener('beforeunload', (event) => {
    localforage.setItem('last-map-view', {
      lat: map.getCenter().lat,
      lng: map.getCenter().lng,
      zoom: map.getZoom(),
    });
  });

  setupEventListeners();
}
<commit_msg>Disable scroll wheel zoom when iframed, #600<commit_after>import { throttle } from 'lodash';
import localforage from 'localforage';
import L from 'leaflet';
import Tangram from 'tangram'; // eslint-disable-line import/no-extraneous-dependencies, import/no-unresolved
import LeafletHash from './leaflet-hash';
import EventEmitter from '../components/event-emitter';

import { hideSceneLoadingIndicator } from './MapLoading';
import { handleInspectionHoverEvent, handleInspectionClickEvent } from './inspection';

// We need to manually set the image path when Leaflet is bundled.
// See https://github.com/Leaflet/Leaflet/issues/766
L.Icon.Default.imagePath = './data/imgs';

/* eslint-disable import/no-mutable-exports */
// ^ Not ideal, but this is what it is right now
export let map;

// Declare these exports now, but Tangram is set up later.
// See initTangram() and loadScene().
export let tangramLayer = null;
export let tangramScene = null;

/**
 * Initializes Tangram
 * Tangram must be initialized with a scene file. Only initialize Tangram when
 * Tangram Play knows what scene to load, not before. See loadScene().
 *
 * @param {string} pathToSceneFile - url of scene file to load. Can be a BlobURL.
 * @param {string} sceneBasePath - scene base path, if different from a base path
 *          parsed from `pathToSceneFile`. Requires Tangram v0.10.5+.
 */
function initTangram(pathToSceneFile, sceneBasePath) {
  // Add Tangram Layer
  tangramLayer = Tangram.leafletLayer({
    scene: pathToSceneFile,
    sceneBasePath,
    events: {
      hover: handleInspectionHoverEvent,
      click: handleInspectionClickEvent,
    },
  });

  tangramLayer.addTo(map);

  tangramLayer.scene.subscribe({
    load(args) {
      EventEmitter.dispatch('tangram:sceneupdate', args);
    },

    // Hides loading indicator after vector tiles have downloaded and rendered
    // Plus a short delay to ease the transition
    /* eslint-disable camelcase */
    view_complete() {
      window.setTimeout(() => {
        hideSceneLoadingIndicator();
      }, 250);
    },
    /* eslint-enable camelcase */
  });

  // Attach scene to export
  tangramScene = tangramLayer.scene;

  // Export to window for debugging.
  window.layer = tangramLayer;
  window.scene = tangramLayer.scene;
}

// Sends a scene path and base path to Tangram.
export function loadScene(pathToSceneFile, { reset = false, basePath = null } = {}) {
  // Initialize Tangram if the layer does not exist, or if the layer
  // exists but had been previously removed from the map.
  // Tangrm cannot be initialized at app start-up because it needs to
  // know what scene file to use. So only initialize Tangram when Tangram Play
  // knows what scene to load, not before.
  if (!tangramLayer || tangramLayer.getContainer() === null) {
    return initTangram(pathToSceneFile, basePath);
  }

  // If `reset` is `false`, we are updating content from an already open scene.
  // (`reset` is `true` if a new scene is loaded.) We need to preserve the
  // scene base path, which allows Tangram to continue accessing resources
  // relative to it, even though the updated scene exists only in memory.
  // If the original scene already has this `config_path` set, we re-use it.
  const path = basePath || tangramLayer.scene.config_path;

  return tangramLayer.scene.load(pathToSceneFile, path);
}

export function destroyScene() {
  if (tangramLayer) {
    // Removing the layer from Leaflet calls scene.destroy() internally,
    // freeing up the canvas and GL resources
    tangramLayer.remove();
  }
}

function getMapStartLocation() {
  // Set default location
  const defaultStartLocation = {
    latlng: [0.0, 0.0],
    zoom: 3,
  };

  // URL Parsing
  // Leaflet-style URL hash pattern: ?scene.yaml#[zoom],[lat],[lng]
  let urlHash = window.location.hash.slice(1).split('/');
  if (urlHash.length === 3) {
    // Convert from strings
    urlHash = urlHash.map(Number);

    return Promise.resolve({
      latlng: [urlHash[1], urlHash[2]],
      zoom: urlHash[0],
    });
  }

  // If no valid URL hash is provided, check local storage to see if
  // lat & lng & zoom have been saved from a previous session
  return localforage.getItem('last-map-view')
    .then((view) => {
      if (view && view.lat && view.lng && view.zoom) {
        return {
          latlng: [view.lat, view.lng],
          zoom: view.zoom,
        };
      }

      return defaultStartLocation;
    });
}

/* New section to handle React components */

// Need to setup dispatch services to let the React component MapPanel
// know when map has changed. These are throttled to prevent expensive
// React operations to be performed in rapid succession.
function setupEventListeners() {
  // Make sure that map zoom label changes when the map is done zooming
  map.on('zoomend', throttle((e) => {
    EventEmitter.dispatch('leaflet:zoomend', {});
  }), 500);
  // Any other time the map moves: drag, bookmark select, tangram play edit
  map.on('moveend', throttle((e) => {
    EventEmitter.dispatch('leaflet:moveend', {});
  }), 1000);
}

// Initializes Leaflet-based map
export function initMap() {
  // Initalize Leaflet
  map = L.map('map', {
    zoomControl: false,
    attributionControl: false,
    maxZoom: 20,
    keyboardZoomOffset: 0.05,
    // Enables fractional zoom.
    zoomSnap: 0,
    // Prevents scroll wheel zoom when iframed.
    scrollWheelZoom: (window.self === window.top)
  });

  // Provide alternate zoom in/zoom out button controls in embedded version
  // since, when iframed, the scroll wheel zoom will no longer work.
  if (window.isEmbedded) {
    const zoomControl = L.control.zoom();
    zoomControl.addTo(map);
  }

  // Get map start position
  getMapStartLocation()
    .then((mapStartLocation) => {
      // Create Leaflet map
      map.setView(mapStartLocation.latlng, mapStartLocation.zoom);

      // Add leaflet-hash (forked version)
      const hash = new LeafletHash(map, { refreshInterval: 250 }); // eslint-disable-line no-unused-vars

      // Report ready to other things that depend on map state.
      // The problem is that the other map-based sub-components like MapPanel
      // cannot assume that the map exists already when they're mounted,
      // because the children of this component will be mounted before
      // the parent's componentDidMount() is called. So, like all other
      // inter-component communication outside of the React framework, we
      // currently use an EventEmitter to report a ready state, which then
      // populates the sub-components' state. I'd imagine this situation to
      // improve when we look into a react-leaflet implementation.
      EventEmitter.dispatch('map:init');
    })
    .catch((error) => {
      console.error(error);
    });

  // Force Leaflet to update itself.
  // This resolves an issue where the map may sometimes not appear
  // or only partially appear when this app is first loaded.
  window.setTimeout(() => {
    map.invalidateSize(false);
  }, 0);

  // Set up a listener to record current map view settings when user leaves
  window.addEventListener('beforeunload', (event) => {
    localforage.setItem('last-map-view', {
      lat: map.getCenter().lat,
      lng: map.getCenter().lng,
      zoom: map.getZoom(),
    });
  });

  setupEventListeners();
}
<|endoftext|>


<commit_before>//
// message.js
// http://nicovideo.cdn.nimg.jp/uni/scripts/pages/my/nicorepo/message/ja-jp.js
//

'use strict';

const Author = {
    user: { value: 'user', label: 'ユーザー' },
    channel: { value: 'channel', label: 'チャンネル' },
    community: { value: 'community', label: 'コミュニティ' },
};

const Message = {
    messages: [
        {
            label: '動画の投稿',
            type: 'nicovideo.user.video.upload',
            regexp: /.*? さんが 動画を投稿しました。/,
            author: Author.user,
        },
        {
            label: '動画ランキング上昇',
            type: 'nicovideo.user.video.update_highest_rankings',
            regexp: /.*? さんの動画が .*? ランキングで .*? 位を達成しました。/,
            author: Author.user,
        },
        {
            label: '生放送紹介',
            type: 'nicovideo.user.video.live.introduce',
            regexp: /.*? さんの動画が 生放送 .*? で紹介されました。/,
            author: Author.user,
        },
        {
            label: '再生数キリ番達成',
            type: 'nicovideo.user.video.kiriban.play',
            regexp: /.*? さんの動画が .*? 再生を達成しました。/,
            author: Author.user,
        },
        {
            label: 'ニコニ広告の宣伝(された)',
            type: 'nicovideo.user.video.advertised_announce',
            // also nicoad.user.advertised.video.announce
            // also nicoad.user.advertised.program.announce
            // also nicoad.user.advertised.program.cas.announce
            // also nicoad.user.advertised.game.announce
            regexp: /.*? さんの(?:動画|生放送(?:（実験放送）|ゲーム)?)が .*? さんにニコニ広告(?:で宣伝)?されました。 .*?/,
            author: Author.user,
        },
        {
            label: 'ニコニ広告の宣伝(した)',
            type: 'nicovideo.user.video.advertise',
            // also nicoad.user.advertise.video
            // also nicoad.user.advertise.program
            // also nicoad.user.advertise.program.cas
            // also nicoad.user.advertise.game
            regexp: /.*? さんが ?ニコニ広告(?:で宣伝)?しました。 .*?/,
            author: Author.user,
        },
        {
            label: 'とりあえずマイリスト登録[動画]',
            type: 'nicovideo.user.temporary_mylist.add.video',
            regexp: /.*? さんが とりあえずマイリスト に動画を登録しました。/,
            author: Author.user,
        },
        {
            label: 'とりあえずマイリスト登録[マンガ]',
            type: 'nicovideo.user.temporary_mylist.add.manga.episode',
            regexp: /.*? さんが とりあえずマイリスト にマンガ .*? を登録しました。/,
            author: Author.user,
        },
        {
            label: 'とりあえずマイリスト登録[書籍]',
            type: 'nicovideo.user.temporary_mylist.add.book',
            regexp: /.*? さんが とりあえずマイリスト に書籍を登録しました。/,
            author: Author.user,
        },
        {
            label: 'とりあえずマイリスト登録[ブロマガ]',
            type: 'nicovideo.user.temporary_mylist.add.blomaga.article',
            regexp: /.*? さんが とりあえずマイリスト にブロマガを登録しました。/,
            author: Author.user,
        },
        {
            label: 'マイリスト登録[動画]',
            type: 'nicovideo.user.mylist.add.video',
            regexp: /.*? さんが マイリスト .*? に動画を登録しました。/,
            author: Author.user,
        },
        {
            label: 'マイリスト登録[マンガ]',
            type: 'nicovideo.user.mylist.add.manga.episode',
            regexp: /.*? さんが マイリスト .*? にマンガ .*? を登録しました。/,
            author: Author.user,
        },
        {
            label: 'マイリスト登録[書籍]',
            type: 'nicovideo.user.mylist.add.book',
            regexp: /.*? さんが マイリスト .*? に書籍を登録しました。/,
            author: Author.user,
        },
        {
            label: 'マイリスト登録[ブロマガ]',
            type: 'nicovideo.user.mylist.add.blomaga.article',
            regexp: /.*? さんが マイリスト .*? にブロマガを登録しました。/,
            author: Author.user,
        },
        {
            label: 'スタンプ取得',
            type: 'nicovideo.user.stamp.obtain',
            regexp: /.*? さんがスタンプを取得しました。/,
            author: Author.user,
        },
        {
            label: '立体の投稿',
            type: 'nicovideo.user.solid.upload',
            regexp: /.*? さんが立体を投稿しました。/,
            author: Author.user,
        },
        {
            label: '立体の更新',
            type: 'nicovideo.user.solid.update',
            regexp: /.*? さんが立体の配布データを更新しました。/,
            author: Author.user,
        },
        {
            label: '立体のお気に入り登録',
            type: 'nicovideo.user.solid.favorite',
            regexp: /.*? さんが立体をお気に入り登録しました。/,
            author: Author.user,
        },
        {
            label: '立体の公開',
            type: 'nicovideo.user.solid.distribute',
            regexp: /.*? さんが立体の配布データを公開しました。/,
            author: Author.user,
        },
        {
            label: 'ゲームの投稿',
            type: 'nicovideo.user.nicogame.upload',
            // also nicogame.user.game.upload
            regexp: /.*? さんがゲームを投稿しました。/,
            author: Author.user,
        },
        {
            label: 'ゲームの更新',
            type: 'nicovideo.user.nicogame.update',
            // also nicogame.user.game.update
            regexp: /.*? さんがゲームを更新しました。/,
            author: Author.user,
        },
        {
            label: 'ナレッジの投稿',
            type: 'nicovideo.user.knowledge.upload',
            regexp: /.*? さんが ナレッジを投稿しました。/,
            author: Author.user,
        },
        {
            label: '記事の投稿',
            type: 'nicovideo.user.blomaga.upload',
            regexp: /.*? さんが記事を投稿しました。/,
            author: Author.user,
        },
        {
            label: 'アプリの開始',
            type: 'nicovideo.user.app.install',
            regexp: /.*? さんが アプリを遊びはじめました。/,
            author: Author.user,
        },
        {
            label: 'マンガのお気に入り',
            type: 'nicoseiga.user.manga.content.favorite',
            regexp: /.*? さんがマンガをお気に入りしました。/,
            author: Author.user,
        },
        {
            label: 'マンガの投稿',
            type: 'nicoseiga.user.manga.episode.upload',
            regexp: /.*? さんが +マンガ .*? を投稿しました。/,
            author: Author.user,
        },
        {
            label: 'イラストの投稿',
            type: 'nicoseiga.user.illust.upload',
            regexp: /.*? さんが イラストを投稿しました。/,
            author: Author.user,
        },
        {
            label: 'イラストのクリップ',
            type: 'nicoseiga.user.illust.clip',
            regexp: /.*? さんが イラストをクリップしました。/,
            author: Author.user,
        },
        {
            label: '生放送の開始',
            type: 'live.user.program.cas.onairs',
            regexp: /.*? さんが コミュニティ .*? で生放送(?:（実験放送）)?を開始しました。/,
            author: Author.user,
        },
        {
            label: '動画の追加',
            type: 'nicovideo.channel.video.upload',
            regexp: /チャンネル .*? に動画が追加されました。/,
            author: Author.channel,
        },
        {
            label: 'お知らせの追加',
            type: 'nicovideo.channel.info.add',
            regexp: /チャンネル .*? にお知らせが追加されました。/,
            author: Author.channel,
        },
        {
            label: '記事の追加',
            type: 'nicovideo.channel.blomaga.upload',
            regexp: /チャンネル .*? に記事が追加されました。/,
            author: Author.channel,
        },
        {
            label: '生放送の予約',
            type: 'live.channel.program.reserve',
            regexp: /チャンネル .*? で .*? に生放送が予約されました。/,
            author: Author.channel,
        },
        {
            label: '生放送の開始',
            type: 'live.channel.program.onairs',
            regexp: /チャンネル .*? で生放送が開始されました。/,
            author: Author.channel,
        },
        {
            label: '動画の投稿',
            type: 'nicovideo.user.community_member_only_video.upload',
            regexp: /.*? さんが コミュニティ専用動画を投稿しました。/,
            author: Author.community,
        },
        {
            label: '動画の登録',
            type: 'nicovideo.user.community.video.add',
            // also nicommunity.user.video.registered
            regexp: /.*? さんが コミュニティ .*? に動画を登録しました。/,
            author: Author.community,
        },
        {
            label: 'イラストの登録',
            type: 'nicommunity.user.illust.registered',
            regexp: /.*? さんが コミュニティ .*? にイラストを登録しました。/,
            author: Author.community,
        },
        {
            label: 'お知らせの登録',
            type: 'nicovideo.user.community.info.add',
            // also nicommunity.user.community_news.created
            regexp: /.*? さんが コミュニティ .*? にお知らせを追加しました。|お知らせ .*? を コミュニティ .*? に登録しました。/,
            author: Author.community,
        },
        {
            label: 'レベル上昇',
            type: 'nicovideo.community.level.raise',
            // also nicommunity.community.no_privileged.levelup
            // also nicommunity.community.privileged.levelup
            regexp: /.*? の コミュニティレベルが .*? になりました。/,
            author: Author.community,
        },
        {
            label: '生放送の予約',
            type: 'live.user.program.reserve',
            regexp: /.*? さんが コミュニティ .*? で .*? に生放送を予約しました。/,
            author: Author.community,
        },
        {
            label: '生放送の開始',
            type: 'live.user.program.onairs',
            regexp: /.*? さんが コミュニティ .*? で生放送を開始しました。/,
            author: Author.community,
        },
    ],
    unknown: { type: null, regexp: null, author: null, label: null },
    /**
     * @param {string} message regexp
     * @return {Object} message
     */
    findByText(text) {
        return this.messages.find(message => message.regexp.test(text)) || this.unknown;
    },
    /**
     * @param {string} message type
     * @return {Object} message
     */
    findByType(type) {
        return this.messages.find(message => message.type === type) || this.unknown;
    },
    /**
     * @param {Object} author
     * @return {Object[]} messages
     */
    findByAuthor(author) {
        return this.messages.filter(message => message.author.value === author.value);
    },
};
<commit_msg>Fix message regexp<commit_after>//
// message.js
// http://nicovideo.cdn.nimg.jp/uni/scripts/pages/my/nicorepo/message/ja-jp.js
//

'use strict';

const Author = {
    user: { value: 'user', label: 'ユーザー' },
    channel: { value: 'channel', label: 'チャンネル' },
    community: { value: 'community', label: 'コミュニティ' },
};

const Message = {
    messages: [
        {
            label: '動画の投稿',
            type: 'nicovideo.user.video.upload',
            regexp: /.*? さんが 動画を投稿しました。/,
            author: Author.user,
        },
        {
            label: '動画ランキング上昇',
            type: 'nicovideo.user.video.update_highest_rankings',
            regexp: /.*? さんの動画が .*? ランキングで .*? 位を達成しました。/,
            author: Author.user,
        },
        {
            label: '生放送紹介',
            type: 'nicovideo.user.video.live.introduce',
            regexp: /.*? さんの動画が 生放送 .*? で紹介されました。/,
            author: Author.user,
        },
        {
            label: '再生数キリ番達成',
            type: 'nicovideo.user.video.kiriban.play',
            regexp: /.*? さんの動画が .*? 再生を達成しました。/,
            author: Author.user,
        },
        {
            label: 'ニコニ広告の宣伝(された)',
            type: 'nicovideo.user.video.advertised_announce',
            // also nicoad.user.advertised.video.announce
            // also nicoad.user.advertised.program.announce
            // also nicoad.user.advertised.program.cas.announce
            // also nicoad.user.advertised.game.announce
            regexp: /.*? さんの(?:動画|生放送(?:（実験放送）)?|ゲーム)が .*? さんにニコニ広告(?:で宣伝)?されました。 .*?/,
            author: Author.user,
        },
        {
            label: 'ニコニ広告の宣伝(した)',
            type: 'nicovideo.user.video.advertise',
            // also nicoad.user.advertise.video
            // also nicoad.user.advertise.program
            // also nicoad.user.advertise.program.cas
            // also nicoad.user.advertise.game
            regexp: /.*? さんが ?ニコニ広告(?:で宣伝)?しました。 .*?/,
            author: Author.user,
        },
        {
            label: 'とりあえずマイリスト登録[動画]',
            type: 'nicovideo.user.temporary_mylist.add.video',
            regexp: /.*? さんが とりあえずマイリスト に動画を登録しました。/,
            author: Author.user,
        },
        {
            label: 'とりあえずマイリスト登録[マンガ]',
            type: 'nicovideo.user.temporary_mylist.add.manga.episode',
            regexp: /.*? さんが とりあえずマイリスト にマンガ .*? を登録しました。/,
            author: Author.user,
        },
        {
            label: 'とりあえずマイリスト登録[書籍]',
            type: 'nicovideo.user.temporary_mylist.add.book',
            regexp: /.*? さんが とりあえずマイリスト に書籍を登録しました。/,
            author: Author.user,
        },
        {
            label: 'とりあえずマイリスト登録[ブロマガ]',
            type: 'nicovideo.user.temporary_mylist.add.blomaga.article',
            regexp: /.*? さんが とりあえずマイリスト にブロマガを登録しました。/,
            author: Author.user,
        },
        {
            label: 'マイリスト登録[動画]',
            type: 'nicovideo.user.mylist.add.video',
            regexp: /.*? さんが マイリスト .*? に動画を登録しました。/,
            author: Author.user,
        },
        {
            label: 'マイリスト登録[マンガ]',
            type: 'nicovideo.user.mylist.add.manga.episode',
            regexp: /.*? さんが マイリスト .*? にマンガ .*? を登録しました。/,
            author: Author.user,
        },
        {
            label: 'マイリスト登録[書籍]',
            type: 'nicovideo.user.mylist.add.book',
            regexp: /.*? さんが マイリスト .*? に書籍を登録しました。/,
            author: Author.user,
        },
        {
            label: 'マイリスト登録[ブロマガ]',
            type: 'nicovideo.user.mylist.add.blomaga.article',
            regexp: /.*? さんが マイリスト .*? にブロマガを登録しました。/,
            author: Author.user,
        },
        {
            label: 'スタンプ取得',
            type: 'nicovideo.user.stamp.obtain',
            regexp: /.*? さんがスタンプを取得しました。/,
            author: Author.user,
        },
        {
            label: '立体の投稿',
            type: 'nicovideo.user.solid.upload',
            regexp: /.*? さんが立体を投稿しました。/,
            author: Author.user,
        },
        {
            label: '立体の更新',
            type: 'nicovideo.user.solid.update',
            regexp: /.*? さんが立体の配布データを更新しました。/,
            author: Author.user,
        },
        {
            label: '立体のお気に入り登録',
            type: 'nicovideo.user.solid.favorite',
            regexp: /.*? さんが立体をお気に入り登録しました。/,
            author: Author.user,
        },
        {
            label: '立体の公開',
            type: 'nicovideo.user.solid.distribute',
            regexp: /.*? さんが立体の配布データを公開しました。/,
            author: Author.user,
        },
        {
            label: 'ゲームの投稿',
            type: 'nicovideo.user.nicogame.upload',
            // also nicogame.user.game.upload
            regexp: /.*? さんがゲームを投稿しました。/,
            author: Author.user,
        },
        {
            label: 'ゲームの更新',
            type: 'nicovideo.user.nicogame.update',
            // also nicogame.user.game.update
            regexp: /.*? さんがゲームを更新しました。/,
            author: Author.user,
        },
        {
            label: 'ナレッジの投稿',
            type: 'nicovideo.user.knowledge.upload',
            regexp: /.*? さんが ナレッジを投稿しました。/,
            author: Author.user,
        },
        {
            label: '記事の投稿',
            type: 'nicovideo.user.blomaga.upload',
            regexp: /.*? さんが記事を投稿しました。/,
            author: Author.user,
        },
        {
            label: 'アプリの開始',
            type: 'nicovideo.user.app.install',
            regexp: /.*? さんが アプリを遊びはじめました。/,
            author: Author.user,
        },
        {
            label: 'マンガのお気に入り',
            type: 'nicoseiga.user.manga.content.favorite',
            regexp: /.*? さんがマンガをお気に入りしました。/,
            author: Author.user,
        },
        {
            label: 'マンガの投稿',
            type: 'nicoseiga.user.manga.episode.upload',
            regexp: /.*? さんが +マンガ .*? を投稿しました。/,
            author: Author.user,
        },
        {
            label: 'イラストの投稿',
            type: 'nicoseiga.user.illust.upload',
            regexp: /.*? さんが イラストを投稿しました。/,
            author: Author.user,
        },
        {
            label: 'イラストのクリップ',
            type: 'nicoseiga.user.illust.clip',
            regexp: /.*? さんが イラストをクリップしました。/,
            author: Author.user,
        },
        {
            label: '生放送の開始',
            type: 'live.user.program.cas.onairs',
            regexp: /.*? さんが コミュニティ .*? で生放送(?:（実験放送）)?を開始しました。/,
            author: Author.user,
        },
        {
            label: '動画の追加',
            type: 'nicovideo.channel.video.upload',
            regexp: /チャンネル .*? に動画が追加されました。/,
            author: Author.channel,
        },
        {
            label: 'お知らせの追加',
            type: 'nicovideo.channel.info.add',
            regexp: /チャンネル .*? にお知らせが追加されました。/,
            author: Author.channel,
        },
        {
            label: '記事の追加',
            type: 'nicovideo.channel.blomaga.upload',
            regexp: /チャンネル .*? に記事が追加されました。/,
            author: Author.channel,
        },
        {
            label: '生放送の予約',
            type: 'live.channel.program.reserve',
            regexp: /チャンネル .*? で .*? に生放送が予約されました。/,
            author: Author.channel,
        },
        {
            label: '生放送の開始',
            type: 'live.channel.program.onairs',
            regexp: /チャンネル .*? で生放送が開始されました。/,
            author: Author.channel,
        },
        {
            label: '動画の投稿',
            type: 'nicovideo.user.community_member_only_video.upload',
            regexp: /.*? さんが コミュニティ専用動画を投稿しました。/,
            author: Author.community,
        },
        {
            label: '動画の登録',
            type: 'nicovideo.user.community.video.add',
            // also nicommunity.user.video.registered
            regexp: /.*? さんが コミュニティ .*? に動画を登録しました。/,
            author: Author.community,
        },
        {
            label: 'イラストの登録',
            type: 'nicommunity.user.illust.registered',
            regexp: /.*? さんが コミュニティ .*? にイラストを登録しました。/,
            author: Author.community,
        },
        {
            label: 'お知らせの登録',
            type: 'nicovideo.user.community.info.add',
            // also nicommunity.user.community_news.created
            regexp: /.*? さんが コミュニティ .*? にお知らせを追加しました。|お知らせ .*? を コミュニティ .*? に登録しました。/,
            author: Author.community,
        },
        {
            label: 'レベル上昇',
            type: 'nicovideo.community.level.raise',
            // also nicommunity.community.no_privileged.levelup
            // also nicommunity.community.privileged.levelup
            regexp: /.*? の コミュニティレベルが .*? になりました。/,
            author: Author.community,
        },
        {
            label: '生放送の予約',
            type: 'live.user.program.reserve',
            regexp: /.*? さんが コミュニティ .*? で .*? に生放送を予約しました。/,
            author: Author.community,
        },
        {
            label: '生放送の開始',
            type: 'live.user.program.onairs',
            regexp: /.*? さんが コミュニティ .*? で生放送を開始しました。/,
            author: Author.community,
        },
    ],
    unknown: { type: null, regexp: null, author: null, label: null },
    /**
     * @param {string} message regexp
     * @return {Object} message
     */
    findByText(text) {
        return this.messages.find(message => message.regexp.test(text)) || this.unknown;
    },
    /**
     * @param {string} message type
     * @return {Object} message
     */
    findByType(type) {
        return this.messages.find(message => message.type === type) || this.unknown;
    },
    /**
     * @param {Object} author
     * @return {Object[]} messages
     */
    findByAuthor(author) {
        return this.messages.filter(message => message.author.value === author.value);
    },
};
<|endoftext|>


<commit_before>/*
-- App.postman --
Handles the fetching, storing and rendering of
all posts. The REST endpoint is passed as an
argument when it is initialized.
 */
function Postman (endpoint) {
  var url = document.URL + endpoint,
    models;
  function constructor () { }
  constructor.prototype.XHR = function (method, data, url, async, cb) {
    var req = new XMLHttpRequest();
    req.open(method, url, async);
    req.responseType = '';
    req.onload = function () {
      if (req.status >= 200 && req.status < 400) {
        if (req.responseText) {
          cb(req.responseText);
        } else {
          cb();
        }
      } else {
        return false;
      }
    }
    req.send();
  };
  constructor.prototype.fetch = function () {
    return this.XHR('GET', null, url, true, function (data) {
      models = JSON.parse(data);
    });
  };
  constructor.prototype.show = function () {
    return models;
  }
  return new constructor();
}
App.postman = new Postman('api/v1/posts');

// function microAjax(url, callbackFunction)
// {
//   this.bindFunction = function (caller, object) {
//     return function() {
//       return caller.apply(object, [object]);
//     };
//   };

//   this.stateChange = function (object) {
//     if (this.request.readyState==4)
//       this.callbackFunction(this.request.responseText);
//   };

//   this.getRequest = function() {
//     if (window.ActiveXObject)
//       return new ActiveXObject('Microsoft.XMLHTTP');
//     else if (window.XMLHttpRequest)
//       return new XMLHttpRequest();
//     return false;
//   };

//   this.postBody = (arguments[2] || "");

//   this.callbackFunction=callbackFunction;
//   this.url=url;
//   this.request = this.getRequest();

//   if(this.request) {
//     var req = this.request;
//     req.onreadystatechange = this.bindFunction(this.stateChange, this);

//     if (this.postBody!=="") {
//       req.open("POST", url, true);
//       req.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
//       req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
//       req.setRequestHeader('Connection', 'close');
//     } else {
//       req.open("GET", url, true);
//     }

//     req.send(this.postBody);
//   }

// request = new XMLHttpRequest();
// request.open('GET', '/my/url', true);

// request.onload = function() {
//   if (request.status >= 200 && request.status < 400){
//     // Success!
//     data = JSON.parse(request.responseText);
//   } else {
//     // We reached our target server, but it returned an error

//   }
// };

// request.onerror = function() {
//   // There was a connection error of some sort
// };

// request.send();
// }

<commit_msg>working on postman ajax<commit_after>/*
-- App.postman --
Handles the fetching, storing and rendering of
all posts. The REST endpoint is passed as an
argument when it is initialized.
 */
function Postman (endpoint) {
  var url = document.URL + endpoint,
    models;
  function constructor () { }
  constructor.prototype.XHR = function (method, data, url, async, cb) {
    var req = new XMLHttpRequest();
    req.open(method, url, async);
    req.responseType = '';
    req.onload = function () {
      if (req.status >= 200 && req.status < 400) {
        if (req.responseText) {
          cb(req.responseText);
        } else {
          cb();
        }
      } else {
        return false;
      }
    };
    if (data) {
      req.send(data);
    } else {
      req.send();
    }
  };
  constructor.prototype.fetch = function () {
    return this.XHR('GET', null, url, true, function (data) {
      models = JSON.parse(data);
    });
  };
  constructor.prototype.show = function () {
    return models;
  }
  return new constructor();
}
App.postman = new Postman('api/v1/posts');

// function microAjax(url, callbackFunction)
// {
//   this.bindFunction = function (caller, object) {
//     return function() {
//       return caller.apply(object, [object]);
//     };
//   };

//   this.stateChange = function (object) {
//     if (this.request.readyState==4)
//       this.callbackFunction(this.request.responseText);
//   };

//   this.getRequest = function() {
//     if (window.ActiveXObject)
//       return new ActiveXObject('Microsoft.XMLHTTP');
//     else if (window.XMLHttpRequest)
//       return new XMLHttpRequest();
//     return false;
//   };

//   this.postBody = (arguments[2] || "");

//   this.callbackFunction=callbackFunction;
//   this.url=url;
//   this.request = this.getRequest();

//   if(this.request) {
//     var req = this.request;
//     req.onreadystatechange = this.bindFunction(this.stateChange, this);

//     if (this.postBody!=="") {
//       req.open("POST", url, true);
//       req.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
//       req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
//       req.setRequestHeader('Connection', 'close');
//     } else {
//       req.open("GET", url, true);
//     }

//     req.send(this.postBody);
//   }

// request = new XMLHttpRequest();
// request.open('GET', '/my/url', true);

// request.onload = function() {
//   if (request.status >= 200 && request.status < 400){
//     // Success!
//     data = JSON.parse(request.responseText);
//   } else {
//     // We reached our target server, but it returned an error

//   }
// };

// request.onerror = function() {
//   // There was a connection error of some sort
// };

// request.send();
// }

<|endoftext|>


<commit_before>/* $ComponentName$ */
+function($, window, document, Math)
{
    "use strict";

    $.extend(
    {
        uuid: function()
        {
            var d = (new Date).getTime();
            while(d < 10000000000000000)
            {
               d *= 10;
            }
            return  d + Math.floor(Math.random() * 9999);
        },

        getPropertyCount: function(obj)
        {
           if(typeof(obj) != 'object' || obj == null) return 0;
           return Object.getOwnPropertyNames(obj).length;
        },

        callEvent: function(func, event, proxy)
        {
            if($.isFunction(func))
            {
                if(typeof proxy != 'undefined')
                {
                    func = $.proxy(func, proxy);
                }
                event.result = func(event);
                return !(event.result != undefined && (!event.result));
            }
            return 1;
        }
    });

    $.fn.callEvent = function(name, event, modal)
    {
        var $this = $(this);
        var dotIndex = name.indexOf('.zui.');
        var shortName = name;
        if(dotIndex < 0 && modal && modal.name)
        {
            name += '.' + modal.name;
        }
        else
        {
            shortName = name.substring(0, dotIndex);
        }
        var e     = $.Event(name, event);

        var result =$this.trigger(e);

        if((typeof modal === 'undefined') && dotIndex > 0)
        {
            modal = $this.data(name.substring(dotIndex + 1));
        }

        if(modal && modal.options)
        {
            var func = modal.options[shortName];
            if($.isFunction(func))
            {
                $.callEvent(modal.options[shortName], e, modal);
            }
        }
        return e;
    };
}(jQuery,window,document,Math);
<commit_msg> * added helper function to get client lang.<commit_after>/* $ComponentName$ */
+function($, window, document, Math)
{
    "use strict";

    $.extend(
    {
        uuid: function()
        {
            var d = (new Date).getTime();
            while(d < 10000000000000000)
            {
               d *= 10;
            }
            return  d + Math.floor(Math.random() * 9999);
        },

        getPropertyCount: function(obj)
        {
           if(typeof(obj) != 'object' || obj == null) return 0;
           return Object.getOwnPropertyNames(obj).length;
        },

        callEvent: function(func, event, proxy)
        {
            if($.isFunction(func))
            {
                if(typeof proxy != 'undefined')
                {
                    func = $.proxy(func, proxy);
                }
                event.result = func(event);
                return !(event.result != undefined && (!event.result));
            }
            return 1;
        },

        clientLang: function()
        {
            var lang;
            if(typeof(window.config) != 'undefined' && window.config.clientLang)
            {
                lang = window.config.clientLang;
            }
            else
            {
                var hl = $('html').attr('lang');
                lang = hl ? hl : 'zh_cn';
            }
            return lang.replace('-', '_').toLowerCase();
        }
    });

    $.fn.callEvent = function(name, event, modal)
    {
        var $this = $(this);
        var dotIndex = name.indexOf('.zui.');
        var shortName = name;
        if(dotIndex < 0 && modal && modal.name)
        {
            name += '.' + modal.name;
        }
        else
        {
            shortName = name.substring(0, dotIndex);
        }
        var e     = $.Event(name, event);

        var result =$this.trigger(e);

        if((typeof modal === 'undefined') && dotIndex > 0)
        {
            modal = $this.data(name.substring(dotIndex + 1));
        }

        if(modal && modal.options)
        {
            var func = modal.options[shortName];
            if($.isFunction(func))
            {
                $.callEvent(modal.options[shortName], e, modal);
            }
        }
        return e;
    };
}(jQuery,window,document,Math);
<|endoftext|>