<commit_before>import { parseGIF } from './lib/gifsparser'

if (typeof AFRAME === 'undefined') {
  throw 'Component attempted to register before AFRAME was available.'
}

/* get util from AFRAME */
const { parseUrl } = AFRAME.utils.srcLoader
const { debug } = AFRAME.utils
// debug.enable('shader:gif:*')
debug.enable('shader:gif:warn')
const warn = debug('shader:gif:warn')
const log = debug('shader:gif:debug')

/* store data so that you won't load same data */
const gifData = {}

/* create error message */
function createError (err, src) {
  return { status: 'error', src: src, message: err, timestamp: Date.now() }
}

AFRAME.registerShader('gif', {

  /**
   * For material component:
   * @see https://github.com/aframevr/aframe/blob/60d198ef8e2bfbc57a13511ae5fca7b62e01691b/src/components/material.js
   * For example of `registerShader`:
   * @see https://github.com/aframevr/aframe/blob/41a50cd5ac65e462120ecc2e5091f5daefe3bd1e/src/shaders/flat.js
   * For MeshBasicMaterial
   * @see http://threejs.org/docs/#Reference/Materials/MeshBasicMaterial
   */
  schema: {

    /* For material */
    color: { type: 'color' },
    fog: { default: true },

    /* For texuture */
    src: { default: null },
    autoplay: { default: true },

  },

  /**
   * Initialize material. Called once.
   * @protected
   */
  init (data) {
    log('init', data)
    log(this.el.components)
    this.__cnv = document.createElement('canvas')
    this.__cnv.width = 2
    this.__cnv.height = 2
    this.__ctx = this.__cnv.getContext('2d')
    this.__texture = new THREE.Texture(this.__cnv) //renders straight from a canvas
    if (data.repeat) {
      this.__texture.wrapS = THREE.RepeatWrapping;
      this.__texture.wrapT = THREE.RepeatWrapping;
      this.__texture.repeat.set( data.repeat.x, data.repeat.y );
    }
    this.__material = {}
    this.__reset()
    this.material = new THREE.MeshBasicMaterial({ map: this.__texture })
    this.el.sceneEl.addBehavior(this)
    return this.material
  },

  /**
   * Update or create material.
   * @param {object|null} oldData
   */
  update (oldData) {
    log('update', oldData)
    this.__updateMaterial(oldData)
    this.__updateTexture(oldData)
    return this.material
  },

  /**
   * Called on each scene tick.
   * @protected
   */
  tick (t) {
    if (!this.__frames || this.paused()) return
    if (Date.now() - this.__startTime >= this.__nextFrameTime) {
      this.nextFrame()
    }
  },

  /*================================
  =            material            =
  ================================*/

  /**
   * Updating existing material.
   * @param {object} data - Material component data.
   */
  __updateMaterial (data) {
    const { material } = this
    const newData = this.__getMaterialData(data)
    Object.keys(newData).forEach(key => {
      material[key] = newData[key]
    })
  },

  /**
   * Builds and normalize material data, normalizing stuff along the way.
   * @param {Object} data - Material data.
   * @return {Object} data - Processed material data.
   */
  __getMaterialData (data) {
    return {
      fog: data.fog,
      color: new THREE.Color(data.color),
    }
  },

  /*==============================
  =            texure            =
  ==============================*/

  /**
   * set texure
   * @private
   * @param {Object} data
   * @property {string} status - success / error
   * @property {string} src - src url
   * @property {array} times - array of time length of each image
   * @property {number} cnt - total counts of gif images
   * @property {array} frames - array of each image
   * @property {Date} timestamp - created at the texure
   */

  __setTexure (data) {
    log('__setTexure', data)
    if (data.status === 'error') {
      warn(`Error: ${data.message}\nsrc: ${data.src}`)
      this.__reset()
    }
    else if (data.status === 'success' && data.src !== this.__textureSrc) {
      this.__reset()
      /* Texture added or changed */
      this.__ready(data)
    }
  },

  /**
   * Update or create texure.
   * @param {Object} data - Material component data.
   */
  __updateTexture (data) {
    const { src, autoplay } = data

    /* autoplay */
    if (typeof autoplay === 'boolean') {
      this.__autoplay = autoplay
    }
    else if (typeof autoplay === 'undefined') {
      this.__autoplay = true
    }
    if (this.__autoplay && this.__frames) { this.play() }

    /* src */
    if (src) {
      this.__validateSrc(src, this.__setTexure.bind(this))
    } else {
      /* Texture removed */
      this.__reset()
    }
  },

  /*=============================================
  =            varidation for texure            =
  =============================================*/

  __validateSrc (src, cb) {

    /* check if src is a url */
    const url = parseUrl(src)
    if (url) {
      this.__getImageSrc(url, cb)
      return
    }

    let message

    /* check if src is a query selector */
    const el = this.__validateAndGetQuerySelector(src)
    if (!el || typeof el !== 'object') { return }
    if (el.error) {
      message = el.error
    }
    else {
      const tagName = el.tagName.toLowerCase()
      if (tagName === 'video') {
        src = el.src
        message = 'For video, please use `aframe-video-shader`'
      }
      else if (tagName === 'img') {
        this.__getImageSrc(el.src, cb)
        return
      }
      else {
        message = `For <${tagName}> element, please use \`aframe-html-shader\``
      }
    }

    /* if there is message, create error data */
    if (message) {
      const srcData = gifData[src]
      const errData = createError(message, src)
      /* callbacks */
      if (srcData && srcData.callbacks) {
        srcData.callbacks.forEach(cb => cb(errData))
      }
      else {
        cb(errData)
      }
      /* overwrite */
      gifData[src] = errData
    }

  },

  /**
   * Validate src is a valid image url
   * @param  {string} src - url that will be tested
   * @param  {function} cb - callback with the test result
   */
  __getImageSrc (src, cb) {

    /* if src is same as previous, ignore this */
    if (src === this.__textureSrc) { return }

    /* check if we already get the srcData */
    let srcData = gifData[src]
    if (!srcData || !srcData.callbacks) {
      /* create callback */
      srcData = gifData[src] = { callbacks: [] }
      srcData.callbacks.push(cb)
    }
    else if (srcData.src) {
      cb(srcData)
      return
    }
    else if (srcData.callbacks) {
      /* add callback */
      srcData.callbacks.push(cb)
      return
    }
    const tester = new Image()
    tester.crossOrigin = 'Anonymous'
    tester.addEventListener('load', e => {
      /* check if it is gif */
      this.__getUnit8Array(src, arr => {
        if (!arr) {
          onError('This is not gif. Please use `shader:flat` instead')
          return
        }
        /* parse data */
        parseGIF(arr, (times, cnt, frames) => {
          /* store data */
          const newData = { status: 'success', src: src, times: times, cnt: cnt, frames: frames, timestamp: Date.now() }
          /* callbacks */
          if (srcData.callbacks) {
            srcData.callbacks.forEach(cb => cb(newData))
            /* overwrite */
            gifData[src] = newData
          }
        }, (err) => onError(err))
      })
    })
    tester.addEventListener('error', e => onError('Could be the following issue\n - Not Image\n - Not Found\n - Server Error\n - Cross-Origin Issue'))
    function onError(message) {
      /* create error data */
      const errData = createError(message, src)
      /* callbacks */
      if (srcData.callbacks) {
        srcData.callbacks.forEach(cb => cb(errData))
        /* overwrite */
        gifData[src] = errData
      }
    }
    tester.src = src
  },

  /**
   *
   * get mine type
   *
   */
  __getUnit8Array(src, cb) {
    if (typeof cb !== 'function') { return }

    const xhr = new XMLHttpRequest()
    xhr.open('GET', src)
    xhr.responseType = 'arraybuffer'
    xhr.addEventListener('load', e => {
      const uint8Array = new Uint8Array(e.target.response)
      const arr = (uint8Array).subarray(0, 4)
      // const header = arr.map(value => value.toString(16)).join('')
      let header = ''
      for(let i = 0; i < arr.length; i++) {
        header += arr[i].toString(16)
      }
      if (header === '47494638') { cb(uint8Array) }
      else { cb() }
    })
    xhr.addEventListener('error', e => {
      log(e)
      cb()
    })
    xhr.send()
  },

  /**
   * Query and validate a query selector,
   *
   * @param  {string} selector - DOM selector.
   * @return {object} Selected DOM element | error message object.
   */
  __validateAndGetQuerySelector (selector) {
    try {
      var el = document.querySelector(selector)
      if (!el) {
        return { error: 'No element was found matching the selector' }
      }
      return el
    } catch (e) {  // Capture exception if it's not a valid selector.
      return { error: 'no valid selector' }
    }
  },

  /*================================
  =            playback            =
  ================================*/

  /**
   * Pause gif
   * @public
   */
  pause () {
    log('pause')
    this.__paused = true
  },

  /**
   * Play gif
   * @public
   */
  play () {
    log('play')
    this.__paused = false
  },

  /**
   * Toggle playback. play if paused and pause if played.
   * @public
   */

  togglePlayback () {

    if (this.paused()) { this.play() }
    else { this.pause() }

  },

  /**
   * Return if the playback is paused.
   * @public
   * @return {boolean}
   */
  paused () {
    return this.__paused
  },

  /**
   * Go to next frame
   * @public
   */
  nextFrame () {
    this.__draw()

    /* update next frame time */
    while ((Date.now() - this.__startTime) >= this.__nextFrameTime) {

      this.__nextFrameTime += this.__delayTimes[this.__frameIdx++]
      if ((this.__infinity || this.__loopCnt) && this.__frameCnt <= this.__frameIdx) {
        /* go back to the first */
        this.__frameIdx = 0
      }
    }

  },

  /*==============================
   =            canvas            =
   ==============================*/

  /**
   * clear canvas
   * @private
   */
  __clearCanvas () {
    this.__ctx.clearRect(0, 0, this.__width, this.__height)
    this.__texture.needsUpdate = true
  },

  /**
   * draw
   * @private
   */
  __draw () {
    if(this.__frameIdx != 0){
      const lastFrame = this.__frames[this.__frameIdx -1 ]
      // Disposal method indicates if you should clear or not the background.
      // This flag is represented in binary and is a packed field which can also represent transparency.
      // http://matthewflickinger.com/lab/whatsinagif/animation_and_transparency.asp
      if(lastFrame.disposalMethod == 8 || lastFrame.disposalMethod == 9){
        this.__clearCanvas();
      }
    } else {
      this.__clearCanvas();
    }
    const actualFrame = this.__frames[this.__frameIdx]
    this.__ctx.drawImage(actualFrame, 0, 0, this.__width, this.__height)
    this.__texture.needsUpdate = true
  },

  /*============================
  =            ready            =
  ============================*/

  /**
   * setup gif animation and play if autoplay is true
   * @private
   * @property {string} src - src url
   * @param {array} times - array of time length of each image
   * @param {number} cnt - total counts of gif images
   * @param {array} frames - array of each image
   */
  __ready ({ src, times, cnt, frames }) {
    log('__ready')
    this.__textureSrc = src
    this.__delayTimes = times
    cnt ? this.__loopCnt = cnt : this.__infinity = true
    this.__frames = frames
    this.__frameCnt = times.length
    this.__startTime = Date.now()
    this.__width = THREE.Math.floorPowerOfTwo(frames[0].width)
    this.__height = THREE.Math.floorPowerOfTwo(frames[0].height)
    this.__cnv.width = this.__width
    this.__cnv.height = this.__height
    this.__draw()
    if (this.__autoplay) {
      this.play()
    }
    else {
      this.pause()
    }
  },

  /*=============================
  =            reset            =
  =============================*/
  /**
   * @private
   */
  __reset () {
    this.pause()
    this.__clearCanvas()
    this.__startTime = 0
    this.__nextFrameTime = 0
    this.__frameIdx = 0
    this.__frameCnt = 0
    this.__delayTimes = null
    this.__infinity = false
    this.__loopCnt = 0
    this.__frames = null
    this.__textureSrc = null
  },
})
<commit_msg>Fix for edge case when gif data is invalid by @UXVirtual<commit_after>import { parseGIF } from './lib/gifsparser'

