<commit_before>import * as I from 'immutable';

import { rationalLinearAlgebra,
         rationalLinearAlgebraModular } from '../arithmetic/types';

import modularSolver from '../arithmetic/solveRational';
import * as solveRational from '../arithmetic/solveRational';

import * as util from '../common/util';

let _timers = null;

class VectorLabeledEdge {
  constructor(head, tail, shift) {
    this.head = head;
    this.tail = tail;
    this.shift = shift;
  }

  toString() {
    return `VectorLabeledEdge(${this.head}, ${this.tail}, ${this.shift})`;
  }

  reverse() {
    return new VectorLabeledEdge(
      this.tail, this.head, rationalLinearAlgebra.negative(this.shift));
  }

  canonical() {
    if (this.tail < this.head
        || (this.tail == this.head && this.shift.find(x => x != 0) < 0)
       )
      return this.reverse();
    else
      return this;
  }

  get __typeName() { return 'VectorLabeledEdge'; }
};

export const ops = rationalLinearAlgebra.register({
  __repr__  : {
    VectorLabeledEdge: x => ({
      head: ops.repr(x.head),
      tail: ops.repr(x.tail),
      shift: ops.repr(x.shift)
    })
  },
  __VectorLabeledEdge__: {
    Object: ({ VectorLabeledEdge: obj }) =>
      new VectorLabeledEdge(
        ops.fromRepr(obj.head),
        ops.fromRepr(obj.tail),
        ops.fromRepr(obj.shift))
  }
});

class Graph {
  constructor(dim, edges) {
    this.dim = dim;
    this.edges = edges;
  }

  toString() {
    return `PGraph(${this.edges})`;
  }
};

const encode = value => ops.serialize(value);
const decode = value => ops.deserialize(value);

export function makeEdge(head, tail, shift) {
  return new VectorLabeledEdge(head, tail, shift);
};

export function make(data) {
  const ereps = data.map(([h, t, s]) => encode(makeEdge(h, t, s).canonical()));
  const edges = I.OrderedSet(ereps).map(decode);

  if (edges.size == 0)
    throw new Error('cannot be empty');

  const dim = edges.first().shift.length;
  if (edges.some(e => e.shift.length != dim))
    throw new Error('must have consistent shift dimensions');

  return new Graph(dim, edges);
};

export const asObject = graph => ({
  PeriodicGraph: {
    dim: graph.dim,
    edges: graph.edges.map(e => ops.repr(e)).toArray()
  }
});

export const fromObject = ({ PeriodicGraph: obj }) => new Graph(
  obj.dim,
  I.OrderedSet(obj.edges.map(e => ops.fromRepr(e)))
);

export function vertices(graph) {
  const result = I.OrderedSet().asMutable();
  for (const e of graph.edges) {
    result.add(e.head);
    result.add(e.tail);
  }
  return I.List(result);
};

export function adjacencies(graph) {
  const target = e => ({ v: e.tail, s: e.shift });

  let res = I.Map();
  for (const e of graph.edges) {
    res = res
      .update(e.head, a => (a || I.List()).push(target(e)))
      .update(e.tail, a => (a || I.List()).push(target(e.reverse())));
  }
  return res;
};

export function coordinationSeq(graph, start, dist) {
  const adj  = adjacencies(graph);
  const zero = ops.vector(graph.dim);

  let oldShell = I.Set();
  let thisShell = I.Set([I.fromJS([start, zero])]);
  const res = [1];

  for (const i of I.Range(1, dist+1)) {
    let nextShell = I.Set().asMutable();
    for (const item of thisShell) {
      const [v, s] = item.toJS();
      for (const { v: w, s: t } of adj.get(v)) {
        const key = I.fromJS([w, ops.plus(s, t)]);
        if (!oldShell.contains(key) && !thisShell.contains(key))
          nextShell.add(key);
      }
    }

    res.push(nextShell.size);
    oldShell = thisShell;
    thisShell = nextShell;
  }

  return res;
};

const _componentInOrbitGraph = (graph, start) => {
  const adj = adjacencies(graph);
  const queue = [start]
  const bridges = [];
  const nodeShifts = I.Map([[start, ops.vector(graph.dim)]]).asMutable();

  while (queue.length) {
    const v = queue.shift();
    const av = nodeShifts.get(v);

    for (const {v: w, s: shift} of adj.get(v)) {
      if (nodeShifts.get(w) == undefined) {
        queue.push(w);
        nodeShifts.set(w, ops.minus(av, shift));
      }
      else {
        const aw = nodeShifts.get(w);
        const newShift = ops.plus(shift, ops.minus(aw, av));
        const pivot = newShift.find(x => ops.ne(x, 0));

        if (pivot != undefined && ops.gt(pivot, 0))
          bridges.push({ v, w, s: newShift });
      }
    }
  }

  return {
    nodes: I.Set(nodeShifts.keys()),
    nodeShifts,
    bridges
  };
};

const _isConnectedOrbitGraph = (graph) => {
  const verts = vertices(graph);
  const comp = _componentInOrbitGraph(graph, verts.first());

  return comp.nodes.size >= verts.size;
};

const _makeBasis = M => {
  let basis = null;
  for (const row of M)
    basis = rationalLinearAlgebraModular.extendBasis(row, basis);
  return basis;
};

const _makeCoordinateTransform = (B, dim) => {
  if (B.length < dim) {
    B = B.slice();
    for (const vec of ops.identityMatrix(dim)) {
      if (ops.rank(B.concat([vec])) > ops.rank(B)) {
        B.push(vec);
      }
    }
  }

  return ops.inverse(B);
};

const _componentInCoverGraph = (graph, start) => {
  const { nodes, nodeShifts, bridges } = _componentInOrbitGraph(graph, start);
  const basis = _makeBasis(bridges.map(b => b.s));
  const thisDim = basis.length;
  const transform = _makeCoordinateTransform(basis, graph.dim);
  const old2new = I.Map(I.List(nodes).zip(I.Range(1, nodes.size+1)));

  const newEdges = graph.edges
    .filter(({ head, tail }) =>
            old2new.get(head) != null && old2new.get(tail) != null)
    .map(({ head, tail, shift }) => {
      const [v, w] = [old2new.get(head), old2new.get(tail)];
      const [av, aw] = [nodeShifts.get(head), nodeShifts.get(tail)];
      const t = ops.times(ops.plus(shift, ops.minus(aw, av)), transform);
      return [v, w, t.slice(0, thisDim)];
    });

  const multiplicity =
    thisDim == graph.dim ? ops.abs(ops.determinant(basis)) : 0;

  return {
    basis,
    multiplicity,
    nodes: nodes.toArray(),
    graph: make(newEdges)
  };
};

export function isConnected(graph) {
  _timers && _timers.start('isConnected');
  const verts = vertices(graph);
  const comp = _componentInCoverGraph(graph, verts.first());
  _timers && _timers.stop('isConnected');

  return comp.nodes.length >= verts.size && comp.multiplicity == 1;
};

export function connectedComponents(graph) {
  const verts = vertices(graph);
  const seen = I.Set().asMutable();
  const result = [];

  for (const start of verts) {
    if (!seen.contains(start)) {
      const comp = _componentInCoverGraph(graph, start);
      result.push(comp);
      comp.nodes.forEach(v => seen.add(v));
    }
  }

  return result;
};

export function barycentricPlacement(graph) {
  if (graph._$pos != undefined)
    return graph._$pos;

  _timers && _timers.start('barycentricPlacement');

  const adj   = adjacencies(graph);
  const verts = vertices(graph);
  const vIdcs = I.Map(I.Range(0, verts.size).map(i => [verts.get(i), i]));

  const n = verts.size;
  const d = graph.dim;
  let A = ops.matrix(n, n);
  let t = ops.matrix(n, d);

  A[0][0] = 1;

  verts.forEach((v, i) => {
    if (i > 0) {
      adj.get(v).forEach(c => {
        if (c.v != v) {
          const j = vIdcs.get(c.v);
          A[i][j] -= 1;
          A[i][i] += 1;
          t[i] = ops.plus(t[i], c.s);
        }
      });
    }
  });

  const p = modularSolver(A, t);

  const result = I.Map(I.Range(0, n).map(i => [verts.get(i), p[i]]));

  _timers && _timers.stop('barycentricPlacement');

  graph._$pos = result;

  return result;
};

export function isStable(graph) {
  const pos = barycentricPlacement(graph);
  const verts = vertices(graph);
  const seen = I.Set().asMutable();

  for (const v of verts) {
    const p = pos.get(v);
    const key = encode(p.map(x => ops.mod(x, 1)));
    if (seen.contains(key))
      return false;
    else
      seen.add(key);
  }

  return true;
};

export function isLocallyStable(graph) {
  const pos = barycentricPlacement(graph);

  _timers && _timers.start('isLocallyStable');
  const adj = adjacencies(graph);
  const verts = vertices(graph);

  for (const v of verts) {
    const seen = I.Set().asMutable();

    for (const w of adj.get(v)) {
      const p = ops.plus(pos.get(w.v), w.s);
      const key = encode(p);
      if (seen.contains(key)) {
        _timers && _timers.stop('isLocallyStable');
        return false;
      }
      else
        seen.add(key);
    }
  }

  _timers && _timers.stop('isLocallyStable');
  return true;
};

export function allIncidences(graph, v, adj = adjacencies(graph)) {
  return adj.get(v).map(({v: w, s}) => makeEdge(v, w, s)).toJS();
};

export function edgeVector(e, pos) {
  return ops.plus(e.shift, ops.minus(pos.get(e.tail), pos.get(e.head)));
};

export function useTimers(timers) {
  _timers = timers;
}

if (require.main == module) {
  Array.prototype.toString = function() {
    return '[ ' + this.map(x => x.toString()).join(', ') + ' ]';
  };

  const test = function test(g) {
    console.log('g = '+g);
    console.log('  cs  = '+coordinationSeq(g, 1, 10));
    if (isConnected(g)) {
      console.log('  pos = '+barycentricPlacement(g));
      console.log('  stable: '+isStable(g));
      console.log('  locally stable: '+isLocallyStable(g));
    }

    for (const comp of connectedComponents(g)) {
      console.log('  component:');
      console.log('    nodes = '+comp.nodes);
      console.log('    graph = '+comp.graph);
      console.log('    basis = '+comp.basis);
      console.log('    multiplicity = '+comp.multiplicity);
    }
    console.log();
  };

  test(make([ [ 1, 2, [ 0, 0 ] ],
              [ 1, 2, [ 1, 0 ] ],
              [ 1, 2, [ 0, 1 ] ] ]));

  test(make([ [ 1, 2, [ 0, 0, 0 ] ],
              [ 1, 2, [ 0, 1, 0 ] ],
              [ 1, 2, [ 0, 0, 1 ] ] ]));

  test(make([ [ 1, 2, [ 0, 0, 0 ] ],
              [ 1, 2, [ 1, 0, 0 ] ],
              [ 1, 2, [ 0, 1, 0 ] ],
              [ 1, 2, [ 0, 0, 1 ] ] ]));

  test(make([ [ 1, 1, [ 1, 0 ] ],
              [ 1, 1, [ 0, 1 ] ],
              [ 1, 2, [ 0, 0 ] ],
              [ 1, 2, [ 1, 1 ] ],
              [ 1, 3, [ 0, 0 ] ],
              [ 1, 3, [ 1, -1 ] ] ]));

  test(make([ [ 1, 1, [ 1, 0 ] ],
              [ 1, 1, [ 0, 1 ] ],
              [ 1, 2, [ 0, 0 ] ],
              [ 1, 2, [ 1, 1 ] ],
              [ 1, 3, [ 0, 0 ] ],
              [ 1, 3, [ 1, -1 ] ],
              [ 1, 4, [ 0, 0 ] ],
              [ 1, 4, [ 1, -1 ] ] ]));

  test(make([ [ 1, 1, [ -1,  1,  1 ] ],
              [ 1, 1, [  0, -1,  1 ] ],
              [ 1, 1, [  0,  0, -1 ] ] ]));

  test(make([ [ 1, 2, [ 0, 0, 0 ] ],
              [ 1, 2, [ 2, 0, 0 ] ],
              [ 1, 2, [ 0, 2, 0 ] ],
              [ 1, 2, [ 0, 0, 2 ] ] ]));

  test(make([ [ 1, 3, [ 0, 0, 0 ] ],
              [ 1, 3, [ 2, 0, 0 ] ],
              [ 1, 3, [ 0, 2, 0 ] ],
              [ 1, 3, [ 0, 0, 2 ] ],
              [ 2, 4, [ 0, 0, 0 ] ],
              [ 2, 4, [ 2, 0, 0 ] ],
              [ 2, 4, [ 0, 2, 0 ] ],
              [ 2, 4, [ 0, 0, 2 ] ] ]));

  test(make([ [ 1, 2, [ 0, 0, 0 ] ],
              [ 2, 3, [ 0, 0, 0 ] ],
              [ 3, 4, [ 0, 0, 0 ] ],
              [ 4, 5, [ 0, 0, 0 ] ],
              [ 5, 6, [ 0, 0, 0 ] ],
              [ 6, 1, [ 0, 0, 0 ] ],
              [ 1, 2, [ 1, 0, 0 ] ],
              [ 2, 3, [ 0, 1, 0 ] ],
              [ 3, 4, [ 0, 0, 1 ] ],
              [ 4, 5, [ -1, 0, 0 ] ],
              [ 5, 6, [ 0, -1, 0 ] ],
              [ 6, 1, [ 0, 0, -1 ] ] ]));
}
<commit_msg>remove function keyword and timing instructions<commit_after>import * as I from 'immutable';

