<commit_before>import {Promise} from 'bluebird'
import {Registry} from './registry'
import {Set} from 'immutable'
import * as u from './useful'
import {read, set, remove} from './firebase_useful'
import log4js from 'log4js'
import {TODO_TRX_PATH, DONE_TRX_PATH} from './settings'

let logger = log4js.getLogger('transactor')

function configureLogging() {
  if (!log4js.configured) {
    log4js.configure({
      appenders: [{
        type: 'console',
        layout: {
          type: 'pattern',
          pattern: '[%[%5.5p%]] - %m'
        }
      }]
    })
    logger.setLevel('INFO')
  }
}

class AbortError {
  constructor(msg) {
    this.msg = msg
  }
}

class UserAbort {
  constructor(msg) {
    this.msg = msg
  }
}

export function transactor(firebase, handlers, options = {}) {

  const {
    todoTrxRef = firebase.child(TODO_TRX_PATH),
    closedTrxRef = firebase.child(DONE_TRX_PATH),
    trCountLimit = 50,
    rescheduleDelay = 100
  } = options

  if (!log4js.configured) configureLogging()

  const runs = {} // run id mapped to trData
  const waiting = []
  const trSummary = {aborted: 0, tried: 0, processed: 0}
  const registry = new Registry()
  let nextTrId = 0
  let nextRunId = 0
  let trCount = 0

  function _abort(id) {
    registry.cleanup(id)
    delete runs[id]
  }

  function refFromPath(path) {
    return firebase.child(path.join('/'))
  }

  function pushWaiting(val) {
    if (val == null) {
      throw new Error('val should be trx, not null/undefined')
    }
    waiting.push(val)
    tryConsumeWaiting()
  }

  function scheduleLater(trData) {
    setTimeout(() => {pushWaiting(trData)}, rescheduleDelay)
  }

  // --abortAndReschedule
  function abortAndReschedule(id) {
    if (runs[id] == null) {
      throw new Error('shouldnt abort transaction that is already aborted')
    }
    logger.debug(`CLEANUP & ABORT : tr no ${id}`)
    trSummary['aborted'] += 1
    scheduleLater(runs[id])
    _abort(id)
  }

  // --processTr
  function processTr(id) {
    logger.debug(`starting process ${id}`)
    const {type, data} = runs[id]
    const handler = handlers[type]
    if (handler == null) {
      throw new Error(`handler for type ${type} does not exist. Full trData: ${runs[id]}`)
    }

    if (runs[id] == null) {
      throw new Error('given run does not exist!')
    }

    return Promise.resolve()
      .then(() => {
        logger.debug(`starting handler ${id}`)
        trSummary['tried'] += 1
        return handler({
          abort: userAbort,
          change: userChange,
          getId: userGetId,
          push: userPush,
          read: userRead,
          set: userSet,
          update: userUpdate,
        }, data)
      })
      .catch((err) => {
        if (err instanceof UserAbort) {
          logger.debug(`user abort ${id}, msg: ${err.msg}`)
          return {error: err.msg}
        }
        throw err
      })
      .then((result) => {
        // even if no Error is thrown, the transaction might be aborted in the
        // very last moment. Better check for it (userabort is not relevant now, as
        // we want to process such transaction)
        if (runs[id] != null) {
          logger.debug(`FINISH: tr no ${id}`)
          let userAborted = runs[id].status === 'useraborted'
          let writes = userAborted ? [] : registry.writesByTrx.get(id, [])
          let writesRef = firebase.child('__internal/writes').child(id)
          trSummary['processed'] += 1
          runs[id].status = 'finishing'
          set(writesRef, writes)
          writes.forEach((write) => {
            // TODO immutable destructuring
            set(refFromPath(write.get('path')), write.get('value'))
          })
          let trData = runs[id]
          remove(writesRef)
          try {
            // TODO handle this better (some parts of result might get lost
            // here, so warn user about it)
            result = JSON.parse(JSON.stringify({result}))
          } catch (e) {
            let msg = `Error for transaction id=${id}, result cannot be saved\n to firebase (result=${result})`
            throw new Error(msg)
          }
          set(closedTrxRef.child(trData.frbId), {data: trData, ...result})
          remove(todoTrxRef.child(trData.frbId))
          registry.cleanup(id)
          delete runs[id]
        }
      })
      .catch((err) => {
        if (err instanceof AbortError) {
          logger.debug(`abort error ${id}`)
          return
        }
        throw err
      })

    function checkAbort() {
      if (runs[id] == null || runs[id].status === 'useraborted') {
        logger.debug('checkAbort: throwing abort')
        throw new AbortError('Transaction was aborted')
      }
      if (runs[id] === 'useraborted') {
        logger.debug('checkAbort: throwing useraborted')
        throw new AbortError('Transaction was aborted')
      }

    }

    function handlePossibleConflict(_conflict) {
      if (_conflict.constructor !== Set) {
        throw new Error('conflict must be of a type immutable.Set')
      }
      checkAbort()
      let conflict = _conflict.delete(id)
      let conflictTrIds = conflict.map((i) => runs[i].trId)
      let trId = runs[id].trId
      let finishingTrIds = conflict.filter((i) => runs[i].status === 'finishing').map((i) => runs[i].trId)
      if (!conflict.isEmpty()) {
        if (u.any(conflict, (i) => runs[i].status === 'finishing') ||
            conflictTrIds.min() < trId) {
          logger.debug(`aborting ${trId}, because of ${conflictTrIds}, finishing: ${finishingTrIds}`)
          abortAndReschedule(id)
        } else {
          logger.debug(`aborting ${conflictTrIds}, because of ${trId}, finishing: ${finishingTrIds}`)
          conflict.forEach(abortAndReschedule)
        }
      }
      checkAbort()
    }

    function normalizePath(path) {
      if (u.isImmutable(path)) path = path.toJS()
      if (u.isArray(path)) return path
      throw new Error(`path must be an array or immutable list; got ${path} instead`)
    }

    // TODO if possible, make DB operations accept also firebase ref
    function userRead(path) {
      path = normalizePath(path)
      if (u.any(path, (e) => (e == null))) throw new Error(`READ ERROR: undefined / null present in path: ${path}`)
      handlePossibleConflict(registry.conflictingWithRead(path))
      registry.addRead(id, path)
      return Promise.resolve()
        .then(() => read(refFromPath(path)))
        .then((val) => {
          checkAbort()
          return registry.readAsIfTrx(id, path, val)
        })
    }

    function userSet(path, value) {
      path = normalizePath(path)
      handlePossibleConflict(registry.conflictingWithWrite(path))
      registry.addWrite(id, path, value)
    }

    function userAbort(msg) {
      checkAbort()
      runs[id].abortMsg = msg
      runs[id].status = 'useraborted'
      throw new UserAbort(msg)
    }

    function userPush(path, value) {
      path = normalizePath(path)
      let ref = refFromPath(path).push()
      userSet([...path, ref.key()], value)
      return ref
    }

    function userGetId() {
      return firebase.push().key()
    }

    function userUpdate(path, values) {
      path = normalizePath(path)
      if ((values == null) || (values.constructor !== Object)) {
        throw new Error(`The value argument in update must be a JS Object, found ${values}`)
      }
      Object.keys(values).forEach((key) => userSet([...path, key], values[key]))
    }

    function userChange(path, fn) {
      path = normalizePath(path)
      if (typeof fn !== 'function') {
        throw new Error(`fn argument must be a function, got ${fn} instead`)
      }
      return userRead(path)
        .then((val) => fn(val))
        .then((res) => userSet(path, res))
    }
  }

  function tryConsumeWaiting() {
    while (waiting.length > 0 && trCount < trCountLimit) {
      trCount += 1
      let trData = waiting.shift()
      let id = nextRunId++
      runs[id] = {...trData, status: 'inprocess', id}
      processTr(id)
        .then(() => {
          trCount -= 1
          tryConsumeWaiting()
        })
    }
  }

  let onChildAdded = todoTrxRef.on('child_added', (childSnapshot, prevChildKey) => {
    let trData = childSnapshot.val()
    if (trData.type == null) {
      logger.error('malformed data: ', trData)
      throw new Error('malformed trData')
    }
    trData.trId = nextTrId++
    logger.debug(`SCHEDULED: tr no ${trData.trId} data: ${JSON.stringify(trData)}`)
    trData.frbId = childSnapshot.key()
    pushWaiting(trData)
  })

  return {
    'stop': () => todoTrxRef.off('child_added', onChildAdded),
    'registry': registry,
    'trSummary': trSummary,
  }

}

// Synchronous variant of finishing transaction code
//let toWait = []
//toWait.push(set(writesRef, writes))
//writes.forEach((write) => {
//  // TODO immutable destructuring
//  toWait.push(set(refFromPath(write.get('path')), write.get('value')))
//})
//let trData = runs[id]
//  Promise.all(toWait).then((_) => {
//  remove(writesRef)
//  set(firebase.child(DONE_TRX_PATH).child(trData.frbId), trData)
//  remove(firebase.child(TODO_TRX_PATH).child(trData.frbId))
//  registry.cleanup(id)
//  delete runs[id]
//})
<commit_msg>When user's transaction throws custom error, complete transaction with error<commit_after>import {Promise} from 'bluebird'
import {Registry} from './registry'
import {Set} from 'immutable'
import * as u from './useful'
import {read, set, remove} from './firebase_useful'
import log4js from 'log4js'
import {TODO_TRX_PATH, DONE_TRX_PATH} from './settings'

