<commit_before>// Copyright 2014 Globo.com Player authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

var Playback = require('playback')
var Styler = require('../../base/styler')
var JST = require('../../base/jst')
var _ = require("underscore")
var Mediator = require('mediator')
var Browser = require('browser')

var objectIE = '<object type="application/x-shockwave-flash" id="<%= cid %>" class="hls-playback" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" data-hls="" width="100%" height="100%"><param name="movie" value="<%= swfPath %>"> <param name="quality" value="autohigh"> <param name="swliveconnect" value="true"> <param name="allowScriptAccess" value="always"> <param name="bgcolor" value="#001122"> <param name="allowFullScreen" value="false"> <param name="wmode" value="transparent"> <param name="tabindex" value="1"> <param name=FlashVars value="playbackId=<%= playbackId %>" /> </object>'

class HLS extends Playback {
  get name() { return 'hls' }
  get tagName() { return 'object' }
  get template() { return JST.hls }
  get attributes() {
    return {
      'class': 'hls-playback',
      'data-hls': '',
      'type': 'application/x-shockwave-flash'
    }
  }

  constructor(options) {
    super(options)
    this.src = options.src
    this.swfPath = options.swfPath || "http://cdn.clappr.io/latest/assets/HLSPlayer.swf"
    this.flushLiveURLCache = (options.flushLiveURLCache === undefined)? true: options.flushLiveURLCache
    this.capLevelToStage = (options.capLevelToStage === undefined)? false: options.capLevelToStage
    this.highDefinition = false
    this.autoPlay = options.autoPlay
    this.defaultSettings = {
      left: ["playstop"],
      default: ['seekbar'],
      right: ["fullscreen", "volume", "hd-indicator"],
      seekEnabled: false
    }
    this.settings = _.extend({}, this.defaultSettings)
    this.playbackType = 'live'
    this.addListeners()
  }

  addListeners() {
    Mediator.on(this.uniqueId + ':flashready', () => this.bootstrap())
    Mediator.on(this.uniqueId + ':timeupdate', () => this.updateTime())
    Mediator.on(this.uniqueId + ':playbackstate', (state) => this.setPlaybackState(state))
    Mediator.on(this.uniqueId + ':highdefinition', (isHD) => this.updateHighDefinition(isHD))
    Mediator.on(this.uniqueId + ':playbackerror', () => this.flashPlaybackError())
  }

  stopListening() {
    super()
    Mediator.off(this.uniqueId + ':flashready')
    Mediator.off(this.uniqueId + ':timeupdate')
    Mediator.off(this.uniqueId + ':playbackstate')
    Mediator.off(this.uniqueId + ':highdefinition')
    Mediator.off(this.uniqueId + ':playbackerror')
  }

  bootstrap() {
    this.el.width = "100%"
    this.el.height = "100%"
    this.isReady = true
    this.trigger('playback:ready', this.name)
    this.currentState = "IDLE"
    this.setFlashSettings()
    this.autoPlay && this.play()
  }

  setFlashSettings() {
    this.el.globoPlayerSetflushLiveURLCache(this.flushLiveURLCache)
    this.el.globoPlayerCapLeveltoStage(this.capLevelToStage)
    this.el.globoPlayerSetmaxBufferLength(0)
  }

  updateHighDefinition(isHD) {
    this.highDefinition = (isHD === "true");
    this.trigger('playback:highdefinitionupdate')
    this.trigger('playback:bitrate', {'bitrate': this.getCurrentBitrate()})
  }

  updateTime() {
    var duration = this.getDuration()
    var position = Math.min(Math.max(this.el.globoGetPosition(), 0), duration)
    var previousDVRStatus = this.dvrEnabled
    var livePlayback = (this.playbackType === 'live')
    this.dvrEnabled = (livePlayback && duration > 240)

    if (duration === 100 || livePlayback === undefined) {
      return;
    }

    if (this.dvrEnabled !== previousDVRStatus) {
      this.updateSettings()
      this.trigger('playback:settingsupdate', this.name)
    }

    if (livePlayback && (!this.dvrEnabled || !this.dvrInUse)) {
      position = duration
    }

    this.trigger('playback:timeupdate', position, duration, this.name)
  }

  play() {
    if(this.currentState === 'PAUSED') {
      this.el.globoPlayerResume()
    } else if (this.currentState !== "PLAYING") {
      this.firstPlay()
    }
    this.trigger('playback:play', this.name)
  }

  getPlaybackType() {
    return this.playbackType? this.playbackType: null
  }

  getCurrentBitrate() {
    var currentLevel = this.getLevels()[this.el.globoGetLevel()]
    return currentLevel.bitrate
  }

  getLastProgramDate() {
    var programDate = this.el.globoGetLastProgramDate()
    // normalizing for BRT
    return programDate - 1.08e+7
  }

  isHighDefinitionInUse() {
    return this.highDefinition
  }

  getLevels() {
    if (!this.levels || this.levels.length === 0) {
      this.levels = this.el.globoGetLevels()
    }
    return this.levels
  }

  setPlaybackState(state) {
    var bufferLength = this.el.globoGetbufferLength()
    if (state === "PLAYING_BUFFERING" && bufferLength < 1)  {
      this.trigger('playback:buffering', this.name)
      this.updateCurrentState(state)
    } else if (state === "PLAYING") {
      if (_.contains(["PLAYING_BUFFERING", "PAUSED", "IDLE"], this.currentState)) {
        this.trigger('playback:bufferfull', this.name)
        this.updateCurrentState(state)
      }
    } else if (state === "PAUSED") {
      this.updateCurrentState(state)
    } else if (state === "IDLE") {
      this.trigger('playback:ended', this.name)
      this.trigger('playback:timeupdate', 0, this.el.globoGetDuration(), this.name)
      this.updateCurrentState(state)
    }
    this.lastBufferLength = bufferLength
  }

  updateCurrentState(state) {
    this.currentState = state
    this.updatePlaybackType()
  }

  updatePlaybackType() {
    this.playbackType = this.el.globoGetType()
    if (this.playbackType) {
      this.playbackType = this.playbackType.toLowerCase()
      if (this.playbackType === 'vod') {
        this.startReportingProgress()
      } else {
        this.stopReportingProgress()
      }
    }
    this.trigger('playback:playbackstate')
  }

  startReportingProgress() {
    if (!this.reportingProgress) {
      this.reportingProgress = true
      Mediator.on(this.uniqueId + ':fragmentloaded', () => this.onFragmentLoaded())
    }
  }

  stopReportingProgress() {
    Mediator.off(this.uniqueId + ':fragmentloaded')
  }

  onFragmentLoaded() {
    var buffered = this.el.globoGetPosition() + this.el.globoGetbufferLength()
    this.trigger('playback:progress', this.el.globoGetPosition(), buffered, this.getDuration(), this.name)
  }

  firstPlay() {
    this.el.globoPlayerLoad(this.src)
    this.el.globoPlayerPlay()
  }

  volume(value) {
    if (this.isReady) {
      this.el.globoPlayerVolume(value)
    } else {
      this.listenToOnce(this, 'playback:bufferfull', () => this.volume(value))
    }
  }

  pause() {
    if (this.playbackType !== 'live' || this.dvrEnabled) {
      this.el.globoPlayerPause()
      if (this.playbackType === 'live' && this.dvrEnabled) {
        this.updateDvr(true)
      }
    }
  }

  stop() {
    this.el.globoPlayerStop()
    this.trigger('playback:timeupdate', 0, this.name)
  }

  isPlaying() {
    if (this.currentState) {
      return !!(this.currentState.match(/playing/i))
    }
    return false
  }

  getDuration() {
    var duration = this.el.globoGetDuration()
    if (this.playbackType === 'live') {
      // estimate 10 seconds of buffer time for live streams for seek positions
      duration = duration - 10
    }
    return duration
  }

  seek(time) {
    var duration = this.getDuration()
    if (time > 0) {
      time = duration * time / 100
    }

    if (this.playbackType === 'live') {
      // seek operations to a time within 5 seconds from live stream will position playhead back to live
      var dvrInUse = (time >= 0 && duration - time > 5)
      if (!dvrInUse) {
        time = -1
      }
      this.updateDvr(dvrInUse)
    }
    this.el.globoPlayerSeek(time)
    this.trigger('playback:timeupdate', time, duration, this.name)
  }

  updateDvr(dvrInUse) {
    var previousDvrInUse = !!this.dvrInUse
    this.dvrInUse = dvrInUse
    if (this.dvrInUse !== previousDvrInUse) {
      this.updateSettings()
      this.trigger('playback:dvr', this.dvrInUse)
      this.trigger('playback:stats:add', {'dvr': this.dvrInUse})
    }
  }

  flashPlaybackError() {
    this.trigger('playback:stop')
  }

  timeUpdate(time, duration) {
    this.trigger('playback:timeupdate', time, duration, this.name)
  }

  destroy() {
    this.stopListening()
    this.$el.remove()
  }

  setupFirefox() {
    var $el = this.$('embed')
    $el.attr('data-hls', '')
    this.setElement($el)
  }

  setupIE() {
    this.setElement($(_.template(objectIE)({cid: this.cid, swfPath: this.swfPath, playbackId: this.uniqueId})))
  }

  updateSettings() {
    this.settings = _.extend({}, this.defaultSettings)
    if (this.playbackType === "vod" || this.dvrInUse) {
      this.settings.left = ["playpause", "position", "duration"]
      this.settings.seekEnabled = true
    } else if (this.dvrEnabled) {
      this.settings.left = ["playpause"]
      this.settings.seekEnabled = true
    } else {
      this.settings.seekEnabled = false
    }
  }

  setElement(element) {
    this.$el = element
    this.el = element[0]
  }

  render() {
    var style = Styler.getStyleFor(this.name)
    if(Browser.isLegacyIE) {
      this.setupIE()
    } else {
      this.$el.html(this.template({cid: this.cid, swfPath: this.swfPath, playbackId: this.uniqueId}))
      if(Browser.isFirefox) {
        this.setupFirefox()
      } else if (Browser.isIE) {
        this.$('embed').remove()
      }
    }
    this.el.id = this.cid
    this.$el.append(style)
    return this
  }
}

HLS.canPlay = function(resource) {
  return !!resource.match(/^http(.*).m3u8?/)
}

module.exports = HLS
<commit_msg>hls: avoid Mediator.off call leak to child objects<commit_after>// Copyright 2014 Globo.com Player authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

var Playback = require('playback')
var Styler = require('../../base/styler')
var JST = require('../../base/jst')
var _ = require("underscore")
var Mediator = require('mediator')
var Browser = require('browser')

var objectIE = '<object type="application/x-shockwave-flash" id="<%= cid %>" class="hls-playback" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" data-hls="" width="100%" height="100%"><param name="movie" value="<%= swfPath %>"> <param name="quality" value="autohigh"> <param name="swliveconnect" value="true"> <param name="allowScriptAccess" value="always"> <param name="bgcolor" value="#001122"> <param name="allowFullScreen" value="false"> <param name="wmode" value="transparent"> <param name="tabindex" value="1"> <param name=FlashVars value="playbackId=<%= playbackId %>" /> </object>'

class HLS extends Playback {
  get name() { return 'hls' }
  get tagName() { return 'object' }
  get template() { return JST.hls }
  get attributes() {
    return {
      'class': 'hls-playback',
      'data-hls': '',
      'type': 'application/x-shockwave-flash'
    }
  }

  constructor(options) {
    super(options)
    this.src = options.src
    this.swfPath = options.swfPath || "http://cdn.clappr.io/latest/assets/HLSPlayer.swf"
    this.flushLiveURLCache = (options.flushLiveURLCache === undefined)? true: options.flushLiveURLCache
    this.capLevelToStage = (options.capLevelToStage === undefined)? false: options.capLevelToStage
    this.highDefinition = false
    this.autoPlay = options.autoPlay
    this.defaultSettings = {
      left: ["playstop"],
      default: ['seekbar'],
      right: ["fullscreen", "volume", "hd-indicator"],
      seekEnabled: false
    }
    this.settings = _.extend({}, this.defaultSettings)
    this.playbackType = 'live'
    this.addListeners()
  }

