<commit_before>import { isNil } from '../util'
import Coordinate from './Coordinate'
import Selection from './Selection'
import PropertySelection from './PropertySelection'

/**
  A selection spanning multiple nodes.

  @class
  @extends PropertySelection

  @example

  ```js
  var containerSel = doc.createSelection({
    type: 'container',
    containerId: 'body',
    startPath: ['p1', 'content'],
    startOffset: 5,
    endPath: ['p3', 'content'],
    endOffset: 4,
  });
  ```
*/
class ContainerSelection extends Selection {

  constructor(containerId, startPath, startOffset, endPath, endOffset, reverse, surfaceId) {
    super()

    if (arguments.length === 1) {
      let data = arguments[0]
      containerId = data.containerId
      startPath = data.startPath
      startOffset = data.startOffset
      endPath = data.endPath
      endOffset = data.endOffset
      reverse = data.reverse
      surfaceId = data.surfaceId
    }

    /**
      @type {String}
    */
    this.containerId = containerId;
    if (!this.containerId) throw new Error('Invalid arguments: `containerId` is mandatory');

    this.start = new Coordinate(startPath, startOffset)
    this.end = new Coordinate(isNil(endPath) ? startPath : endPath, isNil(endOffset) ? startOffset : endOffset)

    this.reverse = Boolean(reverse);

    this.surfaceId = surfaceId;
  }

  get startPath() {
    console.warn('DEPRECATED: use sel.start.path instead.')
    return this.start.path
  }

  get startOffset() {
    console.warn('DEPRECATED: use sel.start.offset instead.')
    return this.start.offset
  }

  get endPath() {
    console.warn('DEPRECATED: use sel.end.path instead.')
    return this.end.path
  }

  get endOffset() {
    console.warn('DEPRECATED: use sel.end.offset instead.')
    return this.end.offset
  }

  toJSON() {
    return {
      type: 'container',
      containerId: this.containerId,
      startPath: this.start.path,
      startOffset: this.start.offset,
      endPath: this.end.path,
      endOffset: this.end.offset,
      reverse: this.reverse,
      surfaceId: this.surfaceId
    };
  }

  isContainerSelection() {
    return true;
  }

  getType() {
    return 'container';
  }

  isNull() {
    return false;
  }

  isCollapsed() {
    return this.start.equals(this.end);
  }

  isReverse() {
    return this.reverse;
  }

  equals(other) {
    return (
      Selection.prototype.equals.call(this, other) &&
      this.containerId === other.containerId &&
      (this.start.equals(other.start) && this.end.equals(other.end))
    );
  }

  toString() {
    /* istanbul ignore next */
    return [
      "ContainerSelection(",
      this.containerId, ", ",
      JSON.stringify(this.start.path), ", ", this.start.offset,
      " -> ",
      JSON.stringify(this.end.path), ", ", this.end.offset,
      (this.reverse?", reverse":""),
      (this.surfaceId?(", "+this.surfaceId):""),
      ")"
    ].join('');
  }

  /**
    @return {model/Container} The container node instance for this selection.
  */
  getContainer() {
    if (!this._internal.container) {
      this._internal.container = this.getDocument().get(this.containerId);
    }
    return this._internal.container;
  }

  isInsideOf(other, strict) {
    // Note: this gets called from PropertySelection.contains()
    // because this implementation can deal with mixed selection types.
    if (other.isNull()) return false;
    strict = Boolean(strict);
    var r1 = this._range(this);
    var r2 = this._range(other);
    return (r2.start.isBefore(r1.start, strict) &&
      r1.end.isBefore(r2.end, strict));
  }

  contains(other, strict) {
    // Note: this gets called from PropertySelection.isInsideOf()
    // because this implementation can deal with mixed selection types.
    if (other.isNull()) return false;
    strict = Boolean(strict);
    var r1 = this._range(this);
    var r2 = this._range(other);
    return (r1.start.isBefore(r2.start, strict) &&
      r2.end.isBefore(r1.end, strict));
  }

  containsNode(nodeId, strict) {
    const container = this.getContainer()
    if (!container.contains(nodeId)) return false
    const coor = new Coordinate([nodeId], 0)
    const address = container.getAddress(coor)
    const r = this._range(this)
    // console.log('ContainerSelection.containsNode()', address, 'is within', r.start, '->', r.end, '?');
    let contained = r.start.isBefore(address, strict)
    if (contained) {
      address.offset = 1
      contained = r.end.isAfter(address, strict)
    }
    return contained
  }

  overlaps(other) {
    var r1 = this._range(this);
    var r2 = this._range(other);
    // it overlaps if they are not disjunct
    return !(r1.end.isBefore(r2.start, false) ||
      r2.end.isBefore(r1.start, false));
  }

  isLeftAlignedWith(other) {
    var r1 = this._range(this);
    var r2 = this._range(other);
    return r1.start.isEqual(r2.start);
  }

  isRightAlignedWith(other) {
    var r1 = this._range(this);
    var r2 = this._range(other);
    return r1.end.isEqual(r2.end);
  }

  /**
    Collapse a selection to chosen direction.

    @param {String} direction either left of right
    @returns {PropertySelection}
  */
  collapse(direction) {
    var coor;
    if (direction === 'left') {
      coor = this.start;
    } else {
      coor = this.end;
    }
    return _createNewSelection(this, coor, coor);
  }

  expand(other) {
    var r1 = this._range(this);
    var r2 = this._range(other);
    var start;
    var end;

    if (r1.start.isEqual(r2.start)) {
      start = new Coordinate(this.start.path, Math.min(this.start.offset, other.start.offset));
    } else if (r1.start.isAfter(r2.start)) {
      start = new Coordinate(other.start.path, other.start.offset);
    } else {
      start = this.start;
    }
    if (r1.end.isEqual(r2.end)) {
      end = new Coordinate(this.end.path, Math.max(this.end.offset, other.end.offset));
    } else if (r1.end.isBefore(r2.end, false)) {
      end = new Coordinate(other.end.path, other.end.offset);
    } else {
      end = this.end;
    }

    return _createNewSelection(this, start, end);
  }

  truncateWith(other) {
    if (other.isInsideOf(this, 'strict')) {
      // the other selection should overlap only on one side
      throw new Error('Can not truncate with a contained selections');
    }
    if (!this.overlaps(other)) {
      return this;
    }
    var r1 = this._range(this);
    var r2 = this._range(other);
    var start, end;
    if (r2.start.isBefore(r1.start, 'strict') && r2.end.isBefore(r1.end, 'strict')) {
      start = other.end;
      end = this.end;
    } else if (r1.start.isBefore(r2.start, 'strict') && r1.end.isBefore(r2.end, 'strict')) {
      start = this.start;
      end = other.start;
    } else if (r1.start.isEqual(r2.start)) {
      if (r2.end.isBefore(r1.end, 'strict')) {
        start = other.end;
        end = this.end;
      } else {
        // the other selection is larger which eliminates this one
        return Selection.nullSelection;
      }
    } else if (r1.end.isEqual(r2.end)) {
      if (r1.start.isBefore(r2.start, 'strict')) {
        start = this.start;
        end = other.start;
      } else {
        // the other selection is larger which eliminates this one
        return Selection.nullSelection;
      }
    } else if (this.isInsideOf(other)) {
      return Selection.nullSelection;
    } else {
      throw new Error('Could not determine coordinates for truncate. Check input');
    }
    return _createNewSelection(this, start, end);
  }

  /**
    Get the node ids covered by this selection.

    @returns {String[]} an array of ids
  */
  getNodeIds() {
    const container = this.getContainer()
    const startPos = container.getPosition(this.start.path[0])
    const endPos = container.getPosition(this.end.path[0])
    return container.nodes.slice(startPos, endPos+1)
  }

  /**
    Splits a container selection into property selections.

    @returns {PropertySelection[]}
  */
  splitIntoPropertySelections() {
    var sels = [];
    var fragments = this.getFragments();
    fragments.forEach(function(fragment) {
      if (fragment instanceof Selection.Fragment) {
        sels.push(
          new PropertySelection(fragment.path, fragment.startOffset,
            fragment.endOffset, false, this.containerId, this.surfaceId)
        );
      }
    }.bind(this));
    return sels;
  }

  _clone() {
    return new ContainerSelection(this);
  }

  _range(sel) {
    // EXPERIMENTAL: caching the internal address based range
    // as we use it very often.
    // However, this is dangerous as this data can get invalid by a change
    if (sel._internal.addressRange) {
      return sel._internal.addressRange;
    }

    var container = this.getContainer();
    var startAddress = container.getAddress(sel.start);
    var endAddress;
    if (sel.isCollapsed()) {
      endAddress = startAddress;
    } else {
      endAddress = container.getAddress(sel.end);
    }
    var addressRange = {
      start: startAddress,
      end: endAddress
    };
    if (sel._isContainerSelection) {
      sel._internal.addressRange = addressRange;
    }
    return addressRange;
  }

  get path() {
    throw new Error('ContainerSelection has no path property. Use startPath and endPath instead');
  }

}

ContainerSelection.prototype._isContainerSelection = true

ContainerSelection.fromJSON = function(properties) {
  var sel = new ContainerSelection(properties);
  return sel;
}

function _createNewSelection(containerSel, start, end) {
  let newSel

  if (start === end) {
    newSel = new PropertySelection({
      path: start.path,
      startOffset: start.offset,
      endOffset: start.offset,
      containerId: containerSel.containerId,
      surfaceId: containerSel.surfaceId
    })
  } else {
    newSel = new ContainerSelection(containerSel.containerId,
    start.path, start.offset, end.path, end.offset, false, containerSel.surfaceId);
  }
  // we need to attach the new selection
  const doc = containerSel._internal.doc;
  if (doc) {
    newSel.attach(doc);
  }
  return newSel;
}

export default ContainerSelection
<commit_msg>Cleanup.<commit_after>import { isNil } from '../util'
import Coordinate from './Coordinate'
import Selection from './Selection'
import PropertySelection from './PropertySelection'

/**
  A selection spanning multiple nodes.

  @class
  @extends PropertySelection

  @example

  ```js
  let containerSel = doc.createSelection({
    type: 'container',
    containerId: 'body',
    startPath: ['p1', 'content'],
    startOffset: 5,
    endPath: ['p3', 'content'],
    endOffset: 4,
  })
  ```
*/
class ContainerSelection extends Selection {