if (typeof AFRAME === 'undefined') {
  throw 'Component attempted to register before AFRAME was available.'
}

/* get util from AFRAME */
const { parseUrl } = AFRAME.utils.srcLoader
const { debug } = AFRAME.utils
// debug.enable('shader:gif:*')
debug.enable('shader:gif:warn')
const warn = debug('shader:gif:warn')
const log = debug('shader:gif:debug')

/* store data so that you won't load same data */
const gifData = {}

/* create error message */
function createError (err, src) {
  return { status: 'error', src: src, message: err, timestamp: Date.now() }
}

AFRAME.registerShader('gif', {

  /**
   * For material component:
   * @see https://github.com/aframevr/aframe/blob/60d198ef8e2bfbc57a13511ae5fca7b62e01691b/src/components/material.js
   * For example of `registerShader`:
   * @see https://github.com/aframevr/aframe/blob/41a50cd5ac65e462120ecc2e5091f5daefe3bd1e/src/shaders/flat.js
   * For MeshBasicMaterial
   * @see http://threejs.org/docs/#Reference/Materials/MeshBasicMaterial
   */
  schema: {

    /* For material */
    color: { type: 'color' },
    fog: { default: true },

    /* For texuture */
    src: { default: null },
    autoplay: { default: true },

  },

  /**
   * Initialize material. Called once.
   * @protected
   */
  init (data) {
    log('init', data)
    log(this.el.components)
    this.__cnv = document.createElement('canvas')
    this.__cnv.width = 2
    this.__cnv.height = 2
    this.__ctx = this.__cnv.getContext('2d')
    this.__texture = new THREE.Texture(this.__cnv) //renders straight from a canvas
    if (data.repeat) {
      this.__texture.wrapS = THREE.RepeatWrapping;
      this.__texture.wrapT = THREE.RepeatWrapping;
      this.__texture.repeat.set( data.repeat.x, data.repeat.y );
    }
    this.__material = {}
    this.__reset()
    this.material = new THREE.MeshBasicMaterial({ map: this.__texture })
    this.el.sceneEl.addBehavior(this)
    return this.material
  },

  /**
   * Update or create material.
   * @param {object|null} oldData
   */
  update (oldData) {
    log('update', oldData)
    this.__updateMaterial(oldData)
    this.__updateTexture(oldData)
    return this.material
  },

  /**
   * Called on each scene tick.
   * @protected
   */
  tick (t) {
    if (!this.__frames || this.paused()) return
    if (Date.now() - this.__startTime >= this.__nextFrameTime) {
      this.nextFrame()
    }
  },

  /*================================
  =            material            =
  ================================*/

  /**
   * Updating existing material.
   * @param {object} data - Material component data.
   */
  __updateMaterial (data) {
    const { material } = this
    const newData = this.__getMaterialData(data)
    Object.keys(newData).forEach(key => {
      material[key] = newData[key]
    })
  },

  /**
   * Builds and normalize material data, normalizing stuff along the way.
   * @param {Object} data - Material data.
   * @return {Object} data - Processed material data.
   */
  __getMaterialData (data) {
    return {
      fog: data.fog,
      color: new THREE.Color(data.color),
    }
  },

  /*==============================
  =            texure            =
  ==============================*/

  /**
   * set texure
   * @private
   * @param {Object} data
   * @property {string} status - success / error
   * @property {string} src - src url
   * @property {array} times - array of time length of each image
   * @property {number} cnt - total counts of gif images
   * @property {array} frames - array of each image
   * @property {Date} timestamp - created at the texure
   */

  __setTexure (data) {
    log('__setTexure', data)
    if (data.status === 'error') {
      warn(`Error: ${data.message}\nsrc: ${data.src}`)
      this.__reset()
    }
    else if (data.status === 'success' && data.src !== this.__textureSrc) {
      this.__reset()
      /* Texture added or changed */
      this.__ready(data)
    }
  },

  /**
   * Update or create texure.
   * @param {Object} data - Material component data.
   */
  __updateTexture (data) {
    const { src, autoplay } = data

    /* autoplay */
    if (typeof autoplay === 'boolean') {
      this.__autoplay = autoplay
    }
    else if (typeof autoplay === 'undefined') {
      this.__autoplay = true
    }
    if (this.__autoplay && this.__frames) { this.play() }

    /* src */
    if (src) {
      this.__validateSrc(src, this.__setTexure.bind(this))
    } else {
      /* Texture removed */
      this.__reset()
    }
  },

  /*=============================================
  =            varidation for texure            =
  =============================================*/

  __validateSrc (src, cb) {

    /* check if src is a url */
    const url = parseUrl(src)
    if (url) {
      this.__getImageSrc(url, cb)
      return
    }

    let message

    /* check if src is a query selector */
    const el = this.__validateAndGetQuerySelector(src)
    if (!el || typeof el !== 'object') { return }
    if (el.error) {
      message = el.error
    }
    else {
      const tagName = el.tagName.toLowerCase()
      if (tagName === 'video') {
        src = el.src
        message = 'For video, please use `aframe-video-shader`'
      }
      else if (tagName === 'img') {
        this.__getImageSrc(el.src, cb)
        return
      }
      else {
        message = `For <${tagName}> element, please use \`aframe-html-shader\``
      }
    }

    /* if there is message, create error data */
    if (message) {
      const srcData = gifData[src]
      const errData = createError(message, src)
      /* callbacks */
      if (srcData && srcData.callbacks) {
        srcData.callbacks.forEach(cb => cb(errData))
      }
      else {
        cb(errData)
      }
      /* overwrite */
      gifData[src] = errData
    }

  },

  /**
   * Validate src is a valid image url
   * @param  {string} src - url that will be tested
   * @param  {function} cb - callback with the test result
   */
  __getImageSrc (src, cb) {

    /* if src is same as previous, ignore this */
    if (src === this.__textureSrc) { return }

    /* check if we already get the srcData */
    let srcData = gifData[src]
    if (!srcData || !srcData.callbacks) {
      /* create callback */
      srcData = gifData[src] = { callbacks: [] }
      srcData.callbacks.push(cb)
    }
    else if (srcData.src) {
      cb(srcData)
      return
    }
    else if (srcData.callbacks) {
      /* add callback */
      srcData.callbacks.push(cb)
      return
    }
    const tester = new Image()
    tester.crossOrigin = 'Anonymous'
    tester.addEventListener('load', e => {
      /* check if it is gif */
      this.__getUnit8Array(src, arr => {
        if (!arr) {
          onError('This is not gif. Please use `shader:flat` instead')
          return
        }
        /* parse data */
        parseGIF(arr, (times, cnt, frames) => {
          /* store data */
          const newData = { status: 'success', src: src, times: times, cnt: cnt, frames: frames, timestamp: Date.now() }
          /* callbacks */
          if (srcData.callbacks) {
            srcData.callbacks.forEach(cb => cb(newData))
            /* overwrite */
            gifData[src] = newData
          }
        }, (err) => onError(err))
      })
    })
    tester.addEventListener('error', e => onError('Could be the following issue\n - Not Image\n - Not Found\n - Server Error\n - Cross-Origin Issue'))
    function onError(message) {
      /* create error data */
      const errData = createError(message, src)
      /* callbacks */
      if (srcData.callbacks) {
        srcData.callbacks.forEach(cb => cb(errData))
        /* overwrite */
        gifData[src] = errData
      }
    }
    tester.src = src
  },

  /**
   *
   * get mine type
   *
   */
  __getUnit8Array(src, cb) {
    if (typeof cb !== 'function') { return }

    const xhr = new XMLHttpRequest()
    xhr.open('GET', src)
    xhr.responseType = 'arraybuffer'
    xhr.addEventListener('load', e => {
      const uint8Array = new Uint8Array(e.target.response)
      const arr = (uint8Array).subarray(0, 4)
      // const header = arr.map(value => value.toString(16)).join('')
      let header = ''
      for(let i = 0; i < arr.length; i++) {
        header += arr[i].toString(16)
      }
      if (header === '47494638') { cb(uint8Array) }
      else { cb() }
    })
    xhr.addEventListener('error', e => {
      log(e)
      cb()
    })
    xhr.send()
  },

  /**
   * Query and validate a query selector,
   *
   * @param  {string} selector - DOM selector.
   * @return {object} Selected DOM element | error message object.
   */
  __validateAndGetQuerySelector (selector) {
    try {
      var el = document.querySelector(selector)
      if (!el) {
        return { error: 'No element was found matching the selector' }
      }
      return el
    } catch (e) {  // Capture exception if it's not a valid selector.
      return { error: 'no valid selector' }
    }
  },

  /*================================
  =            playback            =
  ================================*/

  /**
   * Pause gif
   * @public
   */
  pause () {
    log('pause')
    this.__paused = true
  },

  /**
   * Play gif
   * @public
   */
  play () {
    log('play')
    this.__paused = false
  },

  /**
   * Toggle playback. play if paused and pause if played.
   * @public
   */

  togglePlayback () {

    if (this.paused()) { this.play() }
    else { this.pause() }

  },

  /**
   * Return if the playback is paused.
   * @public
   * @return {boolean}
   */
  paused () {
    return this.__paused
  },

  /**
   * Go to next frame
   * @public
   */
  nextFrame () {
    this.__draw()

    /* update next frame time */
    while ((Date.now() - this.__startTime) >= this.__nextFrameTime) {

      this.__nextFrameTime += this.__delayTimes[this.__frameIdx++]
      if ((this.__infinity || this.__loopCnt) && this.__frameCnt <= this.__frameIdx) {
        /* go back to the first */
        this.__frameIdx = 0
      }
    }

  },

  /*==============================
   =            canvas            =
   ==============================*/

  /**
   * clear canvas
   * @private
   */
  __clearCanvas () {
    this.__ctx.clearRect(0, 0, this.__width, this.__height)
    this.__texture.needsUpdate = true
  },

  /**
   * draw
   * @private
   */
  __draw () {
    if(this.__frameIdx != 0){
      const lastFrame = this.__frames[this.__frameIdx -1 ]
      // Disposal method indicates if you should clear or not the background.
      // This flag is represented in binary and is a packed field which can also represent transparency.
      // http://matthewflickinger.com/lab/whatsinagif/animation_and_transparency.asp
      if(lastFrame.disposalMethod == 8 || lastFrame.disposalMethod == 9){
        this.__clearCanvas();
      }
    } else {
      this.__clearCanvas();
    }
    const actualFrame = this.__frames[this.__frameIdx]
    if(typeof actualFrame !== 'undefined') {
      this.__ctx.drawImage(actualFrame, 0, 0, this.__width, this.__height)
      this.__texture.needsUpdate = true
    }
  },

  /*============================
  =            ready            =
  ============================*/

  /**
   * setup gif animation and play if autoplay is true
   * @private
   * @property {string} src - src url
   * @param {array} times - array of time length of each image
   * @param {number} cnt - total counts of gif images
   * @param {array} frames - array of each image
   */
  __ready ({ src, times, cnt, frames }) {
    log('__ready')
    this.__textureSrc = src
    this.__delayTimes = times
    cnt ? this.__loopCnt = cnt : this.__infinity = true
    this.__frames = frames
    this.__frameCnt = times.length
    this.__startTime = Date.now()
    this.__width = THREE.Math.floorPowerOfTwo(frames[0].width)
    this.__height = THREE.Math.floorPowerOfTwo(frames[0].height)
    this.__cnv.width = this.__width
    this.__cnv.height = this.__height
    this.__draw()
    if (this.__autoplay) {
      this.play()
    }
    else {
      this.pause()
    }
  },

  /*=============================
  =            reset            =
  =============================*/
  /**
   * @private
   */
  __reset () {
    this.pause()
    this.__clearCanvas()
    this.__startTime = 0
    this.__nextFrameTime = 0
    this.__frameIdx = 0
    this.__frameCnt = 0
    this.__delayTimes = null
    this.__infinity = false
    this.__loopCnt = 0
    this.__frames = null
    this.__textureSrc = null
  },
})
<|endoftext|>