  addListeners() {
    Mediator.on(this.uniqueId + ':flashready', () => this.bootstrap())
    Mediator.on(this.uniqueId + ':timeupdate', () => this.updateTime())
    Mediator.on(this.uniqueId + ':playbackstate', (state) => this.setPlaybackState(state))
    Mediator.on(this.uniqueId + ':highdefinition', (isHD) => this.updateHighDefinition(isHD))
    Mediator.on(this.uniqueId + ':playbackerror', () => this.flashPlaybackError())
  }

  stopListening() {
    super()
    Mediator.off(this.uniqueId + ':flashready')
    Mediator.off(this.uniqueId + ':timeupdate')
    Mediator.off(this.uniqueId + ':playbackstate')
    Mediator.off(this.uniqueId + ':highdefinition')
    Mediator.off(this.uniqueId + ':playbackerror')
  }

  bootstrap() {
    this.el.width = "100%"
    this.el.height = "100%"
    this.isReady = true
    this.trigger('playback:ready', this.name)
    this.currentState = "IDLE"
    this.setFlashSettings()
    this.autoPlay && this.play()
  }

  setFlashSettings() {
    this.el.globoPlayerSetflushLiveURLCache(this.flushLiveURLCache)
    this.el.globoPlayerCapLeveltoStage(this.capLevelToStage)
    this.el.globoPlayerSetmaxBufferLength(0)
  }

  updateHighDefinition(isHD) {
    this.highDefinition = (isHD === "true");
    this.trigger('playback:highdefinitionupdate')
    this.trigger('playback:bitrate', {'bitrate': this.getCurrentBitrate()})
  }

  updateTime() {
    var duration = this.getDuration()
    var position = Math.min(Math.max(this.el.globoGetPosition(), 0), duration)
    var previousDVRStatus = this.dvrEnabled
    var livePlayback = (this.playbackType === 'live')
    this.dvrEnabled = (livePlayback && duration > 240)

    if (duration === 100 || livePlayback === undefined) {
      return;
    }

    if (this.dvrEnabled !== previousDVRStatus) {
      this.updateSettings()
      this.trigger('playback:settingsupdate', this.name)
    }

    if (livePlayback && (!this.dvrEnabled || !this.dvrInUse)) {
      position = duration
    }

    this.trigger('playback:timeupdate', position, duration, this.name)
  }

  play() {
    if(this.currentState === 'PAUSED') {
      this.el.globoPlayerResume()
    } else if (this.currentState !== "PLAYING") {
      this.firstPlay()
    }
    this.trigger('playback:play', this.name)
  }

  getPlaybackType() {
    return this.playbackType? this.playbackType: null
  }

  getCurrentBitrate() {
    var currentLevel = this.getLevels()[this.el.globoGetLevel()]
    return currentLevel.bitrate
  }

  getLastProgramDate() {
    var programDate = this.el.globoGetLastProgramDate()
    // normalizing for BRT
    return programDate - 1.08e+7
  }

  isHighDefinitionInUse() {
    return this.highDefinition
  }

  getLevels() {
    if (!this.levels || this.levels.length === 0) {
      this.levels = this.el.globoGetLevels()
    }
    return this.levels
  }

  setPlaybackState(state) {
    var bufferLength = this.el.globoGetbufferLength()
    if (state === "PLAYING_BUFFERING" && bufferLength < 1)  {
      this.trigger('playback:buffering', this.name)
      this.updateCurrentState(state)
    } else if (state === "PLAYING") {
      if (_.contains(["PLAYING_BUFFERING", "PAUSED", "IDLE"], this.currentState)) {
        this.trigger('playback:bufferfull', this.name)
        this.updateCurrentState(state)
      }
    } else if (state === "PAUSED") {
      this.updateCurrentState(state)
    } else if (state === "IDLE") {
      this.trigger('playback:ended', this.name)
      this.trigger('playback:timeupdate', 0, this.el.globoGetDuration(), this.name)
      this.updateCurrentState(state)
    }
    this.lastBufferLength = bufferLength
  }

  updateCurrentState(state) {
    this.currentState = state
    this.updatePlaybackType()
  }

  updatePlaybackType() {
    this.playbackType = this.el.globoGetType()
    if (this.playbackType) {
      this.playbackType = this.playbackType.toLowerCase()
      if (this.playbackType === 'vod') {
        this.startReportingProgress()
      } else {
        this.stopReportingProgress()
      }
    }
    this.trigger('playback:playbackstate')
  }

  startReportingProgress() {
    if (!this.reportingProgress) {
      this.reportingProgress = true
      Mediator.on(this.uniqueId + ':fragmentloaded', this.onFragmentLoaded)
    }
  }

  stopReportingProgress() {
    Mediator.off(this.uniqueId + ':fragmentloaded', this.onFragmentLoaded, this)
  }

  onFragmentLoaded() {
    var buffered = this.el.globoGetPosition() + this.el.globoGetbufferLength()
    this.trigger('playback:progress', this.el.globoGetPosition(), buffered, this.getDuration(), this.name)
  }

  firstPlay() {
    this.el.globoPlayerLoad(this.src)
    this.el.globoPlayerPlay()
  }

  volume(value) {
    if (this.isReady) {
      this.el.globoPlayerVolume(value)
    } else {
      this.listenToOnce(this, 'playback:bufferfull', () => this.volume(value))
    }
  }

  pause() {
    if (this.playbackType !== 'live' || this.dvrEnabled) {
      this.el.globoPlayerPause()
      if (this.playbackType === 'live' && this.dvrEnabled) {
        this.updateDvr(true)
      }
    }
  }

  stop() {
    this.el.globoPlayerStop()
    this.trigger('playback:timeupdate', 0, this.name)
  }

  isPlaying() {
    if (this.currentState) {
      return !!(this.currentState.match(/playing/i))
    }
    return false
  }

  getDuration() {
    var duration = this.el.globoGetDuration()
    if (this.playbackType === 'live') {
      // estimate 10 seconds of buffer time for live streams for seek positions
      duration = duration - 10
    }
    return duration
  }

  seek(time) {
    var duration = this.getDuration()
    if (time > 0) {
      time = duration * time / 100
    }

    if (this.playbackType === 'live') {
      // seek operations to a time within 5 seconds from live stream will position playhead back to live
      var dvrInUse = (time >= 0 && duration - time > 5)
      if (!dvrInUse) {
        time = -1
      }
      this.updateDvr(dvrInUse)
    }
    this.el.globoPlayerSeek(time)
    this.trigger('playback:timeupdate', time, duration, this.name)
  }

  updateDvr(dvrInUse) {
    var previousDvrInUse = !!this.dvrInUse
    this.dvrInUse = dvrInUse
    if (this.dvrInUse !== previousDvrInUse) {
      this.updateSettings()
      this.trigger('playback:dvr', this.dvrInUse)
      this.trigger('playback:stats:add', {'dvr': this.dvrInUse})
    }
  }

  flashPlaybackError() {
    this.trigger('playback:stop')
  }

  timeUpdate(time, duration) {
    this.trigger('playback:timeupdate', time, duration, this.name)
  }

  destroy() {
    this.stopListening()
    this.$el.remove()
  }

  setupFirefox() {
    var $el = this.$('embed')
    $el.attr('data-hls', '')
    this.setElement($el)
  }

  setupIE() {
    this.setElement($(_.template(objectIE)({cid: this.cid, swfPath: this.swfPath, playbackId: this.uniqueId})))
  }

  updateSettings() {
    this.settings = _.extend({}, this.defaultSettings)
    if (this.playbackType === "vod" || this.dvrInUse) {
      this.settings.left = ["playpause", "position", "duration"]
      this.settings.seekEnabled = true
    } else if (this.dvrEnabled) {
      this.settings.left = ["playpause"]
      this.settings.seekEnabled = true
    } else {
      this.settings.seekEnabled = false
    }
  }

  setElement(element) {
    this.$el = element
    this.el = element[0]
  }

  render() {
    var style = Styler.getStyleFor(this.name)
    if(Browser.isLegacyIE) {
      this.setupIE()
    } else {
      this.$el.html(this.template({cid: this.cid, swfPath: this.swfPath, playbackId: this.uniqueId}))
      if(Browser.isFirefox) {
        this.setupFirefox()
      } else if (Browser.isIE) {
        this.$('embed').remove()
      }
    }
    this.el.id = this.cid
    this.$el.append(style)
    return this
  }
}

HLS.canPlay = function(resource) {
  return !!resource.match(/^http(.*).m3u8?/)
}

module.exports = HLS
<|endoftext|>


<commit_before>import Plugin from "./../Plugin";

export default class UserStats extends Plugin {
    static get plugin() {
        return {
            name: "UserStats",
            description: "Get user stats on message count.",
            help: "Enter /userstats to get statistics.",
            type: Plugin.Type.PROXY,
            needs: {
                database: true
            }
        };
    }

    wordCount(text) {
        let words = text.trim().replace(/\s+/gi, ' ').split(' ');
        if (words[0])
            return words.length;
        return 0;
    }

    proxy(eventName, message) {
        if (!message.from.username) return;
        const chatId = message.chat.id;
        const userId = message.from.id;
        if (!this.db["stat" + chatId]) {
            this.db["stat" + chatId] = {};
            this.db["stat" + chatId].totalMessageCount = 0;
            this.db["stat" + chatId].totalWordCount = 0;
        }
        if (!this.db["stat" + chatId][userId]) {
            this.db["stat" + chatId][userId] = {
                userId,
                username: message.from.username,
                messageCount: 0,
                wordCount: 0
            };
        }
        this.db["stat" + chatId][userId].messageCount++;
        this.db["stat" + chatId].totalMessageCount++;
        const wc = this.wordCount(message.text);
        if (!this.db["stat" + chatId].totalWordCount) {
            this.db["stat" + chatId].totalWordCount = 0;
        }
        this.db["stat" + chatId].totalWordCount += wc;
        if (!this.db["stat" + chatId][userId].wordCount) {
            this.db["stat" + chatId][userId].wordCount = 0;
        }
        this.db["stat" + chatId][userId].wordCount += wc;
        return Promise.resolve(message);
    }

    onCommand({message, command, args}, reply) {
        if (command !== "userstats") return;

        let text = `Total messages:\n\n`;
        const statsObject = this.db["stat" + message.chat.id];
        const totalCount = statsObject.totalMessageCount;
        const userList = Object.keys(statsObject)
            .map(item => statsObject[item])
            .filter(item => typeof item === "object")
            .sort((a, b) => b.messageCount - a.messageCount);

        for (const user of userList) {
            const percentage = (user.messageCount / totalCount * 100).toFixed(4);
            const averageWords = user.wordCount / user.messageCount.toFixed(4);
            text += `${user.username} [${user.userId}]: ${user.messageCount} (${percentage}%) - ${user.wordCount} words (${averageWords} words/message)\n`;
        }
        reply({type: 'text', text});
    }
}
<commit_msg>Add comment to wordCount to make it clearer<commit_after>import Plugin from "./../Plugin";

export default class UserStats extends Plugin {
    static get plugin() {
        return {
            name: "UserStats",
            description: "Get user stats on message count.",
            help: "Enter /userstats to get statistics.",
            type: Plugin.Type.PROXY,
            needs: {
                database: true
            }
        };
    }

    wordCount(text) {
        let words = text.trim().replace(/\s+/gi, ' ').split(' ');
        // This checks if the first element of the `words` array is there, and
        // if it is truthy (an empty string, which is the case when `text` is
        // empty or contains only spaces, will not trigger this, returning 0).
        if (words[0])
            return words.length;
        return 0;
    }