  constructor(containerId, startPath, startOffset, endPath, endOffset, reverse, surfaceId) {
    super()

    if (arguments.length === 1) {
      let data = arguments[0]
      containerId = data.containerId
      startPath = data.startPath
      startOffset = data.startOffset
      endPath = data.endPath
      endOffset = data.endOffset
      reverse = data.reverse
      surfaceId = data.surfaceId
    }

    /**
      @type {String}
    */
    this.containerId = containerId
    if (!this.containerId) throw new Error('Invalid arguments: `containerId` is mandatory')

    this.start = new Coordinate(startPath, startOffset)
    this.end = new Coordinate(isNil(endPath) ? startPath : endPath, isNil(endOffset) ? startOffset : endOffset)

    this.reverse = Boolean(reverse)

    this.surfaceId = surfaceId
  }

  /* istanbul ignore start */

  get startPath() {
    console.warn('DEPRECATED: use sel.start.path instead.')
    return this.start.path
  }

  get startOffset() {
    console.warn('DEPRECATED: use sel.start.offset instead.')
    return this.start.offset
  }

  get endPath() {
    console.warn('DEPRECATED: use sel.end.path instead.')
    return this.end.path
  }

  get endOffset() {
    console.warn('DEPRECATED: use sel.end.offset instead.')
    return this.end.offset
  }

  /* istanbul ignore end */

  toJSON() {
    return {
      type: 'container',
      containerId: this.containerId,
      startPath: this.start.path,
      startOffset: this.start.offset,
      endPath: this.end.path,
      endOffset: this.end.offset,
      reverse: this.reverse,
      surfaceId: this.surfaceId
    }
  }

  isContainerSelection() {
    return true
  }

  getType() {
    return 'container'
  }

  isNull() {
    return false
  }

  isCollapsed() {
    return this.start.equals(this.end)
  }

  isReverse() {
    return this.reverse
  }

  equals(other) {
    return (
      Selection.prototype.equals.call(this, other) &&
      this.containerId === other.containerId &&
      (this.start.equals(other.start) && this.end.equals(other.end))
    )
  }

  toString() {
    /* istanbul ignore next */
    return [
      "ContainerSelection(",
      this.containerId, ", ",
      JSON.stringify(this.start.path), ", ", this.start.offset,
      " -> ",
      JSON.stringify(this.end.path), ", ", this.end.offset,
      (this.reverse?", reverse":""),
      (this.surfaceId?(", "+this.surfaceId):""),
      ")"
    ].join('')
  }

  /**
    @return {model/Container} The container node instance for this selection.
  */
  getContainer() {
    if (!this._internal.container) {
      this._internal.container = this.getDocument().get(this.containerId)
    }
    return this._internal.container
  }

  isInsideOf(other, strict) {
    // Note: this gets called from PropertySelection.contains()
    // because this implementation can deal with mixed selection types.
    if (other.isNull()) return false
    strict = Boolean(strict)
    let r1 = this._range(this)
    let r2 = this._range(other)
    return (r2.start.isBefore(r1.start, strict) &&
      r1.end.isBefore(r2.end, strict))
  }

  contains(other, strict) {
    // Note: this gets called from PropertySelection.isInsideOf()
    // because this implementation can deal with mixed selection types.
    if (other.isNull()) return false
    strict = Boolean(strict)
    let r1 = this._range(this)
    let r2 = this._range(other)
    return (r1.start.isBefore(r2.start, strict) &&
      r2.end.isBefore(r1.end, strict))
  }

  containsNode(nodeId, strict) {
    const container = this.getContainer()
    if (!container.contains(nodeId)) return false
    const coor = new Coordinate([nodeId], 0)
    const address = container.getAddress(coor)
    const r = this._range(this)
    // console.log('ContainerSelection.containsNode()', address, 'is within', r.start, '->', r.end, '?')
    let contained = r.start.isBefore(address, strict)
    if (contained) {
      address.offset = 1
      contained = r.end.isAfter(address, strict)
    }
    return contained
  }

  overlaps(other) {
    let r1 = this._range(this)
    let r2 = this._range(other)
    // it overlaps if they are not disjunct
    return !(r1.end.isBefore(r2.start, false) ||
      r2.end.isBefore(r1.start, false))
  }

  isLeftAlignedWith(other) {
    let r1 = this._range(this)
    let r2 = this._range(other)
    return r1.start.isEqual(r2.start)
  }

  isRightAlignedWith(other) {
    let r1 = this._range(this)
    let r2 = this._range(other)
    return r1.end.isEqual(r2.end)
  }

  /**
    Collapse a selection to chosen direction.

    @param {String} direction either left of right
    @returns {PropertySelection}
  */
  collapse(direction) {
    let coor
    if (direction === 'left') {
      coor = this.start
    } else {
      coor = this.end
    }
    return _createNewSelection(this, coor, coor)
  }

  expand(other) {
    let r1 = this._range(this)
    let r2 = this._range(other)
    let start
    let end

    if (r1.start.isEqual(r2.start)) {
      start = new Coordinate(this.start.path, Math.min(this.start.offset, other.start.offset))
    } else if (r1.start.isAfter(r2.start)) {
      start = new Coordinate(other.start.path, other.start.offset)
    } else {
      start = this.start
    }
    if (r1.end.isEqual(r2.end)) {
      end = new Coordinate(this.end.path, Math.max(this.end.offset, other.end.offset))
    } else if (r1.end.isBefore(r2.end, false)) {
      end = new Coordinate(other.end.path, other.end.offset)
    } else {
      end = this.end
    }

    return _createNewSelection(this, start, end)
  }

  truncateWith(other) {
    if (other.isInsideOf(this, 'strict')) {
      // the other selection should overlap only on one side
      throw new Error('Can not truncate with a contained selections')
    }
    if (!this.overlaps(other)) {
      return this
    }
    let r1 = this._range(this)
    let r2 = this._range(other)
    let start, end
    if (r2.start.isBefore(r1.start, 'strict') && r2.end.isBefore(r1.end, 'strict')) {
      start = other.end
      end = this.end
    } else if (r1.start.isBefore(r2.start, 'strict') && r1.end.isBefore(r2.end, 'strict')) {
      start = this.start
      end = other.start
    } else if (r1.start.isEqual(r2.start)) {
      if (r2.end.isBefore(r1.end, 'strict')) {
        start = other.end
        end = this.end
      } else {
        // the other selection is larger which eliminates this one
        return Selection.nullSelection
      }
    } else if (r1.end.isEqual(r2.end)) {
      if (r1.start.isBefore(r2.start, 'strict')) {
        start = this.start
        end = other.start
      } else {
        // the other selection is larger which eliminates this one
        return Selection.nullSelection
      }
    } else if (this.isInsideOf(other)) {
      return Selection.nullSelection
    } else {
      throw new Error('Could not determine coordinates for truncate. Check input')
    }
    return _createNewSelection(this, start, end)
  }

  /**
    Get the node ids covered by this selection.

    @returns {String[]} an array of ids
  */
  getNodeIds() {
    const container = this.getContainer()
    const startPos = container.getPosition(this.start.path[0])
    const endPos = container.getPosition(this.end.path[0])
    return container.nodes.slice(startPos, endPos+1)
  }

  /**
    Splits a container selection into property selections.

    @returns {PropertySelection[]}
  */
  splitIntoPropertySelections() {
    let sels = []
    let fragments = this.getFragments()
    fragments.forEach(function(fragment) {
      if (fragment instanceof Selection.Fragment) {
        sels.push(
          new PropertySelection(fragment.path, fragment.startOffset,
            fragment.endOffset, false, this.containerId, this.surfaceId)
        )
      }
    }.bind(this))
    return sels
  }

  _clone() {
    return new ContainerSelection(this)
  }

  _range(sel) {
    // EXPERIMENTAL: caching the internal address based range
    // as we use it very often.
    // However, this is dangerous as this data can get invalid by a change
    if (sel._internal.addressRange) {
      return sel._internal.addressRange
    }

    let container = this.getContainer()
    let startAddress = container.getAddress(sel.start)
    let endAddress
    if (sel.isCollapsed()) {
      endAddress = startAddress
    } else {
      endAddress = container.getAddress(sel.end)
    }
    let addressRange = {
      start: startAddress,
      end: endAddress
    }
    if (sel._isContainerSelection) {
      sel._internal.addressRange = addressRange
    }
    return addressRange
  }

  get path() {
    throw new Error('ContainerSelection has no path property. Use startPath and endPath instead')
  }

}

ContainerSelection.prototype._isContainerSelection = true

ContainerSelection.fromJSON = function(properties) {
  let sel = new ContainerSelection(properties)
  return sel
}

function _createNewSelection(containerSel, start, end) {
  let newSel

  if (start === end) {
    newSel = new PropertySelection({
      path: start.path,
      startOffset: start.offset,
      endOffset: start.offset,
      containerId: containerSel.containerId,
      surfaceId: containerSel.surfaceId
    })
  } else {
    newSel = new ContainerSelection(containerSel.containerId,
    start.path, start.offset, end.path, end.offset, false, containerSel.surfaceId)
  }
  // we need to attach the new selection
  const doc = containerSel._internal.doc
  if (doc) {
    newSel.attach(doc)
  }
  return newSel
}

export default ContainerSelection
<|endoftext|>


<commit_before>/*
 * json-diff.c
 *
 * Copyright (C) 2014 Andreas Gruenbacher <andreas.gruenbacher@gmail.com>
 *
 * Licensed under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3,
 * http://www.fsf.org/licensing/licenses/agpl-3.0.html, or any later version.
 */

/*
 * Function json_diff(a, b)
 *
 * Create an RFC 6902 style patch between two JSON objects.
 * For example, json_diff({a: [9, 7]}, {a: [9, 8, 7]}) results in:
 * [{op: 'add', path: '/a/1', value: 8}].
 *
 * No cyclic references are allowed in a or b. Because the algorithm computes
 * differences between arrays in a way similar to the diff utility, it will
 * become slow for large arrays of complex objects.
 */