let logger = log4js.getLogger('transactor')

function configureLogging() {
  if (!log4js.configured) {
    log4js.configure({
      appenders: [{
        type: 'console',
        layout: {
          type: 'pattern',
          pattern: '[%[%5.5p%]] - %m'
        }
      }]
    })
    logger.setLevel('INFO')
  }
}

class AbortError {
  constructor(msg) {
    this.msg = msg
  }
}

class UserAbort {
  constructor(msg) {
    this.msg = msg
  }
}

export function transactor(firebase, handlers, options = {}) {

  const {
    todoTrxRef = firebase.child(TODO_TRX_PATH),
    closedTrxRef = firebase.child(DONE_TRX_PATH),
    trCountLimit = 50,
    rescheduleDelay = 100
  } = options

  if (!log4js.configured) configureLogging()

  const runs = {} // run id mapped to trData
  const waiting = []
  const trSummary = {aborted: 0, tried: 0, processed: 0}
  const registry = new Registry()
  let nextTrId = 0
  let nextRunId = 0
  let trCount = 0

  function _abort(id) {
    registry.cleanup(id)
    delete runs[id]
  }

  function refFromPath(path) {
    return firebase.child(path.join('/'))
  }

  function pushWaiting(val) {
    if (val == null) {
      throw new Error('val should be trx, not null/undefined')
    }
    waiting.push(val)
    tryConsumeWaiting()
  }

  function scheduleLater(trData) {
    setTimeout(() => {pushWaiting(trData)}, rescheduleDelay)
  }

  // --abortAndReschedule
  function abortAndReschedule(id) {
    if (runs[id] == null) {
      throw new Error('shouldnt abort transaction that is already aborted')
    }
    logger.debug(`CLEANUP & ABORT : tr no ${id}`)
    trSummary['aborted'] += 1
    scheduleLater(runs[id])
    _abort(id)
  }

  // --processTr
  function processTr(id) {
    logger.debug(`starting process ${id}`)
    const {type, data} = runs[id]
    const handler = handlers[type]
    if (handler == null) {
      throw new Error(`handler for type ${type} does not exist. Full trData: ${runs[id]}`)
    }

    if (runs[id] == null) {
      throw new Error('given run does not exist!')
    }

    return Promise.resolve()
      .then(() => {
        logger.debug(`starting handler ${id}`)
        trSummary['tried'] += 1
        return handler({
          abort: userAbort,
          change: userChange,
          getId: userGetId,
          push: userPush,
          read: userRead,
          set: userSet,
          update: userUpdate,
        }, data)
      })
      .catch((err) => {
        if (!(err instanceof AbortError)) {
          if (err instanceof UserAbort) {
            logger.debug(`user abort ${id}, msg: ${err.msg}`)
            return {error: err.msg}
          } else {
            if (!process.env.supressErrors) {
              logger.error(`Unknown error abort ${err} ${err.stack}`)
              console.error(err)
              console.error(err.stack)
            }
            return {exception: `${err}`}
          }
        }
        throw err
      })
      .then((result) => {
        // even if no Error is thrown, the transaction might be aborted in the
        // very last moment. Better check for it (userabort is not relevant now, as
        // we want to process such transaction)
        if (runs[id] != null) {
          logger.debug(`FINISH: tr no ${id}`)
          let userAborted = runs[id].status === 'useraborted'
          let writes = userAborted ? [] : registry.writesByTrx.get(id, [])
          let writesRef = firebase.child('__internal/writes').child(id)
          trSummary['processed'] += 1
          runs[id].status = 'finishing'
          set(writesRef, writes)
          writes.forEach((write) => {
            // TODO immutable destructuring
            set(refFromPath(write.get('path')), write.get('value'))
          })
          let trData = runs[id]
          remove(writesRef)
          try {
            // TODO handle this better (some parts of result might get lost
            // here, so warn user about it)
            result = JSON.parse(JSON.stringify({result}))
          } catch (e) {
            let msg = `Error for transaction id=${id}, result cannot be saved\n to firebase (result=${result})`
            throw new Error(msg)
          }
          set(closedTrxRef.child(trData.frbId), {data: trData, ...result})
          remove(todoTrxRef.child(trData.frbId))
          registry.cleanup(id)
          delete runs[id]
        }
      })
      .catch((err) => {
        if (err instanceof AbortError) {
          logger.debug(`abort error ${id}`)
          return
        }
        throw err
      })

    function checkAbort() {
      if (runs[id] == null || runs[id].status === 'useraborted') {
        logger.debug('checkAbort: throwing abort')
        throw new AbortError('Transaction was aborted')
      }
      if (runs[id] === 'useraborted') {
        logger.debug('checkAbort: throwing useraborted')
        throw new AbortError('Transaction was aborted')
      }

    }

    function handlePossibleConflict(_conflict) {
      if (_conflict.constructor !== Set) {
        throw new Error('conflict must be of a type immutable.Set')
      }
      checkAbort()
      let conflict = _conflict.delete(id)
      let conflictTrIds = conflict.map((i) => runs[i].trId)
      let trId = runs[id].trId
      let finishingTrIds = conflict.filter((i) => runs[i].status === 'finishing').map((i) => runs[i].trId)
      if (!conflict.isEmpty()) {
        if (u.any(conflict, (i) => runs[i].status === 'finishing') ||
            conflictTrIds.min() < trId) {
          logger.debug(`aborting ${trId}, because of ${conflictTrIds}, finishing: ${finishingTrIds}`)
          abortAndReschedule(id)
        } else {
          logger.debug(`aborting ${conflictTrIds}, because of ${trId}, finishing: ${finishingTrIds}`)
          conflict.forEach(abortAndReschedule)
        }
      }
      checkAbort()
    }

    function normalizePath(path) {
      if (u.isImmutable(path)) path = path.toJS()
      if (u.isArray(path)) return path
      throw new Error(`path must be an array or immutable list; got ${path} instead`)
    }

    // TODO if possible, make DB operations accept also firebase ref
    function userRead(path) {
      path = normalizePath(path)
      if (u.any(path, (e) => (e == null))) throw new Error(`READ ERROR: undefined / null present in path: ${path}`)
      handlePossibleConflict(registry.conflictingWithRead(path))
      registry.addRead(id, path)
      return Promise.resolve()
        .then(() => read(refFromPath(path)))
        .then((val) => {
          checkAbort()
          return registry.readAsIfTrx(id, path, val)
        })
    }

    function userSet(path, value) {
      path = normalizePath(path)
      handlePossibleConflict(registry.conflictingWithWrite(path))
      registry.addWrite(id, path, value)
    }

    function userAbort(msg) {
      checkAbort()
      runs[id].abortMsg = msg
      runs[id].status = 'useraborted'
      throw new UserAbort(msg)
    }

    function userPush(path, value) {
      path = normalizePath(path)
      let ref = refFromPath(path).push()
      userSet([...path, ref.key()], value)
      return ref
    }

    function userGetId() {
      return firebase.push().key()
    }

    function userUpdate(path, values) {
      path = normalizePath(path)
      if ((values == null) || (values.constructor !== Object)) {
        throw new Error(`The value argument in update must be a JS Object, found ${values}`)
      }
      Object.keys(values).forEach((key) => userSet([...path, key], values[key]))
    }

    function userChange(path, fn) {
      path = normalizePath(path)
      if (typeof fn !== 'function') {
        throw new Error(`fn argument must be a function, got ${fn} instead`)
      }
      return userRead(path)
        .then((val) => fn(val))
        .then((res) => userSet(path, res))
    }
  }

  function tryConsumeWaiting() {
    while (waiting.length > 0 && trCount < trCountLimit) {
      trCount += 1
      let trData = waiting.shift()
      let id = nextRunId++
      runs[id] = {...trData, status: 'inprocess', id}
      processTr(id)
        .then(() => {
          trCount -= 1
          tryConsumeWaiting()
        })
    }
  }

  let onChildAdded = todoTrxRef.on('child_added', (childSnapshot, prevChildKey) => {
    let trData = childSnapshot.val()
    if (trData.type == null) {
      logger.error('malformed data: ', trData)
      throw new Error('malformed trData')
    }
    trData.trId = nextTrId++
    logger.debug(`SCHEDULED: tr no ${trData.trId} data: ${JSON.stringify(trData)}`)
    trData.frbId = childSnapshot.key()
    pushWaiting(trData)
  })

  return {
    'stop': () => todoTrxRef.off('child_added', onChildAdded),
    'registry': registry,
    'trSummary': trSummary,
  }

}

// Synchronous variant of finishing transaction code
//let toWait = []
//toWait.push(set(writesRef, writes))
//writes.forEach((write) => {
//  // TODO immutable destructuring
//  toWait.push(set(refFromPath(write.get('path')), write.get('value')))
//})
//let trData = runs[id]
//  Promise.all(toWait).then((_) => {
//  remove(writesRef)
//  set(firebase.child(DONE_TRX_PATH).child(trData.frbId), trData)
//  remove(firebase.child(TODO_TRX_PATH).child(trData.frbId))
//  registry.cleanup(id)
//  delete runs[id]
//})
<|endoftext|>


<commit_before>var miio = require('./lib');

// var vacuum = python.import('mirobot/protocol');