    proxy(eventName, message) {
        if (!message.from.username) return;
        const chatId = message.chat.id;
        const userId = message.from.id;
        if (!this.db["stat" + chatId]) {
            this.db["stat" + chatId] = {};
            this.db["stat" + chatId].totalMessageCount = 0;
            this.db["stat" + chatId].totalWordCount = 0;
        }
        if (!this.db["stat" + chatId][userId]) {
            this.db["stat" + chatId][userId] = {
                userId,
                username: message.from.username,
                messageCount: 0,
                wordCount: 0
            };
        }
        this.db["stat" + chatId][userId].messageCount++;
        this.db["stat" + chatId].totalMessageCount++;
        const wc = this.wordCount(message.text);
        if (!this.db["stat" + chatId].totalWordCount) {
            this.db["stat" + chatId].totalWordCount = 0;
        }
        this.db["stat" + chatId].totalWordCount += wc;
        if (!this.db["stat" + chatId][userId].wordCount) {
            this.db["stat" + chatId][userId].wordCount = 0;
        }
        this.db["stat" + chatId][userId].wordCount += wc;
        return Promise.resolve(message);
    }

    onCommand({message, command, args}, reply) {
        if (command !== "userstats") return;

        let text = `Total messages:\n\n`;
        const statsObject = this.db["stat" + message.chat.id];
        const totalCount = statsObject.totalMessageCount;
        const userList = Object.keys(statsObject)
            .map(item => statsObject[item])
            .filter(item => typeof item === "object")
            .sort((a, b) => b.messageCount - a.messageCount);

        for (const user of userList) {
            const percentage = (user.messageCount / totalCount * 100).toFixed(4);
            const averageWords = user.wordCount / user.messageCount.toFixed(4);
            text += `${user.username} [${user.userId}]: ${user.messageCount} (${percentage}%) - ${user.wordCount} words (${averageWords} words/message)\n`;
        }
        reply({type: 'text', text});
    }
}
<|endoftext|>


<commit_before>

var app = angular.module("demoapp", ['leaflet-directive']);

app.controller("EventsController", [ '$scope', '$http', function($scope, $http) {

	angular.extend($scope, {
		france: {
			lat: 47.374004,
			lng: 4.890359,
			zoom: 5
		},
		haiti: {
			lat: 19,
			lng: -73,
			zoom: 5
		},
		defaults: {
			scrollWheelZoom: false
		},
		events: {
			map: {
				enable: ['click'],
				logic: 'emit'
			}
		},

		villageBorders : {
			data: {
			  "type": "FeatureCollection",
			  "features": [
				{
				  "type": "Feature",
				  "properties": {},
				  "geometry": {
					"type": "Polygon",
					"coordinates": [
                                          [
                                            [
                                              -41.8359375,
                                              28.92163128242129
                                            ],
                                            [
                                              -41.8359375,
                                              38.272688535980976
                                            ],
                                            [
                                              -26.015625,
                                              38.272688535980976
                                            ],
                                            [
                                              -26.015625,
                                              28.92163128242129
                                            ],
                                            [
                                              -41.8359375,
                                              28.92163128242129
                                            ]
                                          ]
                                        ]
				  }
				},
				{
				  "type": "Feature",
				  "properties": {},
				  "geometry": {
					"type": "Polygon",
					"coordinates": [
                                          [
                                            [
                                              -51.8359375,
                                              28.92163128242129
                                            ],
                                            [
                                              -51.8359375,
                                              38.272688535980976
                                            ],
                                            [
                                              -36.015625,
                                              38.272688535980976
                                            ],
                                            [
                                              -36.015625,
                                              28.92163128242129
                                            ],
                                            [
                                              -31.8359375,
                                              28.92163128242129
                                            ]
                                          ]
                                        ]
				  }
				}

			  ]
			},
			style: {
				fillColor: "green",
					weight: 2,
					opacity: 1,
					color: 'blue',
					dashArray: '3',
					fillOpacity: 0.0
			}
		}
	});

	$scope.markers = {};
	$scope.targetMarkers = {};

	$scope.$on('leafletDirectiveMap.click', function(event, args){

		var jsonLatLng = JSON.stringify( args.leafletEvent.latlng);
		console.log( jsonLatLng);

		console.log("post for villageCoordinates before");

		$http.post('http://localhost:8001/villageCoordinates', jsonLatLng ).then
		(
			function(response) { // success handler
				console.log(response.status);
				console.log('village villageCoordinates answer', response.data.X, response.data.Y);

				message = "village "+response.data.X+" "+response.data.Y+" "+response.data.Distance+" "+response.data.LatClosest+" "+response.data.LngClosest;

				parseFloat(response.data.LatClosest);

				$scope.markers['clickPos'] = {
					lat: args.leafletEvent.latlng.lat,
					lng: args.leafletEvent.latlng.lng,
					message: message,
					focus: true,
					draggable: false,
					options: {
						noHide: true
					}
				};

				lat = parseFloat(response.data.LatClosest);
				lng = parseFloat(response.data.LngClosest);

				latTarget = parseFloat(response.data.LatTarget);
				lngTarget = parseFloat(response.data.LngTarget);

				$scope.markers['closestVillage'] = {
					lat: lat,
					lng: lng,
					focus: false,
				}

				$scope.targetMarkers['targetVillage'] = {
					lat: latTarget,
					lng: lngTarget,
					focus: false,
				}

				$http.post('http://localhost:8001/villageTargetBorder', jsonLatLng ).then
				(
					function(response) { // success handler
						console.log(response.status);
						console.log('target village villageCoordinates before ', $scope.villageBorders.data.features[0].geometry.coordinates[0] )

						// convert response data field to float
						$scope.villageBorders.data.features[0].geometry.coordinates = [ [ [] ] ];
						$scope.villageBorders.data.features[0].geometry.coordinates[0] = new Array()
						$scope.villageBorders.data.features[0].geometry.coordinates[0].length = response.data[0].length

						for (var i = 0; i < response.data[0].length; i++) {
							$scope.villageBorders.data.features[0].geometry.coordinates[0][i] = new Array(2)
							$scope.villageBorders.data.features[0].geometry.coordinates[0][i][0] = parseFloat(response.data[0][i][0]);
							$scope.villageBorders.data.features[0].geometry.coordinates[0][i][1] = parseFloat(response.data[0][i][1]);
						}

						console.log('target village villageCoordinates answer ', $scope.villageBorders.data.features[0].geometry.coordinates[0] )

						$http.post('http://localhost:8001/villageSourceBorder', jsonLatLng ).then
						(
							function(response) { // success handler
								console.log(response.status);
								console.log('source village villageCoordinates before ', $scope.villageBorders.data.features[1].geometry.coordinates[0] )

								// convert response data field to float
								$scope.villageBorders.data.features[1].geometry.coordinates = [ [ [] ] ];
								$scope.villageBorders.data.features[1].geometry.coordinates[0] = new Array()
								$scope.villageBorders.data.features[1].geometry.coordinates[0].length = response.data[0].length

								for (var i = 0; i < response.data[0].length; i++) {
									$scope.villageBorders.data.features[1].geometry.coordinates[0][i] = new Array(2)
									$scope.villageBorders.data.features[1].geometry.coordinates[0][i][0] = parseFloat(response.data[0][i][0]);
									$scope.villageBorders.data.features[1].geometry.coordinates[0][i][1] = parseFloat(response.data[0][i][1]);
								}

								console.log('source village villageCoordinates answer ', $scope.villageBorders.data.features[1].geometry.coordinates[0] )

							},
							function(errResponse) { // error handler
								console.error('error while posting jsonLatLng');
								console.error(errResponse);
							}
						);
					},
					function(errResponse) { // error handler
						console.error('error while posting jsonLatLng');
						console.error(errResponse);
					}
				);
			},
			function(errResponse) { // error handler
				console.error('error while posting jsonLatLng');
				console.error(errResponse);
			}
		);

		console.log("post for villageCoordinates is over");

	}); // end of click
}]);
<commit_msg>simplified js<commit_after>

var app = angular.module("demoapp", ['leaflet-directive']);

app.controller("EventsController", [ '$scope', '$http', function($scope, $http) {

	angular.extend($scope, {
		france: {
			lat: 47.374004,
			lng: 4.890359,
			zoom: 5
		},
		haiti: {
			lat: 19,
			lng: -73,
			zoom: 5
		},
		defaults: {
			scrollWheelZoom: false
		},
		events: {
			map: {
				enable: ['click'],
				logic: 'emit'
			}
		},

		villageBorders : {
			data: {
			  "type": "FeatureCollection",
			  "features": [
				{
				  "type": "Feature",
				  "properties": {},
				  "geometry": {
					"type": "Polygon",
					"coordinates": []
				  }
				},
				{
				  "type": "Feature",
				  "properties": {},
				  "geometry": {
					"type": "Polygon",
					"coordinates": []
				  }
				}

			  ]
			},
			style: {
				fillColor: "green",
					weight: 2,
					opacity: 1,
					color: 'blue',
					dashArray: '3',
					fillOpacity: 0.0
			}
		}
	});

	$scope.markers = {};
	$scope.targetMarkers = {};

	$scope.$on('leafletDirectiveMap.click', function(event, args){

		var jsonLatLng = JSON.stringify( args.leafletEvent.latlng);
		console.log( jsonLatLng);

		console.log("post for villageCoordinates before");

		$http.post('http://localhost:8001/villageCoordinates', jsonLatLng ).then
		(
			function(response) { // success handler
				console.log(response.status);
				console.log('village villageCoordinates answer', response.data.X, response.data.Y);

				message = "village "+response.data.X+" "+response.data.Y+" "+response.data.Distance+" "+response.data.LatClosest+" "+response.data.LngClosest;

				parseFloat(response.data.LatClosest);

				$scope.markers['clickPos'] = {
					lat: args.leafletEvent.latlng.lat,
					lng: args.leafletEvent.latlng.lng,
					message: message,
					focus: true,
					draggable: false,
					options: {
						noHide: true
					}
				};

				lat = parseFloat(response.data.LatClosest);
				lng = parseFloat(response.data.LngClosest);

				latTarget = parseFloat(response.data.LatTarget);
				lngTarget = parseFloat(response.data.LngTarget);

				$scope.markers['closestVillage'] = {
					lat: lat,
					lng: lng,
					focus: false,
				}

				$scope.targetMarkers['targetVillage'] = {
					lat: latTarget,
					lng: lngTarget,
					focus: false,
				}

				$http.post('http://localhost:8001/villageTargetBorder', jsonLatLng ).then
				(
					function(response) { // success handler
						console.log(response.status);
						console.log('target village villageCoordinates before ', $scope.villageBorders.data.features[0].geometry.coordinates[0] )

						// convert response data field to float
						$scope.villageBorders.data.features[0].geometry.coordinates = [ [ [] ] ];
						$scope.villageBorders.data.features[0].geometry.coordinates[0] = new Array()
						$scope.villageBorders.data.features[0].geometry.coordinates[0].length = response.data[0].length

						for (var i = 0; i < response.data[0].length; i++) {
							$scope.villageBorders.data.features[0].geometry.coordinates[0][i] = new Array(2)
							$scope.villageBorders.data.features[0].geometry.coordinates[0][i][0] = parseFloat(response.data[0][i][0]);
							$scope.villageBorders.data.features[0].geometry.coordinates[0][i][1] = parseFloat(response.data[0][i][1]);
						}

						console.log('target village villageCoordinates answer ', $scope.villageBorders.data.features[0].geometry.coordinates[0] )

						$http.post('http://localhost:8001/villageSourceBorder', jsonLatLng ).then
						(
							function(response) { // success handler
								console.log(response.status);
								console.log('source village villageCoordinates before ', $scope.villageBorders.data.features[1].geometry.coordinates[0] )

								// convert response data field to float
								$scope.villageBorders.data.features[1].geometry.coordinates = [ [ [] ] ];
								$scope.villageBorders.data.features[1].geometry.coordinates[0] = new Array()
								$scope.villageBorders.data.features[1].geometry.coordinates[0].length = response.data[0].length

								for (var i = 0; i < response.data[0].length; i++) {
									$scope.villageBorders.data.features[1].geometry.coordinates[0][i] = new Array(2)
									$scope.villageBorders.data.features[1].geometry.coordinates[0][i][0] = parseFloat(response.data[0][i][0]);
									$scope.villageBorders.data.features[1].geometry.coordinates[0][i][1] = parseFloat(response.data[0][i][1]);
								}

								console.log('source village villageCoordinates answer ', $scope.villageBorders.data.features[1].geometry.coordinates[0] )

							},
							function(errResponse) { // error handler
								console.error('error while posting jsonLatLng');
								console.error(errResponse);
							}
						);
					},
					function(errResponse) { // error handler
						console.error('error while posting jsonLatLng');
						console.error(errResponse);
					}
				);
			},
			function(errResponse) { // error handler
				console.error('error while posting jsonLatLng');
				console.error(errResponse);
			}
		);

		console.log("post for villageCoordinates is over");

	}); // end of click
}]);
<|endoftext|>