import { rationalLinearAlgebra,
         rationalLinearAlgebraModular } from '../arithmetic/types';

import modularSolver from '../arithmetic/solveRational';
import * as solveRational from '../arithmetic/solveRational';

import * as util from '../common/util';

class VectorLabeledEdge {
  constructor(head, tail, shift) {
    this.head = head;
    this.tail = tail;
    this.shift = shift;
  }

  toString() {
    return `VectorLabeledEdge(${this.head}, ${this.tail}, ${this.shift})`;
  }

  reverse() {
    return new VectorLabeledEdge(
      this.tail, this.head, rationalLinearAlgebra.negative(this.shift));
  }

  canonical() {
    if (this.tail < this.head
        || (this.tail == this.head && this.shift.find(x => x != 0) < 0)
       )
      return this.reverse();
    else
      return this;
  }

  get __typeName() { return 'VectorLabeledEdge'; }
};

export const ops = rationalLinearAlgebra.register({
  __repr__  : {
    VectorLabeledEdge: x => ({
      head: ops.repr(x.head),
      tail: ops.repr(x.tail),
      shift: ops.repr(x.shift)
    })
  },
  __VectorLabeledEdge__: {
    Object: ({ VectorLabeledEdge: obj }) =>
      new VectorLabeledEdge(
        ops.fromRepr(obj.head),
        ops.fromRepr(obj.tail),
        ops.fromRepr(obj.shift))
  }
});

class Graph {
  constructor(dim, edges) {
    this.dim = dim;
    this.edges = edges;
  }

  toString() {
    return `PGraph(${this.edges})`;
  }
};

const encode = value => ops.serialize(value);
const decode = value => ops.deserialize(value);

export const makeEdge = (head, tail, shift) =>
  new VectorLabeledEdge(head, tail, shift);

export const make = data => {
  const ereps = data.map(([h, t, s]) => encode(makeEdge(h, t, s).canonical()));
  const edges = I.OrderedSet(ereps).map(decode);

  if (edges.size == 0)
    throw new Error('cannot be empty');

  const dim = edges.first().shift.length;
  if (edges.some(e => e.shift.length != dim))
    throw new Error('must have consistent shift dimensions');

  return new Graph(dim, edges);
};

export const asObject = graph => ({
  PeriodicGraph: {
    dim: graph.dim,
    edges: graph.edges.map(e => ops.repr(e)).toArray()
  }
});

export const fromObject = ({ PeriodicGraph: obj }) => new Graph(
  obj.dim,
  I.OrderedSet(obj.edges.map(e => ops.fromRepr(e)))
);

export const vertices = graph => {
  const result = I.OrderedSet().asMutable();
  for (const e of graph.edges) {
    result.add(e.head);
    result.add(e.tail);
  }
  return I.List(result);
};

export const adjacencies = graph => {
  const target = e => ({ v: e.tail, s: e.shift });

  let res = I.Map();
  for (const e of graph.edges) {
    res = res
      .update(e.head, a => (a || I.List()).push(target(e)))
      .update(e.tail, a => (a || I.List()).push(target(e.reverse())));
  }
  return res;
};

export const coordinationSeq = (graph, start, dist) => {
  const adj  = adjacencies(graph);
  const zero = ops.vector(graph.dim);

  let oldShell = I.Set();
  let thisShell = I.Set([I.fromJS([start, zero])]);
  const res = [1];

  for (const i of I.Range(1, dist+1)) {
    let nextShell = I.Set().asMutable();
    for (const item of thisShell) {
      const [v, s] = item.toJS();
      for (const { v: w, s: t } of adj.get(v)) {
        const key = I.fromJS([w, ops.plus(s, t)]);
        if (!oldShell.contains(key) && !thisShell.contains(key))
          nextShell.add(key);
      }
    }

    res.push(nextShell.size);
    oldShell = thisShell;
    thisShell = nextShell;
  }

  return res;
};

const _componentInOrbitGraph = (graph, start) => {
  const adj = adjacencies(graph);
  const queue = [start]
  const bridges = [];
  const nodeShifts = I.Map([[start, ops.vector(graph.dim)]]).asMutable();

  while (queue.length) {
    const v = queue.shift();
    const av = nodeShifts.get(v);

    for (const {v: w, s: shift} of adj.get(v)) {
      if (nodeShifts.get(w) == undefined) {
        queue.push(w);
        nodeShifts.set(w, ops.minus(av, shift));
      }
      else {
        const aw = nodeShifts.get(w);
        const newShift = ops.plus(shift, ops.minus(aw, av));
        const pivot = newShift.find(x => ops.ne(x, 0));

        if (pivot != undefined && ops.gt(pivot, 0))
          bridges.push({ v, w, s: newShift });
      }
    }
  }

  return {
    nodes: I.Set(nodeShifts.keys()),
    nodeShifts,
    bridges
  };
};

const _isConnectedOrbitGraph = (graph) => {
  const verts = vertices(graph);
  const comp = _componentInOrbitGraph(graph, verts.first());

  return comp.nodes.size >= verts.size;
};

const _makeBasis = M => {
  let basis = null;
  for (const row of M)
    basis = rationalLinearAlgebraModular.extendBasis(row, basis);
  return basis;
};

const _makeCoordinateTransform = (B, dim) => {
  if (B.length < dim) {
    B = B.slice();
    for (const vec of ops.identityMatrix(dim)) {
      if (ops.rank(B.concat([vec])) > ops.rank(B)) {
        B.push(vec);
      }
    }
  }

  return ops.inverse(B);
};

const _componentInCoverGraph = (graph, start) => {
  const { nodes, nodeShifts, bridges } = _componentInOrbitGraph(graph, start);
  const basis = _makeBasis(bridges.map(b => b.s));
  const thisDim = basis.length;
  const transform = _makeCoordinateTransform(basis, graph.dim);
  const old2new = I.Map(I.List(nodes).zip(I.Range(1, nodes.size+1)));

  const newEdges = graph.edges
    .filter(({ head, tail }) =>
            old2new.get(head) != null && old2new.get(tail) != null)
    .map(({ head, tail, shift }) => {
      const [v, w] = [old2new.get(head), old2new.get(tail)];
      const [av, aw] = [nodeShifts.get(head), nodeShifts.get(tail)];
      const t = ops.times(ops.plus(shift, ops.minus(aw, av)), transform);
      return [v, w, t.slice(0, thisDim)];
    });

  const multiplicity =
    thisDim == graph.dim ? ops.abs(ops.determinant(basis)) : 0;

  return {
    basis,
    multiplicity,
    nodes: nodes.toArray(),
    graph: make(newEdges)
  };
};

export const isConnected = graph => {
  const verts = vertices(graph);
  const comp = _componentInCoverGraph(graph, verts.first());

  return comp.nodes.length >= verts.size && comp.multiplicity == 1;
};

export const connectedComponents = graph => {
  const verts = vertices(graph);
  const seen = I.Set().asMutable();
  const result = [];

  for (const start of verts) {
    if (!seen.contains(start)) {
      const comp = _componentInCoverGraph(graph, start);
      result.push(comp);
      comp.nodes.forEach(v => seen.add(v));
    }
  }

  return result;
};

export const barycentricPlacement = graph => {
  if (graph._$pos != undefined)
    return graph._$pos;

  const adj   = adjacencies(graph);
  const verts = vertices(graph);
  const vIdcs = I.Map(I.Range(0, verts.size).map(i => [verts.get(i), i]));

  const n = verts.size;
  const d = graph.dim;
  let A = ops.matrix(n, n);
  let t = ops.matrix(n, d);

  A[0][0] = 1;

  verts.forEach((v, i) => {
    if (i > 0) {
      adj.get(v).forEach(c => {
        if (c.v != v) {
          const j = vIdcs.get(c.v);
          A[i][j] -= 1;
          A[i][i] += 1;
          t[i] = ops.plus(t[i], c.s);
        }
      });
    }
  });

  const p = modularSolver(A, t);

  const result = I.Map(I.Range(0, n).map(i => [verts.get(i), p[i]]));

  graph._$pos = result;

  return result;
};

export const isStable = graph => {
  const pos = barycentricPlacement(graph);
  const verts = vertices(graph);
  const seen = I.Set().asMutable();

  for (const v of verts) {
    const p = pos.get(v);
    const key = encode(p.map(x => ops.mod(x, 1)));
    if (seen.contains(key))
      return false;
    else
      seen.add(key);
  }

  return true;
};

export const isLocallyStable = graph => {
  const pos = barycentricPlacement(graph);

  const adj = adjacencies(graph);
  const verts = vertices(graph);

  for (const v of verts) {
    const seen = I.Set().asMutable();

    for (const w of adj.get(v)) {
      const p = ops.plus(pos.get(w.v), w.s);
      const key = encode(p);
      if (seen.contains(key)) {
        return false;
      }
      else
        seen.add(key);
    }
  }

  return true;
};

export const allIncidences = (graph, v, adj=adjacencies(graph)) =>
  adj.get(v).map(({v: w, s}) => makeEdge(v, w, s)).toJS();

export const edgeVector = (e, pos) =>
  ops.plus(e.shift, ops.minus(pos.get(e.tail), pos.get(e.head)));