var MiHome = function(name, ip, port, token, model) {

  var that = this;

  this.ip = ip;
  this.port = port
  this.token = token;
  this.name = name
  this.model = model
  this.device = null;

// initialize the vacuum independently, and general device will create ether with token or without token
  if (model == 'vacuum') {
    this.device = miio.createDevice({
      address: this.ip,
      token: this.token,
      model: 'rockrobo.vacuum.v1'
    });

    this.device.init()
    .then(() => {
        console.log(this.model + " init success");
        return this;
    })
    .catch(err => console.error(this.model + " init error: " + err));
  }
  else {

    if (token == null) {
      var para = { address: ip };
    }
    else {
      var para = { address: ip, token: token };
    };

    miio.device(para)
      .then(device => {
          this.device = device;
          console.log(this.name + " " + this.device.type + " init success");
          return this;
        })
      .catch(err => console.error(this.name + " init error: " + err));
  }

};

MiHome.prototype.exec = function(cmd) {

  var that = this;

  if (cmd == 'Stop') {
    cmd = 'Charge';
  }

  switch (cmd) {
    case 'Start':

      if (this.device.type == 'vacuum') {
        this.device.start()
          .then(res => console.log(this.name + " start result : " + res))
          .catch(err => console.error(this.name + " start error: " + err));
      }

      break;

    case 'Charge':

      if (this.device.type == 'vacuum') {
        this.device.charge()
          .then(res => console.log(this.name + " charge result : " + res))
          .catch(err => console.error(this.name + " charge error: " + err));
      }

      break;

    case 'Pause':

      if (this.device.type == 'vacuum') {
        this.device.pause()
          .then(res => console.log(this.name + " pause result : " + res))
          .catch(err => console.error(this.name + " pause error: " + err));
      }

      break;

    case 'List':
      if (this.device.type == 'gateway') {
        console.log(this.device.devices);
      }

      break;

    default:
      console.log('Invalid type:' + cmd);
  }

  return;

};

MiHome.prototype.powerOn = function() {

  var that = this;

  if(this.device.hasCapability('power')) {
    this.device.setPower(true)
      .then(console.log(this.name + ":Power On"))
      .catch(console.error(this.name + "Failed to Power On"));
  }

};

module.exports = function(name, ip, port, token, model) {
  return new MiHome(name, ip, port, token, model);
};

<commit_msg>update mihome index<commit_after>var miio = require('./lib');

// var vacuum = python.import('mirobot/protocol');

var MiHome = function(name, ip, port, token, model) {

  var that = this;

  this.ip = ip;
  this.port = port
  this.token = token;
  this.name = name
  this.model = model
  this.device = null;

// initialize the vacuum independently, and general device will create ether with token or without token
  if (model == 'vacuum') {
    this.device = miio.createDevice({
      address: this.ip,
      token: this.token,
      model: 'rockrobo.vacuum.v1'
    });

    this.device.init()
    .then(() => {
        console.log(this.model + " init success");
        return this;
    })
    .catch(err => console.error(this.model + " init error: " + err));
  }
  else {

    if (token == null) {
      var para = { address: ip };
    }
    else {
      var para = { address: ip, token: token };
    };

    miio.device(para)
      .then(device => {
          this.device = device;
          debugger;
          console.log(this.name + " " + this.device.type + " init success");
          return this;
        })
      .catch(err => console.error(this.name + " init error: " + err));
  }

};

MiHome.prototype.exec = function(cmd) {

  var that = this;

  if (cmd == 'Stop') {
    cmd = 'Charge';
  }

  switch (cmd) {
    case 'Start':

      if (this.device.type == 'vacuum') {
        this.device.start()
          .then(res => console.log(this.name + " start result : " + res))
          .catch(err => console.error(this.name + " start error: " + err));
      }

      break;

    case 'Charge':

      if (this.device.type == 'vacuum') {
        this.device.charge()
          .then(res => console.log(this.name + " charge result : " + res))
          .catch(err => console.error(this.name + " charge error: " + err));
      }

      break;

    case 'Pause':

      if (this.device.type == 'vacuum') {
        this.device.pause()
          .then(res => console.log(this.name + " pause result : " + res))
          .catch(err => console.error(this.name + " pause error: " + err));
      }

      break;

    case 'List':
      if (this.device.type == 'gateway') {
        console.log(this.device.devices);
      }

      break;

    default:
      console.log('Invalid type:' + cmd);
  }

  return;

};

MiHome.prototype.powerOn = function() {

  var that = this;

  if(this.device.hasCapability('power')) {
    this.device.setPower(true)
      .then(console.log(this.name + ":Power On"))
      .catch(console.error(this.name + "Failed to Power On"));
  }

};

module.exports = function(name, ip, port, token, model) {
  return new MiHome(name, ip, port, token, model);
};

<|endoftext|>


<commit_before>// Copyright 2013-2017, University of Colorado Boulder

/**
 * Indicates a user's progress on a level by illuminating stars.
 *
 * @author John Blanco
 * @author Sam Reid
 */
define( function( require ) {
  'use strict';

  // modules
  var HBox = require( 'SCENERY/nodes/HBox' );
  var inherit = require( 'PHET_CORE/inherit' );
  var StarNode = require( 'SCENERY_PHET/StarNode' );
  var vegas = require( 'VEGAS/vegas' );

  /**
   * @param {number} numStars
   * @param {Property.<number>} scoreProperty
   * @param {number} perfectScore
   * @param {Object} [options]
   * @constructor
   */
  function ProgressIndicator( numStars, scoreProperty, perfectScore, options ) {

    options = _.extend( {
      starOuterRadius: 10,
      starInnerRadius: 5,
      starFilledLineWidth: 1.5,
      starEmptyLineWidth: 1.5
    }, options );

    HBox.call( this, { spacing: 3, children: [] } );
    var self = this;

    // Update visibility of filled and half-filled stars based on score.
    // TODO: Could be rewritten to use deltas if it needs to animate
    scoreProperty.link( function( score ) {

      assert && assert( score <= perfectScore );

      var children = [];

      var proportion = score / perfectScore;
      var numFilledStars = Math.floor( proportion * numStars );

      var starOptions = {
        outerRadius: options.starOuterRadius,
        innerRadius: options.starInnerRadius,
        filledLineWidth: options.starFilledLineWidth,
        emptyLineWidth: options.starEmptyLineWidth
      };

      for ( var i = 0; i < numFilledStars; i++ ) {
        children.push( new StarNode( _.extend( { value: 1 }, starOptions ) ) );
      }
      var remainder = proportion * numStars - numFilledStars;
      if ( remainder > 1E-6 ) {
        children.push( new StarNode( _.extend( { value: remainder }, starOptions ) ) );
      }
      var numEmptyStars = numStars - children.length;
      for ( i = 0; i < numEmptyStars; i++ ) {
        children.push( new StarNode( _.extend( { value: 0 }, starOptions ) ) );
      }

      self.children = children;
    } );

    this.mutate( options );
  }

  vegas.register( 'ProgressIndicator', ProgressIndicator );

  return inherit( HBox, ProgressIndicator );
} );<commit_msg>mark ProgressIndicator as deprecated<commit_after>// Copyright 2013-2017, University of Colorado Boulder

/**
 * Indicates a user's progress on a level by illuminating stars.
 *
 * @author John Blanco
 * @author Sam Reid
 * @deprecated use ScoreDisplayDiscreteStars
 */
define( function( require ) {
  'use strict';

  // modules
  var HBox = require( 'SCENERY/nodes/HBox' );
  var inherit = require( 'PHET_CORE/inherit' );
  var StarNode = require( 'SCENERY_PHET/StarNode' );
  var vegas = require( 'VEGAS/vegas' );

  /**
   * @param {number} numStars
   * @param {Property.<number>} scoreProperty
   * @param {number} perfectScore
   * @param {Object} [options]
   * @constructor
   */
  function ProgressIndicator( numStars, scoreProperty, perfectScore, options ) {

    options = _.extend( {
      starOuterRadius: 10,
      starInnerRadius: 5,
      starFilledLineWidth: 1.5,
      starEmptyLineWidth: 1.5
    }, options );

    HBox.call( this, { spacing: 3, children: [] } );
    var self = this;

    // Update visibility of filled and half-filled stars based on score.
    // TODO: Could be rewritten to use deltas if it needs to animate
    scoreProperty.link( function( score ) {

      assert && assert( score <= perfectScore );

      var children = [];

      var proportion = score / perfectScore;
      var numFilledStars = Math.floor( proportion * numStars );

      var starOptions = {
        outerRadius: options.starOuterRadius,
        innerRadius: options.starInnerRadius,
        filledLineWidth: options.starFilledLineWidth,
        emptyLineWidth: options.starEmptyLineWidth
      };

      for ( var i = 0; i < numFilledStars; i++ ) {
        children.push( new StarNode( _.extend( { value: 1 }, starOptions ) ) );
      }
      var remainder = proportion * numStars - numFilledStars;
      if ( remainder > 1E-6 ) {
        children.push( new StarNode( _.extend( { value: remainder }, starOptions ) ) );
      }
      var numEmptyStars = numStars - children.length;
      for ( i = 0; i < numEmptyStars; i++ ) {
        children.push( new StarNode( _.extend( { value: 0 }, starOptions ) ) );
      }

      self.children = children;
    } );

    this.mutate( options );
  }

  vegas.register( 'ProgressIndicator', ProgressIndicator );

  return inherit( HBox, ProgressIndicator );
} );<|endoftext|>


