<commit_before>import vdom from 'skatejs-dom-diff/lib/vdom/element';
export default vdom;
<commit_msg>Create vdom abstraction that makes calling Incremental DOM functions easier and more readable for human consumption.<commit_after>import {
  applyProp,
  attr,
  attributes,
  elementClose,
  elementOpen,
  elementOpenEnd,
  elementOpenStart,
  skip,
  text
} from 'incremental-dom';

const factories = {};
const slotAttributeName = 'slot-name';
const slotElementName = 'slot';

// Fake fast mode.
window.process = {
  env: {
    NODE_ENV: 'production'
  }
};

attributes.key = attributes.statics = function () {};
attributes.value = applyProp;

function bind (tname) {
  return factories[tname] = function (attrs, chren) {
    const slot = tname === slotElementName;

    if (typeof attrs === 'object') {
      elementOpenStart(tname, attrs.key, attrs.statics);
      for (let a in attrs) {
        attr(slot && a === 'name' ? slotAttributeName : a, attrs[a]);
      }
      elementOpenEnd();
    } else {
      elementOpen(tname);
      chren = attrs;
    }

    if (slot) {
      skip();
    } else {
      const chrenType = typeof chren;
      if (chrenType === 'function') {
        chren();
      } else if (chrenType === 'string' || chrenType === 'number') {
        text(chren);
      }
    }

    return elementClose(tname);
  };
}

export default function (tname, attrs, chren) {
  return (factories[tname] || bind(tname))(attrs, chren);
}

export const a = bind('a');
export const abbr = bind('abbr');
export const address = bind('address');
export const area = bind('area');
export const article = bind('article');
export const aside = bind('aside');
export const audio = bind('audio');
export const b = bind('b');
export const base = bind('base');
export const bdi = bind('bdi');
export const bdo = bind('bdo');
export const bgsound = bind('bgsound');
export const blockquote = bind('blockquote');
export const body = bind('body');
export const br = bind('br');
export const button = bind('button');
export const canvas = bind('canvas');
export const caption = bind('caption');
export const cite = bind('cite');
export const code = bind('code');
export const col = bind('col');
export const colgroup = bind('colgroup');
export const command = bind('command');
export const content = bind('content');
export const data = bind('data');
export const datalist = bind('datalist');
export const dd = bind('dd');
export const del = bind('del');
export const details = bind('details');
export const dfn = bind('dfn');
export const dialog = bind('dialog');
export const div = bind('div');
export const dl = bind('dl');
export const dt = bind('dt');
export const element = bind('element');
export const em = bind('em');
export const embed = bind('embed');
export const fieldset = bind('fieldset');
export const figcaption = bind('figcaption');
export const figure = bind('figure');
export const font = bind('font');
export const footer = bind('footer');
export const form = bind('form');
export const h1 = bind('h1');
export const h2 = bind('h2');
export const h3 = bind('h3');
export const h4 = bind('h4');
export const h5 = bind('h5');
export const h6 = bind('h6');
export const head = bind('head');
export const header = bind('header');
export const hgroup = bind('hgroup');
export const hr = bind('hr');
export const html = bind('html');
export const i = bind('i');
export const iframe = bind('iframe');
export const image = bind('image');
export const img = bind('img');
export const input = bind('input');
export const ins = bind('ins');
export const kbd = bind('kbd');
export const keygen = bind('keygen');
export const label = bind('label');
export const legend = bind('legend');
export const li = bind('li');
export const link = bind('link');
export const main = bind('main');
export const map = bind('map');
export const mark = bind('mark');
export const marquee = bind('marquee');
export const menu = bind('menu');
export const menuitem = bind('menuitem');
export const meta = bind('meta');
export const meter = bind('meter');
export const multicol = bind('multicol');
export const nav = bind('nav');
export const nobr = bind('nobr');
export const noembed = bind('noembed');
export const noframes = bind('noframes');
export const noscript = bind('noscript');
export const object = bind('object');
export const ol = bind('ol');
export const optgroup = bind('optgroup');
export const option = bind('option');
export const output = bind('output');
export const p = bind('p');
export const param = bind('param');
export const picture = bind('picture');
export const pre = bind('pre');
export const progress = bind('progress');
export const q = bind('q');
export const rp = bind('rp');
export const rt = bind('rt');
export const rtc = bind('rtc');
export const ruby = bind('ruby');
export const s = bind('s');
export const samp = bind('samp');
export const script = bind('script');
export const section = bind('section');
export const select = bind('select');
export const shadow = bind('shadow');
export const slot = bind(slotElementName);
export const small = bind('small');
export const source = bind('source');
export const span = bind('span');
export const strong = bind('strong');
export const style = bind('style');
export const sub = bind('sub');
export const summary = bind('summary');
export const sup = bind('sup');
export const table = bind('table');
export const tbody = bind('tbody');
export const td = bind('td');
export const template = bind('template');
export const textarea = bind('textarea');
export const tfoot = bind('tfoot');
export const th = bind('th');
export const thead = bind('thead');
export const time = bind('time');
export const title = bind('title');
export const tr = bind('tr');
export const track = bind('track');
export const u = bind('u');
export const ul = bind('ul');
export const video = bind('video');
export const wbr = bind('wbr');
<|endoftext|>


<commit_before>import createDebug from 'debug'
import eventToPromise from 'event-to-promise'
import find from 'lodash.find'
import forEach from 'lodash.foreach'
import got from 'got'
import map from 'lodash.map'
import omit from 'lodash.omit'
import unzip from '@julien-f/unzip'
import {PassThrough} from 'stream'
import {promisify} from 'bluebird'
import {Xapi as XapiBase} from 'xen-api'

import {debounce} from './decorators'
import {JsonRpcError} from './api-errors'
import {parseXml, pFinally} from './utils'

const debug = createDebug('xo:xapi')

// ===================================================================

const gotPromise = promisify(got)

const wrapError = error => {
  const e = new Error(error[0])
  e.code = error[0]
  e.params = error.slice(1)
  return e
}

// ===================================================================

export default class Xapi extends XapiBase {
  constructor (...args) {
    super(...args)

    const objectsWatchers = this._objectWatchers = Object.create(null)
    const taskWatchers = this._taskWatchers = Object.create(null)

    // TODO: This is necessary to get UUIDs for host.patches.
    //
    // It will no longer be useful when using xen-api >= 0.5.
    this._refsToUuids = Object.create(null)

    const onAddOrUpdate = objects => {
      forEach(objects, object => {
        const {
          $id: id,
          $ref: ref,
          uuid
        } = object

        if (ref && uuid) {
          this._refsToUuids[ref] = uuid
        }

        // Watched object.
        if (id in objectsWatchers) {
          objectsWatchers[id].resolve(object)
          delete objectsWatchers[id]
        }
        if (ref in objectsWatchers) {
          objectsWatchers[ref].resolve(object)
          delete objectsWatchers[ref]
        }

        // Watched task.
        if (ref in taskWatchers) {
          const {status} = object

          if (status === 'success') {
            taskWatchers[ref].resolve(object.result)
          } else if (status === 'failure') {
            taskWatchers[ref].reject(wrapError(object.error_info))
          } else {
            return
          }

          delete taskWatchers[ref]
        }
      })
    }
    this.objects.on('add', onAddOrUpdate)
    this.objects.on('update', onAddOrUpdate)
  }

  // FIXME: remove this backported methods when xen-api >= 0.5
  getObject (idOrUuidOrRef, defaultValue) {
    const {_objects: {all: objects}} = this
    const object = (
      // if there is an UUID, it is also the $id.
      objects[idOrUuidOrRef] ||
      objects[this._refsToUuids[idOrUuidOrRef]]
    )

    if (object) return object

    if (arguments.length > 1) return defaultValue

    throw new Error('there is not object can be matched to ' + idOrUuidOrRef)
  }
  getObjectByRef (ref, defaultValue) {
    const {
      _refsToUuids: refsToUuids,

      // Objects ids are already UUIDs if they have one.
      _objects: {all: objectsByUuids}
    } = this

    if (ref in refsToUuids) {
      return objectsByUuids[refsToUuids[ref]]
    }

    if (arguments.length > 1) {
      return defaultValue
    }

    throw new Error('there is no object with the ref ' + ref)
  }
  getObjectByUuid (uuid, defaultValue) {
    const {
      // Objects ids are already UUIDs if they have one.
      _objects: {all: objectsByUuids}
    } = this

    if (uuid in objectsByUuids) {
      return objectsByUuids[uuid]
    }

    if (arguments.length > 1) {
      return defaultValue
    }

    throw new Error('there is no object with the UUID ' + uuid)
  }

  // =================================================================

  // Wait for an object to appear or to be updated.
  //
  // TODO: implements a timeout.
  _waitObject (idOrUuidOrRef) {
    let watcher = this._objectWatchers[idOrUuidOrRef]
    if (!watcher) {
      let resolve, reject
      const promise = new Promise((resolve_, reject_) => {
        resolve = resolve_
        reject = reject_
      })

      // Register the watcher.
      watcher = this._objectWatchers[idOrUuidOrRef] = {
        promise,
        resolve,
        reject
      }
    }

    return watcher.promise
  }

  // Returns the objects if already presents or waits for it.
  async _getOrWaitObject (idOrUuidOrRef) {
    return (
      this.getObject(idOrUuidOrRef, undefined) ||
      this._waitObject(idOrUuidOrRef)
    )
  }

  // =================================================================

  // Create a task.
  //
  // Returns the task object from the Xapi.
  async _createTask (name, description = '') {
    const ref = await this.call('task.create', name, description)
    debug('task created: %s', name)

    pFinally(this._watchTask(ref), () => {
      this.call('task.destroy', ref).then(() => {
        debug('task destroyed: %s', name)
      })
    })

    return this._getOrWaitObject(ref)
  }

  // Waits for a task to be resolved.
  _watchTask (ref) {
    // If a task object is passed, unpacked the ref.
    if (typeof ref === 'object' && ref.$ref) ref = ref.$ref

    let watcher = this._taskWatchers[ref]
    if (!watcher) {
      let resolve, reject
      const promise = new Promise((resolve_, reject_) => {
        resolve = resolve_
        reject = reject_
      })

      // Register the watcher.
      watcher = this._taskWatchers[ref] = {
        promise,
        resolve,
        reject
      }
    }

    return watcher.promise
  }

  // =================================================================

  // FIXME: should be static
  @debounce(24 * 60 * 60 * 1000)
  async _getXenUpdates () {
    const [body, {statusCode}] = await gotPromise(
      'http://updates.xensource.com/XenServer/updates.xml'
    )

    if (statusCode !== 200) {
      throw new JsonRpcError('cannot fetch patches list from Citrix')
    }

    const {patchdata: data} = parseXml(body)

    const patches = Object.create(null)
    forEach(data.patches.patch, patch => {
      patches[patch.uuid] = {
        date: patch.timestamp,
        description: patch['name-description'],
        documentationUrl: patch.url,
        guidance: patch['after-apply-guidance'],
        name: patch['name-label'],
        url: patch['patch-url'],

        // TODO: what does it mean, should we handle it?
        // version: patch.version,
      }
    })

    const resolveVersionPatches = function (uuids) {
      const versionPatches = Object.create(null)

      forEach(uuids, ({uuid}) => {
        versionPatches[uuid] = patches[uuid]
      })

      return versionPatches
    }

    const versions = Object.create(null)
    let latestVersion
    forEach(data.serverversions.version, version => {
      versions[version.value] = {
        date: version.timestamp,
        name: version.name,
        id: version.value,
        documentationUrl: version.url,
        patches: resolveVersionPatches(version.patch)
      }

      if (version.latest) {
        latestVersion = versions[version.value]
      }
    })

    return {
      patches,
      latestVersion,
      versions
    }
  }

  // =================================================================