if (require.main == module) {
  Array.prototype.toString = function() {
    return '[ ' + this.map(x => x.toString()).join(', ') + ' ]';
  };

  const test = g => {
    console.log('g = '+g);
    console.log('  cs  = '+coordinationSeq(g, 1, 10));
    if (isConnected(g)) {
      console.log('  pos = '+barycentricPlacement(g));
      console.log('  stable: '+isStable(g));
      console.log('  locally stable: '+isLocallyStable(g));
    }

    for (const comp of connectedComponents(g)) {
      console.log('  component:');
      console.log('    nodes = '+comp.nodes);
      console.log('    graph = '+comp.graph);
      console.log('    basis = '+comp.basis);
      console.log('    multiplicity = '+comp.multiplicity);
    }
    console.log();
  };

  test(make([ [ 1, 2, [ 0, 0 ] ],
              [ 1, 2, [ 1, 0 ] ],
              [ 1, 2, [ 0, 1 ] ] ]));

  test(make([ [ 1, 2, [ 0, 0, 0 ] ],
              [ 1, 2, [ 0, 1, 0 ] ],
              [ 1, 2, [ 0, 0, 1 ] ] ]));

  test(make([ [ 1, 2, [ 0, 0, 0 ] ],
              [ 1, 2, [ 1, 0, 0 ] ],
              [ 1, 2, [ 0, 1, 0 ] ],
              [ 1, 2, [ 0, 0, 1 ] ] ]));

  test(make([ [ 1, 1, [ 1, 0 ] ],
              [ 1, 1, [ 0, 1 ] ],
              [ 1, 2, [ 0, 0 ] ],
              [ 1, 2, [ 1, 1 ] ],
              [ 1, 3, [ 0, 0 ] ],
              [ 1, 3, [ 1, -1 ] ] ]));

  test(make([ [ 1, 1, [ 1, 0 ] ],
              [ 1, 1, [ 0, 1 ] ],
              [ 1, 2, [ 0, 0 ] ],
              [ 1, 2, [ 1, 1 ] ],
              [ 1, 3, [ 0, 0 ] ],
              [ 1, 3, [ 1, -1 ] ],
              [ 1, 4, [ 0, 0 ] ],
              [ 1, 4, [ 1, -1 ] ] ]));

  test(make([ [ 1, 1, [ -1,  1,  1 ] ],
              [ 1, 1, [  0, -1,  1 ] ],
              [ 1, 1, [  0,  0, -1 ] ] ]));

  test(make([ [ 1, 2, [ 0, 0, 0 ] ],
              [ 1, 2, [ 2, 0, 0 ] ],
              [ 1, 2, [ 0, 2, 0 ] ],
              [ 1, 2, [ 0, 0, 2 ] ] ]));

  test(make([ [ 1, 3, [ 0, 0, 0 ] ],
              [ 1, 3, [ 2, 0, 0 ] ],
              [ 1, 3, [ 0, 2, 0 ] ],
              [ 1, 3, [ 0, 0, 2 ] ],
              [ 2, 4, [ 0, 0, 0 ] ],
              [ 2, 4, [ 2, 0, 0 ] ],
              [ 2, 4, [ 0, 2, 0 ] ],
              [ 2, 4, [ 0, 0, 2 ] ] ]));

  test(make([ [ 1, 2, [ 0, 0, 0 ] ],
              [ 2, 3, [ 0, 0, 0 ] ],
              [ 3, 4, [ 0, 0, 0 ] ],
              [ 4, 5, [ 0, 0, 0 ] ],
              [ 5, 6, [ 0, 0, 0 ] ],
              [ 6, 1, [ 0, 0, 0 ] ],
              [ 1, 2, [ 1, 0, 0 ] ],
              [ 2, 3, [ 0, 1, 0 ] ],
              [ 3, 4, [ 0, 0, 1 ] ],
              [ 4, 5, [ -1, 0, 0 ] ],
              [ 5, 6, [ 0, -1, 0 ] ],
              [ 6, 1, [ 0, 0, -1 ] ] ]));
}
<|endoftext|>


<commit_before>"use strict"

const expect = require(`expect`)
const deepFreeze = require(`deep-freeze`)
const filler = require(`../filler`)
const {createStore} = require(`redux`)
const mappoApp = require(`./mappoApp`)

{
  // can set map
  const store = createStore(mappoApp)

  store.dispatch({type: `SET_MAP`, map: {tileLayers: []}})
  expect(store.getState().map).toEqual({tileLayers: []})
}

{
  // setting map marks tileset image bitmap as needing rebuilt
  const store = createStore(mappoApp)

  expect(store.getState().isDirtyTilesetImageBitmap).toBe(undefined)
  store.dispatch({type: `SET_MAP`, map: {tileLayers: []}})
  expect(store.getState().isDirtyTilesetImageBitmap).toBe(true)
}

{
  // can mark tileset image bitmap as built
  const store = createStore(mappoApp)

  store.dispatch({type: `SET_MAP`, map: {}})
  store.dispatch({type: `BUILT_TILESET_IMAGE_BITMAP`})
  expect(store.getState().isDirtyTilesetImageBitmap).toBe(false)
}

{
  // SET_MAP holds a direct reference to the map
  const store = createStore(mappoApp)

  const map = {tileLayers: []}
  store.dispatch({type: `SET_MAP`, map})
  expect(store.getState().map).toBe(map)
}

{
  // can plot a tile
  const store = createStore(mappoApp)

  const tileLayers = [{width: 2, height: 2, tileIndexGrid: filler(2 * 2, 77)}]
  deepFreeze(tileLayers)
  store.dispatch({type: `SET_MAP`, map: {tileLayers}})
  expect(store.getState().map.tileLayers[0].tileIndexGrid).toEqual(filler(2 * 2, 77))

  const plotInfo = {
    x: 0,
    y: 1,
    tileLayerIndex: 0,
    tileLayers,
    tileIndexToPlot: 99,
  }
  store.dispatch(Object.assign({type: `PLOT_TILE`}, plotInfo))
  expect(store.getState().map.tileLayers[0].tileIndexGrid).toEqual([77, 77, 99, 77])
  const {plotHistory, undoIndex} = store.getState().plots
  expect(plotHistory[undoIndex - 1]).toEqual({
    x: 0,
    y: 1,
    l: 0,
    v: 99,
    o: 77,
  })
  expect(store.getState().isMapDirty).toBe(true)
}

{
  // can set editor window size
  const store = createStore(mappoApp)

  store.dispatch({type: `SET_EDITOR_WINDOW_SIZE`, width: 200, height: 300})
  expect(store.getState().editor.width).toEqual(200)
  expect(store.getState().editor.height).toEqual(300)
}

{
  // can load state
  const store = createStore(mappoApp)

  store.dispatch({type: `RELOAD_STORE`, state: {herp: `derp`}})
  expect(store.getState().herp).toEqual(`derp`)
}

{
  // can set map dirty state
  const store = createStore(mappoApp)

  store.dispatch({type: `SET_MAP_DIRTY`, isMapDirty: true})
  expect(store.getState().isMapDirty).toEqual(true)

  store.dispatch({type: `SET_MAP_DIRTY`, isMapDirty: false})
  expect(store.getState().isMapDirty).toEqual(false)
}

<commit_msg>dead code<commit_after>"use strict"

const expect = require(`expect`)
const deepFreeze = require(`deep-freeze`)
const filler = require(`../filler`)
const {createStore} = require(`redux`)
const mappoApp = require(`./mappoApp`)

{
  // can set map
  const store = createStore(mappoApp)

  store.dispatch({type: `SET_MAP`, map: {tileLayers: []}})
  expect(store.getState().map).toEqual({tileLayers: []})
}

{
  // setting map marks tileset image bitmap as needing rebuilt
  const store = createStore(mappoApp)

  expect(store.getState().isDirtyTilesetImageBitmap).toBe(undefined)
  store.dispatch({type: `SET_MAP`, map: {tileLayers: []}})
  expect(store.getState().isDirtyTilesetImageBitmap).toBe(true)
}

{
  // can mark tileset image bitmap as built
  const store = createStore(mappoApp)

  store.dispatch({type: `SET_MAP`, map: {}})
  store.dispatch({type: `BUILT_TILESET_IMAGE_BITMAP`})
  expect(store.getState().isDirtyTilesetImageBitmap).toBe(false)
}

{
  // SET_MAP holds a direct reference to the map
  const store = createStore(mappoApp)

  const map = {tileLayers: []}
  store.dispatch({type: `SET_MAP`, map})
  expect(store.getState().map).toBe(map)
}

{
  // can plot a tile
  const store = createStore(mappoApp)

  const tileLayers = [{width: 2, height: 2, tileIndexGrid: filler(2 * 2, 77)}]
  deepFreeze(tileLayers)
  store.dispatch({type: `SET_MAP`, map: {tileLayers}})
  expect(store.getState().map.tileLayers[0].tileIndexGrid).toEqual(filler(2 * 2, 77))

  const plotInfo = {
    x: 0,
    y: 1,
    tileLayerIndex: 0,
    tileLayers,
    tileIndexToPlot: 99,
  }
  store.dispatch(Object.assign({type: `PLOT_TILE`}, plotInfo))
  expect(store.getState().map.tileLayers[0].tileIndexGrid).toEqual([77, 77, 99, 77])
  const {plotHistory, undoIndex} = store.getState().plots
  expect(plotHistory[undoIndex - 1]).toEqual({
    x: 0,
    y: 1,
    l: 0,
    v: 99,
    o: 77,
  })
  expect(store.getState().isMapDirty).toBe(true)
}

{
  // can set editor window size
  const store = createStore(mappoApp)

  store.dispatch({type: `SET_EDITOR_WINDOW_SIZE`, width: 200, height: 300})
  expect(store.getState().editor.width).toEqual(200)
  expect(store.getState().editor.height).toEqual(300)
}

{
  // can set map dirty state
  const store = createStore(mappoApp)

  store.dispatch({type: `SET_MAP_DIRTY`, isMapDirty: true})
  expect(store.getState().isMapDirty).toEqual(true)

  store.dispatch({type: `SET_MAP_DIRTY`, isMapDirty: false})
  expect(store.getState().isMapDirty).toEqual(false)
}

<|endoftext|>


<commit_before>// remove empty lines (new line character)in a file
// when proceeded by one or more spaces

var emptylines = require('./rex-emptylines')
var replace = require('./replace')

function removeEmptyLines (str) {
  return replace(str, emptylines(), '')
}

module.exports = removeEmptyLines
<commit_msg>update regex/removeEmptyLines.js<commit_after>// remove empty lines (new line character)in a file
// when proceeded by one or more spaces

var emptylines = require('./rex-emptylines')
var replace = require('./replace')

function removeEmptyLines (str) {
  return replace(str, emptylines, '')
}

module.exports = removeEmptyLines
<|endoftext|>


<commit_before>var article_fixture = {
  "title": "Austria",
  "author": "John Doe",
  "children": [
    "preface", "history", "politics"
  ],
  "nodes": {
    "preface": {
      "type": "section",
      "name": "Preface",
      "children": ["text1", "image1", "text2"]
    },
    "history": {
      "type": "section",
      "name": "History",
      "children": ["text3"]
    },
    "politics": {
      "type": "section",
      "name": "Politics",
      "children": ["text4", "text5"]
    },
    "text1": {
      "type": "paragraph",
      "content": "Austria, officially the Republic of Austria (German: Republik Österreich), is a landlocked country of roughly 8.3 million people[3] in Central Europe."
    },
    "image1": {
      "type": "image",
      "url": "http://tmp.vivian.transloadit.com/scratch/9a65045a69dd88c2baf281c28dbd15a7"
    },
    "text2": {
      "type": "paragraph",
      "content": "It is bordered by Germany and the Czech Republic to the north, Slovakia and Hungary to the east, Slovenia and Italy to the south, and Switzerland and Liechtenstein to the west"
    },
    "text3": {
      "type": "paragraph",
      "content": "Settled in ancient times,[11] the Central European land that is now Austria was occupied in pre-Roman times by various Celtic tribes. The Celtic kingdom of Noricum was later claimed by the Roman Empire and made a province."
    },
    "text4": {
      "type": "paragraph",
      "content": "The Parliament of Austria is located in Vienna, the country's largest city and capital. Austria became a federal, parliamentary, democratic republic through the Federal Constitution of 1920. The political system of the Second Republic with its nine states is based on the constitution of 1920 and 1929, which was reenacted on May 1, 1945."
    },
    "text5": {
      "type": "paragraph",
      "content": "The head of state is the Federal President (Bundespräsident), who is directly elected by popular vote. The chairman of the Federal Government is the Federal Chancellor, who is appointed by the president. The government can be removed from office by either a presidential decree or by vote of no confidence in the lower chamber of parliament, the Nationalrat."
    }
  }
};<commit_msg>Umlauts are causing troubles. Removed them for now.<commit_after>var article_fixture = {
  "title": "Austria",
  "author": "John Doe",
  "children": [
    "preface", "history", "politics"
  ],
  "nodes": {
    "preface": {
      "type": "section",
      "name": "Preface",
      "children": ["text1", "image1", "text2"]
    },
    "history": {
      "type": "section",
      "name": "History",
      "children": ["text3"]
    },
    "politics": {
      "type": "section",
      "name": "Politics",
      "children": ["text4", "text5"]
    },
    "text1": {
      "type": "paragraph",
      "content": "Austria, officially the Republic of Austria (German: Republik Oesterreich), is a landlocked country of roughly 8.3 million people[3] in Central Europe."
    },
    "image1": {
      "type": "image",
      "url": "http://tmp.vivian.transloadit.com/scratch/9a65045a69dd88c2baf281c28dbd15a7"
    },
    "text2": {
      "type": "paragraph",
      "content": "It is bordered by Germany and the Czech Republic to the north, Slovakia and Hungary to the east, Slovenia and Italy to the south, and Switzerland and Liechtenstein to the west"
    },
    "text3": {
      "type": "paragraph",
      "content": "Settled in ancient times,[11] the Central European land that is now Austria was occupied in pre-Roman times by various Celtic tribes. The Celtic kingdom of Noricum was later claimed by the Roman Empire and made a province."
    },
    "text4": {
      "type": "paragraph",
      "content": "The Parliament of Austria is located in Vienna, the country's largest city and capital. Austria became a federal, parliamentary, democratic republic through the Federal Constitution of 1920. The political system of the Second Republic with its nine states is based on the constitution of 1920 and 1929, which was reenacted on May 1, 1945."
    },
    "text5": {
      "type": "paragraph",
      "content": "The head of state is the Federal President (Bundespraesident), who is directly elected by popular vote. The chairman of the Federal Government is the Federal Chancellor, who is appointed by the president. The government can be removed from office by either a presidential decree or by vote of no confidence in the lower chamber of parliament, the Nationalrat."
    }
  }
};<|endoftext|>