var json_diff = (function() {
  function equal(a, b) {
    if (a === b)
      return true;
    if (typeof a !== 'object' || typeof b !== 'object')
      return false;
    if (a === null || b === null)
      return false;
    if (a instanceof Array !== b instanceof Array)
      return false;
    if (a instanceof Array) {
      if (a.length != b.length)
	return false;
      for (var n = 0; n < a.length; n++)
	if (!equal(a[n], b[n]))
	  return false;
    } else {
      for (var key in a) {
	if (!(key in b) || !equal(a[key], b[key]))
	  return false;
      }
      for (var key in b) {
	if (!(key in a))
	  return false;
      }
    }
    return true;
  }

  function type(v) {
    if (typeof v === 'object') {
      if (v instanceof Array)
	return 'array';
      if (v === null)
	return 'null';
    }
    return typeof v  // 'undefined', 'string', 'number', 'boolean'
  }

  // The actual json_diff function
  return function(a, b) {
    var path = [], operations = [];

    function json_pointer(tail) {
      if (tail === undefined)
	return '/';
      else {
	path.push(tail);
	var pointer = '';
	for (var n = 0; n < path.length; n++)
	  pointer = pointer + '/' + path[n].toString().replace(/~/g, '~0').replace(/\//g, '~1');
	path.pop();
	return pointer;
      }
    }

    function result(op, tail, value) {
      var pointer = json_pointer(tail);
      if (op === 'remove')
	operations.push({op: op, path: pointer});
      else
	operations.push({op: op, path: pointer, value: value});
    }

    // Based on the Dynamic Programming Longest Common Subsequence algorithm at
    // http://rosettacode.org/wiki/Longest_common_subsequence#JavaScript
    function diffseq(a, b) {
      var i, j, m, n,
	  row = [], c = [],
	  left, diag, latch;
      var skip, edit = [];

      m = a.length;
      n = b.length;

      // ignore equal elements at both ends
      for (; m && n; m--, n--)
	if (!equal(a[m - 1], b[n - 1]))
	  break;
      for (skip = 0; m && n; skip++, m--, n--)
	if (!equal(a[skip], b[skip]))
	  break;

      if (m && n) {
	//build the c-table
	for (j = 0; j < n; row[j++] = 0);
	for (i = 0 ; i < m; i++) {
	  c[i] = row = row.slice();
	  for (diag = 0, j = 0; j < n; j++, diag = latch) {
	    latch = row[j];
	    if (equal(a[i + skip], b[j + skip]))
	      row[j] = diag + 1;
	    else {
	      left = row[j - 1] || 0;
	      if (left > row[j])
		row[j] = left;
	    }
	  }
	}
	i--, j--;

	//row[j] now contains the length of the lcs
	//compute the edit sequence from the table
	while (i > -1 && j > -1) {
	  switch (c[i][j]) {
	    default:
	      edit.unshift('=');
	      i--; j--;
	      break;
	    case j && c[i][j - 1]:
	      edit.unshift('+');
	      j--;
	      break;
	    case i && c[i - 1][j]:
	      edit.unshift('-');
	      i--;
	      break;
	  }
	}
      } else {
	i = m - 1;
	j = n - 1;
      }
      for (; j > -1; j--)
	edit.unshift('+');
      for (; i > -1; i--)
	edit.unshift('-');

      var edit_replace = [];
      for (n = 0; n < edit.length; n++) {
	if (edit[n] === '-') {
	  for (i = n + 1; i < edit.length && edit[i] === '-'; i++);
	  for (j = i; j < edit.length && edit[j] === '+'; j++);
	  if (i - n == j - i) {
	    while (i++ < j)
	      edit_replace.push('*');
	    n = j - 1;
	  } else {
	    for (; n < j; n++)
	      edit_replace.push(edit[n]);
	    n--;
	  }
	} else
	  edit_replace.push(edit[n]);
      }
      // console.log('>>> ' + skip + ' ' + edit.join('') + ' ' + edit_replace.join(''));
      edit = edit_replace;

      for (i = 0, j = 0, n = 0; n < edit.length; n++) {
	switch(edit[n]) {
	  case '*':
	    diff_recursive(a[i + skip], b[j + skip], j + skip);
	  case '=':
	    i++;
	    j++;
	    break;
	  case '-':
	    result('remove', j + skip);
	    i++;
	    break;
	  case '+':
	    result('add', j + skip, b[j + skip]);
	    j++;
	    break;
	}
      }
    }

    function diff_arrays(a, b, tail) {
      if (tail !== undefined)
	path.push(tail);
      diffseq(a, b);
      if (tail !== undefined)
	path.pop();
    }

    function diff_objects(a, b, tail) {
      if (tail !== undefined)
	path.push(tail);
      for (var key in a) {
	if (key in b)
	  diff_recursive(a[key], b[key], key);
	else
	  result('remove', key);
      }
      for (var key in b) {
	if (!(key in a))
	  result('add', key, b[key]);
      }
      if (tail !== undefined)
	path.pop();
    }

    function diff_recursive(a, b, tail) {
      if (a === b)
	return;
      var ta = type(a), tb = type(b);
      if (ta !== tb)
	result('replace', tail, b);
      else {
	switch(ta) {
	  case 'object':
	    diff_objects(a, b, tail);
	    break;
	  case 'array':
	    diff_arrays(a, b, tail);
	    break;
	  default:
	    result('replace', tail, b);
	}
      }
    }

    diff_recursive(a, b, undefined);
    return operations;
  };
})();

if (exports)
  exports.diff = json_diff;
<commit_msg>Fix check for Node.js exports object<commit_after>/*
 * json-diff.c
 *
 * Copyright (C) 2014 Andreas Gruenbacher <andreas.gruenbacher@gmail.com>
 *
 * Licensed under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3,
 * http://www.fsf.org/licensing/licenses/agpl-3.0.html, or any later version.
 */

/*
 * Function json_diff(a, b)
 *
 * Create an RFC 6902 style patch between two JSON objects.
 * For example, json_diff({a: [9, 7]}, {a: [9, 8, 7]}) results in:
 * [{op: 'add', path: '/a/1', value: 8}].
 *
 * No cyclic references are allowed in a or b. Because the algorithm computes
 * differences between arrays in a way similar to the diff utility, it will
 * become slow for large arrays of complex objects.
 */
var json_diff = (function() {
  function equal(a, b) {
    if (a === b)
      return true;
    if (typeof a !== 'object' || typeof b !== 'object')
      return false;
    if (a === null || b === null)
      return false;
    if (a instanceof Array !== b instanceof Array)
      return false;
    if (a instanceof Array) {
      if (a.length != b.length)
	return false;
      for (var n = 0; n < a.length; n++)
	if (!equal(a[n], b[n]))
	  return false;
    } else {
      for (var key in a) {
	if (!(key in b) || !equal(a[key], b[key]))
	  return false;
      }
      for (var key in b) {
	if (!(key in a))
	  return false;
      }
    }
    return true;
  }

  function type(v) {
    if (typeof v === 'object') {
      if (v instanceof Array)
	return 'array';
      if (v === null)
	return 'null';
    }
    return typeof v  // 'undefined', 'string', 'number', 'boolean'
  }

  // The actual json_diff function
  return function(a, b) {
    var path = [], operations = [];

    function json_pointer(tail) {
      if (tail === undefined)
	return '/';
      else {
	path.push(tail);
	var pointer = '';
	for (var n = 0; n < path.length; n++)
	  pointer = pointer + '/' + path[n].toString().replace(/~/g, '~0').replace(/\//g, '~1');
	path.pop();
	return pointer;
      }
    }

    function result(op, tail, value) {
      var pointer = json_pointer(tail);
      if (op === 'remove')
	operations.push({op: op, path: pointer});
      else
	operations.push({op: op, path: pointer, value: value});
    }

    // Based on the Dynamic Programming Longest Common Subsequence algorithm at
    // http://rosettacode.org/wiki/Longest_common_subsequence#JavaScript
    function diffseq(a, b) {
      var i, j, m, n,
	  row = [], c = [],
	  left, diag, latch;
      var skip, edit = [];

      m = a.length;
      n = b.length;

      // ignore equal elements at both ends
      for (; m && n; m--, n--)
	if (!equal(a[m - 1], b[n - 1]))
	  break;
      for (skip = 0; m && n; skip++, m--, n--)
	if (!equal(a[skip], b[skip]))
	  break;

      if (m && n) {
	//build the c-table
	for (j = 0; j < n; row[j++] = 0);
	for (i = 0 ; i < m; i++) {
	  c[i] = row = row.slice();
	  for (diag = 0, j = 0; j < n; j++, diag = latch) {
	    latch = row[j];
	    if (equal(a[i + skip], b[j + skip]))
	      row[j] = diag + 1;
	    else {
	      left = row[j - 1] || 0;
	      if (left > row[j])
		row[j] = left;
	    }
	  }
	}
	i--, j--;

	//row[j] now contains the length of the lcs
	//compute the edit sequence from the table
	while (i > -1 && j > -1) {
	  switch (c[i][j]) {
	    default:
	      edit.unshift('=');
	      i--; j--;
	      break;
	    case j && c[i][j - 1]:
	      edit.unshift('+');
	      j--;
	      break;
	    case i && c[i - 1][j]:
	      edit.unshift('-');
	      i--;
	      break;
	  }
	}
      } else {
	i = m - 1;
	j = n - 1;
      }
      for (; j > -1; j--)
	edit.unshift('+');
      for (; i > -1; i--)
	edit.unshift('-');

      var edit_replace = [];
      for (n = 0; n < edit.length; n++) {
	if (edit[n] === '-') {
	  for (i = n + 1; i < edit.length && edit[i] === '-'; i++);
	  for (j = i; j < edit.length && edit[j] === '+'; j++);
	  if (i - n == j - i) {
	    while (i++ < j)
	      edit_replace.push('*');
	    n = j - 1;
	  } else {
	    for (; n < j; n++)
	      edit_replace.push(edit[n]);
	    n--;
	  }
	} else
	  edit_replace.push(edit[n]);
      }
      // console.log('>>> ' + skip + ' ' + edit.join('') + ' ' + edit_replace.join(''));
      edit = edit_replace;

      for (i = 0, j = 0, n = 0; n < edit.length; n++) {
	switch(edit[n]) {
	  case '*':
	    diff_recursive(a[i + skip], b[j + skip], j + skip);
	  case '=':
	    i++;
	    j++;
	    break;
	  case '-':
	    result('remove', j + skip);
	    i++;
	    break;
	  case '+':
	    result('add', j + skip, b[j + skip]);
	    j++;
	    break;
	}
      }
    }

    function diff_arrays(a, b, tail) {
      if (tail !== undefined)
	path.push(tail);
      diffseq(a, b);
      if (tail !== undefined)
	path.pop();
    }

    function diff_objects(a, b, tail) {
      if (tail !== undefined)
	path.push(tail);
      for (var key in a) {
	if (key in b)
	  diff_recursive(a[key], b[key], key);
	else
	  result('remove', key);
      }
      for (var key in b) {
	if (!(key in a))
	  result('add', key, b[key]);
      }
      if (tail !== undefined)
	path.pop();
    }

    function diff_recursive(a, b, tail) {
      if (a === b)
	return;
      var ta = type(a), tb = type(b);
      if (ta !== tb)
	result('replace', tail, b);
      else {
	switch(ta) {
	  case 'object':
	    diff_objects(a, b, tail);
	    break;
	  case 'array':
	    diff_arrays(a, b, tail);
	    break;
	  default:
	    result('replace', tail, b);
	}
      }
    }

    diff_recursive(a, b, undefined);
    return operations;
  };
})();

if (typeof exports !== 'undefined')
  exports.diff = json_diff;
<|endoftext|>


<commit_before>//copyright Ryan Day 2010 <http://ryanday.org>, Joscha Feth 2013 <http://www.feth.com> [MIT Licensed]

var process_to_xml = function(node_data,options){

  var makeNode = function(name, content, attributes, level, hasSubNodes) {

    var indent_value = options.indent !== undefined ? options.indent : "\t";
    var indent = options.prettyPrint ? '\n' + new Array(level).join(indent_value) : '';

    var node = [indent, '<',name, (attributes || '')];
    if(content && content.length > 0) {
      node.push('>')
      node.push(content);
      hasSubNodes && node.push(indent);
      node.push('</');
      node.push(name);
      node.push('>');
    } else {
      node.push('/>');
    }
    return node.join('');
  };

  return (function fn(node_data,node_descriptor, level){
    var type = typeof node_data;
    if(node_data instanceof Array) {
      type = 'array';
    } else if(node_data instanceof Date) {
      type = 'date';
    }

    switch(type) {
    //if value is an array create child nodes from values
      case 'array':
        var ret = [];
        node_data.map(function(v){
            ret.push(fn(v,1, level+1));
            //entries that are values of an array are the only ones that can be special node descriptors
        });
        options.prettyPrint && ret.push('\n');
        return ret.join('');
        break;

      case 'date':
        // cast dates to ISO 8601 date (soap likes it)
        return node_data.toJSON?node_data.toJSON():node_data+'';
        break;

      case 'object':
        if(node_descriptor == 1 && node_data.name){
          var content = []
          , attributes = []
          ;

          if(node_data.attrs) {
            if(typeof node_data.attrs != 'object') {
            // attrs is a string, etc. - just use it as an attribute
              attributes.push(' ');
              attributes.push(node_data.attrs);
            } else {
              for(var key in node_data.attrs){
                var value = node_data.attrs[key];
                attributes.push(' ');
                attributes.push(key);
                attributes.push('="')
                attributes.push(options.escape ? esc(value) : value);
                attributes.push('"');
              }
            }
          }

          //later attributes can be added here
          if(typeof node_data.value != 'undefined') {
            var c = ''+node_data.value;
            content.push(options.escape ? esc(c) : c);
          } else if(typeof node_data.text != 'undefined') {
            var c = ''+node_data.text;
            content.push(options.escape ? esc(c) : c);
          }

          if(node_data.children){
            content.push(fn(node_data.children,0,level+1));
          }

          return makeNode(node_data.name, content.join(''), attributes.join(''),level,!!node_data.children);

        } else {
          var nodes = [];
          for(var name in node_data){
            nodes.push(makeNode(name, fn(node_data[name],0,level+1),null,level+1));
          }
          options.prettyPrint && nodes.length > 0 && nodes.push('\n');
          return nodes.join('');
        }
        break;

      case 'function':
        return node_data();
        break;

      default:
        return options.escape ? esc(node_data) : ''+node_data;
    }

  }(node_data, 0, 0))
};

var xml_header = function(standalone) {
  var ret = ['<?xml version="1.0" encoding="utf-8"'];

  if(standalone) {
    ret.push(' standalone="yes"');
  }

  ret.push('?>');

  return ret.join('');
};

module.exports = function(obj,options){

  if(typeof obj == 'string' || isBuffer(obj)) {
    try{
      obj = JSON.parse(obj.toString());
    } catch(e){
      return false;
    }
  }

  var xmlheader = '';
  var docType = '';
  if(options) {
    if(typeof options == 'object') {
      // our config is an object

      if(options.xmlHeader) {
        // the user wants an xml header
        xmlheader = xml_header(!!options.xmlHeader.standalone);
      }

      if(typeof options.docType != 'undefined') {
        docType = '<!DOCTYPE '+options.docType+'>'
      }
    } else {
      // our config is a boolean value, so just add xml header
      xmlheader = xml_header();
    }
  }
  options = options || {}

  var ret = [
    xmlheader,
    (options.prettyPrint && docType ? '\n' : ''),
  docType,
    process_to_xml(obj,options)
  ];

  return ret.join('');
}

module.exports.json_to_xml=
module.exports.obj_to_xml = module.exports;

module.exports.escape = esc;

function esc(str){
  return (''+str).replace(/&/g, '&')
      .replace(/</g, '<')
      .replace(/>/g, '>')
      .replace(/"/g, '"');
}

module.exports.cdata = cdata;

function cdata(str){
  if(str) return "<![CDATA["+str.replace(/]]>/g,'')+']]>';
  return "<![CDATA[]]>";
};

function isBuffer(b){
  if(typeof Buffer == 'undefined') return false;
  return (b instanceof Buffer);
}

<commit_msg>reverted buffer check to the way I had it before which works with couchdb (spidermonkey)<commit_after>//copyright Ryan Day 2010 <http://ryanday.org>, Joscha Feth 2013 <http://www.feth.com> [MIT Licensed]

var process_to_xml = function(node_data,options){

  var makeNode = function(name, content, attributes, level, hasSubNodes) {

    var indent_value = options.indent !== undefined ? options.indent : "\t";
    var indent = options.prettyPrint ? '\n' + new Array(level).join(indent_value) : '';

    var node = [indent, '<',name, (attributes || '')];
    if(content && content.length > 0) {
      node.push('>')
      node.push(content);
      hasSubNodes && node.push(indent);
      node.push('</');
      node.push(name);
      node.push('>');
    } else {
      node.push('/>');
    }
    return node.join('');
  };

  return (function fn(node_data,node_descriptor, level){
    var type = typeof node_data;
    if(node_data instanceof Array) {
      type = 'array';
    } else if(node_data instanceof Date) {
      type = 'date';
    }

    switch(type) {
    //if value is an array create child nodes from values
      case 'array':
        var ret = [];
        node_data.map(function(v){
            ret.push(fn(v,1, level+1));
            //entries that are values of an array are the only ones that can be special node descriptors
        });
        options.prettyPrint && ret.push('\n');
        return ret.join('');
        break;

      case 'date':
        // cast dates to ISO 8601 date (soap likes it)
        return node_data.toJSON?node_data.toJSON():node_data+'';
        break;

      case 'object':
        if(node_descriptor == 1 && node_data.name){
          var content = []
          , attributes = []
          ;

          if(node_data.attrs) {
            if(typeof node_data.attrs != 'object') {
            // attrs is a string, etc. - just use it as an attribute
              attributes.push(' ');
              attributes.push(node_data.attrs);
            } else {
              for(var key in node_data.attrs){
                var value = node_data.attrs[key];
                attributes.push(' ');
                attributes.push(key);
                attributes.push('="')
                attributes.push(options.escape ? esc(value) : value);
                attributes.push('"');
              }
            }
          }

          //later attributes can be added here
          if(typeof node_data.value != 'undefined') {
            var c = ''+node_data.value;
            content.push(options.escape ? esc(c) : c);
          } else if(typeof node_data.text != 'undefined') {
            var c = ''+node_data.text;
            content.push(options.escape ? esc(c) : c);
          }

          if(node_data.children){
            content.push(fn(node_data.children,0,level+1));
          }

          return makeNode(node_data.name, content.join(''), attributes.join(''),level,!!node_data.children);

        } else {
          var nodes = [];
          for(var name in node_data){
            nodes.push(makeNode(name, fn(node_data[name],0,level+1),null,level+1));
          }
          options.prettyPrint && nodes.length > 0 && nodes.push('\n');
          return nodes.join('');
        }
        break;

      case 'function':
        return node_data();
        break;

      default:
        return options.escape ? esc(node_data) : ''+node_data;
    }

  }(node_data, 0, 0))
};

var xml_header = function(standalone) {
  var ret = ['<?xml version="1.0" encoding="utf-8"'];

  if(standalone) {
    ret.push(' standalone="yes"');
  }

  ret.push('?>');

  return ret.join('');
};

module.exports = function(obj,options){

  var Buffer = this.Buffer || function Buffer () {};

  if(typeof obj == 'string' || obj instanceof Buffer) {
    try{
      obj = JSON.parse(obj.toString());
    } catch(e){
      return false;
    }
  }

  var xmlheader = '';
  var docType = '';
  if(options) {
    if(typeof options == 'object') {
      // our config is an object

      if(options.xmlHeader) {
        // the user wants an xml header
        xmlheader = xml_header(!!options.xmlHeader.standalone);
      }

      if(typeof options.docType != 'undefined') {
        docType = '<!DOCTYPE '+options.docType+'>'
      }
    } else {
      // our config is a boolean value, so just add xml header
      xmlheader = xml_header();
    }
  }
  options = options || {}

  var ret = [
    xmlheader,
    (options.prettyPrint && docType ? '\n' : ''),
  docType,
    process_to_xml(obj,options)
  ];

  return ret.join('');
}

module.exports.json_to_xml=
module.exports.obj_to_xml = module.exports;

module.exports.escape = esc;

function esc(str){
  return (''+str).replace(/&/g, '&')
      .replace(/</g, '<')
      .replace(/>/g, '>')
      .replace(/"/g, '"');
}

module.exports.cdata = cdata;

function cdata(str){
  if(str) return "<![CDATA["+str.replace(/]]>/g,'')+']]>';
  return "<![CDATA[]]>";
};
<|endoftext|>