<commit_before>function base64ToHex(base64) {
    var base64Digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    var hexDigits = "0123456789abcdef";
    var hex = "";
    for (var i = 0; i < 24; ) {
        var e1 = base64Digits.indexOf(base64[i++]);
        var e2 = base64Digits.indexOf(base64[i++]);
        var e3 = base64Digits.indexOf(base64[i++]);
        var e4 = base64Digits.indexOf(base64[i++]);
        var c1 = (e1 << 2) | (e2 >> 4);
        var c2 = ((e2 & 15) << 4) | (e3 >> 2);
        var c3 = ((e3 & 3) << 6) | e4;
        hex += hexDigits[c1 >> 4];
        hex += hexDigits[c1 & 15];
        if (e3 != 64) {
            hex += hexDigits[c2 >> 4];
            hex += hexDigits[c2 & 15];
        }
        if (e4 != 64) {
            hex += hexDigits[c3 >> 4];
            hex += hexDigits[c3 & 15];
        }
    }
    return hex;
}

function hexToBase64(hex) {
    var base64Digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    var base64 = "";
    var group;
    for (var i = 0; i < 30; i += 6) {
        group = parseInt(hex.substr(i, 6), 16);
        base64 += base64Digits[(group >> 18) & 0x3f];
        base64 += base64Digits[(group >> 12) & 0x3f];
        base64 += base64Digits[(group >> 6) & 0x3f];
        base64 += base64Digits[group & 0x3f];
    }
    group = parseInt(hex.substr(30, 2), 16);
    base64 += base64Digits[(group >> 2) & 0x3f];
    base64 += base64Digits[(group << 4) & 0x3f];
    base64 += "==";
    return base64;
}

var platformSpecificUuidModifications = {
    "java": function (hex) {
        var msb = hex.substr(0, 16);
        var lsb = hex.substr(16, 16);
        msb = msb.substr(14, 2) + msb.substr(12, 2) + msb.substr(10, 2) + msb.substr(8, 2)
            + msb.substr(6, 2) + msb.substr(4, 2) + msb.substr(2, 2) + msb.substr(0, 2);
        lsb = lsb.substr(14, 2) + lsb.substr(12, 2) + lsb.substr(10, 2) + lsb.substr(8, 2)
            + lsb.substr(6, 2) + lsb.substr(4, 2) + lsb.substr(2, 2) + lsb.substr(0, 2);
        return msb + lsb;
    },
    "c#": function (hex) {
        return hex.substr(6, 2) + hex.substr(4, 2) + hex.substr(2, 2) + hex.substr(0, 2)
            + hex.substr(10, 2) + hex.substr(8, 2) + hex.substr(14, 2) + hex.substr(12, 2)
            + hex.substr(16, 16);
    },
	"python": function (hex) {
		return hex;
	},
    "default": function (hex) {
        return hex;
    }
};

function UUID(uuid, type) {
    var hex = uuid.replace(/[{}-]/g, "");
    var typeNum = 4;
    if (type != undefined) {
        typeNum = 3;
        hex = platformSpecificUuidModifications[type](hex);
    }
    return new BinData(typeNum, hexToBase64(hex));
}

function uuidToString(uuid, uuidType) {
    var uuidType = uuidType || mongo_hacker_config['uuid_type'];
    var hex = platformSpecificUuidModifications[uuidType](base64ToHex(uuid.base64()));
    return hex.substr(0, 8) + '-' + hex.substr(8, 4) + '-' + hex.substr(12, 4)
        + '-' + hex.substr(16, 4) + '-' + hex.substr(20, 12);
}
<commit_msg>fix indentation<commit_after>function base64ToHex(base64) {
    var base64Digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    var hexDigits = "0123456789abcdef";
    var hex = "";
    for (var i = 0; i < 24; ) {
        var e1 = base64Digits.indexOf(base64[i++]);
        var e2 = base64Digits.indexOf(base64[i++]);
        var e3 = base64Digits.indexOf(base64[i++]);
        var e4 = base64Digits.indexOf(base64[i++]);
        var c1 = (e1 << 2) | (e2 >> 4);
        var c2 = ((e2 & 15) << 4) | (e3 >> 2);
        var c3 = ((e3 & 3) << 6) | e4;
        hex += hexDigits[c1 >> 4];
        hex += hexDigits[c1 & 15];
        if (e3 != 64) {
            hex += hexDigits[c2 >> 4];
            hex += hexDigits[c2 & 15];
        }
        if (e4 != 64) {
            hex += hexDigits[c3 >> 4];
            hex += hexDigits[c3 & 15];
        }
    }
    return hex;
}

function hexToBase64(hex) {
    var base64Digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    var base64 = "";
    var group;
    for (var i = 0; i < 30; i += 6) {
        group = parseInt(hex.substr(i, 6), 16);
        base64 += base64Digits[(group >> 18) & 0x3f];
        base64 += base64Digits[(group >> 12) & 0x3f];
        base64 += base64Digits[(group >> 6) & 0x3f];
        base64 += base64Digits[group & 0x3f];
    }
    group = parseInt(hex.substr(30, 2), 16);
    base64 += base64Digits[(group >> 2) & 0x3f];
    base64 += base64Digits[(group << 4) & 0x3f];
    base64 += "==";
    return base64;
}

var platformSpecificUuidModifications = {
    "java": function (hex) {
        var msb = hex.substr(0, 16);
        var lsb = hex.substr(16, 16);
        msb = msb.substr(14, 2) + msb.substr(12, 2) + msb.substr(10, 2) + msb.substr(8, 2)
            + msb.substr(6, 2) + msb.substr(4, 2) + msb.substr(2, 2) + msb.substr(0, 2);
        lsb = lsb.substr(14, 2) + lsb.substr(12, 2) + lsb.substr(10, 2) + lsb.substr(8, 2)
            + lsb.substr(6, 2) + lsb.substr(4, 2) + lsb.substr(2, 2) + lsb.substr(0, 2);
        return msb + lsb;
    },
    "c#": function (hex) {
        return hex.substr(6, 2) + hex.substr(4, 2) + hex.substr(2, 2) + hex.substr(0, 2)
            + hex.substr(10, 2) + hex.substr(8, 2) + hex.substr(14, 2) + hex.substr(12, 2)
            + hex.substr(16, 16);
    },
    "python": function (hex) {
        return hex;
    },
    "default": function (hex) {
        return hex;
    }
};

function UUID(uuid, type) {
    var hex = uuid.replace(/[{}-]/g, "");
    var typeNum = 4;
    if (type != undefined) {
        typeNum = 3;
        hex = platformSpecificUuidModifications[type](hex);
    }
    return new BinData(typeNum, hexToBase64(hex));
}

function uuidToString(uuid, uuidType) {
    var uuidType = uuidType || mongo_hacker_config['uuid_type'];
    var hex = platformSpecificUuidModifications[uuidType](base64ToHex(uuid.base64()));
    return hex.substr(0, 8) + '-' + hex.substr(8, 4) + '-' + hex.substr(12, 4)
        + '-' + hex.substr(16, 4) + '-' + hex.substr(20, 12);
}
<|endoftext|>


<commit_before>window.onload = function () {

    var textArea = $('#textContent');

    //
    //  new file is basically clearing the textarea
    //
    document.querySelector('#btnNewFile').onclick = function(){
        $(textArea).val('');
        $(textArea).focus();
    };

    //
    //  opening text files
    //
    document.querySelector('#btnOpenFile').onclick = function () {
        chrome.fileSystem.chooseEntry({
            type: "openFile"
        }, function (fileEntry) {
            chrome.fileSystem.getDisplayPath(fileEntry, function (path) {
                fileEntry.file(function (file) {
                    var reader = new FileReader();

                    reader.onerror = function (e) {
                        console.error(e.message);
                    };

                    reader.onloadend = function (e) {
                        $(textArea).val(e.target.result);
                    };

                    reader.readAsText(file);

                });

            });
        });
    };

    //
    //  saving a file
    //
    document.querySelector('#btnSaveFileAs').onclick = function () {
        chrome.fileSystem.chooseEntry({
            type: "saveFile",
            suggestedName: "sample.txt",
            acceptsAllTypes: true,
            accepts: [{description: "Text files (*.txt)", extensions: ["txt"]}]
        }, function (fileEntry) {
            var textToSave = $(textArea).val();
            console.log(textToSave);
            chrome.fileSystem.getDisplayPath(fileEntry, function (path) {
                fileEntry.createWriter(function(fileWriter){

                    var wasTruncated = false;
                    var data = new Blob([textToSave]);

                    fileWriter.onwriteend = function(e) {
                        if (!wasTruncated) {
                            wasTruncated = true;
                            // You need to explicitly set the file size to truncate
                            // any content that might have been there before
                            this.truncate(blob.size);
                            return;
                        }

                    };

                    fileWriter.onerror = function(e) {
                        console.error(e.message);
                    };

                    fileWriter.write(data);

                });

            });
        });
    };

};
<commit_msg>updated application logic, formatted code<commit_after>window.onload = function () {

    var textArea = $('#textContent');

    //
    //  new file is basically clearing the textarea
    //
    $('#btnNewFile').click(function(){
        $(textArea).val('');
        $(textArea).focus();
    });

    //
    //  opening text files
    //
    $('#btnOpenFile').click(function () {
        chrome.fileSystem.chooseEntry({
            type: "openFile"
        }, function (fileEntry) {
            chrome.fileSystem.getDisplayPath(fileEntry, function (path) {
                fileEntry.file(function (file) {
                    var reader = new FileReader();

                    reader.onerror = function (e) {
                        console.error(e.message);
                    };

                    reader.onloadend = function (e) {
                        $(textArea).val(e.target.result);
                    };

                    reader.readAsText(file);

                });

            });
        });
    });

    //
    //  saving a file
    //
    $('#btnSaveFileAs').click(function () {
        chrome.fileSystem.chooseEntry({
            type: "saveFile",
            suggestedName: "sample.txt",
            acceptsAllTypes: true,
            accepts: [{description: "Text files (*.txt)", extensions: ["txt"]}]
        }, function (fileEntry) {
            var textToSave = $(textArea).val();
            console.log(textToSave);
            chrome.fileSystem.getDisplayPath(fileEntry, function (path) {
                fileEntry.createWriter(function(fileWriter){

                    var wasTruncated = false;
                    var data = new Blob([textToSave]);

                    fileWriter.onwriteend = function(e) {
                        if (!wasTruncated) {
                            wasTruncated = true;
                            // You need to explicitly set the file size to truncate
                            // any content that might have been there before
                            this.truncate(blob.size);
                            return;
                        }

                    };

                    fileWriter.onerror = function(e) {
                        console.error(e.message);
                    };

                    fileWriter.write(data);

                });

            });
        });
    });

};
<|endoftext|>