<commit_before>/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2017-2019 Mickael Jeanroy
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

'use strict';

const hasIn = require('lodash.hasin');
const isEmpty = require('lodash.isempty');
const isNil = require('lodash.isnil');
const omitBy = require('lodash.omitby');
const MagicString = require('magic-string');
const diff = require('diff');
const prettier = require('prettier');

/**
 * The plugin options that are currently supported.
 * @type {Array<string>}
 */
const OPTIONS = [
  'sourcemap',
  'sourceMap',
  'cwd',
];

module.exports = class RollupPluginPrettier {
  /**
   * Initialize plugin & prettier.
   *
   * @param {Object} options Initalization option.
   */
  constructor(options = {}) {
    // Initialize plugin name.
    this.name = 'rollup-plugin-prettier';

    // Initialize main options.
    this._options = omitBy((options), (value, key) => (
      OPTIONS.indexOf(key) >= 0
    ));

    // Try to resolve config file it it exists
    // Be careful, `resolveConfig` function does not exist on old version of prettier.
    if (prettier.resolveConfig) {
      const cwd = hasIn(options, 'cwd') ? options.cwd : process.cwd();
      const configOptions = prettier.resolveConfig.sync(cwd);
      if (configOptions != null) {
        this._options = Object.assign(configOptions, this._options || {});
      }
    }

    // Reset empty options.
    if (isEmpty(this._options)) {
      this._options = undefined;
    }

    // Check if sourcemap is enabled by default.
    if (hasIn(options, 'sourcemap')) {
      this._sourcemap = options.sourcemap;
    } else if (hasIn(options, 'sourceMap')) {
      console.warn(`[${this.name}] The sourceMap option is deprecated, please use sourcemap instead.`);
      this._sourcemap = options.sourceMap;
    } else {
      this._sourcemap = null;
    }
  }

  /**
   * Get the `sourcemap` value.
   *
   * @return {boolean} The `sourcemap` flag value.
   */
  getSourcemap() {
    return this._sourcemap;
  }

  /**
   * Disable sourcemap.
   *
   * @return {void}
   */
  enableSourcemap() {
    this._sourcemap = true;
  }

  /**
   * Reformat source code using prettier.
   *
   * @param {string} source The source code to reformat.
   * @param {boolean} sourcemap If sourcemap should be generated or not.
   * @return {Object} The transformation result.
   */
  reformat(source, sourcemap) {
    const output = prettier.format(source, this._options);

    // Should we generate sourcemap?
    // The sourcemap option may be a boolean or any truthy value (such as a `string`).
    // Note that this option should be false by default as it may take a (very) long time.
    const defaultSourcemap = isNil(this._sourcemap) ? false : this._sourcemap;
    const outputSourcemap = isNil(sourcemap) ? defaultSourcemap : sourcemap;
    if (!outputSourcemap) {
      return {code: output};
    }

    console.warn(`[${this.name}] Sourcemap is enabled, computing diff is required`);
    console.warn(`[${this.name}] This may take a moment (depends on the size of your bundle)`);

    const magicString = new MagicString(source);
    const changes = diff.diffChars(source, output);

    if (changes && changes.length > 0) {
      let idx = 0;

      changes.forEach((part) => {
        if (part.added) {
          magicString.prependLeft(idx, part.value);
          idx -= part.count;
        } else if (part.removed) {
          magicString.remove(idx, idx + part.count);
        }

        idx += part.count;
      });
    }

    return {
      code: magicString.toString(),
      map: magicString.generateMap({
        hires: true,
      }),
    };
  }
};
<commit_msg>chore: use set instead of array<commit_after>/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2017-2019 Mickael Jeanroy
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

'use strict';

const hasIn = require('lodash.hasin');
const isEmpty = require('lodash.isempty');
const isNil = require('lodash.isnil');
const omitBy = require('lodash.omitby');
const MagicString = require('magic-string');
const diff = require('diff');
const prettier = require('prettier');

/**
 * The plugin options that are currently supported.
 * @type {Set<string>}
 */
const OPTIONS = new Set([
  'sourcemap',
  'sourceMap',
  'cwd',
]);

module.exports = class RollupPluginPrettier {
  /**
   * Initialize plugin & prettier.
   *
   * @param {Object} options Initalization option.
   */
  constructor(options = {}) {
    // Initialize plugin name.
    this.name = 'rollup-plugin-prettier';

    // Initialize main options.
    this._options = omitBy((options), (value, key) => (
      OPTIONS.has(key)
    ));

    // Try to resolve config file it it exists
    // Be careful, `resolveConfig` function does not exist on old version of prettier.
    if (prettier.resolveConfig) {
      const cwd = hasIn(options, 'cwd') ? options.cwd : process.cwd();
      const configOptions = prettier.resolveConfig.sync(cwd);
      if (configOptions != null) {
        this._options = Object.assign(configOptions, this._options || {});
      }
    }

    // Reset empty options.
    if (isEmpty(this._options)) {
      this._options = undefined;
    }

    // Check if sourcemap is enabled by default.
    if (hasIn(options, 'sourcemap')) {
      this._sourcemap = options.sourcemap;
    } else if (hasIn(options, 'sourceMap')) {
      console.warn(`[${this.name}] The sourceMap option is deprecated, please use sourcemap instead.`);
      this._sourcemap = options.sourceMap;
    } else {
      this._sourcemap = null;
    }
  }

  /**
   * Get the `sourcemap` value.
   *
   * @return {boolean} The `sourcemap` flag value.
   */
  getSourcemap() {
    return this._sourcemap;
  }

  /**
   * Disable sourcemap.
   *
   * @return {void}
   */
  enableSourcemap() {
    this._sourcemap = true;
  }

  /**
   * Reformat source code using prettier.
   *
   * @param {string} source The source code to reformat.
   * @param {boolean} sourcemap If sourcemap should be generated or not.
   * @return {Object} The transformation result.
   */
  reformat(source, sourcemap) {
    const output = prettier.format(source, this._options);

    // Should we generate sourcemap?
    // The sourcemap option may be a boolean or any truthy value (such as a `string`).
    // Note that this option should be false by default as it may take a (very) long time.
    const defaultSourcemap = isNil(this._sourcemap) ? false : this._sourcemap;
    const outputSourcemap = isNil(sourcemap) ? defaultSourcemap : sourcemap;
    if (!outputSourcemap) {
      return {code: output};
    }

    console.warn(`[${this.name}] Sourcemap is enabled, computing diff is required`);
    console.warn(`[${this.name}] This may take a moment (depends on the size of your bundle)`);

    const magicString = new MagicString(source);
    const changes = diff.diffChars(source, output);

    if (changes && changes.length > 0) {
      let idx = 0;

      changes.forEach((part) => {
        if (part.added) {
          magicString.prependLeft(idx, part.value);
          idx -= part.count;
        } else if (part.removed) {
          magicString.remove(idx, idx + part.count);
        }

        idx += part.count;
      });
    }

    return {
      code: magicString.toString(),
      map: magicString.generateMap({
        hires: true,
      }),
    };
  }
};
<|endoftext|>


<commit_before>import leftPad from 'left-pad'
import request from 'request-promise'
import Particle from 'particle-api-js'
import { COBOT_SUBDOMAIN } from '../../config/config'

const particle = new Particle()
const Checkin = require('mongoose').model('Checkin')
const Membership = require('mongoose').model('Membership')

export default async (req, res) => {

  // Immediately return for Particle
  res.sendStatus(200)

  console.log('[checkin] Request body:', req.body)

  // Hack because cards start with 0's but
  // the Arduino app has them stripped off.
  // RFID card numbers are 10 characters long
  const accessToken = leftPad(req.body.data, 10, '0')

  console.log('[checkin] Access token:', accessToken)

  const member = await Membership.findOne({ accessToken })

  console.log('[checkin] Found member:', member)

  const auth = req.currentAccount.particleAccessToken

  if (member) {

    console.log('[checkin] Card accepted:', accessToken)

    await particle.publishEvent({
      name: 'sentry/allow',
      data: member.name,
      auth,
    })

    const checkin = await Checkin.create({
      accessToken,
      cobotId: member.cobotId,
      memberId: member.id,
      memberName: member.name,
      memberPlan: member.plan,
    })

  } else {

    console.error('[checkin] Card denied:', accessToken)

    await particle.publishEvent({
      name: 'sentry/deny',
      data: {},
      auth,
    })
  }

  // TODO: log checkin in cobot
  //{
    //"eventName": "Your event name",
    //"data": "Your event contents",
    //"published_at": "When it was published",
    //"coreid": "Your device ID"
  //}
  //const memberId =
  //const accessToken = req.currentAccount.cobotAccessToken
  //const tokens = await request({
    //headers: { 'Authorization': `Bearer ${accessToken}` },
    //method: 'POST',
    //json: true,
    //uri: `https://${COBOT_SUBDOMAIN}.cobot.me/api/memberships/${memberId}/work_sessions`,
  //})

}
<commit_msg>Welcome message function.<commit_after>import leftPad from 'left-pad'
import request from 'request-promise'
import Particle from 'particle-api-js'
import { COBOT_SUBDOMAIN } from '../../config/config'

const particle = new Particle()
const Checkin = require('mongoose').model('Checkin')
const Membership = require('mongoose').model('Membership')

function welcomeMessage(member) {
  const lines = [
    'Why hello there!',
    member.name,
  ]
  return lines.join('\n')
}

export default async (req, res) => {

  // Immediately return for Particle
  res.sendStatus(200)

  console.log('[checkin] Request body:', req.body)

  // Hack because cards start with 0's but
  // the Arduino app has them stripped off.
  // RFID card numbers are 10 characters long
  const accessToken = leftPad(req.body.data, 10, '0')

  console.log('[checkin] Access token:', accessToken)

  const member = await Membership.findOne({ accessToken })

  console.log('[checkin] Found member:', member)

  const auth = req.currentAccount.particleAccessToken

  if (member) {

    console.log('[checkin] Card accepted:', accessToken)

    await particle.publishEvent({
      name: 'sentry/allow',
      data: welcomeMessage(member),
      auth,
    })

    const checkin = await Checkin.create({
      accessToken,
      cobotId: member.cobotId,
      memberId: member.id,
      memberName: member.name,
      memberPlan: member.plan,
    })

  } else {

    console.error('[checkin] Card denied:', accessToken)

    await particle.publishEvent({
      name: 'sentry/deny',
      data: {},
      auth,
    })
  }

  // TODO: log checkin in cobot
  //{
    //"eventName": "Your event name",
    //"data": "Your event contents",
    //"published_at": "When it was published",
    //"coreid": "Your device ID"
  //}
  //const memberId =
  //const accessToken = req.currentAccount.cobotAccessToken
  //const tokens = await request({
    //headers: { 'Authorization': `Bearer ${accessToken}` },
    //method: 'POST',
    //json: true,
    //uri: `https://${COBOT_SUBDOMAIN}.cobot.me/api/memberships/${memberId}/work_sessions`,
  //})

}
<|endoftext|>


<commit_before>import request from 'request-promise'
import particle from 'particle-api-js'
import { COBOT_SUBDOMAIN } from '../../config/config'

const Checkin = require('mongoose').model('Checkin')
const Membership = require('mongoose').model('Membership')

const debug = require('debug')('sentry:routes:checkins:create')