<commit_before>var ERR = require('async-stacktrace');
var _ = require('lodash');
var assert = require('assert');
var request = require('request');
var cheerio = require('cheerio');

var testHelperServer = require('./testHelperServer');

var baseUrl = 'http://localhost:3000/pl';

describe('GET /pl', function() {

    before("set up testing server", testHelperServer.before);
    after("shut down testing server", testHelperServer.after);

    var page, $;

    it('should load successfully', function(callback) {
        request(baseUrl, function (error, response, body) {
            if (error) {
                return callback(error);
            }
            if (response.statusCode != 200) {
                return callback(new Error('bad status: ' + response.statusCode));
            }
            page = body;
            callback(null);
        })
    });
    it('should parse', function() {
        $ = cheerio.load(page);
    });
    it('should contain TPL 101', function() {
        assert.ok($('td a:contains("TPL 101")').length)
    });
});
<commit_msg>fix serverPort for tests/getHomepage<commit_after>var ERR = require('async-stacktrace');
var _ = require('lodash');
var assert = require('assert');
var request = require('request');
var cheerio = require('cheerio');

var config = require('../lib/config');
var testHelperServer = require('./testHelperServer');

var baseUrl = 'http://localhost:' + config.serverPort + '/pl';

describe('GET /pl', function() {

    before("set up testing server", testHelperServer.before);
    after("shut down testing server", testHelperServer.after);

    var page, $;

    it('should load successfully', function(callback) {
        request(baseUrl, function (error, response, body) {
            if (error) {
                return callback(error);
            }
            if (response.statusCode != 200) {
                return callback(new Error('bad status: ' + response.statusCode));
            }
            page = body;
            callback(null);
        })
    });
    it('should parse', function() {
        $ = cheerio.load(page);
    });
    it('should contain TPL 101', function() {
        assert.ok($('td a:contains("TPL 101")').length)
    });
});
<|endoftext|>


<commit_before>var ColorfullLogger = require('../src/colorfull-logger');
var fakeConsole = require('./fake-console');
var colorfullLogger = new ColorfullLogger({
	output: fakeConsole
});

var TEN_CHARAC_MESSAGE = 'TEN_CHARAC';

module.exports = {
	setUp: function (callback) {
		callback();
	},

	tearDown: function (callback) {
		callback();
	},

	colorfullLogger_is_an_object: function (test) {
		test.equal('function', typeof ColorfullLogger,
			'ColorfullLogger must be a constructor function');

		test.equal('object', typeof colorfullLogger,
			'colorfullLogger must be an object');

		test.equal('function', typeof colorfullLogger.log,
			'colorfullLogger.log must be a function');

		test.done();
	},

	config_has_his_defaults: function(test) {
		var config = colorfullLogger.config;

		test.ok(config.enabled,
			'enabled === true');

		test.done();
	},

	call_console_debug: function(test) {
		colorfullLogger.log(TEN_CHARAC_MESSAGE);

		test.equal('debug', fakeConsole.logRecorder[0].methodName,
			'expect debug');
		test.equal(TEN_CHARAC_MESSAGE, fakeConsole.logRecorder[0].message,
			'expect TEN_CHARAC_MESSAGE');

		test.done();
	},

	// disabled_on_startup: function(test){
	// 	colorfullLogger = new ColorfullLogger({
	// 		enabled: false
	// 	});

	// 	var mustBeFalse = colorfullLogger.log('it is disabled');
	// 	test.ok(!mustBeFalse,
	// 		'returns false when disabled');

	// 	test.done();
	// },

	// disabled_after_startup: function(test){
	// 	colorfullLogger.configure({
	// 		enabled: false
	// 	});

	// 	var mustBeFalse = colorfullLogger.log('it is disabled');
	// 	test.ok(!mustBeFalse,
	// 		'returns false when disabled');

	// 	test.done();
	// },

	// enabled_after_startup: function(test){
	// 	//create disabled
	// 	colorfullLogger = new ColorfullLogger({
	// 		enabled: false
	// 	});

	// 	//enable afterwards
	// 	colorfullLogger.configure({
	// 		enabled: true
	// 	});

	// 	fakeConsole.consoleCallback.consoleDebug = function(strResult) {
	// 		test.equal(TEN_CHARAC_MESSAGE, strResult,
	// 			'the message must be received by console.debug');
	// 		test.done();
	// 	};

	// 	colorfullLogger.log(TEN_CHARAC_MESSAGE);
	// },
};<commit_msg>nodeunit does not have a great output<commit_after>var ColorfullLogger = require('../src/colorfull-logger');
var fakeConsole = require('./fake-console');
var colorfullLogger = new ColorfullLogger({
	output: fakeConsole
});

var TEN_CHARAC_MESSAGE = 'TEN_CHARAC';

module.exports = {
	setUp: function (callback) {
		callback();
	},

	tearDown: function (callback) {
		callback();
	},

	colorfullLogger_is_an_object: function (test) {
		test.equal('function', typeof ColorfullLogger,
			'ColorfullLogger must be a constructor function');

		test.equal('object', typeof colorfullLogger,
			'colorfullLogger must be an object');

		test.equal('function', typeof colorfullLogger.log,
			'colorfullLogger.log must be a function');

		test.done();
	},

	config_has_his_defaults: function(test) {
		var config = colorfullLogger.config;

		test.ok(config.enabled,
			'enabled === true');

		test.done();
	},

	call_console_debug: function(test) {
		colorfullLogger.log(TEN_CHARAC_MESSAGE);

		test.equal('debug', fakeConsole.logRecorder[0].methodName);
		test.equal(TEN_CHARAC_MESSAGE, fakeConsole.logRecorder[0].message);

		test.done();
	},

	disabled_on_startup: function(test){
		colorfullLogger = new ColorfullLogger({
			enabled: false
		});

		colorfullLogger.log(TEN_CHARAC_MESSAGE);

		test.equal(0, fakeConsole.logRecorder.length);

		test.done();
	},

	// disabled_after_startup: function(test){
	// 	colorfullLogger.configure({
	// 		enabled: false
	// 	});

	// 	var mustBeFalse = colorfullLogger.log('it is disabled');
	// 	test.ok(!mustBeFalse,
	// 		'returns false when disabled');

	// 	test.done();
	// },

	// enabled_after_startup: function(test){
	// 	//create disabled
	// 	colorfullLogger = new ColorfullLogger({
	// 		enabled: false
	// 	});

	// 	//enable afterwards
	// 	colorfullLogger.configure({
	// 		enabled: true
	// 	});

	// 	fakeConsole.consoleCallback.consoleDebug = function(strResult) {
	// 		test.equal(TEN_CHARAC_MESSAGE, strResult,
	// 			'the message must be received by console.debug');
	// 		test.done();
	// 	};

	// 	colorfullLogger.log(TEN_CHARAC_MESSAGE);
	// },
};<|endoftext|>


<commit_before>(function (window, Worker, Blob, URL) {
    'use strict';
    var workerFactory = function (job) {

        var singleThreadWorker = function (jobFunc) {
            /* Single Thread Worker to support fallback in unsupported browsers */
            var worker = function () {
                this.result = null;
                this.error = null;
                this.postMessage = function (data) {
                    try {
                        this.result = jobFunc(data);
                    } catch (e) {
                        this.error = e;
                    }
                };
                this.close = function () {
                    this.result = null;
                    this.error = null;
                };
            }
            return new worker(job);
        };

        var multiThreadWorker = function (jobFunc) {
            /* Multi Thread Worker that uses 'Worker' object */
            var createBlob = function (response) {
                var blob, BlobBuilder;
                try {
                    blob = new Blob([response], { type: 'text/javascript' });
                } catch (e) { /* Backwards-compatibility */
                    BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder;
                    blob = new BlobBuilder();
                    blob.append(response);
                    blob = blob.getBlob();
                }

                return blob;
            },
            jobCode = 'onmessage = function (msg) { var job = ' + jobFunc.toString() + '; var result = job(msg.data); this.postMessage(result); };',
            blob = createBlob(jobCode),
            url = URL.createObjectURL(blob),
            workerInstance = new Worker(url);

            workerInstance.close = function () {
                this.terminate();
                if (blob && url) { /* Release the blob URL when closing Worker */
                    URL.revokeObjectURL(url);
                };
            }
            return workerInstance;
        };

        var create = function (parallel) {
            var workerInstance;
            try {
                workerInstance = (parallel === false) || !Worker ? singleThreadWorker(job) : multiThreadWorker(job);
            } catch (e) {     /* If Worker is not supported by the browser or causes issues, fallback to 'Single Thread Worker' mode */
                workerInstance = singleThreadWorker(job);
            }
            return workerInstance;
        }
        return { create: create };
    };

    var Thread = function (opt) {

        var self = this, options = opt || {}, workerInstance, thenPromise, failPromise;

        this.then = function (callback) {
            if (workerInstance.result) { /* Initialize then promise */
                callback.bind(this)(workerInstance.result);
            }
            thenPromise = callback;
            return this;
        };

        this.fail = function (callback) {
            if (workerInstance.error) { /* Initialize fail promise */
                callback.bind(this)(workerInstance.error);
            }
            failPromise = callback;
            return this;
        };

        this.start = function (data, job) {
            /* Initialize worker instance */
            workerInstance = workerFactory(job).create(options.parallel);

            workerInstance.onmessage = function (msg) {
                /* After executing the job, call 'then' promise */
                if (typeof thenPromise === 'function') {
                    thenPromise.bind(self)(msg.data);
                }
            };

            workerInstance.onerror = function (msg) {
                /* If error occurs while executing code call 'fail' promise */
                if (typeof failPromise === 'function') {
                    failPromise.bind(self)(msg);
                }
            };

            /* Start worker */
            workerInstance.postMessage(data);
            return this;
        };

        this.close = function () {
            workerInstance.close();
        };

        this.reStart = function (data) {
            /* Re-execute job with new parameters */
            if (workerInstance.result) {
                workerInstance.result = null;
            }
            if (workerInstance.error) {
                workerInstance.error = null;
            }
            workerInstance.postMessage(data);
            return this;
        };

        this.id = options.id;
    };

    if (!window.Thread) {
        /* Initialize global Thread object */
        window.Thread = Thread;
    }

    return Thread;

}(window, window.Worker, Blob, window.URL || window.webkitURL));<commit_msg>Refactoring code<commit_after>(function (window, Worker, Blob, URL) {
    'use strict';
    var WorkerFactory = function (job) {

        var singleThreadWorker = function (jobFunc) {
            /* Single Thread Worker to support fallback in unsupported browsers */
            var SingleWorker = function () {
                this.result = null;
                this.error = null;
                this.postMessage = function (data) {
                    try {
                        this.result = jobFunc(data);
                    } catch (e) {
                        this.error = e;
                    }
                };
                this.close = function () {
                    this.result = null;
                    this.error = null;
                };
            };
            return new SingleWorker(job);
        }, multiThreadWorker = function (jobFunc) {
            /* Multi Thread Worker that uses 'Worker' object */
            var createBlob = function (response) {
                var blob, BlobBuilder;
                try {
                    blob = new Blob([response], { type: 'text/javascript' });
                } catch (e) { /* Backwards-compatibility */
                    BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder;
                    blob = new BlobBuilder();
                    blob.append(response);
                    blob = blob.getBlob();
                }

                return blob;
            },
            jobCode = 'onmessage = function (msg) { var job = ' + jobFunc.toString() + '; var result = job(msg.data); this.postMessage(result); };',
            blob = createBlob(jobCode),
            url = URL.createObjectURL(blob),
            workerInstance = new Worker(url);

            workerInstance.close = function () {
                this.terminate();
                if (blob && url) { /* Release the blob URL when closing Worker */
                    URL.revokeObjectURL(url);
                }
            };
            return workerInstance;
        }, create = function (parallel) {
            var workerInstance;
            try {
                workerInstance = (parallel === false) || !Worker ? singleThreadWorker(job) : multiThreadWorker(job);
            } catch (e) { /* If Worker is not supported by the browser or causes issues, fallback to 'Single Thread Worker' mode */
                workerInstance = singleThreadWorker(job);
            }
            return workerInstance;
        };
        return { create: create };
    }, Thread = function (opt) {

        var self = this, options = opt || {}, workerInstance, thenPromise, failPromise;

        this.then = function (callback) {
            if (workerInstance.result) { /* Initialize then promise */
                callback.bind(this)(workerInstance.result);
            }
            thenPromise = callback;
            return this;
        };

        this.fail = function (callback) {
            if (workerInstance.error) { /* Initialize fail promise */
                callback.bind(this)(workerInstance.error);
            }
            failPromise = callback;
            return this;
        };

        this.start = function (data, job) {
            /* Initialize worker instance */
            workerInstance = WorkerFactory(job).create(options.parallel);

            workerInstance.onmessage = function (msg) {
                /* After executing the job, call 'then' promise */
                if (typeof thenPromise === 'function') {
                    thenPromise.bind(self)(msg.data);
                }
            };

            workerInstance.onerror = function (msg) {
                /* If error occurs while executing code call 'fail' promise */
                if (typeof failPromise === 'function') {
                    failPromise.bind(self)(msg);
                }
            };

            /* Start worker */
            workerInstance.postMessage(data);
            return this;
        };

        this.close = function () {
            workerInstance.close();
        };

        this.reStart = function (data) {
            /* Re-execute job with new parameters */
            if (workerInstance.result) {
                workerInstance.result = null;
            }
            if (workerInstance.error) {
                workerInstance.error = null;
            }
            workerInstance.postMessage(data);
            return this;
        };

        this.id = options.id;
    };

    if (!window.Thread) {
        /* Initialize global Thread object */
        window.Thread = Thread;
    }

    return Thread;

}(window, window.Worker, Blob, window.URL || window.webkitURL));<|endoftext|>