  async listMissingPoolPatchesOnHost (hostId) {
    const {
      software_version: {product_version: version},
      patches
    } = this.getObject(hostId)
    console.log
    return omit(
      (await this._getXenUpdates()).versions[version].patches,

      // TODO: simplify when we start to use xen-api >= 0.5
      map(patches, ref => {
        const hostPatch = this.objects.all[this._refsToUuids[ref]]
        return this._refsToUuids[hostPatch.pool_patch]
      })
    )
  }

  // -----------------------------------------------------------------

  async _uploadPoolPatch (stream, length) {
    const task = await this._createTask('Patch upload from XO')

    // TODO: Update when xen-api >= 0.5
    const poolMaster = this.objects.all[this._refsToUuids[this.pool.master]]

    const [, patchRef] = await Promise.all([
      gotPromise('http://' + poolMaster.address + '/pool_patch_upload', {
        method: 'put',
        body: stream,
        query: {
          session_id: this.sessionId,
          task_id: task.$ref
        },
        headers: {
          'content-length': length
        }
      }),
      this._watchTask(task)
    ])

    return this._getOrWaitObject(patchRef)
  }

  async _getOrUploadPoolPatch (uuid) {
    try {
      return this.getObjectByUuid(uuid)
    } catch (error) {}

    debug('downloading patch %s', uuid)

    const patchInfo = (await this._getXenUpdates()).patches[uuid]

    const PATCH_RE = /\.xsupdate$/
    const proxy = new PassThrough()
    got(patchInfo.url).on('error', error => {
      // TODO: better error handling
      console.error(error)
    }).pipe(unzip.Parse()).on('entry', entry => {
      if (PATCH_RE.test(entry.path)) {
        proxy.emit('length', entry.size)
        entry.pipe(proxy)
      } else {
        entry.autodrain()
      }
    }).on('error', error => {
      // TODO: better error handling
      console.error(error)
    })

    const length = await eventToPromise(proxy, 'length')
    return this._uploadPoolPatch(proxy, length)
  }

  async installPoolPatchOnHost (patchUuid, hostId) {
    const patch = await this._getOrUploadPoolPatch(patchUuid)
    const host = this.getObject(hostId)

    debug('installing patch %s', patchUuid)

    await this.call('pool_patch.apply', patch.$ref, host.$ref)
  }

  async installPoolPatchOnAllHosts (patchUuid) {
    const patch = await this._getOrUploadPoolPatch(patchUuid)

    await this.call('pool_patch.pool_apply', patch.$ref)
  }

  // =================================================================

  async _doDockerAction (vmId, action, containerId) {
    const vm = this.getObject(vmId)
    const host = this.getObject(vm.resident_on)

    return await this.call('host.call_plugin', host.$ref, 'xscontainer', action, {
      vmuuid: vm.uuid,
      container: containerId
    })
  }

  async registerDockerContainer (vmId) {
    await this._doDockerAction(vmId, 'register')
  }

  async deregisterDockerContainer (vmId) {
    await this._doDockerAction(vmId, 'deregister')
  }

  async startDockerContainer (vmId, containerId) {
    await this._doDockerAction(vmId, 'start', containerId)
  }

  async stopDockerContainer (vmId, containerId) {
    await this._doDockerAction(vmId, 'stop', containerId)
  }

  async restartDockerContainer (vmId, containerId) {
    await this._doDockerAction(vmId, 'restart', containerId)
  }

  async pauseDockerContainer (vmId, containerId) {
    await this._doDockerAction(vmId, 'pause', containerId)
  }

  async unpauseDockerContainer (vmId, containerId) {
    await this._doDockerAction(vmId, 'unpause', containerId)
  }

  // =================================================================

}
<commit_msg>Fix grammar in error message.<commit_after>import createDebug from 'debug'
import eventToPromise from 'event-to-promise'
import find from 'lodash.find'
import forEach from 'lodash.foreach'
import got from 'got'
import map from 'lodash.map'
import omit from 'lodash.omit'
import unzip from '@julien-f/unzip'
import {PassThrough} from 'stream'
import {promisify} from 'bluebird'
import {Xapi as XapiBase} from 'xen-api'

import {debounce} from './decorators'
import {JsonRpcError} from './api-errors'
import {parseXml, pFinally} from './utils'

const debug = createDebug('xo:xapi')

// ===================================================================

const gotPromise = promisify(got)

const wrapError = error => {
  const e = new Error(error[0])
  e.code = error[0]
  e.params = error.slice(1)
  return e
}

// ===================================================================

export default class Xapi extends XapiBase {
  constructor (...args) {
    super(...args)

    const objectsWatchers = this._objectWatchers = Object.create(null)
    const taskWatchers = this._taskWatchers = Object.create(null)

    // TODO: This is necessary to get UUIDs for host.patches.
    //
    // It will no longer be useful when using xen-api >= 0.5.
    this._refsToUuids = Object.create(null)

    const onAddOrUpdate = objects => {
      forEach(objects, object => {
        const {
          $id: id,
          $ref: ref,
          uuid
        } = object

        if (ref && uuid) {
          this._refsToUuids[ref] = uuid
        }

        // Watched object.
        if (id in objectsWatchers) {
          objectsWatchers[id].resolve(object)
          delete objectsWatchers[id]
        }
        if (ref in objectsWatchers) {
          objectsWatchers[ref].resolve(object)
          delete objectsWatchers[ref]
        }

        // Watched task.
        if (ref in taskWatchers) {
          const {status} = object

          if (status === 'success') {
            taskWatchers[ref].resolve(object.result)
          } else if (status === 'failure') {
            taskWatchers[ref].reject(wrapError(object.error_info))
          } else {
            return
          }

          delete taskWatchers[ref]
        }
      })
    }
    this.objects.on('add', onAddOrUpdate)
    this.objects.on('update', onAddOrUpdate)
  }

  // FIXME: remove this backported methods when xen-api >= 0.5
  getObject (idOrUuidOrRef, defaultValue) {
    const {_objects: {all: objects}} = this
    const object = (
      // if there is an UUID, it is also the $id.
      objects[idOrUuidOrRef] ||
      objects[this._refsToUuids[idOrUuidOrRef]]
    )

    if (object) return object

    if (arguments.length > 1) return defaultValue

    throw new Error('no object can be matched to ' + idOrUuidOrRef)
  }
  getObjectByRef (ref, defaultValue) {
    const {
      _refsToUuids: refsToUuids,

      // Objects ids are already UUIDs if they have one.
      _objects: {all: objectsByUuids}
    } = this

    if (ref in refsToUuids) {
      return objectsByUuids[refsToUuids[ref]]
    }

    if (arguments.length > 1) {
      return defaultValue
    }

    throw new Error('there is no object with the ref ' + ref)
  }
  getObjectByUuid (uuid, defaultValue) {
    const {
      // Objects ids are already UUIDs if they have one.
      _objects: {all: objectsByUuids}
    } = this

    if (uuid in objectsByUuids) {
      return objectsByUuids[uuid]
    }

    if (arguments.length > 1) {
      return defaultValue
    }

    throw new Error('there is no object with the UUID ' + uuid)
  }

  // =================================================================

  // Wait for an object to appear or to be updated.
  //
  // TODO: implements a timeout.
  _waitObject (idOrUuidOrRef) {
    let watcher = this._objectWatchers[idOrUuidOrRef]
    if (!watcher) {
      let resolve, reject
      const promise = new Promise((resolve_, reject_) => {
        resolve = resolve_
        reject = reject_
      })

      // Register the watcher.
      watcher = this._objectWatchers[idOrUuidOrRef] = {
        promise,
        resolve,
        reject
      }
    }

    return watcher.promise
  }

  // Returns the objects if already presents or waits for it.
  async _getOrWaitObject (idOrUuidOrRef) {
    return (
      this.getObject(idOrUuidOrRef, undefined) ||
      this._waitObject(idOrUuidOrRef)
    )
  }

  // =================================================================

  // Create a task.
  //
  // Returns the task object from the Xapi.
  async _createTask (name, description = '') {
    const ref = await this.call('task.create', name, description)
    debug('task created: %s', name)

    pFinally(this._watchTask(ref), () => {
      this.call('task.destroy', ref).then(() => {
        debug('task destroyed: %s', name)
      })
    })

    return this._getOrWaitObject(ref)
  }

  // Waits for a task to be resolved.
  _watchTask (ref) {
    // If a task object is passed, unpacked the ref.
    if (typeof ref === 'object' && ref.$ref) ref = ref.$ref

    let watcher = this._taskWatchers[ref]
    if (!watcher) {
      let resolve, reject
      const promise = new Promise((resolve_, reject_) => {
        resolve = resolve_
        reject = reject_
      })

      // Register the watcher.
      watcher = this._taskWatchers[ref] = {
        promise,
        resolve,
        reject
      }
    }

    return watcher.promise
  }

  // =================================================================

  // FIXME: should be static
  @debounce(24 * 60 * 60 * 1000)
  async _getXenUpdates () {
    const [body, {statusCode}] = await gotPromise(
      'http://updates.xensource.com/XenServer/updates.xml'
    )

    if (statusCode !== 200) {
      throw new JsonRpcError('cannot fetch patches list from Citrix')
    }

    const {patchdata: data} = parseXml(body)

    const patches = Object.create(null)
    forEach(data.patches.patch, patch => {
      patches[patch.uuid] = {
        date: patch.timestamp,
        description: patch['name-description'],
        documentationUrl: patch.url,
        guidance: patch['after-apply-guidance'],
        name: patch['name-label'],
        url: patch['patch-url'],

        // TODO: what does it mean, should we handle it?
        // version: patch.version,
      }
    })

    const resolveVersionPatches = function (uuids) {
      const versionPatches = Object.create(null)

      forEach(uuids, ({uuid}) => {
        versionPatches[uuid] = patches[uuid]
      })

      return versionPatches
    }

    const versions = Object.create(null)
    let latestVersion
    forEach(data.serverversions.version, version => {
      versions[version.value] = {
        date: version.timestamp,
        name: version.name,
        id: version.value,
        documentationUrl: version.url,
        patches: resolveVersionPatches(version.patch)
      }

      if (version.latest) {
        latestVersion = versions[version.value]
      }
    })

    return {
      patches,
      latestVersion,
      versions
    }
  }

  // =================================================================

  async listMissingPoolPatchesOnHost (hostId) {
    const {
      software_version: {product_version: version},
      patches
    } = this.getObject(hostId)
    console.log
    return omit(
      (await this._getXenUpdates()).versions[version].patches,

      // TODO: simplify when we start to use xen-api >= 0.5
      map(patches, ref => {
        const hostPatch = this.objects.all[this._refsToUuids[ref]]
        return this._refsToUuids[hostPatch.pool_patch]
      })
    )
  }

  // -----------------------------------------------------------------

  async _uploadPoolPatch (stream, length) {
    const task = await this._createTask('Patch upload from XO')

    // TODO: Update when xen-api >= 0.5
    const poolMaster = this.objects.all[this._refsToUuids[this.pool.master]]

    const [, patchRef] = await Promise.all([
      gotPromise('http://' + poolMaster.address + '/pool_patch_upload', {
        method: 'put',
        body: stream,
        query: {
          session_id: this.sessionId,
          task_id: task.$ref
        },
        headers: {
          'content-length': length
        }
      }),
      this._watchTask(task)
    ])

    return this._getOrWaitObject(patchRef)
  }

  async _getOrUploadPoolPatch (uuid) {
    try {
      return this.getObjectByUuid(uuid)
    } catch (error) {}

    debug('downloading patch %s', uuid)

    const patchInfo = (await this._getXenUpdates()).patches[uuid]

    const PATCH_RE = /\.xsupdate$/
    const proxy = new PassThrough()
    got(patchInfo.url).on('error', error => {
      // TODO: better error handling
      console.error(error)
    }).pipe(unzip.Parse()).on('entry', entry => {
      if (PATCH_RE.test(entry.path)) {
        proxy.emit('length', entry.size)
        entry.pipe(proxy)
      } else {
        entry.autodrain()
      }
    }).on('error', error => {
      // TODO: better error handling
      console.error(error)
    })

    const length = await eventToPromise(proxy, 'length')
    return this._uploadPoolPatch(proxy, length)
  }