export default async (req, res) => {

  // Immediately return for Particle
  res.status(200)

  console.log('body', req.body)
  debug('checkin', req.body)

  // Hack because cards start with three 0's but
  // the Arduino app has them stripped off.
  const accessToken = '000' + req.body.data

  const member = await Membership.findOne({ accessToken })
  console.log('member', member)

  const auth = req.currentAccount.particleAccessToken

  if (member) {
    // Let them in!
    await particle.publishEvent({
      name: 'sentry/allow',
      data: {},
      auth,
    })
  } else {
    // Lock them out.
    await particle.publishEvent({
      name: 'sentry/deny',
      data: {},
      auth,
    })
  }

  const checkin = await Checkin.create({
    accessToken,
    cobotId: member.cobotId,
    memberId: member.id,
    memberName: member.name,
    memberPlan: member.plan,
  })

  //{
    //"eventName": "Your event name",
    //"data": "Your event contents",
    //"published_at": "When it was published",
    //"coreid": "Your device ID"
  //}
  //const memberId =
  //const accessToken = req.currentAccount.cobotAccessToken
  //const tokens = await request({
    //headers: { 'Authorization': `Bearer ${accessToken}` },
    //method: 'POST',
    //json: true,
    //uri: `https://${COBOT_SUBDOMAIN}.cobot.me/api/memberships/${memberId}/work_sessions`,
  //})

}
<commit_msg>Fix initialization of particle.<commit_after>import request from 'request-promise'
import Particle from 'particle-api-js'
import { COBOT_SUBDOMAIN } from '../../config/config'

const particle = new Particle()
const Checkin = require('mongoose').model('Checkin')
const Membership = require('mongoose').model('Membership')

const debug = require('debug')('sentry:routes:checkins:create')

export default async (req, res) => {

  // Immediately return for Particle
  res.status(200)

  console.log('body', req.body)
  debug('checkin', req.body)

  // Hack because cards start with three 0's but
  // the Arduino app has them stripped off.
  const accessToken = '000' + req.body.data

  const member = await Membership.findOne({ accessToken })
  console.log('member', member)

  const auth = req.currentAccount.particleAccessToken

  if (member) {
    // Let them in!
    await particle.publishEvent({
      name: 'sentry/allow',
      data: {},
      auth,
    })
  } else {
    // Lock them out.
    await particle.publishEvent({
      name: 'sentry/deny',
      data: {},
      auth,
    })
  }

  const checkin = await Checkin.create({
    accessToken,
    cobotId: member.cobotId,
    memberId: member.id,
    memberName: member.name,
    memberPlan: member.plan,
  })

  //{
    //"eventName": "Your event name",
    //"data": "Your event contents",
    //"published_at": "When it was published",
    //"coreid": "Your device ID"
  //}
  //const memberId =
  //const accessToken = req.currentAccount.cobotAccessToken
  //const tokens = await request({
    //headers: { 'Authorization': `Bearer ${accessToken}` },
    //method: 'POST',
    //json: true,
    //uri: `https://${COBOT_SUBDOMAIN}.cobot.me/api/memberships/${memberId}/work_sessions`,
  //})

}
<|endoftext|>


<commit_before>(function(){'use strict';var f=[];function g(a){f.push(a);1===f.length&&l()}function m(){for(;f.length;)f[0](),f.shift()}if(window.MutationObserver){var n=document.createElement("div");(new MutationObserver(m)).observe(n,{attributes:!0});var l=function(){n.setAttribute("x",0)}}else l=function(){setTimeout(m)};function p(a){this.a=q;this.b=void 0;this.f=[];var b=this;try{a(function(a){r(b,a)},function(a){t(b,a)})}catch(c){t(b,c)}}var q=2;function u(a){return new p(function(b,c){c(a)})}function v(a){return new p(function(b){b(a)})}
function r(a,b){if(a.a===q){if(b===a)throw new TypeError("Promise settled with itself.");var c=!1;try{var d=b&&b.then;if(null!==b&&"object"===typeof b&&"function"===typeof d){d.call(b,function(b){c||r(a,b);c=!0},function(b){c||t(a,b);c=!0});return}}catch(e){c||t(a,e);return}a.a=0;a.b=b;w(a)}}function t(a,b){if(a.a===q){if(b===a)throw new TypeError("Promise settled with itself.");a.a=1;a.b=b;w(a)}}
function w(a){g(function(){if(a.a!==q)for(;a.f.length;){var b=a.f.shift(),c=b[0],d=b[1],e=b[2],b=b[3];try{0===a.a?"function"===typeof c?e(c.call(void 0,a.b)):e(a.b):1===a.a&&("function"===typeof d?e(d.call(void 0,a.b)):b(a.b))}catch(h){b(h)}}})}p.prototype.g=function(a){return this.c(void 0,a)};p.prototype.c=function(a,b){var c=this;return new p(function(d,e){c.f.push([a,b,d,e]);w(c)})};
function x(a){return new p(function(b,c){function d(c){return function(d){h[c]=d;e+=1;e===a.length&&b(h)}}var e=0,h=[];0===a.length&&b(h);for(var k=0;k<a.length;k+=1)v(a[k]).c(d(k),c)})}function y(a){return new p(function(b,c){for(var d=0;d<a.length;d+=1)v(a[d]).c(b,c)})};window.Promise||(window.Promise=p,window.Promise.resolve=v,window.Promise.reject=u,window.Promise.race=y,window.Promise.all=x,window.Promise.prototype.then=p.prototype.c,window.Promise.prototype["catch"]=p.prototype.g);}());

(function(){'use strict';function h(a){document.body?a():document.addEventListener("DOMContentLoaded",a)};function v(a){this.a=document.createElement("div");this.a.setAttribute("aria-hidden","true");this.a.appendChild(document.createTextNode(a));this.b=document.createElement("span");this.c=document.createElement("span");this.h=document.createElement("span");this.g=document.createElement("span");this.f=-1;this.b.style.cssText="display:inline-block;position:absolute;height:100%;width:100%;overflow:scroll;font-size:16px;";this.c.style.cssText="display:inline-block;position:absolute;height:100%;width:100%;overflow:scroll;font-size:16px;";
this.g.style.cssText="display:inline-block;position:absolute;height:100%;width:100%;overflow:scroll;font-size:16px;";this.h.style.cssText="display:inline-block;width:200%;height:200%;font-size:16px;";this.b.appendChild(this.h);this.c.appendChild(this.g);this.a.appendChild(this.b);this.a.appendChild(this.c)}
function w(a,c,b){a.a.style.cssText="min-width:20px;min-height:20px;display:inline-block;overflow:hidden;position:absolute;width:auto;margin:0;padding:0;top:-999px;left:-999px;white-space:nowrap;font-size:100px;font-family:"+c+";"+b}function x(a){var c=a.a.offsetWidth,b=c+100;a.g.style.width=b+"px";a.c.scrollLeft=b;a.b.scrollLeft=a.b.scrollWidth+100;return a.f!==c?(a.f=c,!0):!1}
function y(a,c){a.b.addEventListener("scroll",function(){x(a)&&null!==a.a.parentNode&&c(a.f)},!1);a.c.addEventListener("scroll",function(){x(a)&&null!==a.a.parentNode&&c(a.f)},!1);x(a)};function z(a,c){var b=c||{};this.family=a;this.style=b.style||"normal";this.variant=b.variant||"normal";this.weight=b.weight||"normal";this.stretch=b.stretch||"normal";this.featureSettings=b.featureSettings||"normal"}var B=null;
z.prototype.a=function(a,c){var b=a||"BESbswy",C=c||3E3,k="font-style:"+this.style+";font-variant:"+this.variant+";font-weight:"+this.weight+";font-stretch:"+this.stretch+";font-feature-settings:"+this.featureSettings+";-moz-font-feature-settings:"+this.featureSettings+";-webkit-font-feature-settings:"+this.featureSettings+";",g=document.createElement("div"),l=new v(b),m=new v(b),n=new v(b),d=-1,e=-1,f=-1,q=-1,r=-1,t=-1,p=this;return new Promise(function(a,b){function c(){null!==g.parentNode&&g.parentNode.removeChild(g)}
function u(){if(-1!==d&&-1!==e||-1!==d&&-1!==f||-1!==e&&-1!==f)if(d===e||d===f||e===f){if(null===B){var b=/AppleWebKit\/([0-9]+)(?:\.([0-9]+))/.exec(window.navigator.userAgent);B=!!b&&(536>parseInt(b[1],10)||536===parseInt(b[1],10)&&11>=parseInt(b[2],10))}B?d===q&&e===q&&f===q||d===r&&e===r&&f===r||d===t&&e===t&&f===t||(c(),a(p)):(c(),a(p))}}h(function(){function a(){if(Date.now()-D>=C)c(),b(p);else{var A=document.hidden;if(!0===A||void 0===A)d=l.a.offsetWidth,e=m.a.offsetWidth,f=n.a.offsetWidth,
u();setTimeout(a,50)}}var D=Date.now();w(l,"sans-serif",k);w(m,"serif",k);w(n,"monospace",k);g.appendChild(l.a);g.appendChild(m.a);g.appendChild(n.a);document.body.appendChild(g);q=l.a.offsetWidth;r=m.a.offsetWidth;t=n.a.offsetWidth;a();y(l,function(a){d=a;u()});w(l,'"'+p.family+'",sans-serif',k);y(m,function(a){e=a;u()});w(m,'"'+p.family+'",serif',k);y(n,function(a){f=a;u()});w(n,'"'+p.family+'",monospace',k)})})};window.FontFaceObserver=z;window.FontFaceObserver.prototype.check=z.prototype.a;}());
<commit_msg>Add dist build.<commit_after>(function(){'use strict';var f=[];function g(a){f.push(a);1===f.length&&l()}function m(){for(;f.length;)f[0](),f.shift()}if(window.MutationObserver){var n=document.createElement("div");(new MutationObserver(m)).observe(n,{attributes:!0});var l=function(){n.setAttribute("x",0)}}else l=function(){setTimeout(m)};function p(a){this.a=q;this.b=void 0;this.f=[];var b=this;try{a(function(a){r(b,a)},function(a){t(b,a)})}catch(c){t(b,c)}}var q=2;function u(a){return new p(function(b,c){c(a)})}function v(a){return new p(function(b){b(a)})}
function r(a,b){if(a.a===q){if(b===a)throw new TypeError("Promise settled with itself.");var c=!1;try{var d=b&&b.then;if(null!==b&&"object"===typeof b&&"function"===typeof d){d.call(b,function(b){c||r(a,b);c=!0},function(b){c||t(a,b);c=!0});return}}catch(e){c||t(a,e);return}a.a=0;a.b=b;w(a)}}function t(a,b){if(a.a===q){if(b===a)throw new TypeError("Promise settled with itself.");a.a=1;a.b=b;w(a)}}
function w(a){g(function(){if(a.a!==q)for(;a.f.length;){var b=a.f.shift(),c=b[0],d=b[1],e=b[2],b=b[3];try{0===a.a?"function"===typeof c?e(c.call(void 0,a.b)):e(a.b):1===a.a&&("function"===typeof d?e(d.call(void 0,a.b)):b(a.b))}catch(h){b(h)}}})}p.prototype.g=function(a){return this.c(void 0,a)};p.prototype.c=function(a,b){var c=this;return new p(function(d,e){c.f.push([a,b,d,e]);w(c)})};
function x(a){return new p(function(b,c){function d(c){return function(d){h[c]=d;e+=1;e===a.length&&b(h)}}var e=0,h=[];0===a.length&&b(h);for(var k=0;k<a.length;k+=1)v(a[k]).c(d(k),c)})}function y(a){return new p(function(b,c){for(var d=0;d<a.length;d+=1)v(a[d]).c(b,c)})};window.Promise||(window.Promise=p,window.Promise.resolve=v,window.Promise.reject=u,window.Promise.race=y,window.Promise.all=x,window.Promise.prototype.then=p.prototype.c,window.Promise.prototype["catch"]=p.prototype.g);}());