<commit_before>const emojiRegex = require('emoji-regex');

const emojiMap = require('./data/data');

const unicodeMap = Object.keys(emojiMap).reduce((prev, curr) => {
  const entry = emojiMap[curr];
  prev[entry.unicode] = { image: entry.image, shortname: curr };
  return prev;
}, {});

class EmojiShort {
  constructor (cdnUrl, klass = 'emojishort') {
    this.cdnUrl = cdnUrl;
    this.klass = klass;

    this.shortnames = Object.keys(emojiMap).join('|').replace(/\+/g, '\\+');
    this.shortnamePattern = new RegExp(this.shortnames, 'gi');

    this._replaceShortNameWithImgTag = this._replaceShortNameWithImgTag.bind(this);
    this._replaceShortNameWithUnicode = this._replaceShortNameWithUnicode.bind(this);
    this._replaceUnicodeWithImgTag = this._replaceUnicodeWithImgTag.bind(this);

    this.toImage = this.toImage.bind(this);
    this.toUnicode = this.toUnicode.bind(this);
    this.unicodeToImage = this.unicodeToImage.bind(this);
  }

  toImage (string) {
    return string.replace(this.shortnamePattern, this._replaceShortNameWithImgTag);
  }

  toUnicode (string) {
    return string.replace(this.shortnamePattern, this._replaceShortNameWithUnicode);
  }

  unicodeToImage (string) {
    return string.replace(emojiRegex(), this._replaceUnicodeWithImgTag);
  }

  _replaceShortNameWithImgTag (shortname) {
    const image = emojiMap[shortname].image;
    if (!image) return shortname;
    return `<img class="${this.klass}" src="${this.cdnUrl}${image}" alt="${shortname}" title="${shortname}" />`;
  }

  _replaceShortNameWithUnicode (shortname) {
    return emojiMap[shortname].unicode;
  }

  _replaceUnicodeWithImgTag (unicode) {
    const emoji = unicodeMap[unicode];
    if (!emoji) return unicode;
    return `<img class="${this.klass}" src="${this.cdnUrl}${emoji.image}" alt="${emoji.shortname}" title="${emoji.shortname}" />`;
  }
}

module.exports = EmojiShort;
<commit_msg>prevet breaking in _replaceShortNameWithImgTag<commit_after>const emojiRegex = require('emoji-regex');

const emojiMap = require('./data/data');

const unicodeMap = Object.keys(emojiMap).reduce((prev, curr) => {
  const entry = emojiMap[curr];
  prev[entry.unicode] = { image: entry.image, shortname: curr };
  return prev;
}, {});

class EmojiShort {
  constructor (cdnUrl, klass = 'emojishort') {
    this.cdnUrl = cdnUrl;
    this.klass = klass;

    this.shortnames = Object.keys(emojiMap).join('|').replace(/\+/g, '\\+');
    this.shortnamePattern = new RegExp(this.shortnames, 'gi');

    this._replaceShortNameWithImgTag = this._replaceShortNameWithImgTag.bind(this);
    this._replaceShortNameWithUnicode = this._replaceShortNameWithUnicode.bind(this);
    this._replaceUnicodeWithImgTag = this._replaceUnicodeWithImgTag.bind(this);

    this.toImage = this.toImage.bind(this);
    this.toUnicode = this.toUnicode.bind(this);
    this.unicodeToImage = this.unicodeToImage.bind(this);
  }

  toImage (string) {
    return string.replace(this.shortnamePattern, this._replaceShortNameWithImgTag);
  }

  toUnicode (string) {
    return string.replace(this.shortnamePattern, this._replaceShortNameWithUnicode);
  }

  unicodeToImage (string) {
    return string.replace(emojiRegex(), this._replaceUnicodeWithImgTag);
  }

  _replaceShortNameWithImgTag (shortname) {
    const emoji = emojiMap[shortname];
    if (!emoji) return shortname;
    return `<img class="${this.klass}" src="${this.cdnUrl}${emoji.image}" alt="${shortname}" title="${shortname}" />`;
  }

  _replaceShortNameWithUnicode (shortname) {
    return emojiMap[shortname].unicode;
  }

  _replaceUnicodeWithImgTag (unicode) {
    const emoji = unicodeMap[unicode];
    if (!emoji) return unicode;
    return `<img class="${this.klass}" src="${this.cdnUrl}${emoji.image}" alt="${emoji.shortname}" title="${emoji.shortname}" />`;
  }
}

module.exports = EmojiShort;
<|endoftext|>


<commit_before>var express = require('express')
var app = express()

app.get('/', function (req, res) {
  res.send('Hello World!')
})

app.listen(3000, function () {
  console.log('Example app listening on port 3000!')
})
<commit_msg>Removed index.js in favor of app.js<commit_after><|endoftext|>


<commit_before>'use strict';

var mongoose = require('mongoose');

var errorRegex = /index: (.+) dup key:/;
var indexesCache = {};

/**
 * Check if the given error is a unique error.
 *
 * @param {Object} err Error to test.
 * @return {bool} True if and only if it is an unique error.
 */
function isUniqueError(err) {
    return err &&
        (err.name === 'BulkWriteError' || err.name === 'MongoError') &&
        (err.code === 11000 || err.code === 11001);
}

/**
 * Search for the value matching a path in dotted notation
 * inside an object.
 *
 * @example
 * - getValueByPath({a: {b: 2}}, 'a.b') -> 2
 * - getValueByPath({}, 'a.b') -> undefined
 * @param {Object} obj Nested object to search.
 * @param {string} path Path of the value to search for.
 * @return {*} Matching value, or undefined if none.
 */
function getValueByPath(obj, path) {
    var segments = path.split('.');
    var result = obj;

    for (
        var i = 0;
        i < segments.length
            && result !== null
            && result !== undefined;
        ++i
    ) {
        result = result[segments[i]];
    }

    return result;
}

/**
 * Retrieve index information using collection#indexInformation
 * or previously cached data.
 *
 * @param {Object} collection Mongoose collection.
 * @return {Promise} Resolved with index information data.
 */
function getIndexes(collection) {
    return new global.Promise(function (resolve, reject) {
        if (indexesCache[collection.name]) {
            resolve(indexesCache[collection.name]);
            return;
        }

        collection.indexInformation(function (dbErr, indexes) {
            if (dbErr) {
                reject(dbErr);
                return;
            }

            indexesCache[collection.name] = indexes;
            resolve(indexes);
        });
    });
}

/**
 * Beautify an E11000 or 11001 (unique constraint fail) Mongo error
 * by turning it into a validation error
 *
 * @param {MongoError} err Error to process
 * @param {Collection} collection Mongoose collection.
 * @param {Object} values Hashmap containing data about duplicated values
 * @param {Object} messages Map fields to unique error messages
 * @param {String} defaultMessage Default message formatter string
 * @return {Promise.<ValidationError>} Beautified error message
 */
function beautify(error, collection, values, messages, defaultMessage) {
    // Try to recover the list of duplicated fields
    var onSuberrors = global.Promise.resolve({});

    // Extract the failed duplicate index's name from the
    // from the error message (with a hacky regex)
    var matches = errorRegex.exec(error.message);

    if (matches) {
        var indexName = matches[1];

        // Retrieve that index's list of fields
        onSuberrors = getIndexes(collection).then(function (indexes) {
            var suberrors = {};

            // Create a suberror per duplicated field
            if (indexName in indexes) {
                indexes[indexName].forEach(function (field) {
                    var path = field[0];
                    var props = {
                        type: 'unique',
                        path: path,
                        value: getValueByPath(values, path)
                    };

                    if (typeof messages[path] === 'string') {
                        props.message = messages[path];
                    } else {
                        props.message = defaultMessage;
                    }

                    suberrors[path] = new mongoose.Error.ValidatorError(props);
                });
            }

            return suberrors;
        });
    }

    return onSuberrors.then(function (suberrors) {
        var beautifiedError = new mongoose.Error.ValidationError();

        beautifiedError.errors = suberrors;
        return beautifiedError;
    });
}

module.exports = function (schema, options) {
    var tree = schema.tree, key, messages = {};

    options = options || {};

    if (!options.defaultMessage) {
        options.defaultMessage = 'Path `{PATH}` ({VALUE}) is not unique.';
    }

    // Fetch error messages defined in the "unique" field,
    // store them for later use and replace them with true
    for (key in tree) {
        if (tree.hasOwnProperty(key)) {
            if (tree[key] && tree[key].unique) {
                messages[key] = tree[key].unique;
                tree[key].unique = true;
            }
        }
    }

    schema._indexes.forEach(function (index) {
        if (index[0] && index[1] && index[1].unique) {
            Object.keys(index[0]).forEach(function (indexKey) {
                messages[indexKey] = index[1].unique;
            });

            index[1].unique = true;
        }
    });

    // Post hook that gets called after any save or update
    // operation and that filters unique errors
    var postHook = function (error, _, next) {
        // Mongoose ≥5 does no longer pass the document as the
        // second argument of 'update' hooks, so we use this instead
        var doc = this;

        // If the next() function is missing, this might be
        // a sign that we are using an outdated Mongoose
        if (typeof next !== 'function') {
            throw new Error(
                'mongoose-beautiful-unique-validation error: ' +
                'The hook was called incorrectly. Double check that ' +
                'you are using mongoose@>=4.5.0; if you need to use ' +
                'an outdated Mongoose version, please install this module ' +
                'in version 4.0.0'
            );
        }

        if (isUniqueError(error)) {
            // Beautify unicity constraint failure errors
            var collection, values;

            if (this.constructor.name == 'Query') {
                collection = this.model.collection;
                values = this._update;

                if ('$set' in values) {
                    values = Object.assign({}, values, values.$set);
                    delete values.$set;
                }
            } else {
                collection = doc.collection;
                values = doc;
            }

            beautify(
                error, collection, values,
                messages, options.defaultMessage
            )
                .then(next)
                .catch(function (beautifyError) {
                    setTimeout(function () {
                        throw new Error(
                            'mongoose-beautiful-unique-validation error: ' +
                            beautifyError.stack
                        );
                    });
                });
        } else {
            // Pass over other errors
            next(error);
        }
    };

    schema.post('save', postHook);
    schema.post('update', postHook);
    schema.post('findOneAndUpdate', postHook);
};
<commit_msg>Fix custom message detection in nested schemas<commit_after>'use strict';