<commit_before>/**
 * RequireJS config for LivingCukes
 */
require.config({
    urlArgs: 'appVersion=' + (location.host.match(/^(local|192.168|bnowack.github.io|$)/) ? Math.random() : '0.1.2'),
    paths: {
        jquery: 'bower_components/jquery/dist/jquery.min',
        text: 'bower_components/requirejs-plugins/lib/text',
        css: 'bower_components/require-css/css.min',
        mdconv: 'bower_components/pagedown/Markdown.Converter',
        prettify: 'bower_components/prettify/src/prettify',
        lcsrc: 'src'
    },
    shim: {
        mdconv: {
          exports: "Markdown.Converter"
        },
        prettify: {
            deps: ['css!bower_components/prettify/src/prettify'],
            exports: "prettyPrint"
        }
    }
});

<commit_msg>Release version 0.1.3 (#release)<commit_after>/**
 * RequireJS config for LivingCukes
 */
require.config({
    urlArgs: 'appVersion=' + (location.host.match(/^(local|192.168|bnowack.github.io|$)/) ? Math.random() : '0.1.3'),
    paths: {
        jquery: 'bower_components/jquery/dist/jquery.min',
        text: 'bower_components/requirejs-plugins/lib/text',
        css: 'bower_components/require-css/css.min',
        mdconv: 'bower_components/pagedown/Markdown.Converter',
        prettify: 'bower_components/prettify/src/prettify',
        lcsrc: 'src'
    },
    shim: {
        mdconv: {
          exports: "Markdown.Converter"
        },
        prettify: {
            deps: ['css!bower_components/prettify/src/prettify'],
            exports: "prettyPrint"
        }
    }
});

<|endoftext|>


<commit_before>#!/usr/bin/env node

var fs = require("fs");
var path = require("path");

var libdir = path.dirname(process.argv[1]);
var libmod = libdir + (fs.existsSync(libdir + "/node_modules") ? "/node_modules" : "/..");
var command = process.argv[2];
var context = process.argv[3] && path.resolve(process.argv[3]);

// Print version
var version = JSON.parse(fs.readFileSync(libdir + "/package.json")).version;
console.log("Version: react-beaker " + version + "\n");

// Validate arguments
if (!command || !context || ["watch", "build", "publish"].indexOf(command) < 0) {
    help();
    process.exit(1);
}

// Find all JavaScript entries
var entry = {};
var entriesDir = context + "/js/entries/";
fs.readdirSync(entriesDir).forEach(function(filename) {
    if (/.*\.sw.$/.test(filename) === false) {
        entry[filename.replace(/\.[^\.]+$/, "")] = entriesDir + filename;
    }
});

// Find all HTML files
try {
    var pages = fs.readdirSync(context + "/html").filter(function(filename) {
        return /.*\.sw.$/.test(filename) === false;
    });
} catch(_) {
    var pages = [];
}

// Choose options
var options = {};
switch (command) {
    case "watch":
        options = {
            "NODE_ENV": '"development"',
            "filename": "[name].min.js",
            "minimize": false,
            "sourceMap": true,
        };
        break;
    case "build":
        options = {
            "NODE_ENV": '"production"',
            "filename": "[name].js",
            "minimize": false,
            "sourceMap": false,
        };
        break;
    case "publish":
        options = {
            "NODE_ENV": '"production"',
            "filename": "[name].min.js",
            "minimize": true,
            "sourceMap": false,
        };
        break;
}

var webpack = require(libmod + "/webpack");
var HtmlWebpackPlugin = require(libmod + "/html-webpack-plugin");
var autoprefixer = require(libmod + "/autoprefixer");

var compiler = webpack({
    devtool: options.sourceMap && "inline-source-map",
    context: context,
    resolve: {
        extensions: ["", ".js", ".jsx"],
        alias: {
            "react/lib":    libmod + "/react/lib",
            "react":        libdir + "/alias/react.js",
            "react-dom":    libdir + "/alias/react-dom.js",
            "react-router": libdir + "/alias/react-router.js",
            "redux":        libdir + "/alias/redux.js",
        }
    },
    resolveLoader: {
        modulesDirectories: [libmod]
    },
    entry: entry,
    output: {
        path: context + "/dist",
        filename: options.filename,
    },
    module: {
        loaders: [{
            test: /\.jsx?$/,
            exclude: /node_modules/,
            loader: "babel",
            query: {
                presets: [
                    libmod + "/babel-preset-react",
                    libmod + "/babel-preset-es2015",
                ],
                plugins: [
                    libmod + "/babel-plugin-transform-object-assign",
                ],
            },
        }, {
            test: /^[^!]+.css$/,
            loader: "style!css!postcss",
        }, {
            test: /^[^!]+.less$/,
            loader: "style!css!postcss!less",
        }]
    },
    plugins: pages.map(function(filename) {
        return new HtmlWebpackPlugin({
            filename: filename,
            template: context + "/html/" + filename,
        });
    }).concat(options.minimize ? [
        new webpack.optimize.UglifyJsPlugin({minimize: true})
    ] : []).concat([
        new webpack.DefinePlugin({"process.env": {NODE_ENV: options.NODE_ENV}})
    ]),
    postcss: function () {
        return [
            autoprefixer({browsers: ["last 2 versions", "Safari >= 8"]})
        ];
    }
});

function buildReactCore() {
    webpack({
        context: libdir,
        entry: libdir + "/react-toolkit.js",
        output: {
            path: context + "/dist",
            filename: "react-toolkit.min.js",
        },
        plugins: [
            new webpack.optimize.UglifyJsPlugin({minimize: true}),
            new webpack.DefinePlugin({"process.env": {NODE_ENV: '"production"'}})
        ]
    }).run(function(){});
}

function watch() {
    buildReactCore();
    compiler.watch({poll: true}, function(err, stats) {
        console.log(stats.toString({colors: true}));
    });
}

function build() {
    buildReactCore();
    compiler.run(function(err, stats) {
        console.log(stats.toString({colors: true}));
    });
}

function help() {
    console.error("Usage:");
    console.error("  react-beaker watch   <source dir>");
    console.error("  react-beaker build   <source dir>");
    console.error("  react-beaker publish <source dir>");
}

if (command === "watch") watch();
if (command === "build") build();
if (command === "publish") build();
<commit_msg>Fix missing -webkit-flex<commit_after>#!/usr/bin/env node

var fs = require("fs");
var path = require("path");

var libdir = path.dirname(process.argv[1]);
var libmod = libdir + (fs.existsSync(libdir + "/node_modules") ? "/node_modules" : "/..");
var command = process.argv[2];
var context = process.argv[3] && path.resolve(process.argv[3]);

// Print version
var version = JSON.parse(fs.readFileSync(libdir + "/package.json")).version;
console.log("Version: react-beaker " + version + "\n");

// Validate arguments
if (!command || !context || ["watch", "build", "publish"].indexOf(command) < 0) {
    help();
    process.exit(1);
}

// Find all JavaScript entries
var entry = {};
var entriesDir = context + "/js/entries/";
fs.readdirSync(entriesDir).forEach(function(filename) {
    if (/.*\.sw.$/.test(filename) === false) {
        entry[filename.replace(/\.[^\.]+$/, "")] = entriesDir + filename;
    }
});

// Find all HTML files
try {
    var pages = fs.readdirSync(context + "/html").filter(function(filename) {
        return /.*\.sw.$/.test(filename) === false;
    });
} catch(_) {
    var pages = [];
}

// Choose options
var options = {};
switch (command) {
    case "watch":
        options = {
            "NODE_ENV": '"development"',
            "filename": "[name].min.js",
            "minimize": false,
            "sourceMap": true,
        };
        break;
    case "build":
        options = {
            "NODE_ENV": '"production"',
            "filename": "[name].js",
            "minimize": false,
            "sourceMap": false,
        };
        break;
    case "publish":
        options = {
            "NODE_ENV": '"production"',
            "filename": "[name].min.js",
            "minimize": true,
            "sourceMap": false,
        };
        break;
}

var webpack = require(libmod + "/webpack");
var HtmlWebpackPlugin = require(libmod + "/html-webpack-plugin");
var autoprefixer = require(libmod + "/autoprefixer");

var compiler = webpack({
    devtool: options.sourceMap && "inline-source-map",
    context: context,
    resolve: {
        extensions: ["", ".js", ".jsx"],
        alias: {
            "react/lib":    libmod + "/react/lib",
            "react":        libdir + "/alias/react.js",
            "react-dom":    libdir + "/alias/react-dom.js",
            "react-router": libdir + "/alias/react-router.js",
            "redux":        libdir + "/alias/redux.js",
        }
    },
    resolveLoader: {
        modulesDirectories: [libmod]
    },
    entry: entry,
    output: {
        path: context + "/dist",
        filename: options.filename,
    },
    module: {
        loaders: [{
            test: /\.jsx?$/,
            exclude: /node_modules/,
            loader: "babel",
            query: {
                presets: [
                    libmod + "/babel-preset-react",
                    libmod + "/babel-preset-es2015",
                ],
                plugins: [
                    libmod + "/babel-plugin-transform-object-assign",
                ],
            },
        }, {
            test: /^[^!]+.css$/,
            loader: "style!css?-minimize!postcss",
        }, {
            test: /^[^!]+.less$/,
            loader: "style!css?-minimize!postcss!less",
        }]
    },
    plugins: pages.map(function(filename) {
        return new HtmlWebpackPlugin({
            filename: filename,
            template: context + "/html/" + filename,
        });
    }).concat(options.minimize ? [
        new webpack.optimize.UglifyJsPlugin({minimize: true})
    ] : []).concat([
        new webpack.DefinePlugin({"process.env": {NODE_ENV: options.NODE_ENV}})
    ]),
    postcss: function () {
        return [
            autoprefixer({browsers: ["last 2 versions", "Safari >= 8"]})
        ];
    }
});

function buildReactCore() {
    webpack({
        context: libdir,
        entry: libdir + "/react-toolkit.js",
        output: {
            path: context + "/dist",
            filename: "react-toolkit.min.js",
        },
        plugins: [
            new webpack.optimize.UglifyJsPlugin({minimize: true}),
            new webpack.DefinePlugin({"process.env": {NODE_ENV: '"production"'}})
        ]
    }).run(function(){});
}

function watch() {
    buildReactCore();
    compiler.watch({poll: true}, function(err, stats) {
        console.log(stats.toString({colors: true}));
    });
}

function build() {
    buildReactCore();
    compiler.run(function(err, stats) {
        console.log(stats.toString({colors: true}));
    });
}

function help() {
    console.error("Usage:");
    console.error("  react-beaker watch   <source dir>");
    console.error("  react-beaker build   <source dir>");
    console.error("  react-beaker publish <source dir>");
}

if (command === "watch") watch();
if (command === "build") build();
if (command === "publish") build();
<|endoftext|>


<commit_before>import { ActionsObservable } from 'redux-observable';
import { writeFileObservable } from '../../utils/fs';
import {
  SAVE,
  SAVE_AS,
} from '../constants';

import {
  changeFilename,
  save,
  doneSaving,
} from '../actions';

const Rx = require('rxjs/Rx');
const commutable = require('commutable');

const Observable = Rx.Observable;

/**
  * Cleans up the notebook document and saves the file.
  *
  * @param  {ActionObservable}  action$ The SAVE action with the filename and notebook
  */
export function saveEpic(action$) {
  return action$.ofType(SAVE)
    .do((action) => {
      // If there isn't a filename, save-as it instead
      if (!action.filename) {
        throw new Error('save needs a filename');
      }
    })
    .mergeMap(action =>
      writeFileObservable(action.filename,
        JSON.stringify(
          commutable.toJS(
            action.notebook.update('cellMap', cells =>
              cells.map(value =>
                value.deleteIn(['metadata', 'inputHidden'])
                  .deleteIn(['metadata', 'outputHidden'])
                  .deleteIn(['metadata', 'status'])))),
          null,
          1))
        .catch((error) => {
          const input$ = Observable.of({
            type: 'ERROR',
            payload: error,
            error: true,
          });
          return new ActionsObservable(input$);
        })
        .map(doneSaving)
        // .startWith({ type: START_SAVING })
        // since SAVE effectively acts as the same as START_SAVING
        // you could just look for that in your reducers instead of START_SAVING
    );
}

/**
  * Sets the filename for a notebook before saving.
  *
  * @param  {ActionObservable}  action$ The SAVE_AS action with the filename and notebook
  */
export function saveAsEpic(action$) {
  return action$.ofType(SAVE_AS)
    .mergeMap(action => [
      changeFilename(action.filename),
      save(action.filename, action.notebook),
    ]);
}
<commit_msg>flow(epics): lightly type annotate the saving epic<commit_after>/* @flow */

import { ActionsObservable } from 'redux-observable';
import { writeFileObservable } from '../../utils/fs';
import {
  SAVE,
  SAVE_AS,
} from '../constants';

import {
  changeFilename,
  save,
  doneSaving,
} from '../actions';

const Rx = require('rxjs/Rx');
const commutable = require('commutable');

const Observable = Rx.Observable;

/**
  * Cleans up the notebook document and saves the file.
  *
  * @param  {ActionObservable}  action$ The SAVE action with the filename and notebook
  */
export function saveEpic(action$: ActionsObservable) {
  return action$.ofType(SAVE)
    .do((action) => {
      // If there isn't a filename, save-as it instead
      if (!action.filename) {
        throw new Error('save needs a filename');
      }
    })
    .mergeMap(action =>
      writeFileObservable(action.filename,
        JSON.stringify(
          commutable.toJS(
            action.notebook.update('cellMap', cells =>
              cells.map(value =>
                value.deleteIn(['metadata', 'inputHidden'])
                  .deleteIn(['metadata', 'outputHidden'])
                  .deleteIn(['metadata', 'status'])))),
          null,
          1))
        .catch((error: Error) =>
          Observable.of({
            type: 'ERROR_SAVING',
            payload: error,
            error: true,
          })
        )
        .map(doneSaving)
        // .startWith({ type: START_SAVING })
        // since SAVE effectively acts as the same as START_SAVING
        // you could just look for that in your reducers instead of START_SAVING
    );
}

/**
  * Sets the filename for a notebook before saving.
  *
  * @param  {ActionObservable}  action$ The SAVE_AS action with the filename and notebook
  */
export function saveAsEpic(action$: ActionsObservable) {
  return action$.ofType(SAVE_AS)
    .mergeMap(action => [
      changeFilename(action.filename),
      save(action.filename, action.notebook),
    ]);
}
<|endoftext|>


<commit_before>module.exports = function(app, database) {
// -----------------------------------------------------------
  const fs = require( 'fs' );
  // passport
  const passport = require('passport');
  // passport-saml
  const saml_strategy = require('passport-saml').Strategy;

// -----------------------------------------------------------
  // passport setup
  app.use(passport.initialize());

// -----------------------------------------------------------
  // routing
  app.get('/login',
    passport.authenticate(['ip', 'saml'], { failureRedirect: '/auth_fail', successRedirect: '/'})      // TODO
    //function(req, res) {
    //  // If this function gets called, authentication was successful.
    //  //console.log("req: ");     // TODO
    //  //console.log(req);
    //  //console.log("res: ");
    //  //console.log(res);
    //  console.log(req.user);
    //  console.log("auth successful");
    //  res.redirect('/');
    //}
  );
  // TODO - when is the defined strategy callback called ?

  // TODO - different failureRedirects on both routes ?

  app.post('/login/callback',
    //passport.authenticate('saml', { failureRedirect: '/', failureFlash: true }),
    passport.authenticate('saml', { failureRedirect: '/auth_fail' }),       // TODO
    function(req, res) {
      console.log("post login callback");
      res.redirect('/');
    }
  );

// -----------------------------------------------------------
  // saml

  var pvk = fs.readFileSync('cert/etlog.cesnet.cz.key.pem', 'utf-8');
  var cert = fs.readFileSync('cert/etlog.cesnet.cz.crt.pem', 'utf-8');
  var idpcert = fs.readFileSync('cert/idp-cert.pem', 'utf-8');

  var strategy = new saml_strategy({
      callbackUrl: 'https://etlog.cesnet.cz/login/callback',
      entryPoint: 'https://whoami-dev.cesnet.cz/idp/profile/SAML2/Redirect/SSO',
      issuer: 'https://etlog.cesnet.cz/',
      protocol: 'https://',
      decryptionPvk: pvk,
      //ecryptionCert: cert,
      identifierFormat : 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient',
      cert: idpcert,
      //privateCert: cert
    },
    function(profile, done) {         // TODO
      console.log("autentizace OK");

      // tady je treba se dostat k obsahu assertion - informace o prihlasenem uzivateli
      console.log(profile);
      console.log(profile.getAssertionXml());
      console.log("");
      console.log("");
      console.log("");
      console.log("");
      console.log(done());

      //render("OK")
      //findByEmail(profile.email, function(err, user) {
      //  if (err) {
      //    return done(err);
      //  }
      //  return done(null, user);
    });

  passport.use(strategy);
  console.log(strategy.generateServiceProviderMetadata(cert));

// -----------------------------------------------------------
  // ip based authentication
  var db_ips = database.privileged_ips.find({}, { _id: 0 },     // search db for privileged ip addresses
  function(err, items) {
    var allowed_ips = [];

    for(var ip in items) {
      allowed_ips.push(items[ip]["ip"]);      // add to array
    }

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

    const ip_strategy = require('passport-ip').Strategy;

    passport.use(new ip_strategy({
      range: allowed_ips          // use data from db
    }, function(profile, done){
      console.log("profile");
      console.log(profile);
      console.log("done");
      console.log(done);
      done(null, profile);
      //profile.id is the ip address.
    }));
  });

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

  //http://stackoverflow.com/questions/27637609/understanding-passport-serialize-deserialize

  passport.serializeUser(function(user, done) {   //  TODO
        done(null, user);
  });

  passport.deserializeUser(function(user, done) { // TODO
        done(null, user);
  });

// -----------------------------------------------------------
}
<commit_msg>cleaner code<commit_after>module.exports = function(app, database) {
// --------------------------------------------------------------------------------------
  const fs = require( 'fs' );
  // passport
  const passport = require('passport');
  // passport-saml
  const saml_strategy = require('passport-saml').Strategy;

// --------------------------------------------------------------------------------------
  // passport setup
  app.use(passport.initialize());

// --------------------------------------------------------------------------------------
  // routing
  app.get('/login',
    passport.authenticate(['ip', 'saml'], { failureRedirect: '/auth_fail', successRedirect: '/'})      // TODO
    //function(req, res) {
    //  // If this function gets called, authentication was successful.
    //  //console.log("req: ");     // TODO
    //  //console.log(req);
    //  //console.log("res: ");
    //  //console.log(res);
    //  console.log(req.user);
    //  console.log("auth successful");
    //  res.redirect('/');
    //}
  );
  // TODO - when is the defined strategy callback called ?

  // TODO - different failureRedirects on both routes ?

  app.post('/login/callback',
    //passport.authenticate('saml', { failureRedirect: '/', failureFlash: true }),
    passport.authenticate('saml', { failureRedirect: '/auth_fail' }),       // TODO
    function(req, res) {
      console.log("post login callback");
      res.redirect('/');
    }
  );

// --------------------------------------------------------------------------------------
  // saml

  var pvk = fs.readFileSync('cert/etlog.cesnet.cz.key.pem', 'utf-8');
  var cert = fs.readFileSync('cert/etlog.cesnet.cz.crt.pem', 'utf-8');
  var idpcert = fs.readFileSync('cert/idp-cert.pem', 'utf-8');

  var strategy = new saml_strategy({
      callbackUrl: 'https://etlog.cesnet.cz/login/callback',
      entryPoint: 'https://whoami-dev.cesnet.cz/idp/profile/SAML2/Redirect/SSO',
      issuer: 'https://etlog.cesnet.cz/',
      protocol: 'https://',
      decryptionPvk: pvk,
      //ecryptionCert: cert,
      identifierFormat : 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient',
      cert: idpcert,
      //privateCert: cert
    },
    function(profile, done) {         // TODO
      console.log("autentizace OK");

      // tady je treba se dostat k obsahu assertion - informace o prihlasenem uzivateli
      console.log(profile);
      console.log(profile.getAssertionXml());
      console.log("");
      console.log("");
      console.log("");
      console.log("");
      console.log(done());

      //render("OK")
      //findByEmail(profile.email, function(err, user) {
      //  if (err) {
      //    return done(err);
      //  }
      //  return done(null, user);
    });

  passport.use(strategy);
  console.log(strategy.generateServiceProviderMetadata(cert));

// --------------------------------------------------------------------------------------
  // ip based authentication
  var db_ips = database.privileged_ips.find({}, { _id: 0 },     // search db for privileged ip addresses
  function(err, items) {
    var allowed_ips = [];

    for(var ip in items) {
      allowed_ips.push(items[ip]["ip"]);      // add to array
    }

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

    const ip_strategy = require('passport-ip').Strategy;

    passport.use(new ip_strategy({
      range: allowed_ips          // use data from db
    }, function(profile, done){
      console.log("profile");
      console.log(profile);
      console.log("done");
      console.log(done);
      done(null, profile);
      //profile.id is the ip address.
    }));
  });

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

  //http://stackoverflow.com/questions/27637609/understanding-passport-serialize-deserialize

  passport.serializeUser(function(user, done) {   //  TODO
        done(null, user);
  });

  passport.deserializeUser(function(user, done) { // TODO
        done(null, user);
  });

// --------------------------------------------------------------------------------------
}
<|endoftext|>


<commit_before>import {Component, Directive} from 'angular2/src/core/annotations_impl/annotations';
import {FormBuilder, Control, ControlGroup, Validators, formDirectives} from 'angular2/forms';

import {IonicView, Animation, Modal, NavController, IonicComponent} from 'ionic/ionic';

@Component({ selector: 'ion-view' })
@IonicView({
  templateUrl: 'detail.html'
})
export class DetailPage {
  constructor(params: NavParams) {
    this.post = params.post;
  }
}

@Component({ selector: 'ion-view' })
@IonicView({
  templateUrl: 'feed.html'
})
export class FeedPage {
  constructor(nav: NavController) {
    this.nav = nav;

    this.filterForm = new ControlGroup({
      filterControl: new Control("")
    });

    this.posts = [
      {
        text: 'I tried to keep both arts alive, but the camera won. I found that while the camera does not express the soul, perhaps a photograph can!',
        image: 'http://ionic-io-assets.s3.amazonaws.com/images/p.jpg',
        day: 5
      },
      {
        text: 'It is good to realize that if love and peace can prevail on earth, and if we can teach our children to honor nature\'s gifts, the joys and beauties of the outdoors will be here forever.',
        image: 'http://ionic-io-assets.s3.amazonaws.com/images/p1.png',
        day: 6
      },
      {
        text: 'I see humanity now as one vast plant, needing for its highest fulfillment only love, the natural blessings of the great outdoors, and intelligent crossing and selection.',
        image: 'http://ionic-io-assets.s3.amazonaws.com/images/p2.png',
        day: 7
      },
      {
        text: 'You must not lose faith in humanity. Humanity is an ocean; if a few drops of the ocean are dirty, the ocean does not become dirty.',
        image: 'http://ionic-io-assets.s3.amazonaws.com/images/p3.png',
        day: 7
      },
      {
        text: 'Keep close to Nature\'s heart... and break clear away, once in awhile, and climb a mountain or spend a week in the woods. Wash your spirit clean.',
        image: 'http://ionic-io-assets.s3.amazonaws.com/images/p4.png',
        day: 8
      }
    ];
  }

  postClicked(event, post) {
    console.log('Post clicked');
    this.nav.push(DetailPage, {
      post: post
    });
    event.preventDefault();
  }
}

@Component({
  selector: 'ion-app',
  appInjector: [Modal]
})
@IonicView({
  templateUrl: 'main.html'
})
class IonicApp {
  constructor(Modal: Modal) {
    this.Modal = Modal;
    this.rootView = FeedPage;
  }

  openHeart() {
    console.log('openHeart');

    this.Modal.open(HeartModal, {
      enterAnimation: 'my-fade-in',
      leaveAnimation: 'my-fade-out'
    });
  }

  openGear() {
    console.log('openGear');

    this.Modal.open(SettingsModal, {
      enterAnimation: 'my-fade-in',
      leaveAnimation: 'my-fade-out'
    });
  }
}

@IonicComponent(Modal)
@IonicView({
  template: '<ion-view id="settings-modal"><ion-content padding><button primary (click)="close()">Close</button></ion-content></ion-view>'
})
export class SettingsModal extends Modal {}

@IonicComponent(Modal)
@IonicView({
  template: '<ion-view id="heart-modal"><button icon (^click)="close()"></button>
20

You\'re pretty awesome
</ion-view>'
})
export class HeartModal extends Modal {}

export function main(ionicBootstrap) {
  ionicBootstrap(IonicApp);
}

class FadeIn extends Animation {
  constructor(element) {
    super(element);
    this
      .easing('ease')
      .duration(250)
      .fadeIn();
  }
}

Animation.register('my-fade-in', FadeIn);

class FadeOut extends Animation {
  constructor(element) {
    super(element);
    this
      .easing('ease')
      .duration(250)
      .fadeOut();
  }
}

Animation.register('my-fade-out', FadeOut);
<commit_msg>NavParams import<commit_after>import {Component, Directive} from 'angular2/src/core/annotations_impl/annotations';
import {FormBuilder, Control, ControlGroup, Validators, formDirectives} from 'angular2/forms';

import {IonicView, Animation, Modal, NavController, NavParams, IonicComponent} from 'ionic/ionic';

@Component({ selector: 'ion-view' })
@IonicView({
  templateUrl: 'detail.html'
})
export class DetailPage {
  constructor(params: NavParams) {
    this.post = params.post;
  }
}

@Component({ selector: 'ion-view' })
@IonicView({
  templateUrl: 'feed.html'
})
export class FeedPage {
  constructor(nav: NavController) {
    this.nav = nav;

    this.filterForm = new ControlGroup({
      filterControl: new Control("")
    });

    this.posts = [
      {
        text: 'I tried to keep both arts alive, but the camera won. I found that while the camera does not express the soul, perhaps a photograph can!',
        image: 'http://ionic-io-assets.s3.amazonaws.com/images/p.jpg',
        day: 5
      },
      {
        text: 'It is good to realize that if love and peace can prevail on earth, and if we can teach our children to honor nature\'s gifts, the joys and beauties of the outdoors will be here forever.',
        image: 'http://ionic-io-assets.s3.amazonaws.com/images/p1.png',
        day: 6
      },
      {
        text: 'I see humanity now as one vast plant, needing for its highest fulfillment only love, the natural blessings of the great outdoors, and intelligent crossing and selection.',
        image: 'http://ionic-io-assets.s3.amazonaws.com/images/p2.png',
        day: 7
      },
      {
        text: 'You must not lose faith in humanity. Humanity is an ocean; if a few drops of the ocean are dirty, the ocean does not become dirty.',
        image: 'http://ionic-io-assets.s3.amazonaws.com/images/p3.png',
        day: 7
      },
      {
        text: 'Keep close to Nature\'s heart... and break clear away, once in awhile, and climb a mountain or spend a week in the woods. Wash your spirit clean.',
        image: 'http://ionic-io-assets.s3.amazonaws.com/images/p4.png',
        day: 8
      }
    ];
  }

  postClicked(event, post) {
    console.log('Post clicked');
    this.nav.push(DetailPage, {
      post: post
    });
    event.preventDefault();
  }
}

@Component({
  selector: 'ion-app',
  appInjector: [Modal]
})
@IonicView({
  templateUrl: 'main.html'
})
class IonicApp {
  constructor(Modal: Modal) {
    this.Modal = Modal;
    this.rootView = FeedPage;
  }

  openHeart() {
    console.log('openHeart');

    this.Modal.open(HeartModal, {
      enterAnimation: 'my-fade-in',
      leaveAnimation: 'my-fade-out'
    });
  }

  openGear() {
    console.log('openGear');

    this.Modal.open(SettingsModal, {
      enterAnimation: 'my-fade-in',
      leaveAnimation: 'my-fade-out'
    });
  }
}

@IonicComponent(Modal)
@IonicView({
  template: '<ion-view id="settings-modal"><ion-content padding><button primary (click)="close()">Close</button></ion-content></ion-view>'
})
export class SettingsModal extends Modal {}

@IonicComponent(Modal)
@IonicView({
  template: '<ion-view id="heart-modal"><button icon (^click)="close()"></button>
20

You\'re pretty awesome
</ion-view>'
})
export class HeartModal extends Modal {}

export function main(ionicBootstrap) {
  ionicBootstrap(IonicApp);
}

class FadeIn extends Animation {
  constructor(element) {
    super(element);
    this
      .easing('ease')
      .duration(250)
      .fadeIn();
  }
}

Animation.register('my-fade-in', FadeIn);

class FadeOut extends Animation {
  constructor(element) {
    super(element);
    this
      .easing('ease')
      .duration(250)
      .fadeOut();
  }
}

Animation.register('my-fade-out', FadeOut);
<|endoftext|>


<commit_before>module.exports = Screen

var ansi = require("ansi")
var cursor = ansi(process.stdout)
var xtend = require("xtend")

function Screen(options) {
  if (!(this instanceof Screen)) {
    return new Screen(options)
  }
  options = xtend({border: true}, options)

  if (options.width >>> 0 != options.width) {
    throw new TypeError("width must be a positive integer")
  }
  if (options.height >>> 0 != options.height) {
    throw new TypeError("height must be a positive integer")
  }

  this.width = options.width
  this.height = options.height
  this.border = options.border
  this.needsPaint = true
  this.content = emptySpace(this.width * this.height, " ")
  cursor.hide()
  // TODO color bins (bg/fg)

  var self = this
  process.once("SIGTERM", function sigterm() {
    self.cleanup()
  })
  process.once("SIGINT", function sigint() {
    self.cleanup()
  })
}

Screen.prototype.cleanup = function cleanup() {
  cursor.reset()
  cursor.show()
}

Screen.prototype._clear = function _clear() {
  process.stdout.write("\u001B[2J\u001B[0;0f")
}

Screen.prototype.paint = function paint(force) {
  if (!this.needsPaint && !force) {
    return
  }

  this._clear()

  var horizontalBorder = ""
  var verticalBorder = ""

  if (this.border) {
    var horizontalBorder = emptySpace(this.width, "─")
    var verticalBorder = "│"

    cursor.write("┌")
    cursor.write(horizontalBorder)
    cursor.write("┐")
    cursor.write("\n")
  }

  // TODO colors

  var rows = this._rows()

  for (var i = 0; i < rows.length; i++) {
    cursor.write(verticalBorder)
    cursor.write(rows[i])
    cursor.write(verticalBorder)
    cursor.write("\n")
  }

  if (this.border) {
    cursor.write("└")
    cursor.write(horizontalBorder)
    cursor.write("┘")
    cursor.write("\n")
  }

  this.needsPaint = false
}

// Returns the rows as an array of strings per row
Screen.prototype._rows = function _rows() {
  var rows = new Array(this.height)
  for (var i = 0; i < this.height; i++) {
    rows[i] = this.content.substr(i * this.width, this.width)
  }
  return rows
}

Screen.prototype.output = function output(row, column, text) {
  if (row >>> 0 != row) {
    throw new TypeError("row must be a positive integer")
  }
  if (column >>> 0 != column) {
    throw new TypeError("column must be a positive integer")
  }
  if (row >= this.height) {
    throw new RangeError("row must be <= window height")
  }
  if (column >= this.width) {
    throw new RangeError("column must be <= window width")
  }

  if (text == null || text.length == 0) {
    return
  }
  // scrub newlines
  text = text.replace(/\s/g, " ")

  var pos = row * this.width + column

  var prevContent = this.content.substr(0, pos)
  var afterContent = this.content.substr(pos + text.length)

  this.content = (prevContent + text + afterContent).substr(0, this.height * this.width)
  this.needsPaint = true
}

Screen.prototype.getChar = function getChar(row, column) {
  if (row >>> 0 != row) {
    throw new TypeError("row must be a positive integer")
  }
  if (column >>> 0 != column) {
    throw new TypeError("column must be a positive integer")
  }
  if (row >= this.height) {
    throw new RangeError("row must be <= window height")
  }
  if (column >= this.width) {
    throw new RangeError("column must be <= window width")
  }
  return this._rows()[row][column]
}

function emptySpace(n, char) {
  if (char == null) {
    char = " "
  }
  var collect = []
  for (var i = 0; i < n; i++) {
    collect.push(char)
  }
  return collect.join("")
}
<commit_msg>Replace signal handlers with exit listener<commit_after>module.exports = Screen

var ansi = require("ansi")
var cursor = ansi(process.stdout)
var xtend = require("xtend")

function Screen(options) {
  if (!(this instanceof Screen)) {
    return new Screen(options)
  }
  options = xtend({border: true}, options)

  if (options.width >>> 0 != options.width) {
    throw new TypeError("width must be a positive integer")
  }
  if (options.height >>> 0 != options.height) {
    throw new TypeError("height must be a positive integer")
  }

  this.width = options.width
  this.height = options.height
  this.border = options.border
  this.needsPaint = true
  this.content = emptySpace(this.width * this.height, " ")
  cursor.hide()
  // TODO color bins (bg/fg)

  var self = this
  process.once("exit", function onExit() {
    self.cleanup()
  })
}

Screen.prototype.cleanup = function cleanup() {
  cursor.reset()
  cursor.show()
}

Screen.prototype._clear = function _clear() {
  process.stdout.write("\u001B[2J\u001B[0;0f")
}

Screen.prototype.paint = function paint(force) {
  if (!this.needsPaint && !force) {
    return
  }

  this._clear()

  var horizontalBorder = ""
  var verticalBorder = ""

  if (this.border) {
    var horizontalBorder = emptySpace(this.width, "─")
    var verticalBorder = "│"

    cursor.write("┌")
    cursor.write(horizontalBorder)
    cursor.write("┐")
    cursor.write("\n")
  }

  // TODO colors

  var rows = this._rows()

  for (var i = 0; i < rows.length; i++) {
    cursor.write(verticalBorder)
    cursor.write(rows[i])
    cursor.write(verticalBorder)
    cursor.write("\n")
  }

  if (this.border) {
    cursor.write("└")
    cursor.write(horizontalBorder)
    cursor.write("┘")
    cursor.write("\n")
  }

  this.needsPaint = false
}

// Returns the rows as an array of strings per row
Screen.prototype._rows = function _rows() {
  var rows = new Array(this.height)
  for (var i = 0; i < this.height; i++) {
    rows[i] = this.content.substr(i * this.width, this.width)
  }
  return rows
}

Screen.prototype.output = function output(row, column, text) {
  if (row >>> 0 != row) {
    throw new TypeError("row must be a positive integer")
  }
  if (column >>> 0 != column) {
    throw new TypeError("column must be a positive integer")
  }
  if (row >= this.height) {
    throw new RangeError("row must be <= window height")
  }
  if (column >= this.width) {
    throw new RangeError("column must be <= window width")
  }

  if (text == null || text.length == 0) {
    return
  }
  // scrub newlines
  text = text.replace(/\s/g, " ")

  var pos = row * this.width + column

  var prevContent = this.content.substr(0, pos)
  var afterContent = this.content.substr(pos + text.length)

  this.content = (prevContent + text + afterContent).substr(0, this.height * this.width)
  this.needsPaint = true
}

Screen.prototype.getChar = function getChar(row, column) {
  if (row >>> 0 != row) {
    throw new TypeError("row must be a positive integer")
  }
  if (column >>> 0 != column) {
    throw new TypeError("column must be a positive integer")
  }
  if (row >= this.height) {
    throw new RangeError("row must be <= window height")
  }
  if (column >= this.width) {
    throw new RangeError("column must be <= window width")
  }
  return this._rows()[row][column]
}

function emptySpace(n, char) {
  if (char == null) {
    char = " "
  }
  var collect = []
  for (var i = 0; i < n; i++) {
    collect.push(char)
  }
  return collect.join("")
}
<|endoftext|>