<commit_before>/*
 * kakahiaka.js
 *
 * 2013 Minori Yamashita <ympbyc@gmail.com>
 */

var kakahiaka = (function () {
    /*** private ***/

    function reset_BANG_ (app, state) {
        app._state = state;
        return app;
    }

    function get_watcher (app, key) {
        return app._watchers[key];
    }

    function add_watch (app, key, f) {
        app._watchers[key] = f;
        return app;
    }

    /*** public ***/

    /**
     * app :: state * (state -> IO ()) * (() -> state) -> App
     */
    function app (x, persist, recover) {
        return { _state: recover ? _.conj(x, recover()) : x
               , _watchers: {}
               , _persist: persist };
    }

    function deref (app) {
        return app._state;
    }

    /**
     * deftransition :: ({state} * ... -> {patch}) -> (App * ... -> undefined)
     */
    function deftransition (f) {
        return _.optarg(function (app, args) {
            var old_s = deref(app);
            var diff  = _.apply(_.partial(f, old_s), args);
            if ( ! diff) return undefined;
            var new_s = _.merge(old_s, diff);
            _.each(diff, function (__, key_changed) {
                var watcher = get_watcher(app, key_changed);
                if (watcher)
                    setTimeout(function () {
                        watcher(new_s, old_s);
                    }, 0);
            });
            reset_BANG_(app, new_s);
            app._persist(new_s);
            return undefined;
        });
    }

    /**
     * watch_transition :: App * String * ({new state} * {old state} -> undefined) -> undefined
     */
    var watch_transition = add_watch;

    return {
        app:              app,
        deref:            deref,
        deftransition:    deftransition,
        watch_transition: watch_transition
    };
}());
<commit_msg>add most often used transition -- simple_update<commit_after>/*
 * kakahiaka.js
 *
 * 2013 Minori Yamashita <ympbyc@gmail.com>
 */