  async installPoolPatchOnHost (patchUuid, hostId) {
    const patch = await this._getOrUploadPoolPatch(patchUuid)
    const host = this.getObject(hostId)

    debug('installing patch %s', patchUuid)

    await this.call('pool_patch.apply', patch.$ref, host.$ref)
  }

  async installPoolPatchOnAllHosts (patchUuid) {
    const patch = await this._getOrUploadPoolPatch(patchUuid)

    await this.call('pool_patch.pool_apply', patch.$ref)
  }

  // =================================================================

  async _doDockerAction (vmId, action, containerId) {
    const vm = this.getObject(vmId)
    const host = this.getObject(vm.resident_on)

    return await this.call('host.call_plugin', host.$ref, 'xscontainer', action, {
      vmuuid: vm.uuid,
      container: containerId
    })
  }

  async registerDockerContainer (vmId) {
    await this._doDockerAction(vmId, 'register')
  }

  async deregisterDockerContainer (vmId) {
    await this._doDockerAction(vmId, 'deregister')
  }

  async startDockerContainer (vmId, containerId) {
    await this._doDockerAction(vmId, 'start', containerId)
  }

  async stopDockerContainer (vmId, containerId) {
    await this._doDockerAction(vmId, 'stop', containerId)
  }

  async restartDockerContainer (vmId, containerId) {
    await this._doDockerAction(vmId, 'restart', containerId)
  }

  async pauseDockerContainer (vmId, containerId) {
    await this._doDockerAction(vmId, 'pause', containerId)
  }

  async unpauseDockerContainer (vmId, containerId) {
    await this._doDockerAction(vmId, 'unpause', containerId)
  }

  // =================================================================

}
<|endoftext|>


<commit_before>const TASKS 	= require('./yake/tasks.js');
const TC 		= require('./yake/task_collection.js');
const MAIN 		= require('./yake/main.js');
const Logger 	= require('./yake/logger.js');
const SHELL 	= require('./yake/shell.js');
const YERROR    = require('./yake/error.js');

const raiseError = YERROR.raiseError;
const chokidar = require('chokidar');

/**
 * This file exports the functions used in a jakefile to define tasks and add descriptions to those tasks.
 * The jakefile is then executed via the jake command line utility in the usual jake/ruby/make
 * style
 *
 */

exports.task = task;
exports.abort = abort;
exports.run = run;
exports.invokeTask = invokeTask;
exports.watch = watch;

// now add a bunch of other convenient stuff into the YAKE namespace
Object.assign(exports, Logger.logger, SHELL, YERROR);

function task()
{
    const args = TASKS.normalizeArguments.apply(this, arguments);

    if (args === undefined)
        { raiseError(`_Yake.js::task args undefined arguments: ${arguments}`); }

    /**
     * @NOTE - using a global - see notes in tasks.js for explanation
     *
     */
    const tc = TASKS.globals.globalTaskCollection;

    const tc2 = TASKS.defineTask(tc, args.name, args.description, args.prereqs, args.action);
    /**
     * @NOTE - using a global - see notes in tasks.js for explanation
     *
     */
     TASKS.globals.globalTaskCollection = tc2;

}

/**
 * Watch files and then invoke a task
 */
 function watch(glob, taskName)
 {
    var watcher = chokidar.watch
    (
        glob,
        {persistent: true,}
    );
    watcher.on('all', (event, path)=>
    {
        invokeTask(taskName);
    });
 }

/**
 * Invoke a task by name
 */
function invokeTask(taskName)
{
    const tc = TASKS.globals.globalTaskCollection;
    const t = tc.getByName(taskName);
    if (t === undefined)
    {
        YERROR.raiseError(`task ${taskName} not found`);
    }

    TASKS.invokeTask(tc, t);
}

/**
 * called within a yake task's action function to abort task processing
 */
function abort(message, returnCode = -1)
{
    raiseError(message);
    process.exit(returnCode);
}

/**
 * called at the end of a taskfile style yake build file to kick off the processing
 */
function runTaskfile()
{
    MAIN.taskFileMain(undefined, undefined);
}
// just for an easier name
function run()
{
    runTaskfile();
}
<commit_msg>refiing watch<commit_after>const TASKS 	= require('./yake/tasks.js');
const TC 		= require('./yake/task_collection.js');
const MAIN 		= require('./yake/main.js');
const Logger 	= require('./yake/logger.js');
const SHELL 	= require('./yake/shell.js');
const YERROR    = require('./yake/error.js');

const raiseError = YERROR.raiseError;
const chokidar = require('chokidar');

/**
 * This file exports the functions used in a jakefile to define tasks and add descriptions to those tasks.
 * The jakefile is then executed via the jake command line utility in the usual jake/ruby/make
 * style
 *
 */

exports.task = task;
exports.abort = abort;
exports.run = run;
exports.invokeTask = invokeTask;
exports.watch = watch;

// now add a bunch of other convenient stuff into the YAKE namespace
Object.assign(exports, Logger.logger, SHELL, YERROR);

function task()
{
    const args = TASKS.normalizeArguments.apply(this, arguments);

    if (args === undefined)
        { raiseError(`_Yake.js::task args undefined arguments: ${arguments}`); }

    /**
     * @NOTE - using a global - see notes in tasks.js for explanation
     *
     */
    const tc = TASKS.globals.globalTaskCollection;

    const tc2 = TASKS.defineTask(tc, args.name, args.description, args.prereqs, args.action);
    /**
     * @NOTE - using a global - see notes in tasks.js for explanation
     *
     */
     TASKS.globals.globalTaskCollection = tc2;

}

/**
 * Watch files and then invoke a task
 */
 function watch(glob, taskName)
 {
    var watcher = chokidar.watch(glob, {persistent: true,});
    watcher.on('ready', ()=>
    {
        watcher.on('all', (event, path)=>
        {
            invokeTask(taskName);
        });
    });
 }

/**
 * Invoke a task by name
 */
function invokeTask(taskName)
{
    const tc = TASKS.globals.globalTaskCollection;
    const t = tc.getByName(taskName);
    if (t === undefined)
    {
        YERROR.raiseError(`task ${taskName} not found`);
    }

    TASKS.invokeTask(tc, t);
}

/**
 * called within a yake task's action function to abort task processing
 */
function abort(message, returnCode = -1)
{
    raiseError(message);
    process.exit(returnCode);
}

/**
 * called at the end of a taskfile style yake build file to kick off the processing
 */
function runTaskfile()
{
    MAIN.taskFileMain(undefined, undefined);
}
// just for an easier name
function run()
{
    runTaskfile();
}
<|endoftext|>


<commit_before>if(typeof(QIMEN_STAR) == "undefined") var QIMEN_STAR = {};
(function(_e) {
  "use strict";
  function info(y,m,d,h,i,s,ms,_g) {
    // output
    var _out = new Object();
    //
    var jiqi = _e.jiqi.GetJiqiInfo(y,m,d,h,i,s,ms);
    var gender = _g;
    var fcol   = jiqi.bazi;
    var jtoday = jiqi.julian;
    var jqpos  = jiqi.currentJiqiIdx - 1;
    var jqTime = jiqi.wholeYear.slice();
    // add bazi into output
    _out.bazi = jiqi.bazi;
    // 起大運
    var posneg = ("甲乙丙丁戊己庚辛壬癸".indexOf(fcol.charAt(0))+1) % 2; // 陰陽年: 陰年0, 陽年1
    var start_time = 0;
    //

    if((gender == 1 && posneg == 1) || (gender == 0 && posneg == 0)) { // 陽男, 陰女
      var jqpos2 = jqpos + (jqpos%2 == 0 ? 2 : 1);
      if(jqpos2 > 23) {
        jqpos2 = 0;
        _e.jiqi.CalJiqiByYear(y+1,m,d,h,i,s,jqTime);
      }
      start_time = jqTime[jqpos2] - jtoday;//console.log('陽男陰女',jtoday,jqTime[jqpos2],start_time,jqpos,jqpos2);
    } else { // 陰男, 陽女
      var jqpos2 = jqpos - (jqpos%2 == 0 ? 0 : 1);
      if(jqpos2 < 0) {
        jqpos2 = 22;
        _e.jiqi.CalJiqiByYear(y-1,m,d,h,i,s,jqTime);
      }
      start_time = jtoday - jqTime[jqpos2];//console.log('陰男陽女',jtoday,jqTime[jqpos2],start_time,jqpos,jqpos2);
    }
    //console.log('gender',gender,'posneg',posneg,'jqpos2',jqpos2);
    // 計小運
    var start_small = start_time / 3;
    _out.small = new Array();
    if(start_small < 1) {
      _out.small.push("即年入大運");
    } else {
      var tinIdx = "甲乙丙丁戊己庚辛壬癸".indexOf(fcol[6]);
      var deiIdx = "子丑寅卯辰巳午未申酉戌亥".indexOf((fcol[7]));
      for(var i = 1; i <= start_small; i++) {
        _out.small.push(
          "甲乙丙丁戊己庚辛壬癸".charAt((tinIdx+i)%10) +
          "子丑寅卯辰巳午未申酉戌亥".charAt((deiIdx+i)%12)
        );
      }
    }
    // 計大運起始
    //console.log(jtoday,_e.jiqi.JTime(jtoday),start_time);
    start_time = jtoday + 365.25 * (start_time / 3.0);
    _out.start = start_time;
    _out.start_text = _e.jiqi.JTime(start_time);
    // 計一百二十年大運, 參照吠陀占星及法達星限120年大限
    _out.big = new Array();
    var tinIdx = "甲乙丙丁戊己庚辛壬癸".indexOf(fcol[2]);
    var deiIdx = "子丑寅卯辰巳午未申酉戌亥".indexOf((fcol[3]));
    if((gender==1 && posneg==1) || (gender==0 && posneg==0)) { // 陽男, 陰女
      for(var i = 1; i < 13; i++) {
        _out.big.push(
          "甲乙丙丁戊己庚辛壬癸".charAt((tinIdx+i)%10)+
          "子丑寅卯辰巳午未申酉戌亥".charAt((deiIdx+i)%12)
        );
      }
    } else { // 陰男, 陽女
      for(var i = 1; i < 13; i++) {
        var _tmp = (tinIdx-i)%10;
        if(_tmp < 0) _tmp += 10;
        var tcol = "甲乙丙丁戊己庚辛壬癸".charAt(_tmp);
        var _tmp = (deiIdx-i)%12;
        if(_tmp < 0) _tmp += 12;
        var dcol = "子丑寅卯辰巳午未申酉戌亥".charAt(_tmp);
        _out.big.push(tcol+dcol);
      }
    }
    // return
    return _out;
  }
  /*
  var d = new Date();
  console.log(info(d.getFullYear(),d.getMonth()+1,d.getDate(),d.getHours(),d.getMinutes(),d.getSeconds(),d.getMilliseconds(),1));
  */
  _e.bazi = new Object();
  _e.bazi.info = function(y,m,d,h,i,s,ms,_g) {
    return info(y,m,d,h,i,s,ms,_g);
  };
})(QIMEN_STAR || {});<commit_msg>slove bazi's minor fortune(八字小運) calculation error<commit_after>if(typeof(QIMEN_STAR) == "undefined") var QIMEN_STAR = {};
(function(_e) {
  "use strict";
  function info(y,m,d,h,i,s,ms,_g) {
    // output
    var _out = new Object();
    //
    var jiqi = _e.jiqi.GetJiqiInfo(y,m,d,h,i,s,ms);
    var gender = _g;
    var fcol   = jiqi.bazi;
    var jtoday = jiqi.julian;
    var jqpos  = jiqi.currentJiqiIdx - 1;
    var jqTime = jiqi.wholeYear.slice();
    // add bazi into output
    _out.bazi = jiqi.bazi;
    // 起大運
    var posneg = ("甲乙丙丁戊己庚辛壬癸".indexOf(fcol.charAt(0))+1) % 2; // 陰陽年: 陰年0, 陽年1
    var start_time = 0;
    //

    if((gender == 1 && posneg == 1) || (gender == 0 && posneg == 0)) { // 陽男, 陰女
      var jqpos2 = jqpos + (jqpos%2 == 0 ? 2 : 1);
      if(jqpos2 > 23) {
        jqpos2 = 0;
        _e.jiqi.CalJiqiByYear(y+1,m,d,h,i,s,jqTime);
      }
      start_time = jqTime[jqpos2] - jtoday;//console.log('陽男陰女',jtoday,jqTime[jqpos2],start_time,jqpos,jqpos2);
    } else { // 陰男, 陽女
      var jqpos2 = jqpos - (jqpos%2 == 0 ? 0 : 1);
      if(jqpos2 < 0) {
        jqpos2 = 22;
        _e.jiqi.CalJiqiByYear(y-1,m,d,h,i,s,jqTime);
      }
      start_time = jtoday - jqTime[jqpos2];//console.log('陰男陽女',jtoday,jqTime[jqpos2],start_time,jqpos,jqpos2);
    }
    //console.log('gender',gender,'posneg',posneg,'jqpos2',jqpos2);
    // 計小運
    var start_small = start_time / 3;
    _out.small = new Array();
    if(start_small < 1) {
      _out.small.push("即年入大運");
    } else {
      var tinIdx = "甲乙丙丁戊己庚辛壬癸".indexOf(fcol[6]);
      var deiIdx = "子丑寅卯辰巳午未申酉戌亥".indexOf((fcol[7]));
      if((gender == 1 && posneg == 1) || (gender == 0 && posneg == 0)) { // 陽男, 陰女
        for(var i = 1; i <= start_small; i++) {
          _out.small.push(
            "甲乙丙丁戊己庚辛壬癸".charAt((tinIdx+i)%10) +
            "子丑寅卯辰巳午未申酉戌亥".charAt((deiIdx+i)%12)
          );
        }
      } else {// 陽女, 陰男
        for(var i = 1; i <= start_small; i++) {
          _out.small.push(
            "甲乙丙丁戊己庚辛壬癸".charAt((tinIdx-i)%10) +
            "子丑寅卯辰巳午未申酉戌亥".charAt((deiIdx-i)%12)
          );
        }
      }
    }
    // 計大運起始
    //console.log(jtoday,_e.jiqi.JTime(jtoday),start_time);
    start_time = jtoday + 365.25 * (start_time / 3.0);
    _out.start = start_time;
    _out.start_text = _e.jiqi.JTime(start_time);
    // 計一百二十年大運, 參照吠陀占星及法達星限120年大限
    _out.big = new Array();
    var tinIdx = "甲乙丙丁戊己庚辛壬癸".indexOf(fcol[2]);
    var deiIdx = "子丑寅卯辰巳午未申酉戌亥".indexOf((fcol[3]));
    if((gender==1 && posneg==1) || (gender==0 && posneg==0)) { // 陽男, 陰女
      for(var i = 1; i < 13; i++) {
        _out.big.push(
          "甲乙丙丁戊己庚辛壬癸".charAt((tinIdx+i)%10)+
          "子丑寅卯辰巳午未申酉戌亥".charAt((deiIdx+i)%12)
        );
      }
    } else { // 陰男, 陽女
      for(var i = 1; i < 13; i++) {
        var _tmp = (tinIdx-i)%10;
        if(_tmp < 0) _tmp += 10;
        var tcol = "甲乙丙丁戊己庚辛壬癸".charAt(_tmp);
        var _tmp = (deiIdx-i)%12;
        if(_tmp < 0) _tmp += 12;
        var dcol = "子丑寅卯辰巳午未申酉戌亥".charAt(_tmp);
        _out.big.push(tcol+dcol);
      }
    }
    // return
    return _out;
  }
  /*
  var d = new Date();
  console.log(info(d.getFullYear(),d.getMonth()+1,d.getDate(),d.getHours(),d.getMinutes(),d.getSeconds(),d.getMilliseconds(),1));
  */
  _e.bazi = new Object();
  _e.bazi.info = function(y,m,d,h,i,s,ms,_g) {
    return info(y,m,d,h,i,s,ms,_g);
  };
})(QIMEN_STAR || {});<|endoftext|>