<commit_before>PLUGINS.imagemap = function(api, area, corner, adjustMethod)
{
	if(!area.jquery) { area = $(area); }

	var shape = area.attr('shape').toLowerCase().replace('poly', 'polygon'),
		image = $('img[usemap="#'+area.parent('map').attr('name')+'"]'),
		coordsString = area.attr('coords'),
		coordsArray = coordsString.split(','),
		imageOffset, coords, i, next, result, len;

	// If we can't find the image using the map...
	if(!image.length) { return FALSE; }

	// Pass coordinates string if polygon
	if(shape === 'polygon') {
		result = PLUGINS.polys.polygon(coordsArray, corner);
	}

	// Otherwise parse the coordinates and pass them as arguments
	else if(PLUGINS.polys[shape]) {
		for(i = -1, len = coordsArray.length, coords = []; ++i < len;) {
			coords.push( parseInt(coordsArray[i], 10) );
		}

		result = PLUGINS.polys[shape].apply(
			this, coords.concat(corner)
		);
	}

	// If no shapre calculation method was found, return false
	else { return FALSE; }

	// Make sure we account for padding and borders on the image
	imageOffset = image.offset();
	imageOffset.left += Math.ceil((image.outerWidth(FALSE) - image.width()) / 2);
	imageOffset.top += Math.ceil((image.outerHeight(FALSE) - image.height()) / 2);

	// Add image position to offset coordinates
	result.position.left += imageOffset.left;
	result.position.top += imageOffset.top;

	return result;
};<commit_msg>Ensure trailing commas in coords doesn't break positioning. Fixes #582<commit_after>PLUGINS.imagemap = function(api, area, corner, adjustMethod)
{
	if(!area.jquery) { area = $(area); }

	var shape = area.attr('shape').toLowerCase().replace('poly', 'polygon'),
		image = $('img[usemap="#'+area.parent('map').attr('name')+'"]'),
		coordsString = $.trim(area.attr('coords')),
		coordsArray = coordsString.replace(/,$/, '').split(','),
		imageOffset, coords, i, next, result, len;

	// If we can't find the image using the map...
	if(!image.length) { return FALSE; }

	// Pass coordinates string if polygon
	if(shape === 'polygon') {
		result = PLUGINS.polys.polygon(coordsArray, corner);
	}

	// Otherwise parse the coordinates and pass them as arguments
	else if(PLUGINS.polys[shape]) {
		for(i = -1, len = coordsArray.length, coords = []; ++i < len;) {
			coords.push( parseInt(coordsArray[i], 10) );
		}

		result = PLUGINS.polys[shape].apply(
			this, coords.concat(corner)
		);
	}

	// If no shapre calculation method was found, return false
	else { return FALSE; }

	// Make sure we account for padding and borders on the image
	imageOffset = image.offset();
	imageOffset.left += Math.ceil((image.outerWidth(FALSE) - image.width()) / 2);
	imageOffset.top += Math.ceil((image.outerHeight(FALSE) - image.height()) / 2);

	// Add image position to offset coordinates
	result.position.left += imageOffset.left;
	result.position.top += imageOffset.top;

	return result;
};<|endoftext|>


<commit_before>var fs = require('fs');
var util = require('util');
var libpb = require('libprofitbricks');
var SSH = require('simple-ssh');
var format = util.format;

var dummyCb = function() {};
// 5 seconds.
var SERVER_QUERY_INTERVAL = 5000;
// 3 minutes total.
var MAX_QUERY_ATTEMPTS = 36;

var PbServer = function(pb, ssh, logger) {
  this.pb = pb;
  this.ssh = ssh;
  if (logger) {
    this.logger = logger;
  }
  else {
    this.logger = console;
    this.logger.debug = this.logger.log;
  }
  this.stateChangeQueryInterval = pb.stateChangeQueryInterval ? pb.stateChangeQueryInterval : SERVER_QUERY_INTERVAL;
  this.maxStateChangeQueryAttempts = pb.maxStateChangeQueryAttempts ? pb.maxStateChangeQueryAttempts : MAX_QUERY_ATTEMPTS;
  this.sshKey = ssh.key ? fs.readFileSync(ssh.key) : null;

  libpb.setauth(this.pb.username, this.pb.password);
  libpb.setdepth(this.pb.depth);

  var self = this;
  var _tracked = function(command, serverToState, vmToState, message, cb) {
    cb = cb ? cb : dummyCb;
    var postCommand = function(err) {
      if (err) {
        cb(err);
      }
      else {
        var stateChangeCallback = function(err, data) {
          if (err) {
            cb(err);
          }
          else {
            self.logger.info(message);
            cb(null, data);
          }
        }
        self.serverStateChange(serverToState, vmToState, stateChangeCallback);
      }
    }
    self[command](postCommand);
  }

  this.startServerTracked = function(cb) {
    _tracked('startServer', 'AVAILABLE', 'RUNNING', 'Server started!', cb);
  }

  this.shutdownServerTracked = function(cb) {
    _tracked('shutdownServer', 'AVAILABLE', 'SHUTOFF', 'Server shut down!', cb);
  }

  this.stopServerTracked = function(cb) {
    _tracked('stopServer', 'INACTIVE', 'SHUTOFF', 'Server stopped!', cb);
  }
}

PbServer.prototype.listDatacenters = function(cb) {
  cb = cb ? cb : dummyCb;
  var apiCallback = function(err, resp, body) {
    if (err) {
      cb(err);
    }
    else {
      var data = JSON.parse(body);
      cb(null, data);
    }
  }
  this.logger.info("Getting datacenter info...");
  libpb.listDatacenters(apiCallback);
}

PbServer.prototype.listServers = function(cb) {
  cb = cb ? cb : dummyCb;
  var apiCallback = function(err, resp, body) {
    if (err) {
      cb(err);
    }
    else {
      var data = JSON.parse(body);
      cb(null, data);
    }
  }
  this.logger.info("Listing servers...");
  libpb.listServers(this.pb.datacenterId, apiCallback)
}

PbServer.prototype.getServer = function(cb) {
  cb = cb ? cb : dummyCb;
  var apiCallback = function(err, resp, body) {
    if (err) {
      cb(err);
    }
    else {
      var data = JSON.parse(body);
      cb(null, data);
    }
  }
  this.logger.info("Getting server status...");
  libpb.getServer(this.pb.datacenterId, this.pb.serverId, apiCallback)
}

PbServer.prototype.startServer = function(cb) {
  this.logger.info("Starting server...");
  libpb.startServer(this.pb.datacenterId, this.pb.serverId, cb)
}

PbServer.prototype.stopServer = function(cb) {
  this.logger.info("Powering off server...");
  libpb.stopServer(this.pb.datacenterId, this.pb.serverId, cb)
}

PbServer.prototype.shutdownServer = function(cb) {
  var self = this;
  cb = cb ? cb : dummyCb;
  this.logger.info("Shutting down server...");
  var ssh = new SSH({
    host: this.ssh.host,
    port: this.ssh.port,
    user: this.ssh.user,
    key: this.sshKey,
  });
  var exit = function(code, stdout, stderr) {
    self.logger.debug(format("SSH command exit code: %s", code));
    if (code === 0) {
      cb(null, code);
    }
    else {
      cb(format('returned with error code: %d, %s', code, stderr));
    }
  }
  var execConfig = {
    exit: exit,
  };
  var startConfig = {
    success: function() {
      self.logger.debug("SSH connection successful...");
    },
    fail: function(err) {
      self.logger.debug(format("SSH connection failed: %s", err));
      cb(err);
    },
  }
  ssh.exec('shutdown -P now shutdown-now&', execConfig).start(startConfig);
}

PbServer.prototype.serverStateChange = function(serverToState, vmToState, cb) {
  var self = this;
  cb = cb ? cb : dummyCb;
  var count = 1;
  var checkState = function(err, data) {
    if (err) {
      self.logger.error(format("ERROR: %s", err));
      cb(err);
    }
    else {
      if (count > self.maxStateChangeQueryAttempts) {
        clearInterval(serverStateChange);
        var message = "Max attempts exceeded.";
        self.logger.error(message);
        cb(message);
      }
      else {
        var serverState = data.metadata.state;
        var vmState = data.properties.vmState;
        self.logger.debug(format("Attempt #%d", count));
        self.logger.debug("-------------------------------------");
        self.logger.debug(format("Power state: %s", serverState));
        self.logger.debug(format("Server state: %s", vmState));
        self.logger.debug("-------------------------------------");
        if (serverState == serverToState && vmState == vmToState) {
          self.logger.info(format("State change to (%s, %s) complete", serverToState , vmToState));
          clearInterval(serverStateChange);
          cb(null, data);
        }
        count++;
      }
    }
  }
  var get = function() {
    self.getServer(checkState);
  }
  this.logger.info(format("Waiting for server state to change to (%s, %s)", serverToState , vmToState));
  get();
  var serverStateChange = setInterval(get, this.stateChangeQueryInterval);
}