var mongoose = require('mongoose');

var errorRegex = /index: (.+) dup key:/;
var indexesCache = {};

/**
 * Check if the given error is a unique error.
 *
 * @param {Object} err Error to test.
 * @return {bool} True if and only if it is an unique error.
 */
function isUniqueError(err) {
    return err &&
        (err.name === 'BulkWriteError' || err.name === 'MongoError') &&
        (err.code === 11000 || err.code === 11001);
}

/**
 * Search for the value matching a path in dotted notation
 * inside an object.
 *
 * @example
 * - getValueByPath({a: {b: 2}}, 'a.b') -> 2
 * - getValueByPath({}, 'a.b') -> undefined
 * @param {Object} obj Nested object to search.
 * @param {string} path Path of the value to search for.
 * @return {*} Matching value, or undefined if none.
 */
function getValueByPath(obj, path) {
    var segments = path.split('.');
    var result = obj;

    for (
        var i = 0;
        i < segments.length
            && result !== null
            && result !== undefined;
        ++i
    ) {
        result = result[segments[i]];
    }

    return result;
}

/**
 * Recursively collect all messages inside a schema tree and
 * change string values to `true`.
 *
 * @param {Object} tree Schema tree to update and collect from.
 * @return {Object} Map of collected messages.
 */
function collectMessages(tree) {
    var result = {};

    for (var key in tree) {
        if (tree.hasOwnProperty(key)) {
            if (
                typeof tree[key] === 'object'
                && tree[key] !== null
            ) {
                if (typeof tree[key].unique === 'string') {
                    // Schema property that has a custom
                    // unique message
                    result[key] = tree[key].unique;
                    tree[key].unique = true;
                } else {
                    // Nested schema
                    result[key] = collectMessages(tree[key]);
                }
            }
        }
    }

    return result;
}

/**
 * Retrieve index information using collection#indexInformation
 * or previously cached data.
 *
 * @param {Object} collection Mongoose collection.
 * @return {Promise} Resolved with index information data.
 */
function getIndexes(collection) {
    return new global.Promise(function (resolve, reject) {
        if (indexesCache[collection.name]) {
            resolve(indexesCache[collection.name]);
            return;
        }

        collection.indexInformation(function (dbErr, indexes) {
            if (dbErr) {
                reject(dbErr);
                return;
            }

            indexesCache[collection.name] = indexes;
            resolve(indexes);
        });
    });
}

/**
 * Beautify an E11000 or 11001 (unique constraint fail) Mongo error
 * by turning it into a validation error
 *
 * @param {MongoError} err Error to process
 * @param {Collection} collection Mongoose collection.
 * @param {Object} values Hashmap containing data about duplicated values
 * @param {Object} messages Map fields to unique error messages
 * @param {String} defaultMessage Default message formatter string
 * @return {Promise.<ValidationError>} Beautified error message
 */
function beautify(error, collection, values, messages, defaultMessage) {
    // Try to recover the list of duplicated fields
    var onSuberrors = global.Promise.resolve({});

    // Extract the failed duplicate index's name from the
    // from the error message (with a hacky regex)
    var matches = errorRegex.exec(error.message);

    if (matches) {
        var indexName = matches[1];

        // Retrieve that index's list of fields
        onSuberrors = getIndexes(collection).then(function (indexes) {
            var suberrors = {};

            // Create a suberror per duplicated field
            if (indexName in indexes) {
                indexes[indexName].forEach(function (field) {
                    var path = field[0];
                    var customMessage = getValueByPath(messages, path);
                    var props = {
                        type: 'unique',
                        path: path,
                        value: getValueByPath(values, path),
                        message: typeof customMessage === 'string'
                            ? customMessage : defaultMessage
                    };

                    suberrors[path] = new mongoose.Error.ValidatorError(props);
                });
            }

            return suberrors;
        });
    }

    return onSuberrors.then(function (suberrors) {
        var beautifiedError = new mongoose.Error.ValidationError();

        beautifiedError.errors = suberrors;
        return beautifiedError;
    });
}

module.exports = function (schema, options) {
    options = options || {};

    if (!options.defaultMessage) {
        options.defaultMessage = 'Path `{PATH}` ({VALUE}) is not unique.';
    }

    // Fetch error messages defined in the "unique" field,
    // store them for later use and replace them with true
    var tree = schema.tree;
    var messages = collectMessages(tree);

    schema._indexes.forEach(function (index) {
        if (index[0] && index[1] && index[1].unique) {
            Object.keys(index[0]).forEach(function (indexKey) {
                messages[indexKey] = index[1].unique;
            });

            index[1].unique = true;
        }
    });

    // Post hook that gets called after any save or update
    // operation and that filters unique errors
    var postHook = function (error, _, next) {
        // Mongoose ≥5 does no longer pass the document as the
        // second argument of 'update' hooks, so we use this instead
        var doc = this;

        // If the next() function is missing, this might be
        // a sign that we are using an outdated Mongoose
        if (typeof next !== 'function') {
            throw new Error(
                'mongoose-beautiful-unique-validation error: ' +
                'The hook was called incorrectly. Double check that ' +
                'you are using mongoose@>=4.5.0; if you need to use ' +
                'an outdated Mongoose version, please install this module ' +
                'in version 4.0.0'
            );
        }

        if (isUniqueError(error)) {
            // Beautify unicity constraint failure errors
            var collection, values;

            if (this.constructor.name == 'Query') {
                collection = this.model.collection;
                values = this._update;

                if ('$set' in values) {
                    values = Object.assign({}, values, values.$set);
                    delete values.$set;
                }
            } else {
                collection = doc.collection;
                values = doc;
            }

            beautify(
                error, collection, values,
                messages, options.defaultMessage
            )
                .then(next)
                .catch(function (beautifyError) {
                    setTimeout(function () {
                        throw new Error(
                            'mongoose-beautiful-unique-validation error: ' +
                            beautifyError.stack
                        );
                    });
                });
        } else {
            // Pass over other errors
            next(error);
        }
    };

    schema.post('save', postHook);
    schema.post('update', postHook);
    schema.post('findOneAndUpdate', postHook);
};
<|endoftext|>


<commit_before>var defaultIsMergeableObject = require('is-mergeable-object')

function emptyTarget(val) {
	return Array.isArray(val) ? [] : {}
}

function cloneUnlessOtherwiseSpecified(value, options) {
	return (options.clone !== false && options.isMergeableObject(value))
		? deepmerge(emptyTarget(value), value, options)
		: value
}

function defaultArrayMerge(target, source, options) {
	return target.concat(source).map(function(element) {
		return cloneUnlessOtherwiseSpecified(element, options)
	})
}

function getMergeFunction(key, options) {
	if (!options.customMerge) {
		return deepmerge
	}
	var customMerge = options.customMerge(key)
	return typeof customMerge === 'function' ? customMerge : deepmerge
}

function getKeys(target) {
    return Object.getOwnPropertySymbols ?
        Object.keys(target).concat(Object.getOwnPropertySymbols(target))
        : Object.keys(target)
}

function mergeObject(target, source, options) {
	var destination = {}
	if (options.isMergeableObject(target)) {
		getKeys(target).forEach(function(key) {
			destination[key] = cloneUnlessOtherwiseSpecified(target[key], options)
		})
	}
	getKeys(source).forEach(function(key) {
		if (!options.isMergeableObject(source[key]) || !target[key]) {
			destination[key] = cloneUnlessOtherwiseSpecified(source[key], options)
		} else {
			destination[key] = getMergeFunction(key, options)(target[key], source[key], options)
		}
	})
	return destination
}

function deepmerge(target, source, options) {
	options = options || {}
	options.arrayMerge = options.arrayMerge || defaultArrayMerge
	options.isMergeableObject = options.isMergeableObject || defaultIsMergeableObject

	var sourceIsArray = Array.isArray(source)
	var targetIsArray = Array.isArray(target)
	var sourceAndTargetTypesMatch = sourceIsArray === targetIsArray

	if (!sourceAndTargetTypesMatch) {
		return cloneUnlessOtherwiseSpecified(source, options)
	} else if (sourceIsArray) {
		return options.arrayMerge(target, source, options)
	} else {
		return mergeObject(target, source, options)
	}
}

deepmerge.all = function deepmergeAll(array, options) {
	if (!Array.isArray(array)) {
		throw new Error('first argument should be an array')
	}

	return array.reduce(function(prev, next) {
		return deepmerge(prev, next, options)
	}, {})
}

module.exports = deepmerge
<commit_msg>Limit to enumerable symbols<commit_after>var defaultIsMergeableObject = require('is-mergeable-object')

function emptyTarget(val) {
	return Array.isArray(val) ? [] : {}
}

function cloneUnlessOtherwiseSpecified(value, options) {
	return (options.clone !== false && options.isMergeableObject(value))
		? deepmerge(emptyTarget(value), value, options)
		: value
}

function defaultArrayMerge(target, source, options) {
	return target.concat(source).map(function(element) {
		return cloneUnlessOtherwiseSpecified(element, options)
	})
}

function getMergeFunction(key, options) {
	if (!options.customMerge) {
		return deepmerge
	}
	var customMerge = options.customMerge(key)
	return typeof customMerge === 'function' ? customMerge : deepmerge
}

function getEnumerableOwnPropertySymbols(target) {
    return Object.getOwnPropertySymbols ?
        Object.getOwnPropertySymbols(target).filter(function(symbol) {
            return target.propertyIsEnumerable(symbol)
        })
        : [];
}

function getKeys(target) {
    return Object.keys(target).concat(getEnumerableOwnPropertySymbols(target))
}

function mergeObject(target, source, options) {
	var destination = {}
	if (options.isMergeableObject(target)) {
		getKeys(target).forEach(function(key) {
			destination[key] = cloneUnlessOtherwiseSpecified(target[key], options)
		})
	}
	getKeys(source).forEach(function(key) {
		if (!options.isMergeableObject(source[key]) || !target[key]) {
			destination[key] = cloneUnlessOtherwiseSpecified(source[key], options)
		} else {
			destination[key] = getMergeFunction(key, options)(target[key], source[key], options)
		}
	})
	return destination
}

function deepmerge(target, source, options) {
	options = options || {}
	options.arrayMerge = options.arrayMerge || defaultArrayMerge
	options.isMergeableObject = options.isMergeableObject || defaultIsMergeableObject

	var sourceIsArray = Array.isArray(source)
	var targetIsArray = Array.isArray(target)
	var sourceAndTargetTypesMatch = sourceIsArray === targetIsArray

	if (!sourceAndTargetTypesMatch) {
		return cloneUnlessOtherwiseSpecified(source, options)
	} else if (sourceIsArray) {
		return options.arrayMerge(target, source, options)
	} else {
		return mergeObject(target, source, options)
	}
}

deepmerge.all = function deepmergeAll(array, options) {
	if (!Array.isArray(array)) {
		throw new Error('first argument should be an array')
	}

	return array.reduce(function(prev, next) {
		return deepmerge(prev, next, options)
	}, {})
}

module.exports = deepmerge
<|endoftext|>


<commit_before>// TODO: Fix neg problem on page.
const express = require('express')
const http = require('http')
const spdy = require('spdy')
const fs = require('fs')
const url = require('url')
const mongoose = require('mongoose')
mongoose.Promise = global.Promise
const redis = require('redis')
const redislock = require('redis-lock')
const compression = require('compression')
const path = require('path')
const elasticsearch = require('elasticsearch')
const WebSocket = require('ws')
let pages