var kakahiaka = (function () {
    /*** private ***/

    function reset_BANG_ (app, state) {
        app._state = state;
        return app;
    }

    function get_watcher (app, key) {
        return app._watchers[key];
    }

    function add_watch (app, key, f) {
        app._watchers[key] = f;
        return app;
    }

    /*** public ***/

    /**
     * app :: state * (state -> IO ()) * (() -> state) -> App
     */
    function app (x, persist, recover) {
        return { _state: recover ? _.conj(x, recover()) : x
               , _watchers: {}
               , _persist: persist };
    }

    function deref (app) {
        return app._state;
    }

    /**
     * deftransition :: ({state} * ... -> {patch}) -> (App * ... -> undefined)
     */
    function deftransition (f) {
        return _.optarg(function (app, args) {
            var old_s = deref(app);
            var diff  = _.apply(_.partial(f, old_s), args);
            if ( ! diff) return undefined;
            var new_s = _.merge(old_s, diff);
            _.each(diff, function (__, key_changed) {
                var watcher = get_watcher(app, key_changed);
                if (watcher)
                    setTimeout(function () {
                        watcher(new_s, old_s);
                    }, 0);
            });
            reset_BANG_(app, new_s);
            app._persist(new_s);
            return undefined;
        });
    }

    /**
     * watch_transition :: App * String * ({new state} * {old state} -> undefined) -> undefined
     */
    var watch_transition = add_watch;

    /**
     * simple_update :: App * String * Any -> undefined
     */
    var simple_update = K.deftransition(function (state, field, value) {
        return _.assoc({}, field, value);
    });

    return {
        app:              app,
        deref:            deref,
        deftransition:    deftransition,
        watch_transition: watch_transition,
        simple_update:    simple_update
    };
}());
<|endoftext|>


<commit_before>/* globals hopscotch: false */

/* ============ */
/* EXAMPLE TOUR */
/* ============ */
var tour = {
  id: 'hello-spark-on-bluemix',
  steps: [
    {
      target: document.querySelectorAll('.data-nav-title')[0],
      title: 'Welcome to the IBM Bluemix Spark-as-a-Service tl;dr',
      content: 'Hey there!  IBM jStart is honored to be a partner in your journey with Apache Spark, Jupyter Notebooks and OpenStack Object Storage. There\'s plenty of time to read doc and sample code, but join us first on this quick feature fly-by!',
      placement: 'bottom',
      arrowOffset: 60
    },
    {
      target: ['instances', 'all-instances'],
      title: 'Apache Spark Instances',
      content: 'A multi-tenant Spark cluster means up to 10 separate instances just for you.  As you create \'em, find them here',
      placement: 'bottom',
      arrowOffset: 60
    },
    {
      target: ['storage'],
      title: 'Object Storage',
      content: 'Jupyter (iPython) notebooks need data from somewhere.  Here\'s your goto spot! A tab that shows you OpenStack Object Storage instances',
      placement: 'bottom',
      arrowOffset: 60
    },
    {
      target: ['notebooks', 'all-notebooks'],
      title: 'Jupyter Notebooks',
      content: 'Did you notice this tab to show you all of your Jupyter (iPython) notebooks?',
      placement: 'bottom',
      arrowOffset: 60
    },
    {
      target: document.querySelectorAll('.data-page-icon.services')[0],
      title: 'Services',
      content: 'Look here! Here\'s a view to explore getting data into your services',
      placement: 'right',
      arrowOffset: 30
    },
    {
      target: document.querySelectorAll('.data-page-icon.data')[0],
      title: 'Data',
      content: 'Got data sources? View and Edit connections for them here',
      placement: 'right',
      arrowOffset: 30
    },
    {
      target: document.querySelectorAll('.data-page-icon.analytics')[0],
      title: 'Analytics',
      content: 'Almost done, this is homebase for Spark using notebooks',
      placement: 'right',
      arrowOffset: 30
    },
    {
      target: document.querySelectorAll('.data-page-icon.exchange')[0],
      title: 'Exchange',
      content: 'Behind Door #3, check out 200+ Datasets via the Analytics Exchange.',
      placement: 'right',
      arrowOffset: 30
    },
    {
      target: document.querySelectorAll('.notebooks-desc')[0],
      title: 'Precipitation Analysis',
      content: 'Rain, rain go away - check out the Precipitation Analysis notebook for a simple starter',
      placement: 'top',
      arrowOffset: 30
    },
  ],
  showPrevButton: true,
  scrollTopMargin: 100
},

/* ========== */
/* TOUR SETUP */
/* ========== */
addClickListener = function(el, fn) {
  if (el.addEventListener) {
    el.addEventListener('click', fn, false);
  }
  else {
    el.attachEvent('onclick', fn);
  }
},

init = function() {
  var startBtnId = 'startTourBtn',
      calloutId = 'startTourCallout',
      mgr = hopscotch.getCalloutManager(),
      state = hopscotch.getState();

  if (state && state.indexOf('hello-hopscotch:') === 0) {
    // Already started the tour at some point!
    hopscotch.startTour(tour);
  }
  else {
    // Looking at the page for the first(?) time.
    setTimeout(function() {
      mgr.createCallout({
        id: calloutId,
        target: startBtnId,
        placement: 'left',
        title: 'Friend, interested in a tour?',
        content: 'We shall not cease from exploration
And the end of all our exploring
Will be to arrive where we started
And know the place for the first time.
∞T.S. Eliot∞',
        yOffset: -25,
        arrowOffset: 20,
        width: 240
      });
    }, 100);
  }

  addClickListener(document.getElementById(startBtnId), function() {
    if (!hopscotch.isActive) {
      mgr.removeAllCallouts();
      hopscotch.startTour(tour);
    }
  });
};

init();
<commit_msg>Updated to use cookie-based run-once approach<commit_after>/* globals hopscotch: false */
/*
 * Elements courtesy of https://github.com/gthomas3
 * https://github.com/linkedin/hopscotch/issues/80#issuecomment-48330396
*/
function setCookie(key, value) {
    var expires = new Date();
    expires.setTime(expires.getTime() + (1 * 24 * 60 * 60 * 1000));
    document.cookie = key + '=' + value + ';path=/' + ';expires=' + expires.toUTCString();
};

function getCookie(key) {
    var keyValue = document.cookie.match('(^|;) ?' + key + '=([^;]*)(;|$)');
    return keyValue ? keyValue[2] : null;
};

var tour = {
  id: 'hello-spark-on-bluemix',
  steps: [
    {
      target: document.querySelectorAll('.data-nav-title')[0],
      title: 'Welcome to the IBM Bluemix Spark-as-a-Service tl;dr',
      content: 'Hey there!  IBM jStart is honored to be a partner in your journey with Apache Spark, Jupyter Notebooks and OpenStack Object Storage. There\'s plenty of time to read doc and sample code, but join us first on this quick feature fly-by!',
      placement: 'bottom',
      arrowOffset: 60
    },
    {
      target: ['instances', 'all-instances'],
      title: 'Apache Spark Instances',
      content: 'A multi-tenant Spark cluster means up to 10 separate instances just for you.  As you create \'em, find them here',
      placement: 'bottom',
      arrowOffset: 60
    },
    {
      target: ['storage'],
      title: 'Object Storage',
      content: 'Jupyter (iPython) notebooks need data from somewhere.  Here\'s your goto spot! A tab that shows you OpenStack Object Storage instances',
      placement: 'bottom',
      arrowOffset: 60
    },
    {
      target: ['notebooks', 'all-notebooks'],
      title: 'Jupyter Notebooks',
      content: 'Did you notice this tab to show you all of your Jupyter (iPython) notebooks?',
      placement: 'bottom',
      arrowOffset: 60
    },
    {
      target: document.querySelectorAll('.data-page-icon.services')[0],
      title: 'Services',
      content: 'Look here! Here\'s a view to explore getting data into your services',
      placement: 'right',
      arrowOffset: 30
    },
    {
      target: document.querySelectorAll('.data-page-icon.data')[0],
      title: 'Data',
      content: 'Got data sources? View and Edit connections for them here',
      placement: 'right',
      arrowOffset: 30
    },
    {
      target: document.querySelectorAll('.data-page-icon.analytics')[0],
      title: 'Analytics',
      content: 'Almost done, this is homebase for Spark using notebooks',
      placement: 'right',
      arrowOffset: 30
    },
    {
      target: document.querySelectorAll('.data-page-icon.exchange')[0],
      title: 'Exchange',
      content: 'Behind Door #3, check out 200+ Datasets via the Analytics Exchange.',
      placement: 'right',
      arrowOffset: 30
    },
    {
      target: document.querySelectorAll('.notebooks-desc')[0],
      title: 'Precipitation Analysis',
      content: 'Rain, rain go away - check out the Precipitation Analysis notebook for a simple starter',
      placement: 'top',
      arrowOffset: 30
    },
  ],
  showPrevButton: true,
  scrollTopMargin: 100,
  onEnd: function() {
      setCookie("toured", "toured");
  },
  onClose: function() {
      setCookie("toured", "toured");
  }
},

/* ========== */
/* TOUR SETUP */
/* ========== */
addClickListener = function(el, fn) {
  if (el.addEventListener) {
    el.addEventListener('click', fn, false);
  }
  else {
    el.attachEvent('onclick', fn);
  }
},

init = function() {
  var startBtnId = 'startTourBtn',
      calloutId = 'startTourCallout',
      mgr = hopscotch.getCalloutManager(),
      state = hopscotch.getState();

  if (state && state.indexOf('hello-hopscotch:') === 0) {
    // Already started the tour at some point!
    // Initialize tour if it's the user's first time
    if (!getCookie("toured")) {
      hopscotch.startTour(tour);
    }
  }
  else {
    // Looking at the page for the first(?) time.
    setTimeout(function() {
      mgr.createCallout({
        id: calloutId,
        target: startBtnId,
        placement: 'left',
        title: 'Friend, interested in a tour?',
        content: 'We shall not cease from exploration
And the end of all our exploring
Will be to arrive where we started
And know the place for the first time.
∞T.S. Eliot∞',
        yOffset: -25,
        arrowOffset: 20,
        width: 240
      });
    }, 100);
  }

  addClickListener(document.getElementById(startBtnId), function() {
    if (!hopscotch.isActive) {
      mgr.removeAllCallouts();
      if (!getCookie("toured")) {
        hopscotch.startTour(tour);
      }
    }
  });
};