(function(){'use strict';function k(a){document.body?a():document.addEventListener("DOMContentLoaded",a)};function v(a){this.a=document.createElement("div");this.a.setAttribute("aria-hidden","true");this.a.appendChild(document.createTextNode(a));this.b=document.createElement("span");this.c=document.createElement("span");this.h=document.createElement("span");this.g=document.createElement("span");this.f=-1;this.b.style.cssText="display:inline-block;position:absolute;height:100%;width:100%;overflow:scroll;font-size:16px;";this.c.style.cssText="display:inline-block;position:absolute;height:100%;width:100%;overflow:scroll;font-size:16px;";
this.g.style.cssText="display:inline-block;position:absolute;height:100%;width:100%;overflow:scroll;font-size:16px;";this.h.style.cssText="display:inline-block;width:200%;height:200%;font-size:16px;";this.b.appendChild(this.h);this.c.appendChild(this.g);this.a.appendChild(this.b);this.a.appendChild(this.c)}
function w(a,c,b){a.a.style.cssText="min-width:20px;min-height:20px;display:inline-block;overflow:hidden;position:absolute;width:auto;margin:0;padding:0;top:-999px;left:-999px;white-space:nowrap;font-size:100px;font-family:"+c+";"+b}function x(a){var c=a.a.offsetWidth,b=c+100;a.g.style.width=b+"px";a.c.scrollLeft=b;a.b.scrollLeft=a.b.scrollWidth+100;return a.f!==c?(a.f=c,!0):!1}
function y(a,c){a.b.addEventListener("scroll",function(){x(a)&&null!==a.a.parentNode&&c(a.f)},!1);a.c.addEventListener("scroll",function(){x(a)&&null!==a.a.parentNode&&c(a.f)},!1);x(a)};function z(a,c){var b=c||{};this.family=a;this.style=b.style||"normal";this.variant=b.variant||"normal";this.weight=b.weight||"normal";this.stretch=b.stretch||"normal";this.featureSettings=b.featureSettings||"normal";this.a=null}var A=null;
z.prototype.b=function(a,c){var b=a||"BESbswy",B=c||3E3,l="font-style:"+this.style+";font-variant:"+this.variant+";font-weight:"+this.weight+";font-stretch:"+this.stretch+";font-feature-settings:"+this.featureSettings+";-moz-font-feature-settings:"+this.featureSettings+";-webkit-font-feature-settings:"+this.featureSettings+";",d=document.createElement("div"),m=new v(b),n=new v(b),p=new v(b),e=-1,f=-1,g=-1,q=-1,r=-1,t=-1,h=this;d.dir="ltr";return new Promise(function(a,b){function c(){null!==d.parentNode&&
d.parentNode.removeChild(d)}function u(){if(-1!==e&&-1!==f||-1!==e&&-1!==g||-1!==f&&-1!==g)if(e===f||e===g||f===g){if(null===A){var b=/AppleWebKit\/([0-9]+)(?:\.([0-9]+))/.exec(window.navigator.userAgent);A=!!b&&(536>parseInt(b[1],10)||536===parseInt(b[1],10)&&11>=parseInt(b[2],10))}A?e===q&&f===q&&g===q||e===r&&f===r&&g===r||e===t&&f===t&&g===t||(c(),clearTimeout(h.a),a(h)):(c(),clearTimeout(h.a),a(h))}}k(function(){function a(){if(Date.now()-C>=B)c(),b(h);else{var d=document.hidden;if(!0===d||void 0===
d)e=m.a.offsetWidth,f=n.a.offsetWidth,g=p.a.offsetWidth,u();h.a=setTimeout(a,50)}}var C=Date.now();w(m,"sans-serif",l);w(n,"serif",l);w(p,"monospace",l);d.appendChild(m.a);d.appendChild(n.a);d.appendChild(p.a);document.body.appendChild(d);q=m.a.offsetWidth;r=n.a.offsetWidth;t=p.a.offsetWidth;a();y(m,function(a){e=a;u()});w(m,'"'+h.family+'",sans-serif',l);y(n,function(a){f=a;u()});w(n,'"'+h.family+'",serif',l);y(p,function(a){g=a;u()});w(p,'"'+h.family+'",monospace',l)})})};window.FontFaceObserver=z;window.FontFaceObserver.prototype.check=z.prototype.b;}());
<|endoftext|>


<commit_before>'use strict';

/*!
 * V4Fire Client Core
 * https://github.com/V4Fire/Client
 *
 * Released under the MIT license
 * https://github.com/V4Fire/Client/blob/master/LICENSE
 */

const
	$C = require('collection.js'),
	stylus = require('stylus'),
	pzlr = require('@pzlr/build-core');

const GLOBAL = {
	kits: {},
	space: {}
};

/**
 * Saves HEXs to the global space with its kit names
 *
 * @param {!Array} colors
 * @param {!string} name
 */
function saveToSpace(colors, name) {
	$C(colors).forEach((el) => {
		GLOBAL.space[el] = name;
	});
}

/**
 * Throws an error if a color
 * already exists in the global space
 *
 * @param {!RGBA} hex
 * @returns {RGBA}
 */
function checkInGlobalSpace(hex) {
	const
		color = hex.raw || hex.string || hex.name;

	if (GLOBAL.space[color]) {
		throw new Error(`Identical HEX was found at the kit with name '${GLOBAL.space[color]}'`);
	}

	return hex;
}

/**
 * Converts stylus nodes collection to a js array
 *
 * @param {!Object} nodes
 * @param {string} name
 * @returns {string[]}
 */
function kitFromNodes(nodes, name) {
	const
		str = nodes.toString().replace(/[ ()]/g, ''),
		values = str.split(',');

	saveToSpace(values, name);
	return values;
}

/**
 * Saves subset of colors to a global color sets variable
 *
 * @param {!Object} kit - subset
 * @param {string} nm - name of a subset
 */
function saveColorsKit(kit, nm) {
	const reduce = (s) => $C(s).reduce((res, el, name) => {
		if (el.nodes) {
			res[name] = [].concat([[]], kitFromNodes(el.nodes, name));
		}

		return res;
	}, {});

	if (nm) {
		GLOBAL.kits[nm] = reduce(kit);

	} else {
		const
			res = reduce(kit);

		$C(GLOBAL.kits).forEach((el, key) => {
			if (Array.isArray(el[0]) && res[key]) {
				res[key][0] = el[0];
			}
		});

		GLOBAL.kits = res;
	}
}

/**
 * Picks an rgba color from the specified hex string
 *
 * @param {string} str - hex value
 * @param {Object=} [meta] - additional info
 * @returns {string}
 */
function pickColor(str, meta = {}) {
	try {
		return new stylus.Parser(str).peek().val;

	} catch {
		throw new Error(`Can't find a color with the hex value ${str}. Additional info ${JSON.stringify(meta)}`);
	}
}

module.exports = function (style) {
	/**
	 * Sets the child colors kit with kits of parent projects
	 *
	 * @param {!Object} parent - parent kit
	 * @param {!Object} children - child kit
	 */
	style.define('inheritColors', (parent, children) => {
		parent = parent.vals;
		children = children.vals;
		saveColorsKit(children);

		$C(pzlr.config.dependencies).forEach((el, i) => {
			if (i === 0) {
				saveColorsKit(parent, el);

			} else if (parent[el]) {
				saveColorsKit(parent[el], el);
			}
		});
	});

	/**
	 * Sets a color theme kit for proto or global colors
	 *
	 * @param {!Object} kit - themed kit
	 * @param {!Object} proto - proto name
	 */
	style.define('setReservedColorKits', (kit, proto) => {
		$C(kit.vals).forEach((el, key) => {
			const
				base = proto ? GLOBAL.kits[proto.val] : GLOBAL.kits;

			if (!base) {
				throw new Error(`Field with name ${proto} not found`);
			}

			if (!base[key]) {
				base[key] = [[]];
			}

			base[key][0] = kitFromNodes(el.nodes, key);
		});
	});

	/**
	 * Sets a global colors kit
	 * @param {!Object} kit
	 */
	style.define('setGlobalColors', (kit) => {
		saveColorsKit(kit);
	});

	/**
	 * Returns a global color value
	 *
	 * @param {!Object} hueInput - color
	 * @param {!Object} numInput - color position in a kit
	 * @param {(!Object|boolean)} reservedInput - is value in reserved kit
	 * @param (!Object|boolean) baseInput - proto field name
	 * @returns {string}
	 */
	style.define('getGlobalColor', (hueInput, numInput, reservedInput = false, baseInput = false) => {
		if (arguments.length === 1 && hueInput.raw) {
			return checkInGlobalSpace(hueInput);
		}

		const
			{dependencies} = pzlr.config,
			hue = hueInput.string || hueInput.name,
			num = numInput.string || numInput.val,
			reserved = reservedInput && reservedInput.val || false,
			base = baseInput && baseInput.val || false,
			meta = {hue, num, reserved, base};

		let
			col = GLOBAL.kits[hue],
			res;

		if (!base) {
			if (col && (reserved && col[0][num - 1] || col[num])) {
				return reserved ? pickColor(col[0][num - 1], meta) : pickColor(col[num], meta);
			}

			$C(dependencies).some((el) => {
				col = GLOBAL.kits[el];

				if (col) {
					if (reserved && col[hue][0][num - 1]) {
						res = col[hue][0][num - 1];
						return true;

					} else if (!reserved && col[hue][num]) {
						res = col[hue][num];
						return true;
					}
				}
			});

		} else {
			const
				kit = GLOBAL.kits[base];

			if (kit && reserved) {
				res = kit[hue][0][num - 1];

			} else if (kit && kit[hue] && kit[hue][num]) {
				res = kit[hue][num];
			}
		}

		return pickColor(res, meta);
	});
};
<commit_msg>Fixed catch<commit_after>'use strict';

/*!
 * V4Fire Client Core
 * https://github.com/V4Fire/Client
 *
 * Released under the MIT license
 * https://github.com/V4Fire/Client/blob/master/LICENSE
 */

const
	$C = require('collection.js'),
	stylus = require('stylus'),
	pzlr = require('@pzlr/build-core');

const GLOBAL = {
	kits: {},
	space: {}
};

/**
 * Saves HEXs to the global space with its kit names
 *
 * @param {!Array} colors
 * @param {!string} name
 */
function saveToSpace(colors, name) {
	$C(colors).forEach((el) => {
		GLOBAL.space[el] = name;
	});
}

/**
 * Throws an error if a color
 * already exists in the global space
 *
 * @param {!RGBA} hex
 * @returns {RGBA}
 */
function checkInGlobalSpace(hex) {
	const
		color = hex.raw || hex.string || hex.name;

	if (GLOBAL.space[color]) {
		throw new Error(`Identical HEX was found at the kit with name '${GLOBAL.space[color]}'`);
	}

	return hex;
}

/**
 * Converts stylus nodes collection to a js array
 *
 * @param {!Object} nodes
 * @param {string} name
 * @returns {string[]}
 */
function kitFromNodes(nodes, name) {
	const
		str = nodes.toString().replace(/[ ()]/g, ''),
		values = str.split(',');

	saveToSpace(values, name);
	return values;
}

/**
 * Saves subset of colors to a global color sets variable
 *
 * @param {!Object} kit - subset
 * @param {string} nm - name of a subset
 */
function saveColorsKit(kit, nm) {
	const reduce = (s) => $C(s).reduce((res, el, name) => {
		if (el.nodes) {
			res[name] = [].concat([[]], kitFromNodes(el.nodes, name));
		}

		return res;
	}, {});

	if (nm) {
		GLOBAL.kits[nm] = reduce(kit);

	} else {
		const
			res = reduce(kit);

		$C(GLOBAL.kits).forEach((el, key) => {
			if (Array.isArray(el[0]) && res[key]) {
				res[key][0] = el[0];
			}
		});

		GLOBAL.kits = res;
	}
}

/**
 * Picks an rgba color from the specified hex string
 *
 * @param {string} str - hex value
 * @param {Object=} [meta] - additional info
 * @returns {string}
 */
function pickColor(str, meta = {}) {
	try {
		return new stylus.Parser(str).peek().val;

	} catch (_) {
		throw new Error(`Can't find a color with the hex value ${str}. Additional info ${JSON.stringify(meta)}`);
	}
}