<commit_before>var db = require('./db');
var compute = require('./compute');
var computeMatchData = compute.computeMatchData;
var utility = require('./utility');
var isRadiant = utility.isRadiant;
var isSignificant = utility.isSignificant;
var async = require('async');
var aggregator = require('./aggregator');
var constants = require('./constants.json');
var preprocessQuery = require('./preprocessQuery');
var filter = require('./filter');

function advQuery(query, cb) {
    //mongo_select
    //js_select
    //js_agg, aggregations to do with js
    //do all aggregations if null, so we need parsed data
    var bGetParsedPlayerData = Boolean(!query.js_agg);
    //limit, pass to mongodb, cap the number of matches to return in mongo
    //skip, pass to mongodb
    //sort, pass to mongodb
    //js_limit, the number of results to return in a page, filtered by js
    //js_start, the position to start a page at, selected by js
    //js_sort, post-process sorter that processes with js
    //build the monk hash
    var monk_options = {
        limit: query.limit,
        skip: query.skip,
        sort: query.sort,
        fields: query.project
    };
    //console.log(query);
    console.time('querying database');
    //console.log(options);
    db.matches.find(query.mongo_select, monk_options, function(err, matches) {
        if (err) {
            return cb(err);
        }
        console.timeEnd('querying database');
        console.time('expanding matches');
        var expanded_matches = [];
        matches.forEach(function(m) {
            if (m.players) {
                //get all_players and primary player
                m.all_players = m.players.slice(0);
                //console.log(m.players.length, m.all_players.length);
                //use the mongodb select criteria to filter the player list
                //create a new match with this primary player
                //all players for tournament games, otherwise player matching select criteria
                m.players.forEach(function(p) {
                    var pass = true;
                    //check mongo query, if starting with player, this means we select a single player, otherwise select all players
                    for (var key in query.mongo_select) {
                        var split = key.split(".");
                        //break apart the query and determine whether we are trying to get a single player for this match
                        if (split[0] === "players" && p[split[1]] !== query.mongo_select[key]) {
                            pass = false;
                        }
                    }
                    if (pass) {
                        m.players = [p];
                        expanded_matches.push(JSON.parse(JSON.stringify(m)));
                    }
                });
            }
        });
        matches = expanded_matches;
        console.timeEnd('expanding matches');
        console.time("retrieving parsed data");
        getParsedPlayerData(matches, bGetParsedPlayerData, function(err) {
            if (err) {
                return cb(err);
            }
            console.timeEnd("retrieving parsed data");
            console.time('computing aggregations');
            matches.forEach(function(m) {
                //post-process the match to get additional stats
                computeMatchData(m);
            });
            var filtered = filter(matches, query.js_select);
            //filtered = sort(filtered, options.js_sort);
            // console.log('aggData: options.js_agg = %s', options.js_agg);
            var aggData = aggregator(filtered, query.js_agg);
            var result = {
                aggData: aggData,
                page: filtered.slice(query.js_skip, query.js_skip + query.js_limit),
                data: filtered,
                unfiltered: matches
            };
            console.timeEnd('computing aggregations');
            cb(err, result);
        });
    });
}
/*
function sort(matches, sorts) {
    //console.log(sorts);
    //dir 1 ascending, -1 descending
    var sortFuncs = {
        match_id: function(a, b, dir) {
            return (a.match_id - b.match_id) * dir;
        },
        duration: function(a, b, dir) {
            return (a.duration - b.duration) * dir;
        },
        game_mode: function(a, b, dir) {
            return (a.game_mode - b.game_mode) * dir;
        },
        "players[0].kills": function(a, b, dir) {
            return (a.players[0].kills - b.players[0].kills) * dir;
        },
        "players[0].deaths": function(a, b, dir) {
            return (a.players[0].deaths - b.players[0].deaths) * dir;
        },
        "players[0].assists": function(a, b, dir) {
            return (a.players[0].assists - b.players[0].assists) * dir;
        },
        "players[0].level": function(a, b, dir) {
            return (a.players[0].level - b.players[0].level) * dir;
        },
        "players[0].last_hits": function(a, b, dir) {
            return (a.players[0].last_hits - b.players[0].last_hits) * dir;
        },
        "players[0].denies": function(a, b, dir) {
            return (a.players[0].denies - b.players[0].denies) * dir;
        },
        "players[0].gold_per_min": function(a, b, dir) {
            return (a.players[0].gold_per_min - b.players[0].gold_per_min) * dir;
        },
        "players[0].xp_per_min": function(a, b, dir) {
            return (a.players[0].xp_per_min - b.players[0].xp_per_min) * dir;
        },
        "players[0].hero_damage": function(a, b, dir) {
            return (a.players[0].hero_damage - b.players[0].hero_damage) * dir;
        },
        "players[0].tower_damage": function(a, b, dir) {
            return (a.players[0].tower_damage - b.players[0].tower_damage) * dir;
        },
        "players[0].hero_healing": function(a, b, dir) {
                return (a.players[0].hero_healing - b.players[0].hero_healing) * dir;
            }
            //TODO implement more sorts
            //game mode
            //hero (sort alpha?)
            //played time
            //result
            //region
            //parse status
    };
    for (var key in sorts) {
        if (key in sortFuncs) {
            matches.sort(function(a, b) {
                return sortFuncs[key](a, b, sorts[key]);
            });
        }
    }
    return matches;
}
/*
function getFullPlayerData(matches, doAction, cb) {
    cb();

    if (!doAction) {
        return cb();
    }
    //create array of ids to use for $in
    var match_ids = matches.map(function(m) {
        return m.match_id;
    });
    db.matches.find({
        match_id: {
            $in: match_ids
        }
    }, {
        fields: {
            "players.account_id": 1,
            "players.hero_id": 1,
            "players.player_slot": 1,
            match_id: 1
        }
    }, function(err, docs) {
        if (err) {
            return cb(err);
        }
        //build hash of match_id to full player data
        var hash = {};
        for (var i = 0; i < docs.length; i++) {
            hash[docs[i].match_id] = docs[i].players;
        }
        //iterate through given matches and populate all_players field
        for (var j = 0; j < matches.length; j++) {
            matches[j].all_players = hash[matches[j].match_id];
        }
        cb(err);
    });
}
    */
function getParsedPlayerData(matches, doAction, cb) {
    if (!doAction) {
        return cb();
    }
    //we optimize by filtering matches for only those with parse_status===2
    var parsed = matches.filter(function(m) {
        return m.parse_status === 2;
    });
    //the following does a query for each parsed match in the set, so could be a lot of queries
    //since we might want a different position on each query, we need to make them individually
    async.each(parsed, function(m, cb) {
        var player = m.players[0];
        var parseSlot = player.player_slot % (128 - 5);
        db.matches.findOne({
            match_id: m.match_id
        }, {
            fields: {
                "parsed_data": 1,
                "parsed_data.version": 1,
                "parsed_data.chat": 1,
                "parsed_data.radiant_gold_adv": 1,
                "parsed_data.radiant_xp_adv": 1,
                "parsed_data.players": {
                    $slice: [parseSlot, 1]
                },
                match_id: 1
            }
        }, function(err, doc) {
            if (err) {
                return cb(err);
            }
            m.parsed_data = doc.parsed_data;
            cb(err);
        });
    }, function(err) {
        cb(err);
    });
}
module.exports = advQuery;<commit_msg>use for instead of foreach for better performance<commit_after>var db = require('./db');
var compute = require('./compute');
var computeMatchData = compute.computeMatchData;
var utility = require('./utility');
var isRadiant = utility.isRadiant;
var isSignificant = utility.isSignificant;
var async = require('async');
var aggregator = require('./aggregator');
var constants = require('./constants.json');
var preprocessQuery = require('./preprocessQuery');
var filter = require('./filter');