init();
<|endoftext|>


<commit_before>displayOutput = function(completedGame) {
  var output = {};

  output.rounds = completedGame.rounds;

  // get players info
  output.players = [];
  for( var pi = 0; pi < completedGame.players.length; pi++) {
    var thisPlayer = {};
    var thisPlayerFromGame = completedGame.players[pi];
    thisPlayer.money = thisPlayerFromGame.money;
    thisPlayer.targetExpensive = thisPlayerFromGame.targetExpensive;

    // sum purchased establishments
    var purchases = {};
    for( var e = 0; e < thisPlayerFromGame.establishments.length; e++) {
      var thisEstablishmentName = thisPlayerFromGame.establishments[e].Name;
      if (!purchases[thisEstablishmentName]) {
        purchases[thisEstablishmentName] = 1;
      } else {
        purchases[thisEstablishmentName] = purchases[thisEstablishmentName] + 1;
      }
    }
    thisPlayer.purchases = purchases;

    // sum purchased landmarks
    var landmarks = [];
    for( var l = 0; l < thisPlayerFromGame.landmarks.length; l++) {
      if (thisPlayerFromGame.landmarks[l].Active) {
        landmarks.push(thisPlayerFromGame.landmarks[l].Name);
      }
    }
    thisPlayer.purchasedLandmarks = landmarks;

    output.players.push(thisPlayer);
  }
  return output;
}

module.exports.displayOutput = displayOutput;

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

getBuiltLandmarkCount = function(player) {
    var count = 0;
    console.log("player posture: " + player.targetExpensive);
    for( var i = 0; i < player.landmarks.length; i++) {
        console.log("active:" + player.landmarks[i].Active);
        if (player.landmarks[i].Active) {
            count++;
        }
    }
    console.log("active landmark count:" + count);
    return count;
}

formatResultDetails = function(completedGame) {
  var output = {};
  output.winner = {};
  output.loserColorRedCount = 0;
  output.loserColorGreenCount = 0;
  output.loserColorBlueCount = 0;
  output.loserThriftyCount = 0;
  output.loserAggressiveCount = 0;

  output.rounds = completedGame.rounds;

  for( var pi = 0; pi < completedGame.players.length; pi++) {
      var activeLandmarkCount = getBuiltLandmarkCount(completedGame.players[pi]);
      console.log("active landmark count: " + activeLandmarkCount);
      if (activeLandmarkCount === 4) {
          // the winner
          var winner = completedGame.players[pi];
          console.log("winner id:" + winner.name)
          console.log("winner is aggressive: " + winner.targetExpensive);
          output.winner.spendingStyle = (winner.targetExpensive ? "Aggressive" : "Thrifty");
          output.winner.colorStyle = winner.colorStyle;
      } else {
          // a loser
          if (completedGame.players[pi].colorStyle == "Red") {
              output.loserColorRedCount++;
          } else if (completedGame.players[pi].colorStyle == "Green") {
              output.loserColorGreenCount++;
          } else if (completedGame.players[pi].colorStyle == "Blue") {
              output.loserColorBlueCount++;
          }

          if (completedGame.players[pi].targetExpensive) {
              output.loserAggressiveCount++;
          } else {
              output.loserThriftyCount;
          }
      }
  }
  return output;
}

module.exports.formatResultDetails = formatResultDetails;

<commit_msg>trivial change to confirm git checkin works even after changing name<commit_after>displayOutput = function(completedGame) {
  var output = {};

  output.rounds = completedGame.rounds;

  // get players info
  output.players = [];
  for( var pi = 0; pi < completedGame.players.length; pi++) {
    var thisPlayer = {};
    var thisPlayerFromGame = completedGame.players[pi];
    thisPlayer.money = thisPlayerFromGame.money;
    thisPlayer.targetExpensive = thisPlayerFromGame.targetExpensive;

    // sum purchased establishments
    var purchases = {};
    for( var e = 0; e < thisPlayerFromGame.establishments.length; e++) {
      var thisEstablishmentName = thisPlayerFromGame.establishments[e].Name;
      if (!purchases[thisEstablishmentName]) {
        purchases[thisEstablishmentName] = 1;
      } else {
        purchases[thisEstablishmentName] = purchases[thisEstablishmentName] + 1;
      }
    }
    thisPlayer.purchases = purchases;

    // sum purchased landmarks
    var landmarks = [];
    for( var l = 0; l < thisPlayerFromGame.landmarks.length; l++) {
      if (thisPlayerFromGame.landmarks[l].Active) {
        landmarks.push(thisPlayerFromGame.landmarks[l].Name);
      }
    }
    thisPlayer.purchasedLandmarks = landmarks;

    output.players.push(thisPlayer);
  }
  return output;
}

module.exports.displayOutput = displayOutput;

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

getBuiltLandmarkCount = function(player) {
    var count = 0;
    console.log("player posture: " + player.targetExpensive);
    for( var i = 0; i < player.landmarks.length; i++) {
        console.log("active:" + player.landmarks[i].Active);
        if (player.landmarks[i].Active) {
            count++;
        }
    }
    console.log("active landmark count:" + count);
    return count;
}

formatResultDetails = function(completedGame) {
  var output = {};
  output.winner = {};
  output.loserColorRedCount = 0;
  output.loserColorGreenCount = 0;
  output.loserColorBlueCount = 0;
  output.loserThriftyCount = 0;
  output.loserAggressiveCount = 0;

  output.rounds = completedGame.rounds;

  for( var pi = 0; pi < completedGame.players.length; pi++) {
      var activeLandmarkCount = getBuiltLandmarkCount(completedGame.players[pi]);
      console.log("active landmark count: " + activeLandmarkCount);
      if (activeLandmarkCount === 4) {
          // the winner
          var winner = completedGame.players[pi];
          console.log("winner id:" + winner.name)
          console.log("winner is aggressive: " + winner.targetExpensive);
          output.winner.spendingStyle = (winner.targetExpensive ? "Aggressive" : "Thrifty");
          output.winner.colorStyle = winner.colorStyle;
      } else {
          // a loser
          if (completedGame.players[pi].colorStyle == "Red") {
              output.loserColorRedCount++;
          } else if (completedGame.players[pi].colorStyle == "Green") {
              output.loserColorGreenCount++;
          } else if (completedGame.players[pi].colorStyle == "Blue") {
              output.loserColorBlueCount++;
          }

          if (completedGame.players[pi].targetExpensive) {
              output.loserAggressiveCount++;
          } else {
              output.loserThriftyCount;
          }
      }
  }
  return output;
}

module.exports.formatResultDetails = formatResultDetails;

<|endoftext|>


<commit_before>/*!
 * Copyright 2014 Apereo Foundation (AF) Licensed under the
 * Educational Community License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may
 * obtain a copy of the License at
 *
 *     http://opensource.org/licenses/ECL-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an "AS IS"
 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

var _ = require('underscore');
var util = require('util');

var AuthzUtil = require('oae-authz/lib/util');
var TenantsAPI = require('oae-tenants');

/**
 * The User model.
 *
 * @param  {String}     tenantAlias                     The tenant this user belongs to.
 * @param  {String}     id                              The globally unique userId for this user. e.g.: u:cam:johndoe
 * @param  {String}     displayName                     The display name of the user
 * @param  {String}     email                           The email address of the user
 * @param  {Object}     [opts]                          Optional additional user properties
 * @param  {String}     [opts.visibility]               The visibility of this user account. e.g.: loggedin
 * @param  {Date}       [opts.deleted]                  The date and time the user was deleted, if deleted
 * @param  {String}     [opts.locale]                   The user's locale
 * @param  {String}     [opts.publicAlias]              The name of the user which is displayed to a user who does not have access to view the user
 * @param  {String}     [opts.smallPictureUri]          The uri of the small picture. It will be made available at user.picture.smallUri
 * @param  {String}     [opts.mediumPictureUri]         The uri of the medium picture. It will be made available at user.picture.mediumUri
 * @param  {String}     [opts.largePictureUri]          The uri of the large picture. It will be made available at user.picture.largeUri
 * @param  {Number}     [opts.notificationsUnread]      The number of notifications that are unread for user
 * @param  {Number}     [opts.notificationsLastRead]    The last time, in millis since the epoc, the notifications for the user were read
 * @param  {Date}       [opts.acceptedTC]               The timestamp at which the user accepted the Terms and Conditions
 * @param  {Date}       [opts.lastModified]             The timestamp at which the user's profile was last changed
 * @param  {String}     [opts.emailPreference]          The user's email preference. One of {@see PrincipalConstants.emailPreferences}
 * @param  {Boolean}    [opts.isGlobalAdmin]            Whether or not the user is a global admin
 * @param  {Boolean}    [opts.isTenantAdmin]            Whether or not the user is a tenant admin
 */