process.on('unhandledRejection', (reason, promise) => {
  console.error('Unhandled promise rejection: ' + reason)
  console.error(reason.stack)
})

// This file will be launched with launcher.js.

var maowtm = function (config) {
  var app = express()
  config = config || {}
  this.config = config
  this._mongodb = config.db || 'mongodb://127.0.0.1'
  this.db = mongoose.createConnection()
  this.es = new elasticsearch.Client({
    host: config.elasticsearch || '127.0.0.1'
  })
  this._redis = config.redis || '127.0.0.1'
  this.redis = redis.createClient({
    host: this._redis
  })
  this.lock = redislock(this.redis)
  // this._listen can be a array of address.
  this._listen = config.listen || []
  this._ssl = config.ssl
  this.acme = config.acme || null
  this.destory = false
  this.mockSecure = config.mockSecure || false
  this.noInitImages = config.noInitImages || false
  this.apps = config.apps || []
  this.rbs = config.rbs || []
  this.registeredWSHandlers = [] // { shouldHandle, onConnection }
  app.mockSecure = this.mockSecure
  var callback = config.callback
  function fail (error) {
    if (this.destory) return
    if (callback) {
      this.destory = true
      if (this.db) {
        this.db.close()
      }
      callback(error, null, function () {})
    } else {
      console.error('Error initalizing server:')
      console.error(error)
      process.exit(2)
    }
  }
  this.es.ping({
    requestTimeout: 10000
  }, err => {
    if (err) {
      fail(err)
    }
  })
  this.db.openUri(this._mongodb).catch(err => fail(err))
  if ((!Array.isArray(this._listen) || this._listen.length > 0) && !this.mockSecure && !(this._ssl && this._ssl.cert && this._ssl.key)) {
    fail(new Error('No SSL certificate provided'))
    return
  }
  var _this = this

  this.db.on('error', function (err) {
    fail(err)
  })
  this.db.on('open', function () {
    app.use(function (req, res, next) {
      console.log(`${req.method.toUpperCase()} ${req.protocol}://${req.hostname}${req.path} from ${req.ip}`)
      next()
    })

    pages = require('./pages')(this.db)

    app.use(compression())
    app.use('/.well-known/', express.static(path.join(__dirname, '.well-known')))
    app.use(function (req, res, next) {
      if (!req.hostname) {
        res.status(404)
        res.end()
        return
      }
      if (!(req.secure || app.mockSecure)) {
        res.redirect(302, 'https://' + req.hostname + req.originalUrl)
      } else {
        res.set('Strict-Transport-Security', 'max-age=155520000; includeSubdomains; preload')
        res.set('X-XSS-Protection', '1; mode=block')
        res.set('X-Frame-Options', 'sameorigin')
        res.set('X-Content-Type-Options', 'nosniff')
        res.set('Expect-CT', 'max-age=155520000, report-uri="https://maowtm.report-uri.com/r/d/ct/enforce"')
        next()
      }
    })

    app.use(require('./subs/static')(_this.db, _this.lock))

    // Add trailing / for all GET for all router below. ( i.e. Not including static and img )
    app.use(function (req, res, next) {
      if (req.hostname.match(/^(img|static|file)/) || req.path.match(/\.[^/\\\s%]+$/)) {
        next()
        return
      }
      var prasedUrl = url.parse(req.originalUrl, false)
      if (req.method === 'GET' && prasedUrl.pathname.substr(-1) !== '/') {
        prasedUrl.pathname += '/'
        res.redirect(302, url.format(prasedUrl))
      } else {
        next()
      }
    })

    app.use(require('./subs/main')(_this.db, _this.lock))
    app.use(require('./subs/rb')(_this.db, _this.lock, _this.rbs))
    app.use(require('./subs/mww')(_this.db, _this.lock))
    app.use(require('./subs/ncicgg')(_this.db, _this.lock))

    app.use(function (req, res, next) {
      if (req.method.toUpperCase() !== 'GET' || req.hostname.toLowerCase() !== 'schsrch.xyz') return void next()
      if (/\/resources\/[0-9a-f]+-clientrender.js/.test(req.path)) {
        res.status(200)
        res.type('js')
        res.send(`navigator.serviceWorker.getRegistration('/').then(reg => reg.unregister(), err => Promise.resolve()).then(() => { window.location = 'https://paper.sc' })`)
      } else {
        next()
      }
    })
    app.use(function (req, res, next) {
      if (req.method.toUpperCase() !== 'GET' || req.hostname.toLowerCase() !== 'schsrch.xyz') return void next()
      if (req.query.__uncache) return void next()
      if (req.path === '/sw.js') return void next()
      if (req.path.startsWith('/resources/')) return void next()
      res.redirect(301, 'https://paper.sc' + req.originalUrl)
    })

    _this.apps.forEach(it => {
      let route = it.init({
        mongodb: _this.db,
        elasticsearch: _this.es,
        addWSHandler: function (hnd) {
          _this.registeredWSHandlers.push(hnd)
        }
      })
      app.use(function (req, res, next) {
        if (it.hostname === req.hostname) {
          route(req, res, next)
        } else {
          next()
        }
      })
    })

    app.use(require('./subs/misc')(_this.db, _this.lock))

    let nodeenv = process.env.NODE_ENV || ''
    app.use(function (err, req, res, next) {
      res.status(500)
      let pageObj = { err, req }
      if (typeof err !== 'string') {
        pageObj.err = err.message
        if (err.stack) {
          pageObj.stack = err.stack.replace(new RegExp(path.join(__dirname, '..').replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1'), 'g'), '...') + // eslint-disable-line no-useless-escape
            `\nServer running in NODE_ENV=${nodeenv}\nTime: ${Date.now() / 1000}`
        }
      }
      res.send(pages.error(pageObj))
      console.error(err)
    })

    function doSetupServer () {
      return new Promise((resolve, reject) => {
        const httpsopts = (_this._ssl ? {
          key: fs.readFileSync(_this._ssl.key),
          cert: fs.readFileSync(_this._ssl.cert)
        } : null)
        if (httpsopts && _this._ssl.ca) {
          httpsopts.ca = fs.readFileSync(_this._ssl.ca)
        }
        _this._servers = {
          http: [],
          http2: []
        }
        doAddImage().then(() => {
          if (!Array.isArray(_this._listen)) {
            if (httpsopts) {
              _this._servers.http2.push(spdy.createServer(httpsopts, app).listen(443, _this._listen))
            }
            _this._servers.http.push(http.createServer(app).listen(80, _this._listen))
          } else {
            _this._listen.forEach(function (address) {
              if (httpsopts) {
                _this._servers.http2.push(spdy.createServer(httpsopts, app).listen(443, address))
              }
              _this._servers.http.push(http.createServer(app).listen(80, address))
            })
          }
          resolve()
        }).catch(reject)
      })
    }
    const Image = _this.db.model('image')
    function doAddImage () {
      return new Promise((resolve, reject) => {
        function addImage (name, path) {
          return new Promise((resolve, reject) => {
            let distName = 's/' + name
            let query = Image.findOne({ name: distName }, 'name')
            query.then(img => {
              if (!img) {
                console.log(`Adding image: ${distName}...`)
                fs.readFile(path, { encoding: null }, (err, data) => {
                  if (err) {
                    reject(err)
                    return
                  }
                  Image.addImage(distName, data, err => {
                    if (err) {
                      reject(new Error(`error when trying to add image ${name}: ${err.toString()}`))
                      return
                    }
                    resolve()
                  })
                })
              } else {
                resolve()
              }
            }).catch(err => {
              reject(new Error(`Can't check image cache: ${err}`))
            })
          })
        }
        function addImageInDir (dir) {
          return new Promise((resolve, reject) => {
            let currentDirPath = path.join(__dirname, 'static', 'imgs', dir)
            fs.readdir(currentDirPath, (err, imgFiles) => {
              if (err) {
                reject(err)
                return
              }
              let promises = []
              imgFiles.forEach(name => {
                promises.push(new Promise((resolve, reject) => {
                  let currentName = path.join(dir, name)
                  let currentFileName = path.join(currentDirPath, name)
                  fs.stat(currentFileName, (err, stat) => {
                    if (err) {
                      reject(err)
                      return
                    }
                    if (stat.isFile()) {
                      addImage(currentName, currentFileName).then(resolve, reject)
                    } else if (stat.isDirectory()) {
                      addImageInDir(currentName).then(resolve, reject)
                    } else {
                      resolve()
                    }
                  })
                }))
              })
              Promise.all(promises).then(resolve, reject)
            })
          })
        }
        if (_this.noInitImages) {
          resolve()
        } else {
          addImageInDir('').then(resolve, reject)
        }
      })
    }
    _this.es.ping({}).then(() => doSetupServer().then(() => new Promise((resolve, reject) => {
      _this._servers.http2.forEach(server => {
        let wss = new WebSocket.Server({
          server,
          backlog: 100,
          maxPayload: 1024 * 1024 * 1 // 10MiB
        })
        wss.shouldHandle = function (req) {
          let reqHost = req.headers.host
          if (reqHost.endsWith(':443')) reqHost = reqHost.substr(0, reqHost.length - 4)
          console.log(`WS on ${reqHost}${req.url}`)
          let hnd = _this.registeredWSHandlers.find(hnd => hnd.hostname === reqHost)
          if (!hnd) return false
          return hnd.shouldHandle(req)
        }
        wss.on('connection', function (ws, req) {
          let reqHost = req.headers.host
          if (reqHost.endsWith(':443')) reqHost = reqHost.substr(0, reqHost.length - 4)
          console.log(`WS on ${reqHost}${req.url} connected.`)
          let hnd = _this.registeredWSHandlers.find(hnd => hnd.hostname === reqHost)
          if (!hnd) {
            ws.close(1011, `Unexpected host ${req.headers.host}`)
            console.error(`ws: Unexpected 'connection' event on host ${req.headers.host}`)
          } else {
            hnd.onConnection(ws, req)
          }
          ws.on('close', () => {
            console.log(`CLOSE WS on ${reqHost}${req.url}`)
          })
        })
      })
      resolve()
    })).then(() => {
      console.log('Server ready.')
      if (callback) {
        callback(null, app, function () {
          _this._servers.http2.forEach(function (s) {
            s.close()
          })
          _this._servers.http.forEach(function (s) {
            s.close()
          })
          _this.destory = true
          _this.db.close()
        })
      }
    }), err => fail(err)).catch(err => { fail(err) })
  })
}

module.exports = maowtm
<commit_msg>Forgot to "enforce"<commit_after>// TODO: Fix neg problem on page.
const express = require('express')
const http = require('http')
const spdy = require('spdy')
const fs = require('fs')
const url = require('url')
const mongoose = require('mongoose')
mongoose.Promise = global.Promise
const redis = require('redis')
const redislock = require('redis-lock')
const compression = require('compression')
const path = require('path')
const elasticsearch = require('elasticsearch')
const WebSocket = require('ws')
let pages

process.on('unhandledRejection', (reason, promise) => {
  console.error('Unhandled promise rejection: ' + reason)
  console.error(reason.stack)
})

// This file will be launched with launcher.js.

var maowtm = function (config) {
  var app = express()
  config = config || {}
  this.config = config
  this._mongodb = config.db || 'mongodb://127.0.0.1'
  this.db = mongoose.createConnection()
  this.es = new elasticsearch.Client({
    host: config.elasticsearch || '127.0.0.1'
  })
  this._redis = config.redis || '127.0.0.1'
  this.redis = redis.createClient({
    host: this._redis
  })
  this.lock = redislock(this.redis)
  // this._listen can be a array of address.
  this._listen = config.listen || []
  this._ssl = config.ssl
  this.acme = config.acme || null
  this.destory = false
  this.mockSecure = config.mockSecure || false
  this.noInitImages = config.noInitImages || false
  this.apps = config.apps || []
  this.rbs = config.rbs || []
  this.registeredWSHandlers = [] // { shouldHandle, onConnection }
  app.mockSecure = this.mockSecure
  var callback = config.callback
  function fail (error) {
    if (this.destory) return
    if (callback) {
      this.destory = true
      if (this.db) {
        this.db.close()
      }
      callback(error, null, function () {})
    } else {
      console.error('Error initalizing server:')
      console.error(error)
      process.exit(2)
    }
  }
  this.es.ping({
    requestTimeout: 10000
  }, err => {
    if (err) {
      fail(err)
    }
  })
  this.db.openUri(this._mongodb).catch(err => fail(err))
  if ((!Array.isArray(this._listen) || this._listen.length > 0) && !this.mockSecure && !(this._ssl && this._ssl.cert && this._ssl.key)) {
    fail(new Error('No SSL certificate provided'))
    return
  }
  var _this = this

  this.db.on('error', function (err) {
    fail(err)
  })
  this.db.on('open', function () {
    app.use(function (req, res, next) {
      console.log(`${req.method.toUpperCase()} ${req.protocol}://${req.hostname}${req.path} from ${req.ip}`)
      next()
    })

    pages = require('./pages')(this.db)

    app.use(compression())
    app.use('/.well-known/', express.static(path.join(__dirname, '.well-known')))
    app.use(function (req, res, next) {
      if (!req.hostname) {
        res.status(404)
        res.end()
        return
      }
      if (!(req.secure || app.mockSecure)) {
        res.redirect(302, 'https://' + req.hostname + req.originalUrl)
      } else {
        res.set('Strict-Transport-Security', 'max-age=155520000; includeSubdomains; preload')
        res.set('X-XSS-Protection', '1; mode=block')
        res.set('X-Frame-Options', 'sameorigin')
        res.set('X-Content-Type-Options', 'nosniff')
        res.set('Expect-CT', 'max-age=155520000, enforce, report-uri="https://maowtm.report-uri.com/r/d/ct/enforce"')
        next()
      }
    })

    app.use(require('./subs/static')(_this.db, _this.lock))

    // Add trailing / for all GET for all router below. ( i.e. Not including static and img )
    app.use(function (req, res, next) {
      if (req.hostname.match(/^(img|static|file)/) || req.path.match(/\.[^/\\\s%]+$/)) {
        next()
        return
      }
      var prasedUrl = url.parse(req.originalUrl, false)
      if (req.method === 'GET' && prasedUrl.pathname.substr(-1) !== '/') {
        prasedUrl.pathname += '/'
        res.redirect(302, url.format(prasedUrl))
      } else {
        next()
      }
    })

    app.use(require('./subs/main')(_this.db, _this.lock))
    app.use(require('./subs/rb')(_this.db, _this.lock, _this.rbs))
    app.use(require('./subs/mww')(_this.db, _this.lock))
    app.use(require('./subs/ncicgg')(_this.db, _this.lock))

    app.use(function (req, res, next) {
      if (req.method.toUpperCase() !== 'GET' || req.hostname.toLowerCase() !== 'schsrch.xyz') return void next()
      if (/\/resources\/[0-9a-f]+-clientrender.js/.test(req.path)) {
        res.status(200)
        res.type('js')
        res.send(`navigator.serviceWorker.getRegistration('/').then(reg => reg.unregister(), err => Promise.resolve()).then(() => { window.location = 'https://paper.sc' })`)
      } else {
        next()
      }
    })
    app.use(function (req, res, next) {
      if (req.method.toUpperCase() !== 'GET' || req.hostname.toLowerCase() !== 'schsrch.xyz') return void next()
      if (req.query.__uncache) return void next()
      if (req.path === '/sw.js') return void next()
      if (req.path.startsWith('/resources/')) return void next()
      res.redirect(301, 'https://paper.sc' + req.originalUrl)
    })

    _this.apps.forEach(it => {
      let route = it.init({
        mongodb: _this.db,
        elasticsearch: _this.es,
        addWSHandler: function (hnd) {
          _this.registeredWSHandlers.push(hnd)
        }
      })
      app.use(function (req, res, next) {
        if (it.hostname === req.hostname) {
          route(req, res, next)
        } else {
          next()
        }
      })
    })

    app.use(require('./subs/misc')(_this.db, _this.lock))

    let nodeenv = process.env.NODE_ENV || ''
    app.use(function (err, req, res, next) {
      res.status(500)
      let pageObj = { err, req }
      if (typeof err !== 'string') {
        pageObj.err = err.message
        if (err.stack) {
          pageObj.stack = err.stack.replace(new RegExp(path.join(__dirname, '..').replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1'), 'g'), '...') + // eslint-disable-line no-useless-escape
            `\nServer running in NODE_ENV=${nodeenv}\nTime: ${Date.now() / 1000}`
        }
      }
      res.send(pages.error(pageObj))
      console.error(err)
    })

    function doSetupServer () {
      return new Promise((resolve, reject) => {
        const httpsopts = (_this._ssl ? {
          key: fs.readFileSync(_this._ssl.key),
          cert: fs.readFileSync(_this._ssl.cert)
        } : null)
        if (httpsopts && _this._ssl.ca) {
          httpsopts.ca = fs.readFileSync(_this._ssl.ca)
        }
        _this._servers = {
          http: [],
          http2: []
        }
        doAddImage().then(() => {
          if (!Array.isArray(_this._listen)) {
            if (httpsopts) {
              _this._servers.http2.push(spdy.createServer(httpsopts, app).listen(443, _this._listen))
            }
            _this._servers.http.push(http.createServer(app).listen(80, _this._listen))
          } else {
            _this._listen.forEach(function (address) {
              if (httpsopts) {
                _this._servers.http2.push(spdy.createServer(httpsopts, app).listen(443, address))
              }
              _this._servers.http.push(http.createServer(app).listen(80, address))
            })
          }
          resolve()
        }).catch(reject)
      })
    }
    const Image = _this.db.model('image')
    function doAddImage () {
      return new Promise((resolve, reject) => {
        function addImage (name, path) {
          return new Promise((resolve, reject) => {
            let distName = 's/' + name
            let query = Image.findOne({ name: distName }, 'name')
            query.then(img => {
              if (!img) {
                console.log(`Adding image: ${distName}...`)
                fs.readFile(path, { encoding: null }, (err, data) => {
                  if (err) {
                    reject(err)
                    return
                  }
                  Image.addImage(distName, data, err => {
                    if (err) {
                      reject(new Error(`error when trying to add image ${name}: ${err.toString()}`))
                      return
                    }
                    resolve()
                  })
                })
              } else {
                resolve()
              }
            }).catch(err => {
              reject(new Error(`Can't check image cache: ${err}`))
            })
          })
        }
        function addImageInDir (dir) {
          return new Promise((resolve, reject) => {
            let currentDirPath = path.join(__dirname, 'static', 'imgs', dir)
            fs.readdir(currentDirPath, (err, imgFiles) => {
              if (err) {
                reject(err)
                return
              }
              let promises = []
              imgFiles.forEach(name => {
                promises.push(new Promise((resolve, reject) => {
                  let currentName = path.join(dir, name)
                  let currentFileName = path.join(currentDirPath, name)
                  fs.stat(currentFileName, (err, stat) => {
                    if (err) {
                      reject(err)
                      return
                    }
                    if (stat.isFile()) {
                      addImage(currentName, currentFileName).then(resolve, reject)
                    } else if (stat.isDirectory()) {
                      addImageInDir(currentName).then(resolve, reject)
                    } else {
                      resolve()
                    }
                  })
                }))
              })
              Promise.all(promises).then(resolve, reject)
            })
          })
        }
        if (_this.noInitImages) {
          resolve()
        } else {
          addImageInDir('').then(resolve, reject)
        }
      })
    }
    _this.es.ping({}).then(() => doSetupServer().then(() => new Promise((resolve, reject) => {
      _this._servers.http2.forEach(server => {
        let wss = new WebSocket.Server({
          server,
          backlog: 100,
          maxPayload: 1024 * 1024 * 1 // 10MiB
        })
        wss.shouldHandle = function (req) {
          let reqHost = req.headers.host
          if (reqHost.endsWith(':443')) reqHost = reqHost.substr(0, reqHost.length - 4)
          console.log(`WS on ${reqHost}${req.url}`)
          let hnd = _this.registeredWSHandlers.find(hnd => hnd.hostname === reqHost)
          if (!hnd) return false
          return hnd.shouldHandle(req)
        }
        wss.on('connection', function (ws, req) {
          let reqHost = req.headers.host
          if (reqHost.endsWith(':443')) reqHost = reqHost.substr(0, reqHost.length - 4)
          console.log(`WS on ${reqHost}${req.url} connected.`)
          let hnd = _this.registeredWSHandlers.find(hnd => hnd.hostname === reqHost)
          if (!hnd) {
            ws.close(1011, `Unexpected host ${req.headers.host}`)
            console.error(`ws: Unexpected 'connection' event on host ${req.headers.host}`)
          } else {
            hnd.onConnection(ws, req)
          }
          ws.on('close', () => {
            console.log(`CLOSE WS on ${reqHost}${req.url}`)
          })
        })
      })
      resolve()
    })).then(() => {
      console.log('Server ready.')
      if (callback) {
        callback(null, app, function () {
          _this._servers.http2.forEach(function (s) {
            s.close()
          })
          _this._servers.http.forEach(function (s) {
            s.close()
          })
          _this.destory = true
          _this.db.close()
        })
      }
    }), err => fail(err)).catch(err => { fail(err) })
  })
}

module.exports = maowtm
<|endoftext|>


<commit_before>'use strict'
const FitbitClient = require('fitbit-client-oauth2')
const request = require('request')
const config = require('./config')
const clientId = config.CLIENT_ID
const clientSecret = config.CONSUMER_SECRET
const client = new FitbitClient(clientId, clientSecret)
const token = client.createToken({
    access_token: config.ACCESS_TOKEN,
    refresh_token: config.REFRESH_TOKEN
})
client.refreshAccessToken(token, {forceRefresh: true}).then((newToken) => {
  client.getTimeSeries(newToken, {resourcePath: 'activities/heart', period: '1d/1sec'}).then((res) => {
    const _config = `module.exports = ${JSON.stringify(Object.assign(config, {
      ACCESS_TOKEN: newToken.token.access_token,
      REFRESH_TOKEN: newToken.token.refresh_token
    }))}`
    require('fs').writeFile('./config.js', _config)
    const dataset = res['activities-heart-intraday'].dataset
    const d = new Date()
    const today = `${d.getFullYear()}-${d.getMonth()+1}-${d.getDate()}`
    const metrics = dataset.map((item) => {return {
      name: 'heartbeat',
      time: Date.parse(`${today} ${item.time}`)/1000,
      value: item.value
    }})
    request.post({
      url: 'https://mackerel.io/api/v0/services/vital/tsdb',
      body: JSON.stringify(metrics),
      headers: {
        'X-Api-Key': config.MACKEREL_API_TOKEN,
        'Content-Type': 'application/json'
      }
    }, function (err, res, body) {
      if (err) return console.error(err)
      console.log(body)
    })
  }).catch((err) => console.log(err.error))
}).catch((e) => console.log(e))
<commit_msg>check token expired?<commit_after>'use strict'
const FitbitClient = require('fitbit-client-oauth2')
const request = require('request')
const config = require('./config')
const clientId = config.CLIENT_ID
const clientSecret = config.CONSUMER_SECRET
const client = new FitbitClient(clientId, clientSecret)
const token = client.createToken({
    access_token: config.ACCESS_TOKEN,
    refresh_token: config.REFRESH_TOKEN,
    expires_at: config.EXPIRES_AT
})
client.refreshAccessToken(token).then((newToken) => {
  client.getTimeSeries(newToken, {resourcePath: 'activities/heart', period: '1d/1sec'}).then((res) => {
    const _config = `module.exports = ${JSON.stringify(Object.assign(config, {
      ACCESS_TOKEN: newToken.token.access_token,
      REFRESH_TOKEN: newToken.token.refresh_token,
      EXPIRES_AT: newToken.token.expires_at
    }))}`
    require('fs').writeFile('./config.js', _config)
    const dataset = res['activities-heart-intraday'].dataset
    const d = new Date()
    const today = `${d.getFullYear()}-${d.getMonth()+1}-${d.getDate()}`
    const metrics = dataset.map((item) => {return {
      name: 'heartbeat',
      time: Date.parse(`${today} ${item.time}`)/1000,
      value: item.value
    }})
    request.post({
      url: 'https://mackerel.io/api/v0/services/vital/tsdb',
      body: JSON.stringify(metrics),
      headers: {
        'X-Api-Key': config.MACKEREL_API_TOKEN,
        'Content-Type': 'application/json'
      }
    }, function (err, res, body) {
      if (err) return console.error(err)
      console.log(body)
    })
  }).catch((err) => console.log(err.error))
}).catch((e) => console.log(e))
<|endoftext|>


<commit_before>'use strict';

var fortress = module.exports;

fortress.server = function server(primus, options) {
  primus.transform('incoming', function incoming(packet, next) {
    var data = packet.data
      , emit = 'object' !== typeof data || !Array.isArray(data.emit)
        ? ['data', data]
        : data.emit;

    //
    // First step of validation, it shouldn't be reserved event.
    //
    if (this.reserved(data.emit[0])) return next(undefined, false);

    primus.emit('fortress:maximus::'+ packet.emit[0], emit, next);
  });

  /**
   * Validate incoming data.
   *
   * @param {String} event The incoming event we need to validate.
   * @param {Function} validator Function that validates.
   * @api public
   */
  primus.validate = function validate(event, validator) {
    var callback = validator.length - 1;

    primus.on('fortress:maximus::'+ event, function validates(emit, next) {
      var event = emit.shift();

      //
      // This is the first step of validation, we want to make sure that we've
      // received the expected amount of data. If we've receive to few or to
      // many events we know that this data is invalid and should be ignored.
      //
      if (emit.length !== callback) return next(undefined, false);

      emit.push(function validated(err) {
        if (err) return next(undefined, false);

        //
        // Re-add the original emitted event again and remove our added callback
        // function which we are currently executing in.
        //
        emit.unshift(event);
        emit.pop();

        next();
      });

      validator.apply(primus, emit);
    });
  };
};
<commit_msg>[minor] Ensure that we actually have listeners for the assigned events.<commit_after>'use strict';

var fortress = module.exports;

fortress.server = function server(primus, options) {
  primus.transform('incoming', function incoming(packet, next) {
    var data = packet.data
      , normal = 'object' !== typeof data || !Array.isArray(data.emit)
      , emit = normal ? ['data', data] : data.emit;

    //
    // Pre-extract the event name as we don't need it in our argument validation
    // as we already know which event we receive. And we need the event and
    // namespace in order to validate that we actually have listeners for
    // these events as we will ignore the message if we don't
    //
    var event = emit.shift()
      , namespace = 'fortress:maximus::'+ event;

    //
    // 1: The event we're about to emit shouldn't be reserved.
    //
    if (!normal && this.reserved(event)) return next(undefined, false);

    //
    // 2: We require all events to be validated, if we don't have a validator
    // for the given event we will assume it's an attack and it will be ignored.
    //
    if (!this.listeners(event).length || !primus.listeners(namespace).length) {
      return next(undefined, false);
    }

    primus.emit(namespace, emit, next);
  });

  /**
   * Validate incoming data.
   *
   * @param {String} event The incoming event we need to validate.
   * @param {Function} validator Function that validates.
   * @api public
   */
  primus.validate = function validate(event, validator) {
    var callback = validator.length - 1;

    primus.on('fortress:maximus::'+ event, function validates(emit, next) {
      //
      // This is the first step of validation, we want to make sure that we've
      // received the expected amount of data. If we've receive to few or to
      // many events we know that this data is invalid and should be ignored.
      //
      if (emit.length !== callback) return next(undefined, false);

      emit.push(function validated(err) {
        if (err) return next(undefined, false);

        //
        // Re-add the original emitted event again and remove our added callback
        // function which we are currently executing in.
        //
        emit.unshift(event);
        emit.pop();

        next();
      });

      validator.apply(primus, emit);
    });
  };
};
<|endoftext|>


<commit_before>var http = require('http'),
    redis = require('redis'),
    moment = require('moment'),
    redisClient = redis.createClient(),
    seed = require('./seed.json'),
    availableSymbols = seed.map(function(x) { return x.symbol });

function threeMinGap(x,y) {
  return (x - y) > 18000;
}

function getStockQuote(symbol) {
  http.request('http://www.bloomberg.com/markets/chart/data/1D/' + symbol + ':PM', function(res) {
   var data = '';
   res
   .on('data', function(c) {
     data += c.toString();
   })
   .on('end', function() {
     console.log(JSON.parse(data));
     redisClient.set('psex:lastCheck', parseInt(moment(new Date()).format('x'), 10), function(err, reply) {
       console.log('psex:lastCheck is ' + reply);
       redisClient.quit();
     });
   })
   .on('error', function(e) {
     console.error(e);
     redisClient.quit();
   });
  }).end();
}

function symbolExists(x) {
  return availableSymbols.indexOf(x.toUpperCase()) > -1
}

function init() {
  var x = process.argv.slice(2)[0];
  if(symbolExists(x)) {
    redisClient.get('psex:lastCheck', function(err, reply) {
      if (!err) {
        if(threeMinGap(parseInt(moment(new Date()).format('x'), 10), parseInt(reply, 10))) {
          getStockQuote(x);
        } else {
          console.log('Wait for three more minutes');
          redisClient.quit();
        }
      } else {
        console.log('There was an error: ' + err);
      }
    });
  }
}

init();
<commit_msg>fix diy cli<commit_after>var http = require('http'),
    redis = require('redis'),
    moment = require('moment'),
    redisClient = redis.createClient(),
    seed = require('./seed.json'),
    availableSymbols = seed.map(function(x) { return x.symbol }),
    bbUrl = 'http://www.bloomberg.com/markets/chart/data/1D/',
    marketSymbol = 'PM';

function threeMinGap(x,y) {
  return (x - y) > 18000;
}

function getStockQuote(symbol) {
  http.request(bbUrl + symbol + ':' + marketSymbol, function(res) {
   var data = '';
   res
   .on('data', function(c) {
     data += c.toString();
   })
   .on('end', function() {
     console.log(JSON.parse(data));
     redisClient.set('psex:lastCheck', parseInt(moment(new Date()).format('x'), 10), function(err, reply) {
       console.log('psex:lastCheck is ' + reply);
       redisClient.quit();
     });
   })
   .on('error', function(e) {
     console.error(e);
     redisClient.quit();
   });
  }).end();
}

function symbolExists(x) {
  return availableSymbols.indexOf(x.toUpperCase()) > -1
}

function usage() {
  var message = 'Usage: \n';
  seed.forEach(function(x) {
    message += x.symbol + '\t' + x.companyName + '\t' + x.sector + '\n';
  });
  console.log(message);
}

function argHandler() {
  var x, args = process.argv.slice(2);
  if (args.length == 0) {
    usage();
    return undefined;
  } else {
    x = args[0];
    return x;
  }
}

function init() {
  var x = argHandler();
  if (x) {
    if(symbolExists(x)) {
      redisClient.get('psex:lastCheck', function(err, reply) {
        if (!err) {
          if(threeMinGap(parseInt(moment(new Date()).format('x'), 10), parseInt(reply, 10))) {
            getStockQuote(x);
          } else {
            console.log('Wait for three more minutes');
            redisClient.quit();
          }
        } else {
          console.log('There was an error: ' + err);
          redisClient.quit();
        }
      });
    } else {
      usage();
      redisClient.quit();
    }
  } else {
    redisClient.quit();
  }
}

init();
<|endoftext|>


<commit_before>function AvlTree(comparisonFunc) {
	if (typeof comparisonFunc !== 'function') {
		this._compare = sortLeftToRight;
	} else {
		this._compare = comparisonFunc;
	}
	this._root = null;
}
module.exports = AvlTree;

// TODO: should this return the node?
AvlTree.prototype.search = function (element) {
	return this._search(element, this._root);
};

AvlTree.prototype._search = function (element, node) {
	var direction = this._compare(element, node.element);
	if (direction < 0) {
		return this._search(element, node.left);
	} else if (direction > 0) {
		return this._search(element, node.right);
	} else {
		return node;
	}
};

AvlTree.prototype.insert = function (element) {
	this._root = this._insert(element, this._root);
};

//TODO: allow duplicates
AvlTree.prototype._insert = function (element, node) {
	if (node === null) {
		return new Node(element);
	}
	var direction = this._compare(element, node.element);
	if (direction < 0) { // go left
		node.left = this._insert(element, node.left);
	} else if (direction > 0) { // go right
		node.right = this._insert(element, node.right);
	}
	node.height = Math.max(getHeight(node.left), getHeight(node.right)) + 1;

	var balance = getBalance(node);

	if (balance < -1) {
		var subLeftDirection = this._compare(element, node.left.element);
		if (subLeftDirection < 0) {
			return this._rightRotate(node);
		} else if (subLeftDirection > 0) {
			node.left = this._leftRotate(node.left);
			return this._rightRotate(node);
		}
	} else if (balance > 1) {
		var subRightDirection = this._compare(element, node.right.element);
		if (subRightDirection > 0) {
			return this._leftRotate(node);
		} else if (subRightDirection < 0) {
			node.right = this._rightRotate(node.right);
			return this._leftRotate(node);
		}
	}

	return node;
};

AvlTree.prototype._rightRotate = function (node) {
	var l = node.left;
	var lr = l.right;
	l.right = node;
	node.left = lr;
	node.height = Math.max(getHeight(node.left), getHeight(node.right)) + 1;
	l.height = Math.max(getHeight(l.left), getHeight(l.right)) + 1;
	return l;
};

AvlTree.prototype._leftRotate = function (node) {
	var r = node.right;
	var rl = r.left;
	r.left = node;
	node.right = rl;
	node.height = Math.max(getHeight(node.left), getHeight(node.right)) + 1;
	r.height = Math.max(getHeight(r.left), getHeight(r.right)) + 1;
	return r;
};

AvlTree.prototype.delete = function (element) {
	this._delete(element, this._root, null);
};

AvlTree.prototype._delete = function (element, node, parent) {
	var direction = this._compare(element, node.element);
	if (direction < 0) { // go left
		return this._delete(element, node.left, node);
	} else if (direction > 0) { // go right
		return this._delete(element, node.right, node);
	} else { // found our target element
		if (node.left !== null && node.right !== null) {
			var detachedNode = this._deleteWithTwoChildren(element, node.right, node);
			if (parent.right === node) { // we are in the right child
				parent.right = detachedNode;
			} else { // we are in the left child
				parent.left = detachedNode;
			}
		} else if (node.left !== null) { // only has left
			if (parent.right === node) {
				parent.right = node.left;
			} else {
				parent.left = node.left;
			}
			node.left = null;
		} else if (node.right !== null) { // only has right
			if (parent.right === node) {
				parent.right = node.right;
			} else {
				parent.left = node.right;
			}
			node.right = null;
		} else { // both children are empty
			if (parent.right === node) {
				parent.right = null;
			} else {
				parent.left = null;
			}
		}
		return this._balance(node, parent); // could be parent, but we need parent's parent
	}
};

AvlTree.prototype._deleteWithTwoChildren = function (element, node, parent) {
	if (node.left === null) {
		if (parent.right === node) { // we are in the right child
			parent.right = null;
		} else { // we are in the left child
			parent.left = null;
		}
		return this._balance(parent); // this is the min value from the right sub tree
	} else {
		return this._deleteWithTwoChildren(node.left, node)
	}
};

AvlTree.prototype.getMin = function (node) {
	if (node.left === null) {
		return node;
	} else {
		return this.getMin(node.left)
	}
};

AvlTree.prototype.getMax = function (element, node) {
	if (node.right === null) {
		return node;
	} else {
		return this.getMax(node.right);
	}
};

AvlTree.prototype._balance = function (node, parent) {
	updateHeight(node);
	var balance = getBalance(node);
	if (balance < -1) {
		var z = node;
		var y = node.left;
		var x = this._getTallestSubtree(y);
		var newRoot = this._triNodeRestructure(x, y, z, parent);
		updateHeight(newRoot);
		return newRoot;
	} else if (balance > 1) {
		var z = node;
		var y = node.right;
		var x = this._getTallestSubtree(y);
		var newRoot = this._triNodeRestructure(x, y, z, parent);
		updateHeight(newRoot);
		return newRoot;
	}
	return node;
};

AvlTree.prototype._getTallestSubtree = function (node) {
	var balance = getBalance(node);
	if (balance < 0) {
		return node.left;
	} else {
		return node.right;
	}
};

//Input: Unbalanced node z , y is the taller child of z, x is the taller child of y, parent is parent of z
//Output: position of the node that goes in the place of z when finished
AvlTree.prototype._triNodeRestructure = function (x, y, z, parent) {
	var a, b, c;
	if (z.right === y && y.left === x) {
		a = z;
		b = x;
		c = y;
	}
	if (z.right === y && y.right === x) {
		a = z;
		b = y;
		c = x;
	}
	if (z.left === y && y.left === x) {
		a = x;
		b = y;
		c = z;
	}
	if (z.left === y && y.right === x) {
		a = y;
		b = x;
		c = z;
	}
	if (z === this._root) {
		this._root = b;
		b.parent = null
	} else {
		if (parent.left === z) {
			parent.left = b;
		} else {
			parent.right = b;
		}
	}
	if (b.left !== x && b.left !== y && b.left !== z) {
		a.right = b.left;
	}
	if (b.right !== x && b.right !== y && b.right !== z) {
		c.left = b.right;
	}
	b.left = a;
	b.right = c;
	return b;
};

AvlTree.prototype.forEach = function (node, func) {
	this.forEach(node.left, func);
	func(node.element);
	this.forEach(node.right, func);
};

function Node(element) {
	this.element = element;
	this.height = 1;
	this.left   = null;
	this.right  = null;
}

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

function getHeight(node) {
	if (node === null) {
		return 0;
	}
	return node.height;
}

function updateHeight(node) {
	node.height = Math.max(getHeight(node.left), getHeight(node.right)) + 1;
}

function getBalance(node) {   // TODO: is something backwards?
	if (node != null) {
		return (getHeight(node.right) - getHeight(node.left));
	}
	return 0;
}

//TODO: union, intersection and set difference, helpers, Split and Join<commit_msg>remove unused parent code, comments<commit_after>function AvlTree(comparisonFunc) {
	if (typeof comparisonFunc !== 'function') {
		this._compare = sortLeftToRight;
	} else {
		this._compare = comparisonFunc;
	}
	this._root = null;
}
module.exports = AvlTree;

// TODO: should this return the node? Or maybe put data on the node.
AvlTree.prototype.search = function (element) {
	return this._search(element, this._root);
};

AvlTree.prototype._search = function (element, node) {
	var direction = this._compare(element, node.element);
	if (direction < 0) {
		return this._search(element, node.left);
	} else if (direction > 0) {
		return this._search(element, node.right);
	} else {
		return node;
	}
};

AvlTree.prototype.insert = function (element) {
	this._root = this._insert(element, this._root);
};

//TODO: allow duplicates
AvlTree.prototype._insert = function (element, node) {
	if (node === null) {
		return new Node(element);
	}
	var direction = this._compare(element, node.element);
	if (direction < 0) { // go left
		node.left = this._insert(element, node.left);
	} else if (direction > 0) { // go right
		node.right = this._insert(element, node.right);
	}
	node.height = Math.max(getHeight(node.left), getHeight(node.right)) + 1;

	var balance = getBalance(node);

	if (balance < -1) {
		var subLeftDirection = this._compare(element, node.left.element);
		if (subLeftDirection < 0) {
			return this._rightRotate(node);
		} else if (subLeftDirection > 0) {
			node.left = this._leftRotate(node.left);
			return this._rightRotate(node);
		}
	} else if (balance > 1) {
		var subRightDirection = this._compare(element, node.right.element);
		if (subRightDirection > 0) {
			return this._leftRotate(node);
		} else if (subRightDirection < 0) {
			node.right = this._rightRotate(node.right);
			return this._leftRotate(node);
		}
	}
	return node;
};

AvlTree.prototype._rightRotate = function (node) {
	var l = node.left;
	var lr = l.right;
	l.right = node;
	node.left = lr;
	node.height = Math.max(getHeight(node.left), getHeight(node.right)) + 1;
	l.height = Math.max(getHeight(l.left), getHeight(l.right)) + 1;
	return l;
};

AvlTree.prototype._leftRotate = function (node) {
	var r = node.right;
	var rl = r.left;
	r.left = node;
	node.right = rl;
	node.height = Math.max(getHeight(node.left), getHeight(node.right)) + 1;
	r.height = Math.max(getHeight(r.left), getHeight(r.right)) + 1;
	return r;
};

AvlTree.prototype.delete = function (element) {
	this._delete(element, this._root, null);
};

AvlTree.prototype._delete = function (element, node, parent) {
	var direction = this._compare(element, node.element);
	if (direction < 0) { // go left
		return this._delete(element, node.left, node);
	} else if (direction > 0) { // go right
		return this._delete(element, node.right, node);
	} else { // found our target element
		if (node.left !== null && node.right !== null) {
			var detachedNode = this._deleteWithTwoChildren(element, node.right, node);
			if (parent.right === node) { // we are in the right child
				parent.right = detachedNode;
			} else { // we are in the left child
				parent.left = detachedNode;
			}
		} else if (node.left !== null) { // only has left
			if (parent.right === node) {
				parent.right = node.left;
			} else {
				parent.left = node.left;
			}
			node.left = null;
		} else if (node.right !== null) { // only has right
			if (parent.right === node) {
				parent.right = node.right;
			} else {
				parent.left = node.right;
			}
			node.right = null;
		} else { // both children are empty
			if (parent.right === node) {
				parent.right = null;
			} else {
				parent.left = null;
			}
		}
		return this._balance(node, parent); // could be parent, but we need parent's parent
	}
};

AvlTree.prototype._deleteWithTwoChildren = function (element, node, parent) {
	if (node.left === null) {
		if (parent.right === node) { // we are in the right child
			parent.right = null;
		} else { // we are in the left child
			parent.left = null;
		}
		return this._balance(parent); // this is the min value from the right sub tree
	} else {
		return this._deleteWithTwoChildren(node.left, node)
	}
};

AvlTree.prototype.getMin = function (node) {
	if (node.left === null) {
		return node;
	} else {
		return this.getMin(node.left)
	}
};

AvlTree.prototype.getMax = function (element, node) {
	if (node.right === null) {
		return node;
	} else {
		return this.getMax(node.right);
	}
};

AvlTree.prototype._balance = function (node, parent) {
	updateHeight(node);
	var balance = getBalance(node);
	if (balance < -1) {
		var z = node;
		var y = node.left;
		var x = this._getTallestSubtree(y);
		var newRoot = this._triNodeRestructure(x, y, z, parent);
		updateHeight(newRoot);
		return newRoot;
	} else if (balance > 1) {
		var z = node;
		var y = node.right;
		var x = this._getTallestSubtree(y);
		var newRoot = this._triNodeRestructure(x, y, z, parent);
		updateHeight(newRoot);
		return newRoot;
	}
	return node;
};

AvlTree.prototype._getTallestSubtree = function (node) {
	var balance = getBalance(node);
	if (balance < 0) {
		return node.left;
	} else {
		return node.right;
	}
};

//Input: Unbalanced node z , y is the taller child of z, x is the taller child of y, parent is parent of z
//Output: position of the node that goes in the place of z when finished
AvlTree.prototype._triNodeRestructure = function (x, y, z, parent) {
	var a, b, c;
	if (z.right === y && y.left === x) {
		a = z;
		b = x;
		c = y;
	}
	if (z.right === y && y.right === x) {
		a = z;
		b = y;
		c = x;
	}
	if (z.left === y && y.left === x) {
		a = x;
		b = y;
		c = z;
	}
	if (z.left === y && y.right === x) {
		a = y;
		b = x;
		c = z;
	}
	if (z === this._root) {
		this._root = b;
	} else {
		if (parent.left === z) {
			parent.left = b;
		} else {
			parent.right = b;
		}
	}
	if (b.left !== x && b.left !== y && b.left !== z) {
		a.right = b.left;
	}
	if (b.right !== x && b.right !== y && b.right !== z) {
		c.left = b.right;
	}
	b.left = a;
	b.right = c;
	return b;
};

AvlTree.prototype.forEach = function (node, func) {
	this.forEach(node.left, func);
	func(node.element);
	this.forEach(node.right, func);
};

function Node(element) {
	this.element = element;
	this.height = 1;
	this.left   = null;
	this.right  = null;
}

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

function getHeight(node) {
	if (node === null) {
		return 0;
	}
	return node.height;
}

function updateHeight(node) {
	node.height = Math.max(getHeight(node.left), getHeight(node.right)) + 1;
}

function getBalance(node) {   // TODO: is something backwards somewhere?
	if (node != null) {
		return (getHeight(node.right) - getHeight(node.left));
	}
	return 0;
}

//TODO: union, intersection and set difference, helpers, Split and Join<|endoftext|>


<commit_before>export { default as CurrencyFormat } from './src/currency';
export { default as DateFormat } from './src/date';
export { default as I18n } from './src/i18n';
export { default as NumberFormat } from './src/number';
export { default as StringFormat } from './src/string';
<commit_msg>Refactor exports<commit_after>import CurrencyFormat from './src/currency';
import DateFormat from './src/date';
import I18n from './src/i18n';
import NumberFormat from './src/number';
import StringFormat from './src/string';

export {
  CurrencyFormat,
  DateFormat,
  I18n,
  NumberFormat,
  StringFormat
};
<|endoftext|>