PbServer.prototype.checkCommand = function(command, cb) {
  var self = this;
  cb = cb ? cb : dummyCb;
  var count = 1;
  // This prevents overlapping checks and messages.
  var timeout = this.stateChangeQueryInterval - 1000;
  this.logger.debug(format("Checking command: %s", command));
  this.logger.debug(format("SSH connection timeout set to %d milliseconds", timeout));
  var exit = function(code, stdout, stderr) {
    if (code === 0) {
      clearInterval(checkCommand);
      self.logger.info("Command succeeded");
      cb(null, code);
    }
    else {
      self.logger.debug(format('Command returned with error code: %d, %s', code, stderr));
    }
  }
  var execConfig = {
    exit: exit,
  };
  var startConfig = {
    success: function() {
      self.logger.debug("SSH connection successful...");
    },
    fail: function(err) {
      self.logger.debug(format("SSH connection failed: %s", err));
    },
  }
  var check = function() {
    if (count > self.maxStateChangeQueryAttempts) {
      clearInterval(checkCommand);
      var message = "Max attempts exceeded.";
      self.logger.error(message);
      cb(message);
    }
    else {
      self.logger.debug(format("Attempt #%d", count));
      var ssh = new SSH({
        host: self.ssh.host,
        port: self.ssh.port,
        user: self.ssh.user,
        key: self.sshKey,
        timeout: timeout,
      });
      ssh.exec(command, execConfig).start(startConfig);
      count++;
    }
  }
  check();
  var checkCommand = setInterval(check, this.stateChangeQueryInterval);
}

if (module.exports) {
  module.exports = PbServer;
}
<commit_msg>better labels for status change debug output.<commit_after>var fs = require('fs');
var util = require('util');
var libpb = require('libprofitbricks');
var SSH = require('simple-ssh');
var format = util.format;

var dummyCb = function() {};
// 5 seconds.
var SERVER_QUERY_INTERVAL = 5000;
// 3 minutes total.
var MAX_QUERY_ATTEMPTS = 36;

var PbServer = function(pb, ssh, logger) {
  this.pb = pb;
  this.ssh = ssh;
  if (logger) {
    this.logger = logger;
  }
  else {
    this.logger = console;
    this.logger.debug = this.logger.log;
  }
  this.stateChangeQueryInterval = pb.stateChangeQueryInterval ? pb.stateChangeQueryInterval : SERVER_QUERY_INTERVAL;
  this.maxStateChangeQueryAttempts = pb.maxStateChangeQueryAttempts ? pb.maxStateChangeQueryAttempts : MAX_QUERY_ATTEMPTS;
  this.sshKey = ssh.key ? fs.readFileSync(ssh.key) : null;

  libpb.setauth(this.pb.username, this.pb.password);
  libpb.setdepth(this.pb.depth);

  var self = this;
  var _tracked = function(command, serverToState, vmToState, message, cb) {
    cb = cb ? cb : dummyCb;
    var postCommand = function(err) {
      if (err) {
        cb(err);
      }
      else {
        var stateChangeCallback = function(err, data) {
          if (err) {
            cb(err);
          }
          else {
            self.logger.info(message);
            cb(null, data);
          }
        }
        self.serverStateChange(serverToState, vmToState, stateChangeCallback);
      }
    }
    self[command](postCommand);
  }

  this.startServerTracked = function(cb) {
    _tracked('startServer', 'AVAILABLE', 'RUNNING', 'Server started!', cb);
  }

  this.shutdownServerTracked = function(cb) {
    _tracked('shutdownServer', 'AVAILABLE', 'SHUTOFF', 'Server shut down!', cb);
  }

  this.stopServerTracked = function(cb) {
    _tracked('stopServer', 'INACTIVE', 'SHUTOFF', 'Server stopped!', cb);
  }
}

PbServer.prototype.listDatacenters = function(cb) {
  cb = cb ? cb : dummyCb;
  var apiCallback = function(err, resp, body) {
    if (err) {
      cb(err);
    }
    else {
      var data = JSON.parse(body);
      cb(null, data);
    }
  }
  this.logger.info("Getting datacenter info...");
  libpb.listDatacenters(apiCallback);
}

PbServer.prototype.listServers = function(cb) {
  cb = cb ? cb : dummyCb;
  var apiCallback = function(err, resp, body) {
    if (err) {
      cb(err);
    }
    else {
      var data = JSON.parse(body);
      cb(null, data);
    }
  }
  this.logger.info("Listing servers...");
  libpb.listServers(this.pb.datacenterId, apiCallback)
}

PbServer.prototype.getServer = function(cb) {
  cb = cb ? cb : dummyCb;
  var apiCallback = function(err, resp, body) {
    if (err) {
      cb(err);
    }
    else {
      var data = JSON.parse(body);
      cb(null, data);
    }
  }
  this.logger.info("Getting server status...");
  libpb.getServer(this.pb.datacenterId, this.pb.serverId, apiCallback)
}

PbServer.prototype.startServer = function(cb) {
  this.logger.info("Starting server...");
  libpb.startServer(this.pb.datacenterId, this.pb.serverId, cb)
}

PbServer.prototype.stopServer = function(cb) {
  this.logger.info("Powering off server...");
  libpb.stopServer(this.pb.datacenterId, this.pb.serverId, cb)
}

PbServer.prototype.shutdownServer = function(cb) {
  var self = this;
  cb = cb ? cb : dummyCb;
  this.logger.info("Shutting down server...");
  var ssh = new SSH({
    host: this.ssh.host,
    port: this.ssh.port,
    user: this.ssh.user,
    key: this.sshKey,
  });
  var exit = function(code, stdout, stderr) {
    self.logger.debug(format("SSH command exit code: %s", code));
    if (code === 0) {
      cb(null, code);
    }
    else {
      cb(format('returned with error code: %d, %s', code, stderr));
    }
  }
  var execConfig = {
    exit: exit,
  };
  var startConfig = {
    success: function() {
      self.logger.debug("SSH connection successful...");
    },
    fail: function(err) {
      self.logger.debug(format("SSH connection failed: %s", err));
      cb(err);
    },
  }
  ssh.exec('shutdown -P now shutdown-now&', execConfig).start(startConfig);
}

PbServer.prototype.serverStateChange = function(serverToState, vmToState, cb) {
  var self = this;
  cb = cb ? cb : dummyCb;
  var count = 1;
  var checkState = function(err, data) {
    if (err) {
      self.logger.error(format("ERROR: %s", err));
      cb(err);
    }
    else {
      if (count > self.maxStateChangeQueryAttempts) {
        clearInterval(serverStateChange);
        var message = "Max attempts exceeded.";
        self.logger.error(message);
        cb(message);
      }
      else {
        var serverState = data.metadata.state;
        var vmState = data.properties.vmState;
        self.logger.debug(format("Attempt #%d", count));
        self.logger.debug("-------------------------------------");
        self.logger.debug(format("Server state: %s", serverState));
        self.logger.debug(format("VM state: %s", vmState));
        self.logger.debug("-------------------------------------");
        if (serverState == serverToState && vmState == vmToState) {
          self.logger.info(format("State change to (%s, %s) complete", serverToState , vmToState));
          clearInterval(serverStateChange);
          cb(null, data);
        }
        count++;
      }
    }
  }
  var get = function() {
    self.getServer(checkState);
  }
  this.logger.info(format("Waiting for server state to change to (%s, %s)", serverToState , vmToState));
  get();
  var serverStateChange = setInterval(get, this.stateChangeQueryInterval);
}

PbServer.prototype.checkCommand = function(command, cb) {
  var self = this;
  cb = cb ? cb : dummyCb;
  var count = 1;
  // This prevents overlapping checks and messages.
  var timeout = this.stateChangeQueryInterval - 1000;
  this.logger.debug(format("Checking command: %s", command));
  this.logger.debug(format("SSH connection timeout set to %d milliseconds", timeout));
  var exit = function(code, stdout, stderr) {
    if (code === 0) {
      clearInterval(checkCommand);
      self.logger.info("Command succeeded");
      cb(null, code);
    }
    else {
      self.logger.debug(format('Command returned with error code: %d, %s', code, stderr));
    }
  }
  var execConfig = {
    exit: exit,
  };
  var startConfig = {
    success: function() {
      self.logger.debug("SSH connection successful...");
    },
    fail: function(err) {
      self.logger.debug(format("SSH connection failed: %s", err));
    },
  }
  var check = function() {
    if (count > self.maxStateChangeQueryAttempts) {
      clearInterval(checkCommand);
      var message = "Max attempts exceeded.";
      self.logger.error(message);
      cb(message);
    }
    else {
      self.logger.debug(format("Attempt #%d", count));
      var ssh = new SSH({
        host: self.ssh.host,
        port: self.ssh.port,
        user: self.ssh.user,
        key: self.sshKey,
        timeout: timeout,
      });
      ssh.exec(command, execConfig).start(startConfig);
      count++;
    }
  }
  check();
  var checkCommand = setInterval(check, this.stateChangeQueryInterval);
}

if (module.exports) {
  module.exports = PbServer;
}
<|endoftext|>


<commit_before>
/**
 * ::Point (<Number:x>, <Number:y>)
 *
 * Returns instance of Point in 2-dimension space (x, y)
 *
 * Sample Usage:
 *   new Point(0, 0) => // Point
 *   new Point(1, 1) => // Point
 *
 * @param {Number}  x  X coordinate
 * @param {Number}  y  Y coordinate
 */
function Point (x, y) {
  if (! [x, y].every(function (input) { return typeof input === 'number'; })) {
    throw new Error('Point(x, y) X and Y must be of Number type');
  }
  this.x = x;
  this.y = y;
}

/**
 * ::Relation (<Point:source>, <Point:target>)
 *
 * Returns instance of Relation that defines relation between two Points
 *
 * Sample Usage:
 *   new Relation(point, point) => // Relation
 *
 * @param {Point}  source  Source Point
 * @param {Point}  target  Destination Point
 */
function Relation (source, target) {
  if (! [source, target].every(function (input) { return input instanceof Point; })) {
    throw new Error('Relation(source, target) Source and Target must be of Point type');
  }
  this.source = source;
  this.target = target;
}

/**
 * ::Matrix (<Array:Point>)
 *
 * Returns Matrix instance, might be loaded with predefined points.
 * Matrix contains of points and relations if needed.
 *
 * Sample Usage:
 *   new Matrix(points) // => Matrix
 *
 * @param {Array}  points  An array of Point objects
 */
function Matrix (points) {
  this.points = points || [];
  this.relations = [];
}

/**
 * ::Matrix::addPoint (<Point:point>)
 *
 * Adds Point to the matrix
 *
 * Sample Usage:
 *   matrix.addPoint(new Point(...)) // => Matrix
 *   matrix
 *     .addPoint(new Point(...))
 *     .addPoint(new Point(...))
 *     // => Matrix
 *
 * @param  {Array}   point   An array of Point objects to be added to the matrix
 * @return {Matrix}          Returns matrix object
 */
Matrix.prototype.addPoint = function (point) {
  if (! point instanceof Point) { throw new Error('Matrix::addPoint Tried to add object of wrong type. Point instance should be used instead.'); }
  this.points.push(point);
  return this;
};

/**
 * ::Matrix::addPoints (<Array:Points|Point>)
 *
 * Triggers Matrix::addPoint with each element of points array.
 * Works with one Point object as well.
 *
 * Sample Usage:
 *   matrix.addPoints( point )
 *   matrix.addPoints( [ point, point, point ] )
 *
 * @param {Array|Point}  points  Points to be added to the matrix
 * @return {Matrix}              Returns matrix object
 */
Matrix.prototype.addPoints = function (points) {
  if (!Array.isArray(points)) { points = [ points ]; }
  points.forEach(this.addPoint.bind(this));
  return this;
};