module.exports.User = function(tenantAlias, id, displayName, email, opts) {
    var TenantsAPI = TenantsAPI || require('oae-tenants');
    opts = opts || {};

    // Explicit checks on true for admin.
    var _isGlobalAdmin = (opts.isGlobalAdmin === true);
    var _isTenantAdmin = (opts.isTenantAdmin === true);

    var tenant = TenantsAPI.getTenant(tenantAlias);
    var resourceId = AuthzUtil.getResourceFromId(id).resourceId;

    var that = {};
    that.tenant = tenant.compact();
    that.id = id;
    that.displayName = displayName;
    that.email = email;
    that.visibility = opts.visibility;
    that.deleted = opts.deleted;
    that.locale = opts.locale;
    that.publicAlias = opts.publicAlias;
    that.profilePath = util.format('/user/%s/%s', tenantAlias, resourceId);
    that.resourceType = 'user';
    that.notificationsUnread = opts.notificationsUnread;
    that.notificationsLastRead = opts.notificationsLastRead;
    that.acceptedTC = opts.acceptedTC;
    that.lastModified = opts.lastModified;
    that.emailPreference = opts.emailPreference;
    that.picture = _.oaeExtendDefined({}, {
        'smallUri': opts.smallPictureUri,
        'mediumUri': opts.mediumPictureUri,
        'largeUri': opts.largePictureUri
    });

    /**
     * Check if a user is a global admin
     *
     * @return {Boolean} Whether or not this user is a global admin.
     */
    that.isGlobalAdmin = function() {
        return _isGlobalAdmin;
    };

    /**
     * Whether or not this user is a tenant admin for the provided tenant.
     *
     * @param  {String}  tenantAlias    The tenant this user is supposed to be an admin of.
     * @return {Boolean}                Whether or not the user is a tenant admin.
     */
    that.isTenantAdmin = function(tenantAlias) {
        return (_isTenantAdmin && tenantAlias === that.tenant.alias);
    };

    /**
     * Checks for both tenant admin as global admin.
     *
     * @param  {Object}  tenantAlias    The tenant this user could a tenant admin of.
     * @return {Boolean}                Whether this user is a tenant or global admin.
     */
    that.isAdmin = function(tenantAlias) {
        return (that.isTenantAdmin(tenantAlias) || that.isGlobalAdmin());
    };

    return that;
};
<commit_msg>Removed extra statement<commit_after>/*!
 * Copyright 2014 Apereo Foundation (AF) Licensed under the
 * Educational Community License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may
 * obtain a copy of the License at
 *
 *     http://opensource.org/licenses/ECL-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an "AS IS"
 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

var _ = require('underscore');
var util = require('util');

var AuthzUtil = require('oae-authz/lib/util');
var TenantsAPI = require('oae-tenants');

/**
 * The User model.
 *
 * @param  {String}     tenantAlias                     The tenant this user belongs to.
 * @param  {String}     id                              The globally unique userId for this user. e.g.: u:cam:johndoe
 * @param  {String}     displayName                     The display name of the user
 * @param  {String}     email                           The email address of the user
 * @param  {Object}     [opts]                          Optional additional user properties
 * @param  {String}     [opts.visibility]               The visibility of this user account. e.g.: loggedin
 * @param  {Date}       [opts.deleted]                  The date and time the user was deleted, if deleted
 * @param  {String}     [opts.locale]                   The user's locale
 * @param  {String}     [opts.publicAlias]              The name of the user which is displayed to a user who does not have access to view the user
 * @param  {String}     [opts.smallPictureUri]          The uri of the small picture. It will be made available at user.picture.smallUri
 * @param  {String}     [opts.mediumPictureUri]         The uri of the medium picture. It will be made available at user.picture.mediumUri
 * @param  {String}     [opts.largePictureUri]          The uri of the large picture. It will be made available at user.picture.largeUri
 * @param  {Number}     [opts.notificationsUnread]      The number of notifications that are unread for user
 * @param  {Number}     [opts.notificationsLastRead]    The last time, in millis since the epoc, the notifications for the user were read
 * @param  {Date}       [opts.acceptedTC]               The timestamp at which the user accepted the Terms and Conditions
 * @param  {Date}       [opts.lastModified]             The timestamp at which the user's profile was last changed
 * @param  {String}     [opts.emailPreference]          The user's email preference. One of {@see PrincipalConstants.emailPreferences}
 * @param  {Boolean}    [opts.isGlobalAdmin]            Whether or not the user is a global admin
 * @param  {Boolean}    [opts.isTenantAdmin]            Whether or not the user is a tenant admin
 */
module.exports.User = function(tenantAlias, id, displayName, email, opts) {
    opts = opts || {};

    // Explicit checks on true for admin.
    var _isGlobalAdmin = (opts.isGlobalAdmin === true);
    var _isTenantAdmin = (opts.isTenantAdmin === true);

    var tenant = TenantsAPI.getTenant(tenantAlias);
    var resourceId = AuthzUtil.getResourceFromId(id).resourceId;

    var that = {};
    that.tenant = tenant.compact();
    that.id = id;
    that.displayName = displayName;
    that.email = email;
    that.visibility = opts.visibility;
    that.deleted = opts.deleted;
    that.locale = opts.locale;
    that.publicAlias = opts.publicAlias;
    that.profilePath = util.format('/user/%s/%s', tenantAlias, resourceId);
    that.resourceType = 'user';
    that.notificationsUnread = opts.notificationsUnread;
    that.notificationsLastRead = opts.notificationsLastRead;
    that.acceptedTC = opts.acceptedTC;
    that.lastModified = opts.lastModified;
    that.emailPreference = opts.emailPreference;
    that.picture = _.oaeExtendDefined({}, {
        'smallUri': opts.smallPictureUri,
        'mediumUri': opts.mediumPictureUri,
        'largeUri': opts.largePictureUri
    });

    /**
     * Check if a user is a global admin
     *
     * @return {Boolean} Whether or not this user is a global admin.
     */
    that.isGlobalAdmin = function() {
        return _isGlobalAdmin;
    };

    /**
     * Whether or not this user is a tenant admin for the provided tenant.
     *
     * @param  {String}  tenantAlias    The tenant this user is supposed to be an admin of.
     * @return {Boolean}                Whether or not the user is a tenant admin.
     */
    that.isTenantAdmin = function(tenantAlias) {
        return (_isTenantAdmin && tenantAlias === that.tenant.alias);
    };

    /**
     * Checks for both tenant admin as global admin.
     *
     * @param  {Object}  tenantAlias    The tenant this user could a tenant admin of.
     * @return {Boolean}                Whether this user is a tenant or global admin.
     */
    that.isAdmin = function(tenantAlias) {
        return (that.isTenantAdmin(tenantAlias) || that.isGlobalAdmin());
    };

    return that;
};
<|endoftext|>


<commit_before>// Meta package for the HomA framework
// 2013 Alexander Rust <mail@alr.st>
// Use it as you like if you find id usefull

var util = require('util');
var events = require('events');
var mqtt = require('mqtt');
var schedule = require('node-schedule');
var log = require('npmlog')
log.disableColor();
var global = this;
var params = require('optimist').describe("brokerHost", "The MQTT broker's hostname or IP adress. Can also be set via ENV HOMA_BROKER_HOST").describe("brokerPort", "The MQTT broker's port. Can also be set via ENV HOMA_BROKER_PORT").describe("systemId", "The unique client ID that determines where settings on the /sys topic are received");
		params = process.env.HOMA_BROKER_HOST ? params.default("brokerHost", process.env.HOMA_BROKER_HOST) : params.demand("brokerHost");
		params = process.env.HOMA_BROKER_PORT ? params.default("brokerPort", process.env.HOMA_BROKER_PORT) : params.default("brokerPort", 1883);

// finalizes parameters and sets default systemId
module.exports.paramsWithDefaultSystemId = function(systemd){
	params = params.default("systemId", systemd).argv;
	return params.systemId;
}

var StringHelper = function() {
	this.pad = function(n, width, symbol, back) {
	  symbol = symbol || '0';
	  n = n + '';

	  if (n.length >= width)
	  	return n;
	  else
	  	return back || false ? n + new Array(width - n.length + 1).join(symbol) : new Array(width - n.length + 1).join(symbol) + n;
	}
}

var SettingsHelper = function() {
	var self = this;
	self.settings = {}; // key is full topic string of the form /sys/$deviceId/someParameter
	self.locked = false;
	self.requiredSettings = [];
	self.optionalSettings = [];

	this.keyToSysTopic = function(key) {
		return "/sys/"+params.systemId+"/"  + key;
	}

	// Check if a required or optional settings value was received from the MQTT broker. If so, save it for later.
	// force allows to store key:value pairs even if they are not required
	// topic is a complete topic string of the form /sys/$deviceId/someParameter
	this.insert = function(topic, value, force){
		if(force || (self.requiredSettings.indexOf(topic) > -1) || self.optionalSettings.indexOf(topic) > -1)
			self.settings[topic] = value;
	}

	// Saves value for a parameter to the MQTT /sys topic for the current clientId
	this.save = function(parameter, value){
			global.mqttHelper.publish(self.keyToSysTopic(parameter), value, true);
			self.settings[self.keyToSysTopic(parameter)] = value;
	}

	// parameter is in the form someParameter, without /sys topic string
	this.require = function(parameter){
		self.requiredSettings.push(self.keyToSysTopic(parameter));
		global.mqttHelper.subscribe(self.keyToSysTopic(parameter));
	}

	this.optional = function(parameter){
		self.optionalSettings.push(self.keyToSysTopic(parameter));
		global.mqttHelper.subscribe(self.keyToSysTopic(parameter));
	}

	// parameter is in the form someParameter, without /sys topic string
	this.get = function(parameter, inputAsSysTopicStr) {
		return inputAsSysTopicStr ? self.settings[parameter] : self.settings[self.keyToSysTopic(parameter)];
	}

	this.isBootstrapCompleted = function() {
		var pass = true
		for(i=0;i<self.requiredSettings.length;i++){
			if(self.get(self.requiredSettings[i], true) == undefined || self.get(self.requiredSettings[i], true) == ""){
				log.info("SETTINGS", "Waiting to receive: " + self.requiredSettings[i]);
				pass = false;
			}
		}
		if(pass) {
			log.info("SETTINGS", "Bootstrap completed");
		}
		return pass;
	}
	this.isLocked = function(){
		return self.locked;
	}
	this.lock = function() {
		self.locked = true;
	}
	this.unlock = function() {
		self.locked = false;
	}
}

var MqttHelper = function() {
	var self = this;
	self.mqttClient;
	self.scheduledPublishes = [];

	this.connect = function(host, port, extraParameters) {
		self.mqttClient = mqtt.createClient(port || params.brokerPort, host || params.brokerHost, extraParameters);
		log.info("MQTT", "Connecting to %s:%s", host || params.brokerHost, port || params.brokerPort);

	  self.mqttClient.on('connect', function() {
         self.emit('connect');
 	  });

	  self.mqttClient.on('close', function() {
	  	log.info("MQTT", "Connection closed");
	    process.exit(-1);
	  });

	  self.mqttClient.on('error', function(e) {
	  	log.error("MQTT", "Error: %s", e);
	    process.exit(-1);
	  });

	 	self.mqttClient.on('message', function(topic, message, etc) {
	 		self.emit('message', {topic: topic, payload: message, qos: etc.qos, retain: etc.retain, messageId: etc.messageId });
		});
	}

	this.publish = function(topic, payload, retained) {
		log.info("MQTT", "Publishing %s:%s (retained=%s)", topic, payload, retained);
		self.mqttClient.publish(topic.toString(), payload.toString(), {qos: 0, retain: retained});
	}

	this.schedulePublish = function(date, topic, payload, retained){
		log.info("SCHEDULE", "At %s, publishing %s:%s (retained=%s)", date, topic, payload, retained);
		var job = exports.scheduler.scheduleJob(date, function(){
				self.publish(topic, payload, retained || false);
		});
		self.scheduledPublishes.push(job);
		return job;
	}

	this.unschedulePublishes = function() {
		for (var i=0; i<self.scheduledPublishes.length; i++) {
			self.scheduledPublishes[i].cancel();
		}
	}

	this.disconnect =function(){
		self.mqttClient.end();
	}

	this.subscribe  = function(topic) {
		self.mqttClient.subscribe(topic);
	}

 	this.unsubscribe = function(topic) {
		self.mqttClient.unsubscribe(topic);
	}
}
util.inherits(MqttHelper, events.EventEmitter);