module.exports = function (style) {
	/**
	 * Sets the child colors kit with kits of parent projects
	 *
	 * @param {!Object} parent - parent kit
	 * @param {!Object} children - child kit
	 */
	style.define('inheritColors', (parent, children) => {
		parent = parent.vals;
		children = children.vals;
		saveColorsKit(children);

		$C(pzlr.config.dependencies).forEach((el, i) => {
			if (i === 0) {
				saveColorsKit(parent, el);

			} else if (parent[el]) {
				saveColorsKit(parent[el], el);
			}
		});
	});

	/**
	 * Sets a color theme kit for proto or global colors
	 *
	 * @param {!Object} kit - themed kit
	 * @param {!Object} proto - proto name
	 */
	style.define('setReservedColorKits', (kit, proto) => {
		$C(kit.vals).forEach((el, key) => {
			const
				base = proto ? GLOBAL.kits[proto.val] : GLOBAL.kits;

			if (!base) {
				throw new Error(`Field with name ${proto} not found`);
			}

			if (!base[key]) {
				base[key] = [[]];
			}

			base[key][0] = kitFromNodes(el.nodes, key);
		});
	});

	/**
	 * Sets a global colors kit
	 * @param {!Object} kit
	 */
	style.define('setGlobalColors', (kit) => {
		saveColorsKit(kit);
	});

	/**
	 * Returns a global color value
	 *
	 * @param {!Object} hueInput - color
	 * @param {!Object} numInput - color position in a kit
	 * @param {(!Object|boolean)} reservedInput - is value in reserved kit
	 * @param (!Object|boolean) baseInput - proto field name
	 * @returns {string}
	 */
	style.define('getGlobalColor', (hueInput, numInput, reservedInput = false, baseInput = false) => {
		if (arguments.length === 1 && hueInput.raw) {
			return checkInGlobalSpace(hueInput);
		}

		const
			{dependencies} = pzlr.config,
			hue = hueInput.string || hueInput.name,
			num = numInput.string || numInput.val,
			reserved = reservedInput && reservedInput.val || false,
			base = baseInput && baseInput.val || false,
			meta = {hue, num, reserved, base};

		let
			col = GLOBAL.kits[hue],
			res;

		if (!base) {
			if (col && (reserved && col[0][num - 1] || col[num])) {
				return reserved ? pickColor(col[0][num - 1], meta) : pickColor(col[num], meta);
			}

			$C(dependencies).some((el) => {
				col = GLOBAL.kits[el];

				if (col) {
					if (reserved && col[hue][0][num - 1]) {
						res = col[hue][0][num - 1];
						return true;

					} else if (!reserved && col[hue][num]) {
						res = col[hue][num];
						return true;
					}
				}
			});

		} else {
			const
				kit = GLOBAL.kits[base];

			if (kit && reserved) {
				res = kit[hue][0][num - 1];

			} else if (kit && kit[hue] && kit[hue][num]) {
				res = kit[hue][num];
			}
		}

		return pickColor(res, meta);
	});
};
<|endoftext|>


<commit_before>var svgImg;

var polygons = {};

let currentPolygon = null;
let selectedPolygon = null;

let polygonId = 0;

function initSvg() {
    svgImg = document.getElementsByClassName('svg-img')[0];
    initCoordinates(svgImg);
}

function svgImgOnClick(event) {
    let point = getPoint(event);

    if (currentPolygon !== null) {
        if (currentPolygon.shouldClose(point.x, point.y)) {
            closePolygon(currentPolygon);
        } else {
            currentPolygon.addPoint(point.x, point.y);
        }
    } else {
        currentPolygon = new Polygon(point.x, point.y, polygonId);
        svgImg.append(currentPolygon.node);
        console.log(currentPolygon.node);

        // resetting classes and parameters values
        resetDOM();
    }
}

function svgImgOnClickSelect(event) {
    if (selectedPolygon !== null) {
        selectedPolygon.setSelected(false);
        selectedPolygon = null;
    }
}

function closePolygon() {
    currentPolygon.close();
    currentPolygon.onPolygonClick = onPolygonClick;
    polygons[polygonId] = currentPolygon;
    polygonId = polygonId + 1;

    // Assigning polygons points to the dataEntity (saving polygons points)
    onPolygonClosed(currentPolygon);

    if (selectedPolygon !== null) {
        selectedPolygon.setSelected(false);
    }

    selectedPolygon = currentPolygon;
    selectedPolygon.setSelected(true);
    showPolygonSelectedMessage();
    onPolygonSelected(selectedPolygon);

    currentPolygon = null;
}

function onPolygonClick(polygon) {

    if (selectedPolygon !== null) {
        selectedPolygon.setSelected(false);
    }

    polygon.setSelected(true);

    selectedPolygon = polygon;

    onPolygonSelected(selectedPolygon);

    showPolygonSelectedMessage();
    // Bring it to top
    svgImg.append(selectedPolygon.node);
}

function showPolygonSelectedMessage() {
    showMessage("Please, characterize the selected object in the right menu.",
        MessageTypeEnum.WARNING);
    setTimeout(function () {
        showMessage(
            "Please, markup the image displayed below using the tools from the block on the left.",
            MessageTypeEnum.INFO);
    }, 5000);
}

function resetSVGPolygonData() {
    polygons = {};
    currentPolygon = null;
    selectedPolygon = null;
    polygonId = 0;
}

/*
 * handles resizing of the svg for 1 to 1 mapping between image height and svg layer height
 * accepts Image() as input
 **/
function resizeSvg(img) {
    // make parent div for svg one
    let parent = document.getElementById("canvas-parent");

    let svg = document.getElementById("svg_img");

    // 28 is the width of two 14px paddings from each side of the canvas, specified in bootstrap
    parent.style.minHeight = (img.height + 28) + "px";

    let height = parent.clientHeight;
    let width = parent.clientWidth;

    // resizing svg if image width is smaller than the width of the block for the image
    // to avoid redundant markup over empty space in the block
    if (width > img.width) {
        width = img.width;
    }

    // modify both svg dimensions
    svg.style.width = width;
    svg.style.height = height;
}<commit_msg>Minor refactoring of svg.js<commit_after>var svgImg;

var polygons = {};

let currentPolygon = null;
let selectedPolygon = null;

let polygonId = 0;

function initSvg() {
    svgImg = document.getElementById("svg-img");
    initCoordinates(svgImg);
}

function svgImgOnClick(event) {
    let point = getPoint(event);

    if (currentPolygon !== null) {
        if (currentPolygon.shouldClose(point.x, point.y)) {
            closePolygon(currentPolygon);
        } else {
            currentPolygon.addPoint(point.x, point.y);
        }
    } else {
        currentPolygon = new Polygon(point.x, point.y, polygonId);
        svgImg.append(currentPolygon.node);
        console.log(currentPolygon.node);

        // resetting classes and parameters values
        resetDOM();
    }
}

function svgImgOnClickSelect(event) {
    if (selectedPolygon !== null) {
        selectedPolygon.setSelected(false);
        selectedPolygon = null;
    }
}

function closePolygon() {
    currentPolygon.close();
    currentPolygon.onPolygonClick = onPolygonClick;
    polygons[polygonId] = currentPolygon;
    polygonId = polygonId + 1;

    // Assigning polygons points to the dataEntity (saving polygons points)
    onPolygonClosed(currentPolygon);

    if (selectedPolygon !== null) {
        selectedPolygon.setSelected(false);
    }

    selectedPolygon = currentPolygon;
    selectedPolygon.setSelected(true);
    showPolygonSelectedMessage();
    onPolygonSelected(selectedPolygon);

    currentPolygon = null;
}

function onPolygonClick(polygon) {

    if (selectedPolygon !== null) {
        selectedPolygon.setSelected(false);
    }

    polygon.setSelected(true);

    selectedPolygon = polygon;

    onPolygonSelected(selectedPolygon);

    showPolygonSelectedMessage();
    // Bring it to top
    svgImg.append(selectedPolygon.node);
}

function showPolygonSelectedMessage() {
    showMessage("Please, characterize the selected object in the right menu.",
        MessageTypeEnum.WARNING);
    setTimeout(function () {
        showMessage(
            "Please, markup the image displayed below using the tools from the block on the left.",
            MessageTypeEnum.INFO);
    }, 5000);
}

function resetSVGPolygonData() {
    polygons = {};
    currentPolygon = null;
    selectedPolygon = null;
    polygonId = 0;
}

/*
 * handles resizing of the svg for 1 to 1 mapping between image height and svg layer height
 * accepts Image() as input
 **/
function resizeSvg(img) {
    // make parent div for svg one
    let parent = document.getElementById("canvas-parent");

    let svg = document.getElementById("svg_img");

    // 28 is the width of two 14px paddings from each side of the canvas, specified in bootstrap
    parent.style.minHeight = (img.height + 28) + "px";

    let height = parent.clientHeight;
    let width = parent.clientWidth;

    // resizing svg if image width is smaller than the width of the block for the image
    // to avoid redundant markup over empty space in the block
    if (width > img.width) {
        width = img.width;
    }

    // modify both svg dimensions
    svg.style.width = width;
    svg.style.height = height;
}<|endoftext|>


<commit_before>var sequential = require('../../src/search/sequential')

describe('Sequential search', () => {

  it('should find 4 in the list', () => {
    expect(sequential([1, 2, 3, 4, 6, 8], 4)).toBe(true)
  })

  it(`shouldn't find 4 in the list`, () => {
    expect(sequential([1,2,3,6,7,8,9,23], 4)).toBe(false)
  })

})
<commit_msg>sequential search test formatting<commit_after>const search = require('./sequential')

test(`find item in the list`, () => {
  expect(search([1, 2, 3, 4, 6, 8], 4)).toBe(true)
})

test(`don't find item in the list`, () => {
  expect(search([1, 2, 3, 6, 7, 8, 9, 23], 4)).toBe(false)
})
<|endoftext|>


<commit_before>import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {withStyles} from 'material-ui/styles';
import {BrowserRouter, Route, Switch} from "react-router-dom";
import Home from "./home/Home";
import Auth from "./auth/Auth";
import Callback from "./callback/Callback";
import People from "./people/People";
import {AppBar, Divider, Drawer, List, Toolbar, Typography} from "material-ui";
import NavLink from "./NavLink";
import PeopleIcon from "material-ui-icons/People";
import HomeIcon from "material-ui-icons/Home";

const navItems = [{
    exact: true,
    label: 'Hjem',
    path: '/',
    icon: <HomeIcon/>,
}, {
    label: 'Personer',
    path: '/people',
    icon: <PeopleIcon/>,
}];

const drawerWidth = 240;

const styles = theme => ({
    root: {
        width: '100%',
        height: 430,
        zIndex: 1,
        overflow: 'hidden',
    },
    appFrame: {
        position: 'relative',
        display: 'flex',
        width: '100%',
        height: '100%',
    },
    appBar: {
        position: 'absolute',
        width: `calc(100% - ${drawerWidth}px)`,
        marginLeft: drawerWidth,
        order: 1,
    },
    drawerPaper: {
        position: 'relative',
        height: 'auto',
        width: drawerWidth,
    },
    drawerHeader: {
        height: 56,
        [theme.breakpoints.up('sm')]: {
            height: 64,
        },
    },
    content: {
        backgroundColor: theme.palette.background.default,
        width: '100%',
        padding: theme.spacing.unit * 3,
        height: 'calc(100% - 56px)',
        marginTop: 56,
        [theme.breakpoints.up('sm')]: {
            height: 'calc(100% - 64px)',
            marginTop: 64,
        },
    },
});

let auth;

const handleAuthentication = (nextState, replace) => {
    if (/access_token|id_token|error/.test(nextState.location.hash)) {
        auth.handleAuthentication(nextState.location.hash);
    }
};

class App extends Component {

    render() {
        auth = new Auth(this.props.auth0Config);

        return (
            <BrowserRouter>
                <Route
                    render={({location}) => (

                                <AppBar className={this.props.classes.appBar}>
                                    <Toolbar>
                                        <Typography
                                            type="title"
                                            color="inherit"
                                            noWrap>
                                            Skotbu Vel
                                        </Typography>
                                    </Toolbar>
                                </AppBar>
                                <Drawer
                                    type="permanent"
                                    classes={{
                                        paper: this.props.classes.drawerPaper,
                                    }}
                                >

                                    <Divider/>
                                    <List>
                                        {navItems.map(props => <NavLink {...props} key={props.path}/>)}
                                    </List>
                                </Drawer>

                                    <Typography type="body2" noWrap>
                                        <Switch key={location.key}>
                                            <Route exact path="/" location={location} component={Home}/>
                                            <Route path="/home" location={location} component={Home}/>
                                            <Route path="/people" location={location} component={People}/>
                                            <Route path="/callback" render={(props) => {
                                                handleAuthentication(props);
                                                return <Callback {...props} />
                                            }}/>
                                        </Switch>
                                    </Typography>

                    )}
                />
            </BrowserRouter>
        );
    }
}