/**
 * ::Matrix::addRelation (<Relation:relation>)
 *
 * Adds Relation to the matrix
 *
 * Sample Usage:
 *   matrix.addRelation(new Relation(...)) // => Matrix
 *   matrix
 *     .addRelation(new Relation(...))
 *     .addRelation(new Relation(...))
 *     // => Matrix
 *
 * @param  {Array}   relation  An array of Relation objects to be added to the matrix
 * @return {Matrix}            Returns matrix object
 */
Matrix.prototype.addRelation = function (relation) {
  if (! relation instanceof Relation) { throw new Error('Matrix::addRelation Tried to add object of wrong type. Relation instance should be used instead.'); }
  this.relations.push(relation);
  return this;
};

/**
 * Matrix::addRelations (<Array:rRelations|Relation>)
 *
 * Triggers Matrix::addRelation with each element of relations array.
 * Works with one Relation object as well.
 *
 * Sample Usage:
 *   matrix.addRelations( relation )
 *   matrix.addRelations( [ relation, relation, relation ] )
 *
 * @param {Array|Relation}  relations  Relations to be added to the matrix
 * @return {Matrix}              Returns matrix object
 */
Matrix.prototype.addRelations = function (relations) {
  if (!Array.isArray(relations)) { relations = [ relations ]; }
  relations.forEach(this.addRelation.bind(this));
  return this;
};

//////////////////////////////////////////////////////////////////

function MatrixCalculator (matrix) {
  if (! matrix instanceof Matrix) { throw new Error('MatrixCalculator::constructor Calculator must be provided with Matrix instance'); }
  this.matrix = matrix;
}

MatrixCalculator.prototype.findShortestPath = function (startingPoint, endingPoint) {
  if (! [startingPoint, endingPoint].every(function (input) { return input instanceof Point; })) {
    throw new Error('Relation(source, target) Source and Target must be of Point type');
  }
  console.log('path found');
};

//////////////////////////////////////////////////////////////////

var points = [
  new Point(0,0),
  new Point(2,2),
  new Point(3,1),
  new Point(5,-1),
  new Point(1,-1),
  new Point(4,-3)
];

var relations = [
  new Relation(points[0], points[1]),
  new Relation(points[0], points[2]),
  new Relation(points[2], points[3]),
  new Relation(points[2], points[4]),
  new Relation(points[3], points[5]),
  new Relation(points[4], points[5])
];

var matrix = new Matrix();
var matrixCalculator = new MatrixCalculator(matrix);

matrix.addPoints(points);
matrix.addRelations(relations);

console.log('::Matrix', matrix);
console.log('::MatrixCalculator', matrixCalculator);

matrixCalculator.findShortestPath(matrixCalculator.matrix.points[0], matrixCalculator.matrix.points[1]);

<commit_msg>Provide correct documentation.<commit_after>
/**
 * ::Point (<Number:x>, <Number:y>)
 *
 * Returns instance of Point in 2-dimension space (x, y)
 *
 * Sample Usage:
 *   new Point(0, 0) => // Point
 *   new Point(1, 1) => // Point
 *
 * @param {Number}  x  X coordinate
 * @param {Number}  y  Y coordinate
 */
function Point (x, y) {
  if (! [x, y].every(function (input) { return typeof input === 'number'; })) {
    throw new Error('Point(x, y) X and Y must be of Number type');
  }
  this.x = x;
  this.y = y;
}

/**
 * ::Relation (<Point:source>, <Point:target>)
 *
 * Returns instance of Relation that defines relation between two Points
 *
 * Sample Usage:
 *   new Relation(point, point) => // Relation
 *
 * @param {Point}  source  Source Point
 * @param {Point}  target  Destination Point
 */
function Relation (source, target) {
  if (! [source, target].every(function (input) { return input instanceof Point; })) {
    throw new Error('Relation(source, target) Source and Target must be of Point type');
  }
  this.source = source;
  this.target = target;
}

/**
 * ::Matrix (<Array:points>)
 *
 * Returns Matrix instance, might be loaded with predefined points.
 * Matrix contains of points and relations if needed.
 *
 * Sample Usage:
 *   new Matrix(points) // => Matrix
 *
 * @param {Array}  points  An array of Point objects
 */
function Matrix (points) {
  this.points = points || [];
  this.relations = [];
}

/**
 * ::Matrix::addPoint (<Point:point>)
 *
 * Adds Point to the matrix
 *
 * Sample Usage:
 *   matrix.addPoint(new Point(...)) // => Matrix
 *   matrix
 *     .addPoint(new Point(...))
 *     .addPoint(new Point(...))
 *     // => Matrix
 *
 * @param  {Point}   point   An array of Point objects to be added to the matrix
 * @return {Matrix}          Returns matrix object
 */
Matrix.prototype.addPoint = function (point) {
  if (! point instanceof Point) { throw new Error('Matrix::addPoint Tried to add object of wrong type. Point instance should be used instead.'); }
  this.points.push(point);
  return this;
};

/**
 * ::Matrix::addPoints (<Array:points|Point>)
 *
 * Triggers Matrix::addPoint with each element of points array.
 * Works with one Point object as well.
 *
 * Sample Usage:
 *   matrix.addPoints( point )
 *   matrix.addPoints( [ point, point, point ] )
 *
 * @param  {Array|Point}  points  Points to be added to the matrix
 * @return {Matrix}               Returns matrix object
 */
Matrix.prototype.addPoints = function (points) {
  if (!Array.isArray(points)) { points = [ points ]; }
  points.forEach(this.addPoint.bind(this));
  return this;
};

/**
 * ::Matrix::addRelation (<Relation:relation>)
 *
 * Adds Relation to the matrix
 *
 * Sample Usage:
 *   matrix.addRelation(new Relation(...)) // => Matrix
 *   matrix
 *     .addRelation(new Relation(...))
 *     .addRelation(new Relation(...))
 *     // => Matrix
 *
 * @param  {Relation}  relation  An array of Relation objects to be added to the matrix
 * @return {Matrix}              Returns matrix object
 */
Matrix.prototype.addRelation = function (relation) {
  if (! relation instanceof Relation) { throw new Error('Matrix::addRelation Tried to add object of wrong type. Relation instance should be used instead.'); }
  this.relations.push(relation);
  return this;
};

/**
 * Matrix::addRelations (<Array:relations|Relation>)
 *
 * Triggers Matrix::addRelation with each element of relations array.
 * Works with one Relation object as well.
 *
 * Sample Usage:
 *   matrix.addRelations( relation )
 *   matrix.addRelations( [ relation, relation, relation ] )
 *
 * @param  {Array|Relation}  relations  Relations to be added to the matrix
 * @return {Matrix}                     Returns matrix object
 */
Matrix.prototype.addRelations = function (relations) {
  if (!Array.isArray(relations)) { relations = [ relations ]; }
  relations.forEach(this.addRelation.bind(this));
  return this;
};

//////////////////////////////////////////////////////////////////

function MatrixCalculator (matrix) {
  if (! matrix instanceof Matrix) { throw new Error('MatrixCalculator::constructor Calculator must be provided with Matrix instance'); }
  this.matrix = matrix;
}

MatrixCalculator.prototype.findShortestPath = function (startingPoint, endingPoint) {
  if (! [startingPoint, endingPoint].every(function (input) { return input instanceof Point; })) {
    throw new Error('Relation(source, target) Source and Target must be of Point type');
  }
  console.log('path found');
};

//////////////////////////////////////////////////////////////////

var points = [
  new Point(0,0),
  new Point(2,2),
  new Point(3,1),
  new Point(5,-1),
  new Point(1,-1),
  new Point(4,-3)
];

var relations = [
  new Relation(points[0], points[1]),
  new Relation(points[0], points[2]),
  new Relation(points[2], points[3]),
  new Relation(points[2], points[4]),
  new Relation(points[3], points[5]),
  new Relation(points[4], points[5])
];

var matrix = new Matrix();
var matrixCalculator = new MatrixCalculator(matrix);

matrix.addPoints(points);
matrix.addRelations(relations);

console.log('::Matrix', matrix);
console.log('::MatrixCalculator', matrixCalculator);

matrixCalculator.findShortestPath(matrixCalculator.matrix.points[0], matrixCalculator.matrix.points[1]);

<|endoftext|>


<commit_before>var express = require('express');
var request = require('superagent');
var dbUtils = require('../utils/dbUtils');
var qiniuUtils = require('../utils/qiniuUtils');
var config = require('../configs/config');
var cookie = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36' };

var router = express.Router();

/**
 * v1 默认返回最新的bing壁纸信息
 */
router.get('/', function(req, res, next) {
    v1(req, res, next);
});
router.post('/', function(req, res, next) {
    v1(req, res, next);
});

var v1 = function(req, res, next) {
    var d = req.query.d || req.body.d;
    var w = req.query.w || req.body.w || '1366';
    var h = req.query.h || req.body.h || '768';
    var t = req.query.type || req.body.type;
    var size = w + 'x' + h;
    var cb = req.query.callback;
    var enddate = 0;
    if (!isNaN(d)) {
        var date = new Date().getTime() - parseInt(d) * 1000 * 60 * 60 * 24;
        var newDate = new Date(date);
        enddate = newDate.getFullYear() + '' + ((newDate.getMonth() + 1) > 9 ? (newDate.getMonth() + 1) : '0' + (newDate.getMonth() + 1)) + '' + (newDate.getDate() > 9 ? newDate.getDate() : '0' + newDate.getDate());
    }
    var params = {
        page: {
            no: 1,
            size: 1
        },
        body: {}
    };
    if (!!enddate) {
        params['body'] = {
            enddate: enddate
        }
    }
    dbUtils.get('bing', params, function(rows) {
        if (rows.length > 0) {
            var data = rows[0];
            if (t === 'json' || !!cb) {
                var output = {
                    data: {
                        enddate: data.enddate,
                        url: data.url,
                        bmiddle_pic: data.bmiddle_pic,
                        original_pic: data.original_pic,
                        thumbnail_pic: data.thumbnail_pic,
                    },
                    status: {
                        code: 200,
                        message: ''
                    }
                };
                if (cb) {
                    res.jsonp(output);
                } else {
                    res.json(output);
                }
            } else {

                if (config.resolutions.indexOf(size) === -1) {
                    data['url'] = config.global_http() + '/bing/' + data.qiniu_url + '_' + size + '.jpg';
                } else {
                    data['url'] = qiniuUtils.imageView(data.qiniu_url, w, h);
                }
                request.get(data['url'])
                    .set({
                        'user-agent': req.headers['user-agent'],
                        'referer': req.headers['host']
                    })
                    .end(function(err, response) {
                        if (err) {
                            res.send(err)
                        } else {
                            res.header('content-type', 'image/jpeg');
                            res.send(response.body);
                        }
                    });
            }
        } else {
            res.json({
                data: {},
                status: {
                    code: -1,
                    message: '好厉害啊，这么隐秘的地方都被你发现了n(*≧▽≦*)n，我已经通知主人咯♪(＾∀＾●)ﾉ，请您稍后再来试一下吧(❤´艸｀❤)!'
                }
            });
        }
    });
}

/**
 * 获得随机图片
 */
router.get('/rand', function(req, res, next) {
    random(req, res, next);
});
router.post('/rand', function(req, res, next) {
    random(req, res, next);
});