function advQuery(query, cb) {
    //mongo_select
    //js_select
    //js_agg, aggregations to do with js
    //do all aggregations if null, so we need parsed data
    var bGetParsedPlayerData = Boolean(!query.js_agg);
    //limit, pass to mongodb, cap the number of matches to return in mongo
    //skip, pass to mongodb
    //sort, pass to mongodb
    //js_limit, the number of results to return in a page, filtered by js
    //js_start, the position to start a page at, selected by js
    //js_sort, post-process sorter that processes with js
    //build the monk hash
    var monk_options = {
        limit: query.limit,
        skip: query.skip,
        sort: query.sort,
        fields: query.project
    };
    //console.log(query);
    console.time('querying database');
    //console.log(options);
    db.matches.find(query.mongo_select, monk_options, function(err, matches) {
        if (err) {
            return cb(err);
        }
        console.timeEnd('querying database');
        console.time('expanding matches');
        var expanded_matches = [];
        for (var i = 0; i < matches.length; i++) {
            var m = matches[i];
            if (m.players) {
                //get all_players and primary player
                m.all_players = m.players.slice(0);
                //console.log(m.players.length, m.all_players.length);
                //use the mongodb select criteria to filter the player list
                //create a new match with this primary player
                //all players for tournament games, otherwise player matching select criteria
                for (var j = 0; j < m.players.length; j++) {
                    var p = m.players[j];
                    var pass = true;
                    //check mongo query, if starting with player, this means we select a single player, otherwise select all players
                    for (var key in query.mongo_select) {
                        var split = key.split(".");
                        //break apart the query and determine whether we are trying to get a single player for this match
                        if (split[0] === "players" && p[split[1]] !== query.mongo_select[key]) {
                            //if the query starts with "players" and the property of this player doesn't match, don't add it to expanded matches
                            pass = false;
                        }
                    }
                    if (pass) {
                        m.players = [p];
                        expanded_matches.push(JSON.parse(JSON.stringify(m)));
                    }
                }
            }
        }
        matches = expanded_matches;
        console.timeEnd('expanding matches');
        console.time("retrieving parsed data");
        getParsedPlayerData(matches, bGetParsedPlayerData, function(err) {
            if (err) {
                return cb(err);
            }
            console.timeEnd("retrieving parsed data");
            console.time('computing aggregations');
            matches.forEach(function(m) {
                //post-process the match to get additional stats
                computeMatchData(m);
            });
            var filtered = filter(matches, query.js_select);
            //filtered = sort(filtered, options.js_sort);
            // console.log('aggData: options.js_agg = %s', options.js_agg);
            var aggData = aggregator(filtered, query.js_agg);
            var result = {
                aggData: aggData,
                page: filtered.slice(query.js_skip, query.js_skip + query.js_limit),
                data: filtered,
                unfiltered: matches
            };
            console.timeEnd('computing aggregations');
            cb(err, result);
        });
    });
}
/*
function sort(matches, sorts) {
    //console.log(sorts);
    //dir 1 ascending, -1 descending
    var sortFuncs = {
        match_id: function(a, b, dir) {
            return (a.match_id - b.match_id) * dir;
        },
        duration: function(a, b, dir) {
            return (a.duration - b.duration) * dir;
        },
        game_mode: function(a, b, dir) {
            return (a.game_mode - b.game_mode) * dir;
        },
        "players[0].kills": function(a, b, dir) {
            return (a.players[0].kills - b.players[0].kills) * dir;
        },
        "players[0].deaths": function(a, b, dir) {
            return (a.players[0].deaths - b.players[0].deaths) * dir;
        },
        "players[0].assists": function(a, b, dir) {
            return (a.players[0].assists - b.players[0].assists) * dir;
        },
        "players[0].level": function(a, b, dir) {
            return (a.players[0].level - b.players[0].level) * dir;
        },
        "players[0].last_hits": function(a, b, dir) {
            return (a.players[0].last_hits - b.players[0].last_hits) * dir;
        },
        "players[0].denies": function(a, b, dir) {
            return (a.players[0].denies - b.players[0].denies) * dir;
        },
        "players[0].gold_per_min": function(a, b, dir) {
            return (a.players[0].gold_per_min - b.players[0].gold_per_min) * dir;
        },
        "players[0].xp_per_min": function(a, b, dir) {
            return (a.players[0].xp_per_min - b.players[0].xp_per_min) * dir;
        },
        "players[0].hero_damage": function(a, b, dir) {
            return (a.players[0].hero_damage - b.players[0].hero_damage) * dir;
        },
        "players[0].tower_damage": function(a, b, dir) {
            return (a.players[0].tower_damage - b.players[0].tower_damage) * dir;
        },
        "players[0].hero_healing": function(a, b, dir) {
                return (a.players[0].hero_healing - b.players[0].hero_healing) * dir;
            }
            //TODO implement more sorts
            //game mode
            //hero (sort alpha?)
            //played time
            //result
            //region
            //parse status
    };
    for (var key in sorts) {
        if (key in sortFuncs) {
            matches.sort(function(a, b) {
                return sortFuncs[key](a, b, sorts[key]);
            });
        }
    }
    return matches;
}
/*
function getFullPlayerData(matches, doAction, cb) {
    cb();

    if (!doAction) {
        return cb();
    }
    //create array of ids to use for $in
    var match_ids = matches.map(function(m) {
        return m.match_id;
    });
    db.matches.find({
        match_id: {
            $in: match_ids
        }
    }, {
        fields: {
            "players.account_id": 1,
            "players.hero_id": 1,
            "players.player_slot": 1,
            match_id: 1
        }
    }, function(err, docs) {
        if (err) {
            return cb(err);
        }
        //build hash of match_id to full player data
        var hash = {};
        for (var i = 0; i < docs.length; i++) {
            hash[docs[i].match_id] = docs[i].players;
        }
        //iterate through given matches and populate all_players field
        for (var j = 0; j < matches.length; j++) {
            matches[j].all_players = hash[matches[j].match_id];
        }
        cb(err);
    });
}
    */
function getParsedPlayerData(matches, doAction, cb) {
    if (!doAction) {
        return cb();
    }
    //we optimize by filtering matches for only those with parse_status===2
    var parsed = matches.filter(function(m) {
        return m.parse_status === 2;
    });
    //the following does a query for each parsed match in the set, so could be a lot of queries
    //since we might want a different position on each query, we need to make them individually
    async.each(parsed, function(m, cb) {
        var player = m.players[0];
        var parseSlot = player.player_slot % (128 - 5);
        db.matches.findOne({
            match_id: m.match_id
        }, {
            fields: {
                "parsed_data": 1,
                "parsed_data.version": 1,
                "parsed_data.chat": 1,
                "parsed_data.radiant_gold_adv": 1,
                "parsed_data.radiant_xp_adv": 1,
                "parsed_data.players": {
                    $slice: [parseSlot, 1]
                },
                match_id: 1
            }
        }, function(err, doc) {
            if (err) {
                return cb(err);
            }
            m.parsed_data = doc.parsed_data;
            cb(err);
        });
    }, function(err) {
        cb(err);
    });
}
module.exports = advQuery;<|endoftext|>


<commit_before><commit_msg>write test for getAttendees function<commit_after>function getAttendees(peopleInvited, responses){

}

// test
var people = ['Easter Bunny', 'Tooth Fairy', 'Frosty the Snowman',
              'Jack Frost', 'Cupid', 'Father Time'];
var responses = [
     {name: 'Easter Bunny', response: 'declined'},
     {name: 'Jack Frost', response: 'declined'},
     {name: 'Tooth Fairy', response: 'accepted'}
   ]

console.log(getAttendees(people, responses) === ['Tooth Fairy', 'Frosty the Snowman', 'Cupid', 'Father Time'])
<|endoftext|>


<commit_before>/**
 * @author mrdoob / http://mrdoob.com/
 */

import { ArrayCamera } from '../../cameras/ArrayCamera.js';
import { EventDispatcher } from '../../core/EventDispatcher.js';
import { Group } from '../../objects/Group.js';
import { Matrix4 } from '../../math/Matrix4.js';
import { PerspectiveCamera } from '../../cameras/PerspectiveCamera.js';
import { Vector3 } from '../../math/Vector3.js';
import { Vector4 } from '../../math/Vector4.js';
import { WebGLAnimation } from '../webgl/WebGLAnimation.js';