App.propTypes = {
    classes: PropTypes.object.isRequired,
};

export default withStyles(styles)(App);<commit_msg>Fixed the app bar line. It's now drawn up in full height. Changed height of main element so all rows in table are visible.<commit_after>import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {withStyles} from 'material-ui/styles';
import {BrowserRouter, Route, Switch} from "react-router-dom";
import Home from "./home/Home";
import Auth from "./auth/Auth";
import Callback from "./callback/Callback";
import People from "./people/People";
import {AppBar, Divider, Drawer, List, Toolbar, Typography} from "material-ui";
import NavLink from "./NavLink";
import PeopleIcon from "material-ui-icons/People";
import HomeIcon from "material-ui-icons/Home";

const navItems = [{
    exact: true,
    label: 'Hjem',
    path: '/',
    icon: <HomeIcon/>,
}, {
    label: 'Personer',
    path: '/people',
    icon: <PeopleIcon/>,
}];

const drawerWidth = 240;

const styles = theme => ({
    root: {
        width: '100%',
        height: '100%',
        zIndex: 1,
        overflow: 'hidden',
    },
    appFrame: {
        position: 'relative',
        display: 'flex',
        width: '100%',
        height: '100%',
    },
    appBar: {
        position: 'absolute',
        width: `calc(100% - ${drawerWidth}px)`,
        marginLeft: drawerWidth,
        order: 1,
    },
    drawerPaper: {
        position: 'relative',
        height: '100%',
        width: drawerWidth,
    },
    drawerHeader: {
        height: 56,
        [theme.breakpoints.up('sm')]: {
            height: 64,
        },
    },
    content: {
        backgroundColor: theme.palette.background.default,
        width: '100%',
        padding: theme.spacing.unit * 3,
        height: 'calc(100% - 56px)',
        marginTop: 56,
        [theme.breakpoints.up('sm')]: {
            height: 'calc(100% - 64px)',
            marginTop: 64,
        },
    },
});

let auth;

const handleAuthentication = (nextState, replace) => {
    if (/access_token|id_token|error/.test(nextState.location.hash)) {
        auth.handleAuthentication(nextState.location.hash);
    }
};

class App extends Component {

    render() {
        auth = new Auth(this.props.auth0Config);

        return (
            <BrowserRouter>
                <Route
                    render={({location}) => (

                                <AppBar className={this.props.classes.appBar}>
                                    <Toolbar>
                                        <Typography
                                            type="title"
                                            color="inherit"
                                            noWrap>
                                            Skotbu Vel
                                        </Typography>
                                    </Toolbar>
                                </AppBar>
                                <Drawer
                                    type="permanent"
                                    classes={{
                                        paper: this.props.classes.drawerPaper,
                                    }}
                                >

                                    <Divider/>
                                    <List>
                                        {navItems.map(props => <NavLink {...props} key={props.path}/>)}
                                    </List>
                                </Drawer>

                                    <Typography type="body2" noWrap>
                                        <Switch key={location.key}>
                                            <Route exact path="/" location={location} component={Home}/>
                                            <Route path="/home" location={location} component={Home}/>
                                            <Route path="/people" location={location} component={People}/>
                                            <Route path="/callback" render={(props) => {
                                                handleAuthentication(props);
                                                return <Callback {...props} />
                                            }}/>
                                        </Switch>
                                    </Typography>

                    )}
                />
            </BrowserRouter>
        );
    }
}

App.propTypes = {
    classes: PropTypes.object.isRequired,
};

export default withStyles(styles)(App);<|endoftext|>


<commit_before>"use strict"

var util = require('./util');
var view = require('./view');
var backendQuery = require('./backendQuery');

var courses = {}
var loadedCourseCodes = {}
var selectedOptions = {}

view.initHandlebars(selectedOptions)

export function addCourse() {
    var courseCode = document.getElementById("course_code").value
    if (loadedCourseCodes[courseCode]) {
        view.setStatusMessage("Předmět " + courseCode + " už je přidán")
        return false
    }
    backendQuery.addCourse(courseCode, function(res, err) {
        if (err) {
            view.setStatusMessage("Chyba: " + err)
        } else {
            res.forEach(addGroup)
            view.renderCourseList(courses)
            loadedCourseCodes[courseCode] = true
            view.setStatusMessage("Přidán předmět " + courseCode)
        }
    })
    view.setStatusMessage("Hledám předmět " + courseCode)

    return false // prevents default form submission behavior (refreshing the page)
}

function addGroup(group) {
    if (group.length == 0) {
        return
    }
    var type = group[0].type
    var name = group[0].name
    var id = type + ";" + name

    if (courses[id] === undefined) {
        courses[id] = {
            id: id,
            name: name + " (" + ((type==='P') ? "přednáška" : "seminář") + ")",
            options: [],
            allowed: true,
        }
    }
    group.allowed = true
    // We remember the index so that we can get back to this group when passing a filtered version
    // of options to the solver
    group.optionId = courses[id].options.length
    courses[id].options.push(group)
}

export function handleCheckbox(checkboxId, courseId, index) {
    if (index === undefined) { // Checkbox for the whole course
        // If something is checked, uncheck everything, otherwise check everything
        var checkAll = !courses[courseId].options.some(function(o) {return o.allowed})

        courses[courseId].options.forEach(function(o, i) {
            document.getElementById(checkboxId + "-" + i).checked = checkAll
            courses[courseId].options[i].allowed = checkAll
        })
        document.getElementById(checkboxId).checked = checkAll
    } else {
        courses[courseId].options[index].allowed = document.getElementById(checkboxId).checked
        document.getElementById(checkboxId.substring(0, checkboxId.length - 2)).checked =
            courses[courseId].options.some(function(o) {return o.allowed})
    }
}

// Called when the "sestavit rozvrh" button is pressed
export function createSchedule() {
    view.setStatusMessage("Sestavuji rozvrh")

    var queryArray =
        Object.values(courses).filter(function(c) {
            return c.allowed && c.options.some(function(o){return o.allowed})}
        ).map(function(c) {
            return {
                id: c.id,
                name: c.name,
                options: c.options.filter(function(o) {
                    return o.allowed
                })
            }
        })

    backendQuery.createSchedule(queryArray, function(res, err) {
        if (err) {
            view.setStatusMessage("Chyba: " + err)
        } else {
            view.setStatusMessage("Rozvrh sestaven: " + res)
            for (var course in courses) {
                delete selectedOptions[course]
            }
            for (var i = 0; i < queryArray.length; i++) {
                selectedOptions[queryArray[i].id] = queryArray[i].options[res[i]].optionId
            }
            view.renderCourseList(courses)
            view.renderSchedule(getNonOverlappingGroups())
        }
    })

    return false // prevents default form submission behavior (refreshing the page)
}

function getNonOverlappingGroups() {
    // For rendering purposes, we split the events into groups with no overlap.
    var eventsByDay = [[],[],[],[],[]]

    for (var course in selectedOptions) {
        courses[course].options[selectedOptions[course]].forEach(function(event) {
            eventsByDay[event.day].push(event)
        })
    }

    return eventsByDay.map(function(eventsOfDay) {
        eventsOfDay.sort(function(a, b) {
            return util.timeToInt(a.time_from) - util.timeToInt(b.time_from)
        })
        var groups = []
        eventsOfDay.forEach(function(event) {
            var validGroup = null
            groups.forEach(function(group) {
                if (util.timeToInt(group[group.length - 1].time_to) <= util.timeToInt(event.time_from)) {
                    validGroup = group
                }
            })
            if (validGroup) {
                validGroup.push(event)
            } else {
                groups.push([event])
            }
        })
        return groups
    })
}
<commit_msg>Handle non-selected courses in frontend<commit_after>"use strict"

var util = require('./util');
var view = require('./view');
var backendQuery = require('./backendQuery');

var courses = {}
var loadedCourseCodes = {}
var selectedOptions = {}

view.initHandlebars(selectedOptions)

export function addCourse() {
    var courseCode = document.getElementById("course_code").value
    if (loadedCourseCodes[courseCode]) {
        view.setStatusMessage("Předmět " + courseCode + " už je přidán")
        return false
    }
    backendQuery.addCourse(courseCode, function(res, err) {
        if (err) {
            view.setStatusMessage("Chyba: " + err)
        } else {
            res.forEach(addGroup)
            view.renderCourseList(courses)
            loadedCourseCodes[courseCode] = true
            view.setStatusMessage("Přidán předmět " + courseCode)
        }
    })
    view.setStatusMessage("Hledám předmět " + courseCode)

    return false // prevents default form submission behavior (refreshing the page)
}

function addGroup(group) {
    if (group.length == 0) {
        return
    }
    var type = group[0].type
    var name = group[0].name
    var id = type + ";" + name

    if (courses[id] === undefined) {
        courses[id] = {
            id: id,
            name: name + " (" + ((type==='P') ? "přednáška" : "seminář") + ")",
            options: [],
            allowed: true,
        }
    }
    group.allowed = true
    // We remember the index so that we can get back to this group when passing a filtered version
    // of options to the solver
    group.optionId = courses[id].options.length
    courses[id].options.push(group)
}

export function handleCheckbox(checkboxId, courseId, index) {
    if (index === undefined) { // Checkbox for the whole course
        // If something is checked, uncheck everything, otherwise check everything
        var checkAll = !courses[courseId].options.some(function(o) {return o.allowed})

        courses[courseId].options.forEach(function(o, i) {
            document.getElementById(checkboxId + "-" + i).checked = checkAll
            courses[courseId].options[i].allowed = checkAll
        })
        document.getElementById(checkboxId).checked = checkAll
    } else {
        courses[courseId].options[index].allowed = document.getElementById(checkboxId).checked
        document.getElementById(checkboxId.substring(0, checkboxId.length - 2)).checked =
            courses[courseId].options.some(function(o) {return o.allowed})
    }
}

// Called when the "sestavit rozvrh" button is pressed
export function createSchedule() {
    view.setStatusMessage("Sestavuji rozvrh")

    var queryArray =
        Object.values(courses).filter(function(c) {
            return c.allowed && c.options.some(function(o){return o.allowed})}
        ).map(function(c) {
            return {
                id: c.id,
                name: c.name,
                options: c.options.filter(function(o) {
                    return o.allowed
                })
            }
        })

    backendQuery.createSchedule(queryArray, function(res, err) {
        if (err) {
            view.setStatusMessage("Chyba: " + err)
        } else {
            view.setStatusMessage("Rozvrh sestaven: " + res)
            for (var course in courses) {
                delete selectedOptions[course]
            }
            for (var i = 0; i < queryArray.length; i++) {
                if (res[i] !== null) { // if null, the course is not selected
                    selectedOptions[queryArray[i].id] = queryArray[i].options[res[i]].optionId
                }
            }
            view.renderCourseList(courses)
            view.renderSchedule(getNonOverlappingGroups())
        }
    })

    return false // prevents default form submission behavior (refreshing the page)
}

function getNonOverlappingGroups() {
    // For rendering purposes, we split the events into groups with no overlap.
    var eventsByDay = [[],[],[],[],[]]

    for (var course in selectedOptions) {
        courses[course].options[selectedOptions[course]].forEach(function(event) {
            eventsByDay[event.day].push(event)
        })
    }

    return eventsByDay.map(function(eventsOfDay) {
        eventsOfDay.sort(function(a, b) {
            return util.timeToInt(a.time_from) - util.timeToInt(b.time_from)
        })
        var groups = []
        eventsOfDay.forEach(function(event) {
            var validGroup = null
            groups.forEach(function(group) {
                if (util.timeToInt(group[group.length - 1].time_to) <= util.timeToInt(event.time_from)) {
                    validGroup = group
                }
            })
            if (validGroup) {
                validGroup.push(event)
            } else {
                groups.push([event])
            }
        })
        return groups
    })
}
<|endoftext|>