module.exports.params = params;
module.exports.stringHelper = new StringHelper();
module.exports.scheduler = schedule;
module.exports.logger = log;
module.exports.mqttHelper = new MqttHelper();
module.exports.settings = new SettingsHelper();

<commit_msg>exposing further parameters for issue #119<commit_after>// Meta package for the HomA framework
// 2013 Alexander Rust <mail@alr.st>
// Use it as you like if you find id usefull

var util = require('util');
var events = require('events');
var mqtt = require('mqtt');
var schedule = require('node-schedule');
var log = require('npmlog')
log.disableColor();
var global = this;
var params = require('optimist').describe("brokerHost", "The MQTT broker's hostname or IP adress. Can also be set via ENV HOMA_BROKER_HOST").describe("brokerPort", "The MQTT broker's port. Can also be set via ENV HOMA_BROKER_PORT").describe("systemId", "The unique client ID that determines where settings on the /sys topic are received");
		params = process.env.HOMA_BROKER_HOST ? params.default("brokerHost", process.env.HOMA_BROKER_HOST) : params.demand("brokerHost");
		params = process.env.HOMA_BROKER_PORT ? params.default("brokerPort", process.env.HOMA_BROKER_PORT) : params.default("brokerPort", 1883);

// finalizes parameters and sets default systemId
module.exports.paramsWithDefaultSystemId = function(systemd){
	params = params.default("systemId", systemd).argv;
	return params.systemId;
}

var StringHelper = function() {
	this.pad = function(n, width, symbol, back) {
	  symbol = symbol || '0';
	  n = n + '';

	  if (n.length >= width)
	  	return n;
	  else
	  	return back || false ? n + new Array(width - n.length + 1).join(symbol) : new Array(width - n.length + 1).join(symbol) + n;
	}
}

var SettingsHelper = function() {
	var self = this;
	self.settings = {}; // key is full topic string of the form /sys/$deviceId/someParameter
	self.locked = false;
	self.requiredSettings = [];
	self.optionalSettings = [];

	this.keyToSysTopic = function(key) {
		return "/sys/"+params.systemId+"/"  + key;
	}

	// Check if a required or optional settings value was received from the MQTT broker. If so, save it for later.
	// force allows to store key:value pairs even if they are not required
	// topic is a complete topic string of the form /sys/$deviceId/someParameter
	this.insert = function(topic, value, force){
		if(force || (self.requiredSettings.indexOf(topic) > -1) || self.optionalSettings.indexOf(topic) > -1)
			self.settings[topic] = value;
	}

	// Saves value for a parameter to the MQTT /sys topic for the current clientId
	this.save = function(parameter, value){
			global.mqttHelper.publish(self.keyToSysTopic(parameter), value, true);
			self.settings[self.keyToSysTopic(parameter)] = value;
	}

	// parameter is in the form someParameter, without /sys topic string
	this.require = function(parameter){
		self.requiredSettings.push(self.keyToSysTopic(parameter));
		global.mqttHelper.subscribe(self.keyToSysTopic(parameter));
	}

	this.optional = function(parameter){
		self.optionalSettings.push(self.keyToSysTopic(parameter));
		global.mqttHelper.subscribe(self.keyToSysTopic(parameter));
	}

	// parameter is in the form someParameter, without /sys topic string
	this.get = function(parameter, inputAsSysTopicStr) {
		return inputAsSysTopicStr ? self.settings[parameter] : self.settings[self.keyToSysTopic(parameter)];
	}

	this.isBootstrapCompleted = function() {
		var pass = true
		for(i=0;i<self.requiredSettings.length;i++){
			if(self.get(self.requiredSettings[i], true) == undefined || self.get(self.requiredSettings[i], true) == ""){
				log.info("SETTINGS", "Waiting to receive: " + self.requiredSettings[i]);
				pass = false;
			}
		}
		if(pass) {
			log.info("SETTINGS", "Bootstrap completed");
		}
		return pass;
	}
	this.isLocked = function(){
		return self.locked;
	}
	this.lock = function() {
		self.locked = true;
	}
	this.unlock = function() {
		self.locked = false;
	}
}

var MqttHelper = function() {
	var self = this;
	self.mqttClient;
	self.scheduledPublishes = [];

	this.connect = function(host, port, extraParameters) {
		self.mqttClient = mqtt.createClient(port || params.brokerPort, host || params.brokerHost, extraParameters);
		log.info("MQTT", "Connecting to %s:%s", host || params.brokerHost, port || params.brokerPort);

	  self.mqttClient.on('connect', function() {
         self.emit('connect');
 	  });

	  self.mqttClient.on('close', function() {
	  	log.info("MQTT", "Connection closed");
	    process.exit(-1);
	  });

	  self.mqttClient.on('error', function(e) {
	  	log.error("MQTT", "Error: %s", e);
	    process.exit(-1);
	  });

	 	self.mqttClient.on('message', function(topic, message, etc) {
	 		self.emit('message', {topic: topic, payload: message, qos: etc.qos, retain: etc.retain, messageId: etc.messageId });
		});
	}

	this.publish = function(topic, payload, retained, ops, callback) {
		var o;
		if(ops)
			o = ops;
		else
			o  = {qos: 0, retain: retained};

		log.info("MQTT", "Publishing %s:%s (retained=%s)", topic, payload, o.retain);
		self.mqttClient.publish(topic.toString(), payload.toString(), o, callback);
	}

	this.schedulePublish = function(date, topic, payload, retained){
		log.info("SCHEDULE", "At %s, publishing %s:%s (retained=%s)", date, topic, payload, retained);
		var job = exports.scheduler.scheduleJob(date, function(){
				self.publish(topic, payload, retained || false);
		});
		self.scheduledPublishes.push(job);
		return job;
	}

	this.unschedulePublishes = function() {
		for (var i=0; i<self.scheduledPublishes.length; i++) {
			self.scheduledPublishes[i].cancel();
		}
	}

	this.disconnect =function(){
		self.mqttClient.end();
	}

	this.subscribe  = function(topic, opts, callback) {
		self.mqttClient.subscribe(topic, opts, callback);
	}

 	this.unsubscribe = function(topic) {
		self.mqttClient.unsubscribe(topic);
	}
}
util.inherits(MqttHelper, events.EventEmitter);

module.exports.params = params;
module.exports.stringHelper = new StringHelper();
module.exports.scheduler = schedule;
module.exports.logger = log;
module.exports.mqttHelper = new MqttHelper();
module.exports.settings = new SettingsHelper();

<|endoftext|>


<commit_before>var request = require('request');

module.exports = function (req, res, next) {
  var text;
  var botPayload = {};
  translate(req.body.text);
  botPayload.username = 'translate-this';
  botPayload.channel = req.body.channel_id;

  send(botPayload, function (error, status, body) {
    if (error) {
      return next(error);
    } else if (status !== 200) {
      // inform user that our Incoming WebHook failed
      return next(new Error('Incoming WebHook: ' + status + ' ' + body));
    } else {
      return res.status(200).end();
    }
  });

};

function translate (text) {
  botPayload.text = req.body.user_name + text
}

function send (payload, callback) {
  var path = process.env.INCOMING_WEBHOOK_PATH;
  var uri = 'https://hooks.slack.com/services' + path;

  request({
    uri: uri,
    method: 'POST',
    body: JSON.stringify(payload)
  }, function (error, response, body) {
    if (error) {
      return callback(error);
    }

    callback(null, response.statusCode, body);
  });
}
<commit_msg>making sure app works without translating<commit_after>var request = require('request');

module.exports = function (req, res, next) {
  var text;
  var botPayload = {};
  botPayload.text = translate(req.body.text);
  botPayload.username = 'translate-this';
  botPayload.channel = req.body.channel_id;

  send(botPayload, function (error, status, body) {
    if (error) {
      return next(error);
    } else if (status !== 200) {
      // inform user that our Incoming WebHook failed
      return next(new Error('Incoming WebHook: ' + status + ' ' + body));
    } else {
      return res.status(200).end();
    }
  });

};

function translate (text) {
  text = text
}

function send (payload, callback) {
  var path = process.env.INCOMING_WEBHOOK_PATH;
  var uri = 'https://hooks.slack.com/services' + path;

  request({
    uri: uri,
    method: 'POST',
    body: JSON.stringify(payload)
  }, function (error, response, body) {
    if (error) {
      return callback(error);
    }

    callback(null, response.statusCode, body);
  });
}
<|endoftext|>


<commit_before>var request = require('request');

module.exports = function (req, res, next) {
  var text;
  var botPayload = {};
  botPayload.text = translate(req.body.text);
  botPayload.username = 'translate-this';
  botPayload.channel = req.body.channel_id;

  send(botPayload, function (error, status, body) {
    if (error) {
      return next(error);
    } else if (status !== 200) {
      // inform user that our Incoming WebHook failed
      return next(new Error('Incoming WebHook: ' + status + ' ' + body));
    } else {
      return res.status(200).end();
    }
  });

};

function translate (text) {
  text = text
}

function send (payload, callback) {
  var path = process.env.INCOMING_WEBHOOK_PATH;
  var uri = 'https://hooks.slack.com/services' + path;

  request({
    uri: uri,
    method: 'POST',
    body: JSON.stringify(payload)
  }, function (error, response, body) {
    if (error) {
      return callback(error);
    }

    callback(null, response.statusCode, body);
  });
}
<commit_msg>curl works<commit_after>var request = require('request');

module.exports = function (req, res, next) {
  var text;
  var botPayload = {};
  var user_name = req.body.user_name;
  text = req.body.text;
  botPayload.text = translate(text);
  // botPayload.text = req.body.text;
  botPayload.user_name = user_name;
  // botPayload.channel = req.body.channel_id;

  if (user_name !== 'translate-this') {
    return res.status(200).send(botPayload);
  } else {
    return res.status(200).end();
  }

  // send(botPayload) {
  //   if (error) {
  //     return next(error);
  //   } else if (status !== 200) {
  //     // inform user that our Incoming WebHook failed
  //     return next(new Error('Incoming WebHook: ' + status + ' ' + body));
  //   } else {
  //     return res.status(200).end();
  //   }
  // });

};

function translate (text) {
  return text + "_is what you meant!_ :flag-uk: please! Thanks!"
}

function send (payload) {
  var path = process.env.INCOMING_WEBHOOK_PATH;
  var uri = 'https://hooks.slack.com/services/' + path;

  request({
    uri: uri,
    method: 'POST',
    body: JSON.stringify(payload)
  });
}
<|endoftext|>