function WebXRManager( renderer, gl ) {

	var scope = this;

	var session = null;

	// var framebufferScaleFactor = 1.0;

	var referenceSpace = null;
	var referenceSpaceType = 'local-floor';

	var pose = null;

	var controllers = [];
	var sortedInputSources = [];

	function isPresenting() {

		return session !== null && referenceSpace !== null;

	}

	//

	var cameraL = new PerspectiveCamera();
	cameraL.layers.enable( 1 );
	cameraL.viewport = new Vector4();

	var cameraR = new PerspectiveCamera();
	cameraR.layers.enable( 2 );
	cameraR.viewport = new Vector4();

	var cameraVR = new ArrayCamera( [ cameraL, cameraR ] );
	cameraVR.layers.enable( 1 );
	cameraVR.layers.enable( 2 );

	//

	this.enabled = false;

	this.getController = function ( id ) {

		var controller = controllers[ id ];

		if ( controller === undefined ) {

			controller = new Group();
			controller.matrixAutoUpdate = false;
			controller.visible = false;

			controllers[ id ] = controller;

		}

		return controller;

	};

	//

	function onSessionEvent( event ) {

		for ( var i = 0; i < controllers.length; i ++ ) {

			if ( sortedInputSources[ i ] === event.inputSource ) {

				controllers[ i ].dispatchEvent( { type: event.type } );

			}

		}

	}

	function onSessionEnd() {

		renderer.setFramebuffer( null );
		renderer.setRenderTarget( renderer.getRenderTarget() ); // Hack #15830
		animation.stop();

		scope.dispatchEvent( { type: 'sessionend' } );

	}

	function onRequestReferenceSpace( value ) {

		referenceSpace = value;

		animation.setContext( session );
		animation.start();

		scope.dispatchEvent( { type: 'sessionstart' } );

	}

	this.setFramebufferScaleFactor = function ( /* value */ ) {

		// framebufferScaleFactor = value;

	};

	this.setReferenceSpaceType = function ( value ) {

		referenceSpaceType = value;

	};

	this.getSession = function () {

		return session;

	};

	this.setSession = function ( value ) {

		session = value;

		if ( session !== null ) {

			session.addEventListener( 'select', onSessionEvent );
			session.addEventListener( 'selectstart', onSessionEvent );
			session.addEventListener( 'selectend', onSessionEvent );
			session.addEventListener( 'squeeze', onSessionEvent );
			session.addEventListener( 'squeezestart', onSessionEvent );
			session.addEventListener( 'squeezeend', onSessionEvent );
			session.addEventListener( 'end', onSessionEnd );

			// eslint-disable-next-line no-undef
			session.updateRenderState( { baseLayer: new XRWebGLLayer( session, gl ) } );

			session.requestReferenceSpace( referenceSpaceType ).then( onRequestReferenceSpace );

			//

			session.addEventListener( 'inputsourceschange', updateInputSources );

			updateInputSources();

		}

	};

	function updateInputSources() {

		for ( var i = 0; i < controllers.length; i ++ ) {

			sortedInputSources[ i ] = findInputSource( i );

		}

	}

	function findInputSource( id ) {

		var inputSources = session.inputSources;

		for ( var i = 0; i < inputSources.length; i ++ ) {

			var inputSource = inputSources[ i ];
			var handedness = inputSource.handedness;

			if ( id === 0 && ( handedness === 'none' || handedness === 'right' ) ) return inputSource;
			if ( id === 1 && ( handedness === 'left' ) ) return inputSource;

		}

	}

	//

	var cameraLPos = new Vector3();
	var cameraRPos = new Vector3();

	/**
	 * @author jsantell / https://www.jsantell.com/
	 *
	 * Assumes 2 cameras that are parallel and share an X-axis, and that
	 * the cameras' projection and world matrices have already been set.
	 * And that near and far planes are identical for both cameras.
	 * Visualization of this technique: https://computergraphics.stackexchange.com/a/4765
	 */
	function setProjectionFromUnion( camera, cameraL, cameraR ) {

		cameraLPos.setFromMatrixPosition( cameraL.matrixWorld );
		cameraRPos.setFromMatrixPosition( cameraR.matrixWorld );

		var ipd = cameraLPos.distanceTo( cameraRPos );

		var projL = cameraL.projectionMatrix.elements;
		var projR = cameraR.projectionMatrix.elements;

		// VR systems will have identical far and near planes, and
		// most likely identical top and bottom frustum extents.
		// Use the left camera for these values.
		var near = projL[ 14 ] / ( projL[ 10 ] - 1 );
		var far = projL[ 14 ] / ( projL[ 10 ] + 1 );
		var topFov = ( projL[ 9 ] + 1 ) / projL[ 5 ];
		var bottomFov = ( projL[ 9 ] - 1 ) / projL[ 5 ];

		var leftFov = ( projL[ 8 ] - 1 ) / projL[ 0 ];
		var rightFov = ( projR[ 8 ] + 1 ) / projR[ 0 ];
		var left = near * leftFov;
		var right = near * rightFov;

		// Calculate the new camera's position offset from the
		// left camera. xOffset should be roughly half `ipd`.
		var zOffset = ipd / ( - leftFov + rightFov );
		var xOffset = zOffset * - leftFov;

		// TODO: Better way to apply this offset?
		cameraL.matrixWorld.decompose( camera.position, camera.quaternion, camera.scale );
		camera.translateX( xOffset );
		camera.translateZ( zOffset );
		camera.matrixWorld.compose( camera.position, camera.quaternion, camera.scale );
		camera.matrixWorldInverse.getInverse( camera.matrixWorld );

		// Find the union of the frustum values of the cameras and scale
		// the values so that the near plane's position does not change in world space,
		// although must now be relative to the new union camera.
		var near2 = near + zOffset;
		var far2 = far + zOffset;
		var left2 = left - xOffset;
		var right2 = right + ( ipd - xOffset );
		var top2 = topFov * far / far2 * near2;
		var bottom2 = bottomFov * far / far2 * near2;

		camera.projectionMatrix.makePerspective( left2, right2, top2, bottom2, near2, far2 );

	}

	function updateCamera( camera, parent ) {

		if ( parent === null ) {

			camera.matrixWorld.copy( camera.matrix );

		} else {

			camera.matrixWorld.multiplyMatrices( parent.matrixWorld, camera.matrix );

		}

		camera.matrixWorldInverse.getInverse( camera.matrixWorld );

	}

	this.getCamera = function ( camera ) {

		var parent = camera.parent;
		var cameras = cameraVR.cameras;

		updateCamera( cameraVR, parent );

		for ( var i = 0; i < cameras.length; i ++ ) {

			updateCamera( cameras[ i ], parent );

		}

		// update camera and its children

		camera.matrixWorld.copy( cameraVR.matrixWorld );

		var children = camera.children;

		for ( var i = 0, l = children.length; i < l; i ++ ) {

			children[ i ].updateMatrixWorld( true );

		}

		setProjectionFromUnion( cameraVR, cameraL, cameraR );

		return cameraVR;

	};

	this.isPresenting = isPresenting;

	// Animation Loop

	var onAnimationFrameCallback = null;

	function onAnimationFrame( time, frame ) {

		pose = frame.getViewerPose( referenceSpace );

		if ( pose !== null ) {

			var views = pose.views;
			var baseLayer = session.renderState.baseLayer;

			renderer.setFramebuffer( baseLayer.framebuffer );

			for ( var i = 0; i < views.length; i ++ ) {

				var view = views[ i ];
				var viewport = baseLayer.getViewport( view );
				var viewMatrix = view.transform.inverse.matrix;

				var camera = cameraVR.cameras[ i ];
				camera.matrix.fromArray( viewMatrix ).getInverse( camera.matrix );
				camera.projectionMatrix.fromArray( view.projectionMatrix );
				camera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height );

				if ( i === 0 ) {

					cameraVR.matrix.copy( camera.matrix );

				}

			}

		}

		//

		for ( var i = 0; i < controllers.length; i ++ ) {

			var controller = controllers[ i ];

			var inputSource = sortedInputSources[ i ];

			if ( inputSource ) {

				var inputPose = frame.getPose( inputSource.targetRaySpace, referenceSpace );

				if ( inputPose !== null ) {

					controller.matrix.fromArray( inputPose.transform.matrix );
					controller.matrix.decompose( controller.position, controller.rotation, controller.scale );
					controller.visible = true;

					continue;

				}

			}

			controller.visible = false;

		}

		if ( onAnimationFrameCallback ) onAnimationFrameCallback( time );

	}

	var animation = new WebGLAnimation();
	animation.setAnimationLoop( onAnimationFrame );

	this.setAnimationLoop = function ( callback ) {

		onAnimationFrameCallback = callback;

	};

	this.dispose = function () {};

	// DEPRECATED

	this.getStandingMatrix = function () {

		console.warn( 'THREE.WebXRManager: getStandingMatrix() is no longer needed.' );
		return new Matrix4();

	};

	this.getDevice = function () {

		console.warn( 'THREE.WebXRManager: getDevice() has been deprecated.' );

	};

	this.setDevice = function () {

		console.warn( 'THREE.WebXRManager: setDevice() has been deprecated.' );

	};

	this.setFrameOfReferenceType = function () {

		console.warn( 'THREE.WebXRManager: setFrameOfReferenceType() has been deprecated.' );

	};

	this.submitFrame = function () {};

}

Object.assign( WebXRManager.prototype, EventDispatcher.prototype );

export { WebXRManager };
<commit_msg>WebXRManaver: Removed deprecated code.<commit_after>/**
 * @author mrdoob / http://mrdoob.com/
 */

import { ArrayCamera } from '../../cameras/ArrayCamera.js';
import { EventDispatcher } from '../../core/EventDispatcher.js';
import { Group } from '../../objects/Group.js';
import { PerspectiveCamera } from '../../cameras/PerspectiveCamera.js';
import { Vector3 } from '../../math/Vector3.js';
import { Vector4 } from '../../math/Vector4.js';
import { WebGLAnimation } from '../webgl/WebGLAnimation.js';

function WebXRManager( renderer, gl ) {

	var scope = this;

	var session = null;

	// var framebufferScaleFactor = 1.0;

	var referenceSpace = null;
	var referenceSpaceType = 'local-floor';

	var pose = null;

	var controllers = [];
	var sortedInputSources = [];

	function isPresenting() {

		return session !== null && referenceSpace !== null;

	}

	//

	var cameraL = new PerspectiveCamera();
	cameraL.layers.enable( 1 );
	cameraL.viewport = new Vector4();

	var cameraR = new PerspectiveCamera();
	cameraR.layers.enable( 2 );
	cameraR.viewport = new Vector4();

	var cameraVR = new ArrayCamera( [ cameraL, cameraR ] );
	cameraVR.layers.enable( 1 );
	cameraVR.layers.enable( 2 );

	//

	this.enabled = false;

	this.getController = function ( id ) {

		var controller = controllers[ id ];

		if ( controller === undefined ) {

			controller = new Group();
			controller.matrixAutoUpdate = false;
			controller.visible = false;

			controllers[ id ] = controller;

		}

		return controller;

	};

	//

	function onSessionEvent( event ) {

		for ( var i = 0; i < controllers.length; i ++ ) {

			if ( sortedInputSources[ i ] === event.inputSource ) {

				controllers[ i ].dispatchEvent( { type: event.type } );

			}

		}

	}

	function onSessionEnd() {

		renderer.setFramebuffer( null );
		renderer.setRenderTarget( renderer.getRenderTarget() ); // Hack #15830
		animation.stop();

		scope.dispatchEvent( { type: 'sessionend' } );

	}

	function onRequestReferenceSpace( value ) {

		referenceSpace = value;

		animation.setContext( session );
		animation.start();

		scope.dispatchEvent( { type: 'sessionstart' } );

	}

	this.setFramebufferScaleFactor = function ( /* value */ ) {

		// framebufferScaleFactor = value;

	};

	this.setReferenceSpaceType = function ( value ) {

		referenceSpaceType = value;

	};

	this.getSession = function () {

		return session;

	};

	this.setSession = function ( value ) {

		session = value;

		if ( session !== null ) {

			session.addEventListener( 'select', onSessionEvent );
			session.addEventListener( 'selectstart', onSessionEvent );
			session.addEventListener( 'selectend', onSessionEvent );
			session.addEventListener( 'squeeze', onSessionEvent );
			session.addEventListener( 'squeezestart', onSessionEvent );
			session.addEventListener( 'squeezeend', onSessionEvent );
			session.addEventListener( 'end', onSessionEnd );

			// eslint-disable-next-line no-undef
			session.updateRenderState( { baseLayer: new XRWebGLLayer( session, gl ) } );

			session.requestReferenceSpace( referenceSpaceType ).then( onRequestReferenceSpace );

			//

			session.addEventListener( 'inputsourceschange', updateInputSources );

			updateInputSources();

		}

	};

	function updateInputSources() {

		for ( var i = 0; i < controllers.length; i ++ ) {

			sortedInputSources[ i ] = findInputSource( i );

		}

	}

	function findInputSource( id ) {

		var inputSources = session.inputSources;

		for ( var i = 0; i < inputSources.length; i ++ ) {

			var inputSource = inputSources[ i ];
			var handedness = inputSource.handedness;

			if ( id === 0 && ( handedness === 'none' || handedness === 'right' ) ) return inputSource;
			if ( id === 1 && ( handedness === 'left' ) ) return inputSource;

		}

	}

	//

	var cameraLPos = new Vector3();
	var cameraRPos = new Vector3();

	/**
	 * @author jsantell / https://www.jsantell.com/
	 *
	 * Assumes 2 cameras that are parallel and share an X-axis, and that
	 * the cameras' projection and world matrices have already been set.
	 * And that near and far planes are identical for both cameras.
	 * Visualization of this technique: https://computergraphics.stackexchange.com/a/4765
	 */
	function setProjectionFromUnion( camera, cameraL, cameraR ) {

		cameraLPos.setFromMatrixPosition( cameraL.matrixWorld );
		cameraRPos.setFromMatrixPosition( cameraR.matrixWorld );

		var ipd = cameraLPos.distanceTo( cameraRPos );

		var projL = cameraL.projectionMatrix.elements;
		var projR = cameraR.projectionMatrix.elements;

		// VR systems will have identical far and near planes, and
		// most likely identical top and bottom frustum extents.
		// Use the left camera for these values.
		var near = projL[ 14 ] / ( projL[ 10 ] - 1 );
		var far = projL[ 14 ] / ( projL[ 10 ] + 1 );
		var topFov = ( projL[ 9 ] + 1 ) / projL[ 5 ];
		var bottomFov = ( projL[ 9 ] - 1 ) / projL[ 5 ];

		var leftFov = ( projL[ 8 ] - 1 ) / projL[ 0 ];
		var rightFov = ( projR[ 8 ] + 1 ) / projR[ 0 ];
		var left = near * leftFov;
		var right = near * rightFov;

		// Calculate the new camera's position offset from the
		// left camera. xOffset should be roughly half `ipd`.
		var zOffset = ipd / ( - leftFov + rightFov );
		var xOffset = zOffset * - leftFov;

		// TODO: Better way to apply this offset?
		cameraL.matrixWorld.decompose( camera.position, camera.quaternion, camera.scale );
		camera.translateX( xOffset );
		camera.translateZ( zOffset );
		camera.matrixWorld.compose( camera.position, camera.quaternion, camera.scale );
		camera.matrixWorldInverse.getInverse( camera.matrixWorld );

		// Find the union of the frustum values of the cameras and scale
		// the values so that the near plane's position does not change in world space,
		// although must now be relative to the new union camera.
		var near2 = near + zOffset;
		var far2 = far + zOffset;
		var left2 = left - xOffset;
		var right2 = right + ( ipd - xOffset );
		var top2 = topFov * far / far2 * near2;
		var bottom2 = bottomFov * far / far2 * near2;

		camera.projectionMatrix.makePerspective( left2, right2, top2, bottom2, near2, far2 );

	}

	function updateCamera( camera, parent ) {

		if ( parent === null ) {

			camera.matrixWorld.copy( camera.matrix );

		} else {

			camera.matrixWorld.multiplyMatrices( parent.matrixWorld, camera.matrix );

		}

		camera.matrixWorldInverse.getInverse( camera.matrixWorld );

	}

	this.getCamera = function ( camera ) {

		var parent = camera.parent;
		var cameras = cameraVR.cameras;

		updateCamera( cameraVR, parent );

		for ( var i = 0; i < cameras.length; i ++ ) {

			updateCamera( cameras[ i ], parent );

		}

		// update camera and its children

		camera.matrixWorld.copy( cameraVR.matrixWorld );

		var children = camera.children;

		for ( var i = 0, l = children.length; i < l; i ++ ) {

			children[ i ].updateMatrixWorld( true );

		}

		setProjectionFromUnion( cameraVR, cameraL, cameraR );

		return cameraVR;

	};

	this.isPresenting = isPresenting;

	// Animation Loop

	var onAnimationFrameCallback = null;

	function onAnimationFrame( time, frame ) {

		pose = frame.getViewerPose( referenceSpace );

		if ( pose !== null ) {

			var views = pose.views;
			var baseLayer = session.renderState.baseLayer;

			renderer.setFramebuffer( baseLayer.framebuffer );

			for ( var i = 0; i < views.length; i ++ ) {

				var view = views[ i ];
				var viewport = baseLayer.getViewport( view );
				var viewMatrix = view.transform.inverse.matrix;

				var camera = cameraVR.cameras[ i ];
				camera.matrix.fromArray( viewMatrix ).getInverse( camera.matrix );
				camera.projectionMatrix.fromArray( view.projectionMatrix );
				camera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height );

				if ( i === 0 ) {

					cameraVR.matrix.copy( camera.matrix );

				}

			}

		}

		//

		for ( var i = 0; i < controllers.length; i ++ ) {

			var controller = controllers[ i ];

			var inputSource = sortedInputSources[ i ];

			if ( inputSource ) {

				var inputPose = frame.getPose( inputSource.targetRaySpace, referenceSpace );

				if ( inputPose !== null ) {

					controller.matrix.fromArray( inputPose.transform.matrix );
					controller.matrix.decompose( controller.position, controller.rotation, controller.scale );
					controller.visible = true;

					continue;

				}

			}

			controller.visible = false;

		}

		if ( onAnimationFrameCallback ) onAnimationFrameCallback( time );

	}

	var animation = new WebGLAnimation();
	animation.setAnimationLoop( onAnimationFrame );

	this.setAnimationLoop = function ( callback ) {

		onAnimationFrameCallback = callback;

	};

	this.dispose = function () {};

}