var random = function(req, res, next) {
    var t = req.query.type || req.body.type;
    var w = req.query.w || req.body.w || '1366';
    var h = req.query.h || req.body.h || '768';
    var size = w + 'x' + h;
    var callback = req.query.callback || req.body.callback;
    dbUtils.getCount('bing', {}, function(rows) {
        if (rows.length > 0) {
            var sum = Number(rows[0].sum);
            var rand = Math.floor(Math.random() * (sum - 1) + 1);
            dbUtils.get('bing', {
                id: rand
            }, function(rs) {
                if (rs.length > 0) {
                    var data = rs[0];
                    if (t === 'json' || !!callback) {
                        var output = {
                            data: {
                                enddate: data.enddate,
                                url: data.url,
                                bmiddle_pic: data.bmiddle_pic,
                                original_pic: data.original_pic,
                                thumbnail_pic: data.thumbnail_pic,
                            },
                            status: {
                                code: 200,
                                message: ''
                            }
                        };
                        if (callback) {
                            res.jsonp(output);
                        } else {
                            res.json(output);
                        }
                    } else {
                        var data = rs[0];
                        if (config.resolutions.indexOf(size) === -1) {
                            data['url'] = config.global_http() + '/bing/' + data.qiniu_url + '_' + size + '.jpg';
                        } else {
                            data['url'] = qiniuUtils.imageView(data.qiniu_url, w, h);
                        }
                        request.get(data['url'])
                            .set({
                                'user-agent': req.headers['user-agent'],
                                'referer': req.headers['host']
                            })
                            .end(function(err, response) {
                                if (err) {
                                    res.send(err)
                                } else {
                                    res.header('content-type', 'image/jpeg');
                                    res.send(response.body);
                                }
                            });
                    }
                } else {
                    var parmas = '?';
                    params += !!t ? '&t=' + t : '';
                    params += !!w ? '&w=' + w : '';
                    params += !!h ? '&h=' + h : '';
                    params += !!callback ? '&callback=' + callback : '';
                    res.redirect('/v1/rand' + params);
                }
            });
        } else {
            var parmas = '?';
            params += !!t ? '&t=' + t : '';
            params += !!w ? '&w=' + w : '';
            params += !!h ? '&h=' + h : '';
            params += !!callback ? '&callback=' + callback : '';
            res.redirect('/v1/rand' + params);
        }
    });
}

/**
 * 获取高斯模糊图片
 */
router.get('/blur', function(req, res, next) {
    blur(req, res, next);
});
router.post('/blur', function(req, res, next) {
    blur(req, res, next);
});

var blur = function(req, res, next) {
    var d = req.query.d || req.body.d;
    var w = req.query.w || req.body.w || 1366;
    var h = req.query.h || req.body.h || 768;
    var r = req.query.r || req.body.r;
    r = isNaN(r) ? 10 : parseInt(r) > 50 ? 50 : parseInt(r) <= 0 ? 1 : r;
    var size = w + 'x' + h;
    var enddate = 0;
    if (!isNaN(d)) {
        var date = new Date().getTime() - parseInt(d) * 1000 * 60 * 60 * 24;
        var newDate = new Date(date);
        enddate = newDate.getFullYear() + '' + ((newDate.getMonth() + 1) > 9 ? (newDate.getMonth() + 1) : '0' + (newDate.getMonth() + 1)) + '' + (newDate.getDate() > 9 ? newDate.getDate() : '0' + newDate.getDate());
    }
    var params = {
        page: {
            no: 1,
            size: 1
        },
        body: {}
    };
    if (!!enddate) {
        params.body = {
            enddate: enddate
        }
    }
    dbUtils.get('bing', params, function(rows) {
        if (rows.length > 0) {
            var data = rows[0];
            var base = config.global_http() + '/bing/';
            if (config.resolutions.indexOf(size) > -1) {
                data['url'] = base + data.qiniu_url + '_' + size + '.jpg';
            }
            var qiniu_url = /^(http|https)/.test(data.url) ? data.url : base + data.qiniu_url + '_1920x1080.jpg';
            qiniu_url += '?imageMogr2/blur/' + r + 'x50'
            request.get(qiniu_url)
                .set({
                    'user-agent': req.headers['user-agent'],
                    'referer': req.headers['host']
                })
                .end(function(err, response) {
                    if (err) {
                        res.send(err)
                    } else {
                        res.header('content-type', 'image/jpg');
                        res.send(response.body);
                    }
                });
        } else {
            res.json({
                data: {},
                status: {
                    code: -1,
                    message: '好厉害啊，这么隐秘的地方都被你发现了n(*≧▽≦*)n，我已经通知主人咯♪(＾∀＾●)ﾉ，请您稍后再来试一下吧(❤´艸｀❤)!'
                }
            });
        }
    });
}

module.exports = router;<commit_msg>get base data<commit_after>var express = require('express');
var request = require('superagent');
var dbUtils = require('../utils/dbUtils');
var qiniuUtils = require('../utils/qiniuUtils');
var config = require('../configs/config');
var cookie = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36' };

var router = express.Router();

/**
 * v1 默认返回最新的bing壁纸信息
 */
router.get('/', function(req, res, next) {
    v1(req, res, next);
});
router.post('/', function(req, res, next) {
    v1(req, res, next);
});

var v1 = function(req, res, next) {
    var d = req.query.d || req.body.d;
    var w = req.query.w || req.body.w || '1366';
    var h = req.query.h || req.body.h || '768';
    var t = req.query.type || req.body.type;
    var size = w + 'x' + h;
    var cb = req.query.callback;
    var enddate = 0;
    if (!isNaN(d)) {
        var date = new Date().getTime() - parseInt(d) * 1000 * 60 * 60 * 24;
        var newDate = new Date(date);
        enddate = newDate.getFullYear() + '' + ((newDate.getMonth() + 1) > 9 ? (newDate.getMonth() + 1) : '0' + (newDate.getMonth() + 1)) + '' + (newDate.getDate() > 9 ? newDate.getDate() : '0' + newDate.getDate());
    }
    var params = {
        page: {
            no: 1,
            size: 1
        },
        body: {}
    };
    if (!!enddate) {
        params['body'] = {
            enddate: enddate
        }
    }
    dbUtils.get('bing', params, function(rows) {
        if (rows.length > 0) {
            var data = rows[0];
            if (t === 'json' || !!cb) {
                var output = {
                    data: {
                        enddate: data.enddate,
                        url: data.url,
                        bmiddle_pic: data.bmiddle_pic,
                        original_pic: data.original_pic,
                        thumbnail_pic: data.thumbnail_pic,
                    },
                    status: {
                        code: 200,
                        message: ''
                    }
                };
                if (cb) {
                    res.jsonp(output);
                } else {
                    res.json(output);
                }
            } else {

                if (config.resolutions.indexOf(size) === -1) {
                    data['url'] = qiniuUtils.imageView(data.qiniu_url, w, h);
                } else {
                    data['url'] = config.global_http() + '/bing/' + data.qiniu_url + '_' + size + '.jpg';
                }
                request.get(data['url'])
                    .set({
                        'user-agent': req.headers['user-agent'],
                        'referer': req.headers['host']
                    })
                    .end(function(err, response) {
                        if (err) {
                            res.send(err)
                        } else {
                            res.header('content-type', 'image/jpeg');
                            res.send(response.body);
                        }
                    });
            }
        } else {
            res.json({
                data: {},
                status: {
                    code: -1,
                    message: '好厉害啊，这么隐秘的地方都被你发现了n(*≧▽≦*)n，我已经通知主人咯♪(＾∀＾●)ﾉ，请您稍后再来试一下吧(❤´艸｀❤)!'
                }
            });
        }
    });
}

/**
 * 获得随机图片
 */
router.get('/rand', function(req, res, next) {
    random(req, res, next);
});
router.post('/rand', function(req, res, next) {
    random(req, res, next);
});

var random = function(req, res, next) {
    var t = req.query.type || req.body.type;
    var w = req.query.w || req.body.w || '1366';
    var h = req.query.h || req.body.h || '768';
    var size = w + 'x' + h;
    var callback = req.query.callback || req.body.callback;
    dbUtils.getCount('bing', {}, function(rows) {
        if (rows.length > 0) {
            var sum = Number(rows[0].sum);
            var rand = Math.floor(Math.random() * (sum - 1) + 1);
            dbUtils.get('bing', {
                id: rand
            }, function(rs) {
                if (rs.length > 0) {
                    var data = rs[0];
                    if (t === 'json' || !!callback) {
                        var output = {
                            data: {
                                enddate: data.enddate,
                                url: data.url,
                                bmiddle_pic: data.bmiddle_pic,
                                original_pic: data.original_pic,
                                thumbnail_pic: data.thumbnail_pic,
                            },
                            status: {
                                code: 200,
                                message: ''
                            }
                        };
                        if (callback) {
                            res.jsonp(output);
                        } else {
                            res.json(output);
                        }
                    } else {
                        var data = rs[0];
                        if (config.resolutions.indexOf(size) === -1) {
                            data['url'] = qiniuUtils.imageView(data.qiniu_url, w, h);
                        } else {
                            data['url'] = config.global_http() + '/bing/' + data.qiniu_url + '_' + size + '.jpg';
                        }
                        request.get(data['url'])
                            .set({
                                'user-agent': req.headers['user-agent'],
                                'referer': req.headers['host']
                            })
                            .end(function(err, response) {
                                if (err) {
                                    res.send(err)
                                } else {
                                    res.header('content-type', 'image/jpeg');
                                    res.send(response.body);
                                }
                            });
                    }
                } else {
                    var parmas = '?';
                    params += !!t ? '&t=' + t : '';
                    params += !!w ? '&w=' + w : '';
                    params += !!h ? '&h=' + h : '';
                    params += !!callback ? '&callback=' + callback : '';
                    res.redirect('/v1/rand' + params);
                }
            });
        } else {
            var parmas = '?';
            params += !!t ? '&t=' + t : '';
            params += !!w ? '&w=' + w : '';
            params += !!h ? '&h=' + h : '';
            params += !!callback ? '&callback=' + callback : '';
            res.redirect('/v1/rand' + params);
        }
    });
}

/**
 * 获取高斯模糊图片
 */
router.get('/blur', function(req, res, next) {
    blur(req, res, next);
});
router.post('/blur', function(req, res, next) {
    blur(req, res, next);
});

var blur = function(req, res, next) {
    var d = req.query.d || req.body.d;
    var w = req.query.w || req.body.w || 1366;
    var h = req.query.h || req.body.h || 768;
    var r = req.query.r || req.body.r;
    r = isNaN(r) ? 10 : parseInt(r) > 50 ? 50 : parseInt(r) <= 0 ? 1 : r;
    var size = w + 'x' + h;
    var enddate = 0;
    if (!isNaN(d)) {
        var date = new Date().getTime() - parseInt(d) * 1000 * 60 * 60 * 24;
        var newDate = new Date(date);
        enddate = newDate.getFullYear() + '' + ((newDate.getMonth() + 1) > 9 ? (newDate.getMonth() + 1) : '0' + (newDate.getMonth() + 1)) + '' + (newDate.getDate() > 9 ? newDate.getDate() : '0' + newDate.getDate());
    }
    var params = {
        page: {
            no: 1,
            size: 1
        },
        body: {}
    };
    if (!!enddate) {
        params.body = {
            enddate: enddate
        }
    }
    dbUtils.get('bing', params, function(rows) {
        if (rows.length > 0) {
            var data = rows[0];
            var base = config.global_http() + '/bing/';
            if (config.resolutions.indexOf(size) > -1) {
                data['url'] = base + data.qiniu_url + '_' + size + '.jpg';
            }
            var qiniu_url = /^(http|https)/.test(data.url) ? data.url : base + data.qiniu_url + '_1920x1080.jpg';
            qiniu_url += '?imageMogr2/blur/' + r + 'x50'
            request.get(qiniu_url)
                .set({
                    'user-agent': req.headers['user-agent'],
                    'referer': req.headers['host']
                })
                .end(function(err, response) {
                    if (err) {
                        res.send(err)
                    } else {
                        res.header('content-type', 'image/jpg');
                        res.send(response.body);
                    }
                });
        } else {
            res.json({
                data: {},
                status: {
                    code: -1,
                    message: '好厉害啊，这么隐秘的地方都被你发现了n(*≧▽≦*)n，我已经通知主人咯♪(＾∀＾●)ﾉ，请您稍后再来试一下吧(❤´艸｀❤)!'
                }
            });
        }
    });
}

module.exports = router;<|endoftext|>