Object.assign( WebXRManager.prototype, EventDispatcher.prototype );

export { WebXRManager };
<|endoftext|>


<commit_before>/*global require, exports, , setInterval, clearInterval*/

var addon = require('./../../erizoAPI/build/Release/addon');
var logger = require('./../common/logger').logger;
var rpc = require('./../common/rpc');

// Logger
var log = logger.getLogger("ErizoJSController");

// Log segfaults
var SegfaultHandler = require('segfault-handler');
SegfaultHandler.registerHandler();

exports.ErizoJSController = function (spec) {
    "use strict";

    var that = {},
        // {id: array of subscribers}
        subscribers = {},
        // {id: OneToManyProcessor}
        publishers = {},

        // {id: ExternalOutput}
        externalOutputs = {},

        INTERVAL_TIME_SDP = 100,
        INTERVAL_TIME_FIR = 100,
        INTERVAL_TIME_KILL = 30*60*1000, // Timeout to kill itself after a timeout since the publisher leaves room.
        waitForFIR,
        initWebRtcConnection,
        getSdp,
        getRoap;

    /*
     * Given a WebRtcConnection waits for the state READY for ask it to send a FIR packet to its publisher.
     */
    waitForFIR = function (wrtc, to) {

        if (publishers[to] !== undefined) {
            var intervarId = setInterval(function () {
              if (publishers[to]!==undefined){
                if (wrtc.getCurrentState() >= 103 && publishers[to].getPublisherState() >=103) {
                    publishers[to].sendFIR();
                    clearInterval(intervarId);
                }
              }

            }, INTERVAL_TIME_FIR);
        }
    };

    /*
     * Given a WebRtcConnection waits for the state CANDIDATES_GATHERED for set remote SDP.
     */
    initWebRtcConnection = function (wrtc, sdp, callback, id_pub, id_sub) {
        if(typeof sdp != 'string') sdp = JSON.stringify(sdp); // fixes some issues with sending json object as json, and not as string

        if (GLOBAL.config.erizoController.sendStats) {
            wrtc.getStats(function (newStats){
                log.info("Received RTCP stats: ", newStats);
                var timeStamp = new Date();
                rpc.callRpc('stats_handler', 'stats', [{pub: id_pub, subs: id_sub, stats: JSON.parse(newStats), timestamp:timeStamp.getTime()}]);
            });
        }

        wrtc.init( function (newStatus){
          var localSdp, answer;
          log.info("webrtc Addon status" + newStatus );
          if (newStatus === 102 && !sdpDelivered) {
            localSdp = wrtc.getLocalSdp();
            answer = getRoap(localSdp, roap);
            callback('callback', answer);
            sdpDelivered = true;

          }
          if (newStatus === 103) {
            callback('onReady');
          }
        });

        var roap = sdp,
            remoteSdp = getSdp(roap);
        wrtc.setRemoteSdp(remoteSdp);

        var sdpDelivered = false;
    };

    /*
     * Gets SDP from roap message.
     */
    getSdp = function (roap) {

        var reg1 = new RegExp(/^.*sdp\":\"(.*)\",.*$/),
            sdp = roap.match(reg1)[1],
            reg2 = new RegExp(/\\r\\n/g);

        sdp = sdp.replace(reg2, '\n');

        return sdp;

    };

    /*
     * Gets roap message from SDP.
     */
    getRoap = function (sdp, offerRoap) {

        var reg1 = new RegExp(/\n/g),
            offererSessionId = offerRoap.match(/("offererSessionId":)(.+?)(,)/)[0],
            answererSessionId = "106",
            answer = ('\n{\n \"messageType\":\"ANSWER\",\n');

        sdp = sdp.replace(reg1, '\\r\\n');

        //var reg2 = new RegExp(/^.*offererSessionId\":(...).*$/);
        //var offererSessionId = offerRoap.match(reg2)[1];

        answer += ' \"sdp\":\"' + sdp + '\",\n';
        //answer += ' \"offererSessionId\":' + offererSessionId + ',\n';
        answer += ' ' + offererSessionId + '\n';
        answer += ' \"answererSessionId\":' + answererSessionId + ',\n \"seq\" : 1\n}\n';

        return answer;
    };

    that.addExternalInput = function (from, url, callback) {

        if (publishers[from] === undefined) {

            log.info("Adding external input peer_id ", from);

            var muxer = new addon.OneToManyProcessor(),
                ei = new addon.ExternalInput(url);

            publishers[from] = muxer;
            subscribers[from] = [];

            ei.setAudioReceiver(muxer);
            ei.setVideoReceiver(muxer);
            muxer.setExternalPublisher(ei);

            var answer = ei.init();

            if (answer >= 0) {
                callback('callback', 'success');
            } else {
                callback('callback', answer);
            }

        } else {
            log.info("Publisher already set for", from);
        }
    };

    that.addExternalOutput = function (to, url) {
        if (publishers[to] !== undefined) {
            log.info("Adding ExternalOutput to " + to + " url " + url);
            var externalOutput = new addon.ExternalOutput(url);
            externalOutput.init();
            publishers[to].addExternalOutput(externalOutput, url);
            externalOutputs[url] = externalOutput;
        }
    };

    that.removeExternalOutput = function (to, url) {
      if (externalOutputs[url] !== undefined && publishers[to]!=undefined) {
        log.info("Stopping ExternalOutput: url " + url);
        publishers[to].removeSubscriber(url);
        delete externalOutputs[url];
      }
    };

    /*
     * Adds a publisher to the room. This creates a new OneToManyProcessor
     * and a new WebRtcConnection. This WebRtcConnection will be the publisher
     * of the OneToManyProcessor.
     */
    that.addPublisher = function (from, sdp, callback) {

        if (publishers[from] === undefined) {

            log.info("Adding publisher peer_id ", from);

            var muxer = new addon.OneToManyProcessor(),
                wrtc = new addon.WebRtcConnection(true, true, GLOBAL.config.erizo.stunserver, GLOBAL.config.erizo.stunport, GLOBAL.config.erizo.minport, GLOBAL.config.erizo.maxport);

            publishers[from] = muxer;
            subscribers[from] = [];

            wrtc.setAudioReceiver(muxer);
            wrtc.setVideoReceiver(muxer);
            muxer.setPublisher(wrtc);

            initWebRtcConnection(wrtc, sdp, callback, from);

            //log.info('Publishers: ', publishers);
            //log.info('Subscribers: ', subscribers);

        } else {
            log.info("Publisher already set for", from);
        }
    };

    /*
     * Adds a subscriber to the room. This creates a new WebRtcConnection.
     * This WebRtcConnection will be added to the subscribers list of the
     * OneToManyProcessor.
     */
    that.addSubscriber = function (from, to, audio, video, sdp, callback) {
        if (typeof sdp != 'string') sdp = JSON.stringify(sdp);

        if (publishers[to] !== undefined && subscribers[to].indexOf(from) === -1 && sdp.match('OFFER') !== null) {

            log.info("Adding subscriber from ", from, 'to ', to, 'audio', audio, 'video', video);

            var wrtc = new addon.WebRtcConnection(audio, video, GLOBAL.config.erizo.stunserver, GLOBAL.config.erizo.stunport, GLOBAL.config.erizo.minport, GLOBAL.config.erizo.maxport);

            subscribers[to].push(from);
            publishers[to].addSubscriber(wrtc, from);

            initWebRtcConnection(wrtc, sdp, callback, to, from);
//            waitForFIR(wrtc, to);

            //log.info('Publishers: ', publishers);
            //log.info('Subscribers: ', subscribers);
        }
    };

    /*
     * Removes a publisher from the room. This also deletes the associated OneToManyProcessor.
     */
    that.removePublisher = function (from) {

        if (subscribers[from] !== undefined && publishers[from] !== undefined) {
            log.info('Removing muxer', from);
            publishers[from].close();
            log.info('Removing subscribers', from);
            delete subscribers[from];
            log.info('Removing publisher', from);
            delete publishers[from];
            var count = 0;
            for (var k in publishers) {
                if (publishers.hasOwnProperty(k)) {
                   ++count;
                }
            }
            log.info("Publishers: ", count);
            if (count === 0)  {
                log.info('Removed all publishers. Killing process.');
                process.exit(0);
            }
        }
    };

    /*
     * Removes a subscriber from the room. This also removes it from the associated OneToManyProcessor.
     */
    that.removeSubscriber = function (from, to) {

        var index = subscribers[to].indexOf(from);
        if (index !== -1) {
            log.info('Removing subscriber ', from, 'to muxer ', to);
            publishers[to].removeSubscriber(from);
            subscribers[to].splice(index, 1);
        }
    };

    /*
     * Removes all the subscribers related with a client.
     */
    that.removeSubscriptions = function (from) {

        var key, index;

        log.info('Removing subscriptions of ', from);
        for (key in subscribers) {
            if (subscribers.hasOwnProperty(key)) {
                index = subscribers[key].indexOf(from);
                if (index !== -1) {
                    log.info('Removing subscriber ', from, 'to muxer ', key);
                    publishers[key].removeSubscriber(from);
                    subscribers[key].splice(index, 1);
                }
            }
        }
    };

    return that;
};
<commit_msg>Added stats for connection status in JS controller<commit_after>/*global require, exports, , setInterval, clearInterval*/

var addon = require('./../../erizoAPI/build/Release/addon');
var logger = require('./../common/logger').logger;
var rpc = require('./../common/rpc');

// Logger
var log = logger.getLogger("ErizoJSController");

// Log segfaults
var SegfaultHandler = require('segfault-handler');
SegfaultHandler.registerHandler();

exports.ErizoJSController = function (spec) {
    "use strict";

    var that = {},
        // {id: array of subscribers}
        subscribers = {},
        // {id: OneToManyProcessor}
        publishers = {},

        // {id: ExternalOutput}
        externalOutputs = {},

        INTERVAL_TIME_SDP = 100,
        INTERVAL_TIME_FIR = 100,
        INTERVAL_TIME_KILL = 30*60*1000, // Timeout to kill itself after a timeout since the publisher leaves room.
        waitForFIR,
        initWebRtcConnection,
        getSdp,
        getRoap;

    /*
     * Given a WebRtcConnection waits for the state READY for ask it to send a FIR packet to its publisher.
     */
    waitForFIR = function (wrtc, to) {

        if (publishers[to] !== undefined) {
            var intervarId = setInterval(function () {
              if (publishers[to]!==undefined){
                if (wrtc.getCurrentState() >= 103 && publishers[to].getPublisherState() >=103) {
                    publishers[to].sendFIR();
                    clearInterval(intervarId);
                }
              }

            }, INTERVAL_TIME_FIR);
        }
    };

    /*
     * Given a WebRtcConnection waits for the state CANDIDATES_GATHERED for set remote SDP.
     */
    initWebRtcConnection = function (wrtc, sdp, callback, id_pub, id_sub) {
        if(typeof sdp != 'string') sdp = JSON.stringify(sdp); // fixes some issues with sending json object as json, and not as string

        if (GLOBAL.config.erizoController.sendStats) {
            wrtc.getStats(function (newStats){
                log.info("Received RTCP stats: ", newStats);
                var timeStamp = new Date();
                rpc.callRpc('stats_handler', 'stats', [{pub: id_pub, subs: id_sub, stats: JSON.parse(newStats), timestamp:timeStamp.getTime()}]);
            });
        }

        wrtc.init( function (newStatus){

          var localSdp, answer;
          log.info("webrtc Addon status" + newStatus );

          if (GLOBAL.config.erizoController.sendStats) {
            var timeStamp = new Date();
            rpc.callRpc('stats_handler', 'event', {pub: id_pub, subs: id_sub, type: 'connection_status', status: newStatus, timestamp:timeStamp.getTime()});
          }
          if (newStatus === 102 && !sdpDelivered) {
            localSdp = wrtc.getLocalSdp();
            answer = getRoap(localSdp, roap);
            callback('callback', answer);
            sdpDelivered = true;

          }
          if (newStatus === 103) {
            callback('onReady');
          }
        });

        var roap = sdp,
            remoteSdp = getSdp(roap);
        wrtc.setRemoteSdp(remoteSdp);

        var sdpDelivered = false;
    };

    /*
     * Gets SDP from roap message.
     */
    getSdp = function (roap) {

        var reg1 = new RegExp(/^.*sdp\":\"(.*)\",.*$/),
            sdp = roap.match(reg1)[1],
            reg2 = new RegExp(/\\r\\n/g);

        sdp = sdp.replace(reg2, '\n');

        return sdp;

    };

    /*
     * Gets roap message from SDP.
     */
    getRoap = function (sdp, offerRoap) {

        var reg1 = new RegExp(/\n/g),
            offererSessionId = offerRoap.match(/("offererSessionId":)(.+?)(,)/)[0],
            answererSessionId = "106",
            answer = ('\n{\n \"messageType\":\"ANSWER\",\n');

        sdp = sdp.replace(reg1, '\\r\\n');

        //var reg2 = new RegExp(/^.*offererSessionId\":(...).*$/);
        //var offererSessionId = offerRoap.match(reg2)[1];

        answer += ' \"sdp\":\"' + sdp + '\",\n';
        //answer += ' \"offererSessionId\":' + offererSessionId + ',\n';
        answer += ' ' + offererSessionId + '\n';
        answer += ' \"answererSessionId\":' + answererSessionId + ',\n \"seq\" : 1\n}\n';

        return answer;
    };

    that.addExternalInput = function (from, url, callback) {

        if (publishers[from] === undefined) {

            log.info("Adding external input peer_id ", from);

            var muxer = new addon.OneToManyProcessor(),
                ei = new addon.ExternalInput(url);

            publishers[from] = muxer;
            subscribers[from] = [];

            ei.setAudioReceiver(muxer);
            ei.setVideoReceiver(muxer);
            muxer.setExternalPublisher(ei);

            var answer = ei.init();

            if (answer >= 0) {
                callback('callback', 'success');
            } else {
                callback('callback', answer);
            }

        } else {
            log.info("Publisher already set for", from);
        }
    };

    that.addExternalOutput = function (to, url) {
        if (publishers[to] !== undefined) {
            log.info("Adding ExternalOutput to " + to + " url " + url);
            var externalOutput = new addon.ExternalOutput(url);
            externalOutput.init();
            publishers[to].addExternalOutput(externalOutput, url);
            externalOutputs[url] = externalOutput;
        }
    };

    that.removeExternalOutput = function (to, url) {
      if (externalOutputs[url] !== undefined && publishers[to]!=undefined) {
        log.info("Stopping ExternalOutput: url " + url);
        publishers[to].removeSubscriber(url);
        delete externalOutputs[url];
      }
    };

    /*
     * Adds a publisher to the room. This creates a new OneToManyProcessor
     * and a new WebRtcConnection. This WebRtcConnection will be the publisher
     * of the OneToManyProcessor.
     */
    that.addPublisher = function (from, sdp, callback) {

        if (publishers[from] === undefined) {

            log.info("Adding publisher peer_id ", from);

            var muxer = new addon.OneToManyProcessor(),
                wrtc = new addon.WebRtcConnection(true, true, GLOBAL.config.erizo.stunserver, GLOBAL.config.erizo.stunport, GLOBAL.config.erizo.minport, GLOBAL.config.erizo.maxport);

            publishers[from] = muxer;
            subscribers[from] = [];

            wrtc.setAudioReceiver(muxer);
            wrtc.setVideoReceiver(muxer);
            muxer.setPublisher(wrtc);

            initWebRtcConnection(wrtc, sdp, callback, from);

            //log.info('Publishers: ', publishers);
            //log.info('Subscribers: ', subscribers);

        } else {
            log.info("Publisher already set for", from);
        }
    };

    /*
     * Adds a subscriber to the room. This creates a new WebRtcConnection.
     * This WebRtcConnection will be added to the subscribers list of the
     * OneToManyProcessor.
     */
    that.addSubscriber = function (from, to, audio, video, sdp, callback) {
        if (typeof sdp != 'string') sdp = JSON.stringify(sdp);

        if (publishers[to] !== undefined && subscribers[to].indexOf(from) === -1 && sdp.match('OFFER') !== null) {

            log.info("Adding subscriber from ", from, 'to ', to, 'audio', audio, 'video', video);

            var wrtc = new addon.WebRtcConnection(audio, video, GLOBAL.config.erizo.stunserver, GLOBAL.config.erizo.stunport, GLOBAL.config.erizo.minport, GLOBAL.config.erizo.maxport);

            subscribers[to].push(from);
            publishers[to].addSubscriber(wrtc, from);

            initWebRtcConnection(wrtc, sdp, callback, to, from);
//            waitForFIR(wrtc, to);

            //log.info('Publishers: ', publishers);
            //log.info('Subscribers: ', subscribers);
        }
    };

    /*
     * Removes a publisher from the room. This also deletes the associated OneToManyProcessor.
     */
    that.removePublisher = function (from) {

        if (subscribers[from] !== undefined && publishers[from] !== undefined) {
            log.info('Removing muxer', from);
            publishers[from].close();
            log.info('Removing subscribers', from);
            delete subscribers[from];
            log.info('Removing publisher', from);
            delete publishers[from];
            var count = 0;
            for (var k in publishers) {
                if (publishers.hasOwnProperty(k)) {
                   ++count;
                }
            }
            log.info("Publishers: ", count);
            if (count === 0)  {
                log.info('Removed all publishers. Killing process.');
                process.exit(0);
            }
        }
    };

    /*
     * Removes a subscriber from the room. This also removes it from the associated OneToManyProcessor.
     */
    that.removeSubscriber = function (from, to) {

        var index = subscribers[to].indexOf(from);
        if (index !== -1) {
            log.info('Removing subscriber ', from, 'to muxer ', to);
            publishers[to].removeSubscriber(from);
            subscribers[to].splice(index, 1);
        }
    };

    /*
     * Removes all the subscribers related with a client.
     */
    that.removeSubscriptions = function (from) {

        var key, index;

        log.info('Removing subscriptions of ', from);
        for (key in subscribers) {
            if (subscribers.hasOwnProperty(key)) {
                index = subscribers[key].indexOf(from);
                if (index !== -1) {
                    log.info('Removing subscriber ', from, 'to muxer ', key);
                    publishers[key].removeSubscriber(from);
                    subscribers[key].splice(index, 1);
                }
            }
        }
    };

    return that;
};
<|endoftext|>


<commit_before>/**
 * @file Require preprocess configuration
 * @since 0.2.9
 */
/*#ifndef(UMD)*/
"use strict";
/*global _gpfRequireConfigureAddOption*/ // Declare a configuration option
/*#endif*/

/**
  * @typedef gpf.typedef.requireResource
  * @property {String} name Resource resolved name
  * @property {String} content Resource content
  * @property {String} type Resource type
  * @since 0.2.9
  */

/**
   * Mocked response callback
   *
   * @callback gpf.typedef.requirePreprocessFunc
   *
   * @param {gpf.typedef.requireResource} resource Resource definition
   * @return {Promise<gpf.typedef.requireResource>}
   * @since 0.2.9
   */

function _gpfRequireConfigureCheckPreprocess (value) {
    if (typeof value !== "function") {
        gpf.Error.invalidRequireConfigureOptionValue();
    }
}

function _gpfRequireConfigurePreprocess (context, value) {
    _gpfRequireConfigureCheckPreprocess(value);
    context.preprocess = value;
}

_gpfRequireConfigureAddOption("preprocess", _gpfRequireConfigurePreprocess);
<commit_msg>Documentation (#306)<commit_after>/**
 * @file Require preprocess configuration
 * @since 0.2.9
 */
/*#ifndef(UMD)*/
"use strict";
/*global _gpfRequireConfigureAddOption*/ // Declare a configuration option
/*#endif*/

/**
  * @typedef gpf.typedef.requireResource
  * @property {String} name Resource resolved name
  * @property {String} content Resource content
  * @property {String} type Resource type (extension, for instance: ".js")
  * @since 0.2.9
  */

/**
   * Resource preprocessing function.
   * It enables changing the resource content and / or the resource type before it is evaluated.
   *
   * @callback gpf.typedef.requirePreprocessFunc
   *
   * @param {gpf.typedef.requireResource} resource Resource definition
   * @return {Promise<gpf.typedef.requireResource>}
   * @since 0.2.9
   */

function _gpfRequireConfigureCheckPreprocess (value) {
    if (typeof value !== "function") {
        gpf.Error.invalidRequireConfigureOptionValue();
    }
}

function _gpfRequireConfigurePreprocess (context, value) {
    _gpfRequireConfigureCheckPreprocess(value);
    context.preprocess = value;
}

_gpfRequireConfigureAddOption("preprocess", _gpfRequireConfigurePreprocess);
<|endoftext|>