<commit_before>var _ = require('../util')
var compile = require('../compiler/compile')
var templateParser = require('../parsers/template')
var transition = require('../transition')

module.exports = {

  bind: function () {
    var el = this.el
    if (!el.__vue__) {
      this.start = _.createAnchor('v-if-start')
      this.end = _.createAnchor('v-if-end')
      _.replace(el, this.end)
      _.before(this.start, this.end)
      if (el.tagName === 'TEMPLATE') {
        this.template = templateParser.parse(el, true)
      } else {
        this.template = document.createDocumentFragment()
        this.template.appendChild(templateParser.clone(el))
      }
      // compile the nested partial
      this.linker = compile(
        this.template,
        this.vm.$options,
        true
      )
    } else {
      this.invalid = true
      _.warn(
        'v-if="' + this.expression + '" cannot be ' +
        'used on an already mounted instance.'
      )
    }
  },

  update: function (value) {
    if (this.invalid) return
    if (value) {
      // avoid duplicate compiles, since update() can be
      // called with different truthy values
      if (!this.unlink) {
        this.compile()
      }
    } else {
      this.teardown()
    }
  },

  compile: function () {
    var vm = this.vm
    var frag = templateParser.clone(this.template)
    // the linker is not guaranteed to be present because
    // this function might get called by v-partial
    this.unlink = this.linker(vm, frag)
    transition.blockAppend(frag, this.end, vm)
    // call attached for all the child components created
    // during the compilation
    if (_.inDoc(vm.$el)) {
      var children = this.getContainedComponents()
      if (children) children.forEach(callAttach)
    }
  },

  teardown: function () {
    if (!this.unlink) return
    // collect children beforehand
    var children
    if (_.inDoc(this.vm.$el)) {
      children = this.getContainedComponents()
    }
    transition.blockRemove(this.start, this.end, this.vm)
    if (children) children.forEach(callDetach)
    this.unlink()
    this.unlink = null
  },

  getContainedComponents: function () {
    var vm = this.vm
    var start = this.start.nextSibling
    var end = this.end
    var selfCompoents =
      vm._children.length &&
      vm._children.filter(contains)
    var transComponents =
      vm._transCpnts &&
      vm._transCpnts.filter(contains)

    function contains (c) {
      var cur = start
      var next
      while (next !== end) {
        next = cur.nextSibling
        if (cur.contains(c.$el)) {
          return true
        }
        cur = next
      }
      return false
    }

    return selfCompoents
      ? transComponents
        ? selfCompoents.concat(transComponents)
        : selfCompoents
      : transComponents
  },

  unbind: function () {
    if (this.unlink) this.unlink()
  }

}

function callAttach (child) {
  if (!child._isAttached) {
    child._callHook('attached')
  }
}

function callDetach (child) {
  if (child._isAttached) {
    child._callHook('detached')
  }
}<commit_msg>fix getContainedComponents in IE 11<commit_after>var _ = require('../util')
var compile = require('../compiler/compile')
var templateParser = require('../parsers/template')
var transition = require('../transition')

module.exports = {

  bind: function () {
    var el = this.el
    if (!el.__vue__) {
      this.start = _.createAnchor('v-if-start')
      this.end = _.createAnchor('v-if-end')
      _.replace(el, this.end)
      _.before(this.start, this.end)
      if (el.tagName === 'TEMPLATE') {
        this.template = templateParser.parse(el, true)
      } else {
        this.template = document.createDocumentFragment()
        this.template.appendChild(templateParser.clone(el))
      }
      // compile the nested partial
      this.linker = compile(
        this.template,
        this.vm.$options,
        true
      )
    } else {
      this.invalid = true
      _.warn(
        'v-if="' + this.expression + '" cannot be ' +
        'used on an already mounted instance.'
      )
    }
  },

  update: function (value) {
    if (this.invalid) return
    if (value) {
      // avoid duplicate compiles, since update() can be
      // called with different truthy values
      if (!this.unlink) {
        this.compile()
      }
    } else {
      this.teardown()
    }
  },

  compile: function () {
    var vm = this.vm
    var frag = templateParser.clone(this.template)
    // the linker is not guaranteed to be present because
    // this function might get called by v-partial
    this.unlink = this.linker(vm, frag)
    transition.blockAppend(frag, this.end, vm)
    // call attached for all the child components created
    // during the compilation
    if (_.inDoc(vm.$el)) {
      var children = this.getContainedComponents()
      if (children) children.forEach(callAttach)
    }
  },

  teardown: function () {
    if (!this.unlink) return
    // collect children beforehand
    var children
    if (_.inDoc(this.vm.$el)) {
      children = this.getContainedComponents()
    }
    transition.blockRemove(this.start, this.end, this.vm)
    if (children) children.forEach(callDetach)
    this.unlink()
    this.unlink = null
  },

  getContainedComponents: function () {
    var vm = this.vm
    var start = this.start.nextSibling
    var end = this.end
    var selfCompoents =
      vm._children.length &&
      vm._children.filter(contains)
    var transComponents =
      vm._transCpnts &&
      vm._transCpnts.filter(contains)

    function contains (c) {
      var cur = start
      var next
      while (next !== end) {
        next = cur.nextSibling
        if (cur.contains && cur.contains(c.$el)) {
          return true
        }
        cur = next
      }
      return false
    }

    return selfCompoents
      ? transComponents
        ? selfCompoents.concat(transComponents)
        : selfCompoents
      : transComponents
  },

  unbind: function () {
    if (this.unlink) this.unlink()
  }

}

function callAttach (child) {
  if (!child._isAttached) {
    child._callHook('attached')
  }
}

function callDetach (child) {
  if (child._isAttached) {
    child._callHook('detached')
  }
}<|endoftext|>


<commit_before>import * as pickler from '../common/pickler';

import * as cosets      from '../fpgroups/cosets';
import * as delaney     from './delaney';
import * as properties  from './properties';
import * as derived     from './derived';
import * as delaney2d   from './delaney2d';
import * as delaney3d   from './delaney3d';
import * as fundamental from './fundamental';
import * as covers      from './covers';
import embed            from '../pgraphs/embedding';
import * as periodic    from '../pgraphs/periodic';
import * as symmetries  from '../pgraphs/symmetries';

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

const opsR = rationalLinearAlgebraModular;
const opsF = numericalLinearAlgebra;

const encode = pickler.serialize;
const decode = pickler.deserialize;

const range = n => [...Array(n).keys()];
const _remainingIndices = (ds, i) => ds.indices().filter(j => j != i);

const _edgeTranslations = cov => {
  const fg  = fundamental.fundamentalGroup(cov);
  const n   = fg.nrGenerators;
  const nul = opsR.nullSpace(cosets.relatorMatrix(n, fg.relators));
  const vec = rel => cosets.relatorAsVector(rel, n);

  return fg.edge2word.map(a => a.map(b => opsR.times(vec(b), nul)));
};

const _cornerShifts = (cov, e2t) => {
  const dim = delaney.dim(cov);
  const zero = opsR.vector(dim);

  const result = new Array(cov.size + 1).fill(0).map(_ => []);

  for (const i of cov.indices()) {
    const idcs = _remainingIndices(cov, i);
    for (const [Dk, k, D] of properties.traversal(cov, idcs, cov.elements())) {
      if (k == properties.traversal.root)
        result[D][i] = zero;
      else
        result[D][i] = opsR.minus(result[Dk][i], e2t[Dk][k] || zero);
    }
  }

  return result;
};

export const skeleton = cov => {
  const e2t = _edgeTranslations(cov);
  const c2s = _cornerShifts(cov, e2t);

  const chamber2node = {};
  let node = 1;
  for (const orb of properties.orbits(cov, _remainingIndices(cov, 0))) {
    for (const D of orb)
      chamber2node[D] = node;
    node += 1;
  }

  const zero = opsR.vector(delaney.dim(cov));
  const edges = properties.orbitReps(cov, _remainingIndices(cov, 1))
    .map(D => {
      const E = cov.s(0, D);
      const v = chamber2node[D];
      const w = chamber2node[E];
      const t = e2t[D][0] || zero;
      const sD = c2s[D][0];
      const sE = c2s[E][0];
      const s = opsR.minus(opsR.plus(t, sE), sD);

      return [v, w, s];
    })
    .toArray();

  return {
    graph: periodic.make(edges),
    chamber2node: chamber2node,
    edgeTranslations: e2t,
    cornerShifts: c2s
  };
};

const facialRing = (start, cov, skel) => {
  const result = [];

  let D = start;
  do {
    const E = cov.s(0, D);
    const sD = skel.cornerShifts[D][0];
    const sE = skel.cornerShifts[E][0];
    const t = skel.edgeTranslations[D][0];

    const head = skel.chamber2node[D];
    const tail = skel.chamber2node[E];
    const shift = opsR.minus(t ? opsR.plus(sE, t) : sE, sD);

    result.push(periodic.makeEdge(head, tail, shift));
    D = cov.s(1, E);
  }
  while (D != start);

  return result;
};

const cmpRingEdges = (a, b) => (
  opsR.cmp(a.head, b.head) || opsR.cmp(a.shift, b.shift)
);

const cmpRingTails = (a, b, i) => (
  i >= a.length ? 0 : cmpRingEdges(a[i], b[i]) || cmpRingTails(a, b, i + 1)
);

const cmpRings = (a, b) => cmpRingTails(a, b, 0);

const ringShifted = (r, i) => r.slice(i).concat(r.slice(0, i));
const ringReverse = r => r.slice().reverse().map(e => e.reverse());

const mapRing = (ring, sym) => ring.map(e => decode(sym.src2img[encode(e)]));

const canonicalRing = ring => {
  const rev = ringReverse(ring);
  let best = null;

  for (let i = 0; i < ring.length; ++i) {
    if (best == null || cmpRings(ringShifted(ring, i), best) < 0)
      best = ringShifted(ring, i);
    if (cmpRings(ringShifted(rev, i), best) < 0)
      best = ringShifted(rev, i);
  }

  return best;
};

const facialRings = (cov, skel) => (
  properties.orbitReps(cov, _remainingIndices(cov, 2))
    .map(D => canonicalRing(facialRing(D, cov, skel)))
);

const facePreservingSymmetries = (cov, skel) => {
  const rings = facialRings(cov, skel);

  const isRing = {};
  for (const r of rings)
    isRing[encode(canonicalRing(r))] = true;

  const syms = symmetries.symmetries(skel.graph).symmetries;
  const good = [];

  for (const sym of syms) {
    if (rings.every(r => isRing[encode(canonicalRing(mapRing(r, sym)))]))
      good.push(sym);
  }

  return good;
};

const chamberPositions = (cov, skel) => {
  const sum = v => v.reduce((x, y) => x == null ? y : opsR.plus(x, y));

  const pos = periodic.barycentricPlacement(skel.graph);
  const result = {};

  for (const D of cov.elements()) {
    const p = pos[skel.chamber2node[D]];
    const t = skel.cornerShifts[D][0];
    result[D] = [opsR.plus(p, t)];
  }

  for (let i = 1; i <= delaney.dim(cov); ++i) {
    const idcs = range(i);

    for (const orb of properties.orbits(cov, idcs, cov.elements())) {
      const s = opsR.div(
        sum(orb.map(E => opsR.minus(result[E][0], skel.cornerShifts[E][i]))),
        orb.length);

      for (const E of orb)
        result[E].push(opsR.plus(s, skel.cornerShifts[E][i]));
    }
  }

  return result;
};

const chamberBasis = (pos, D) => {
  const t = pos[D];
  return t.slice(1).map(v => opsR.minus(v, t[0]));
};

const determinant = M => {
  if (M.length == 2)
    return opsR.minus(opsR.times(M[0][0], M[1][1]),
                      opsR.times(M[0][1], M[1][0]));
  else if (M.length == 3)
    return opsR.times(M[0], opsR.crossProduct(M[1], M[2]));
  else
    return opsR.determinant(M);
};

const chamberDeterminant = (pos, D) => determinant(chamberBasis(pos, D));

const nonDegenerateChamber = (elms, pos) =>
  elms.find(D => opsR.ne(chamberDeterminant(pos, D), 0));

export const makeCover = ds =>
  delaney.dim(ds) == 3 ?
  delaney3d.pseudoToroidalCover(ds) :
  delaney2d.toroidalCover(ds);

const tileSurface3D = (pos, faces) => ({ pos, faces });

const tileSurface2D = (corners, faces) => {
  const pos = [];
  for (const p of corners) {
    pos.push(p.concat(-0.1));
    pos.push(p.concat(0.1));
  }

  const f = faces[0].map(i => 2 * i);

  faces = [f, f.map(x => x + 1).reverse()]
    .concat(f.map((x, i) => {
      const y = f[(i + 1) % f.length];
      return [y, x, x + 1, y + 1];
    }));

  return { pos, faces };
};

const tileSurface = (cov, skel, pos, ori, elms, idcs) => {
  const cOrbs = properties.orbits(cov, idcs.slice(1), elms);
  const cPos = cOrbs.map(
    ([D]) => opsF.plus(pos[skel.chamber2node[D]], skel.cornerShifts[D][0])
  );

  const cIdcs = [];
  cOrbs.forEach((orb, i) => {
    for (const D of orb)
      cIdcs[D] = i;
  });

  const faces = properties.orbits(cov, [0, 1], elms)
    .map(orb => ori[orb[0]] > 0 ? orb.reverse() : orb)
    .map(orb => orb.filter((D, i) => i % 2 == 0).map(D => cIdcs[D]));

  return (delaney.dim(cov) == 3 ? tileSurface3D : tileSurface2D)(cPos, faces);
};

const adjustedOrientation = (cov, pos) => {
  const D0 = nonDegenerateChamber(cov.elements(), pos);
  const sgn = opsR.sgn(chamberDeterminant(pos, D0));

  const ori = properties.partialOrientation(cov);
  if (sgn * ori[D0] < 0) {
    for (const D of cov.elements())
      ori[D] = -ori[D];
  }

  return ori;
};

export const tileSurfaces = (cov, skel, vertexPos, orbitReps) => {
  const dim = delaney.dim(cov);
  const idcs = range(dim);
  const pos = chamberPositions(cov, skel);
  const ori = adjustedOrientation(cov, pos);

  return orbitReps.map(D => tileSurface(
    cov, skel, vertexPos, ori, properties.orbit(cov, idcs, D), idcs));
};

const affineSymmetry = (D0, D1, pos) => {
  const bas = D => chamberBasis(pos, D);
  const linear = opsR.solve(bas(D0), bas(D1));
  const shift = opsR.minus(pos[D1][0], opsR.times(pos[D0][0], linear));

  return linear.map(r => r.concat(0)).concat([shift.concat([1])]);
};

export const tilesByTranslations = (ds, cov, skel) => {
  const dim = delaney.dim(cov);
  const pos = chamberPositions(cov, skel);
  const phi = properties.morphism(cov, 1, ds, 1);
  const idcs = range(dim);
  const tileOrbits = properties.orbits(cov, idcs);

  const orbitReps = [];
  const dsChamberToClassIndex = {};
  const covChamberToLatticeIndex = {};
  const tiles = [];

  for (const elms of tileOrbits) {
    const D0 = nonDegenerateChamber(elms, pos);
    const E0 = phi[D0];

    let classIndex = dsChamberToClassIndex[E0];
    let symmetry = opsR.identityMatrix(dim + 1);

    if (classIndex == null) {
      classIndex = orbitReps.length;

      for (const E of properties.orbit(ds, idcs, E0))
        dsChamberToClassIndex[E] = classIndex;

      orbitReps.push(D0);
    }
    else {
      const D0 = orbitReps[classIndex];
      const D1 = elms.find(D => phi[D] == phi[D0]);
      symmetry = affineSymmetry(D0, D1, pos);
    }

    for (const E of elms)
      covChamberToLatticeIndex[E] = tiles.length;

    tiles.push({ classIndex, symmetry, chambers: elms });
  }

  const e2t = _edgeTranslations(cov);
  const zero = Array(dim).fill(0);

  for (const tile of tiles) {
    const neighbors = [];
    for (const D of properties.orbitReps(cov, [0, 1], tile.chambers)) {
      const E = cov.s(dim, D);
      const latticeIndex = covChamberToLatticeIndex[E];
      const shift = e2t[D][dim] || zero;
      neighbors.push({ latticeIndex, shift });
    }

    tile.neighbors = neighbors;
  }

  const centers = orbitReps.map(D => pos[D][dim]);

  return { orbitReps, tiles, centers };
};

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

  const delaney = require('./delaney');
  const unitCells = require('../geometry/unitCells');

  const test = ds => {
    console.log(`ds = ${ds}`);

    const cov = makeCover(ds);
    console.log(`cover = ${cov}`);

    const skel = skeleton(cov);
    console.log(`skeleton = ${skel.graph}`);

    const embedding = embed(skel.graph).relaxed;
    const pos = embedding.positions;
    console.log(`vertex positions: ${JSON.stringify(pos)}`);

    const basis = unitCells.invariantBasis(embedding.gram);
    console.log(`invariant basis: ${basis}`);

    const rings = facialRings(cov, skel);
    console.log(`facial rings:`);
    for (const ring of rings)
      console.log(`  ${ring}`);

    const allSyms = symmetries.symmetries(skel.graph).symmetries;
    const goodSyms = facePreservingSymmetries(cov, skel);
    console.log(
      `skeleton has ${allSyms.length}, tiling ${goodSyms.length} symmetries`
    );
    const seeds = properties.orbitReps(cov, range(delaney.dim(cov)));
    const surfaces = tileSurfaces(cov, skel, pos, seeds);
    console.log(`tile surfaces:`);
    for (const surface of surfaces)
      console.log(`  ${JSON.stringify(surface)}`);

    console.log();
  }

  test(delaney.parse('<1.1:1 3:1,1,1,1:4,3,4>'));
  test(delaney.parse('<1.1:2 3:2,1 2,1 2,2:6,3 2,6>'));
  test(delaney.parse('<1.1:2 3:1 2,1 2,1 2,2:3 3,3 4,4>'));
  test(delaney.parse(
    '<1.1:6 3:2 4 6,1 2 3 5 6,3 4 5 6,2 3 4 5 6:6 4,2 3 3,8 4>'
  ));
  test(delaney.parse('<1.1:8:2 4 6 8,8 3 5 7,6 5 8 7:4,4>'));
  test(delaney.parse('<1.1:8:2 4 6 8,8 3 5 7,5 6 8 7:4,4>'));
}
<commit_msg>tilings: export facePreservingSymmetries()<commit_after>import * as pickler from '../common/pickler';

import * as cosets      from '../fpgroups/cosets';
import * as delaney     from './delaney';
import * as properties  from './properties';
import * as derived     from './derived';
import * as delaney2d   from './delaney2d';
import * as delaney3d   from './delaney3d';
import * as fundamental from './fundamental';
import * as covers      from './covers';
import embed            from '../pgraphs/embedding';
import * as periodic    from '../pgraphs/periodic';
import * as symmetries  from '../pgraphs/symmetries';

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

const opsR = rationalLinearAlgebraModular;
const opsF = numericalLinearAlgebra;

const encode = pickler.serialize;
const decode = pickler.deserialize;

const range = n => [...Array(n).keys()];
const _remainingIndices = (ds, i) => ds.indices().filter(j => j != i);

const _edgeTranslations = cov => {
  const fg  = fundamental.fundamentalGroup(cov);
  const n   = fg.nrGenerators;
  const nul = opsR.nullSpace(cosets.relatorMatrix(n, fg.relators));
  const vec = rel => cosets.relatorAsVector(rel, n);

  return fg.edge2word.map(a => a.map(b => opsR.times(vec(b), nul)));
};

const _cornerShifts = (cov, e2t) => {
  const dim = delaney.dim(cov);
  const zero = opsR.vector(dim);

  const result = new Array(cov.size + 1).fill(0).map(_ => []);

  for (const i of cov.indices()) {
    const idcs = _remainingIndices(cov, i);
    for (const [Dk, k, D] of properties.traversal(cov, idcs, cov.elements())) {
      if (k == properties.traversal.root)
        result[D][i] = zero;
      else
        result[D][i] = opsR.minus(result[Dk][i], e2t[Dk][k] || zero);
    }
  }

  return result;
};

export const skeleton = cov => {
  const e2t = _edgeTranslations(cov);
  const c2s = _cornerShifts(cov, e2t);

  const chamber2node = {};
  let node = 1;
  for (const orb of properties.orbits(cov, _remainingIndices(cov, 0))) {
    for (const D of orb)
      chamber2node[D] = node;
    node += 1;
  }

  const zero = opsR.vector(delaney.dim(cov));
  const edges = properties.orbitReps(cov, _remainingIndices(cov, 1))
    .map(D => {
      const E = cov.s(0, D);
      const v = chamber2node[D];
      const w = chamber2node[E];
      const t = e2t[D][0] || zero;
      const sD = c2s[D][0];
      const sE = c2s[E][0];
      const s = opsR.minus(opsR.plus(t, sE), sD);

      return [v, w, s];
    })
    .toArray();

  return {
    graph: periodic.make(edges),
    chamber2node: chamber2node,
    edgeTranslations: e2t,
    cornerShifts: c2s
  };
};

const facialRing = (start, cov, skel) => {
  const result = [];

  let D = start;
  do {
    const E = cov.s(0, D);
    const sD = skel.cornerShifts[D][0];
    const sE = skel.cornerShifts[E][0];
    const t = skel.edgeTranslations[D][0];

    const head = skel.chamber2node[D];
    const tail = skel.chamber2node[E];
    const shift = opsR.minus(t ? opsR.plus(sE, t) : sE, sD);

    result.push(periodic.makeEdge(head, tail, shift));
    D = cov.s(1, E);
  }
  while (D != start);

  return result;
};

const cmpRingEdges = (a, b) => (
  opsR.cmp(a.head, b.head) || opsR.cmp(a.shift, b.shift)
);

const cmpRingTails = (a, b, i) => (
  i >= a.length ? 0 : cmpRingEdges(a[i], b[i]) || cmpRingTails(a, b, i + 1)
);

const cmpRings = (a, b) => cmpRingTails(a, b, 0);

const ringShifted = (r, i) => r.slice(i).concat(r.slice(0, i));
const ringReverse = r => r.slice().reverse().map(e => e.reverse());

const mapRing = (ring, sym) => ring.map(e => decode(sym.src2img[encode(e)]));

const canonicalRing = ring => {
  const rev = ringReverse(ring);
  let best = null;

  for (let i = 0; i < ring.length; ++i) {
    if (best == null || cmpRings(ringShifted(ring, i), best) < 0)
      best = ringShifted(ring, i);
    if (cmpRings(ringShifted(rev, i), best) < 0)
      best = ringShifted(rev, i);
  }

  return best;
};

const facialRings = (cov, skel) => (
  properties.orbitReps(cov, _remainingIndices(cov, 2))
    .map(D => canonicalRing(facialRing(D, cov, skel)))
);

export const facePreservingSymmetries = (cov, skel) => {
  const rings = facialRings(cov, skel);

  const isRing = {};
  for (const r of rings)
    isRing[encode(canonicalRing(r))] = true;

  const syms = symmetries.symmetries(skel.graph).symmetries;
  const good = [];

  for (const sym of syms) {
    if (rings.every(r => isRing[encode(canonicalRing(mapRing(r, sym)))]))
      good.push(sym);
  }

  return good;
};

const chamberPositions = (cov, skel) => {
  const sum = v => v.reduce((x, y) => x == null ? y : opsR.plus(x, y));

  const pos = periodic.barycentricPlacement(skel.graph);
  const result = {};

  for (const D of cov.elements()) {
    const p = pos[skel.chamber2node[D]];
    const t = skel.cornerShifts[D][0];
    result[D] = [opsR.plus(p, t)];
  }

  for (let i = 1; i <= delaney.dim(cov); ++i) {
    const idcs = range(i);

    for (const orb of properties.orbits(cov, idcs, cov.elements())) {
      const s = opsR.div(
        sum(orb.map(E => opsR.minus(result[E][0], skel.cornerShifts[E][i]))),
        orb.length);

      for (const E of orb)
        result[E].push(opsR.plus(s, skel.cornerShifts[E][i]));
    }
  }

  return result;
};

const chamberBasis = (pos, D) => {
  const t = pos[D];
  return t.slice(1).map(v => opsR.minus(v, t[0]));
};

const determinant = M => {
  if (M.length == 2)
    return opsR.minus(opsR.times(M[0][0], M[1][1]),
                      opsR.times(M[0][1], M[1][0]));
  else if (M.length == 3)
    return opsR.times(M[0], opsR.crossProduct(M[1], M[2]));
  else
    return opsR.determinant(M);
};

const chamberDeterminant = (pos, D) => determinant(chamberBasis(pos, D));

const nonDegenerateChamber = (elms, pos) =>
  elms.find(D => opsR.ne(chamberDeterminant(pos, D), 0));

export const makeCover = ds =>
  delaney.dim(ds) == 3 ?
  delaney3d.pseudoToroidalCover(ds) :
  delaney2d.toroidalCover(ds);

const tileSurface3D = (pos, faces) => ({ pos, faces });

const tileSurface2D = (corners, faces) => {
  const pos = [];
  for (const p of corners) {
    pos.push(p.concat(-0.1));
    pos.push(p.concat(0.1));
  }

  const f = faces[0].map(i => 2 * i);

  faces = [f, f.map(x => x + 1).reverse()]
    .concat(f.map((x, i) => {
      const y = f[(i + 1) % f.length];
      return [y, x, x + 1, y + 1];
    }));

  return { pos, faces };
};

const tileSurface = (cov, skel, pos, ori, elms, idcs) => {
  const cOrbs = properties.orbits(cov, idcs.slice(1), elms);
  const cPos = cOrbs.map(
    ([D]) => opsF.plus(pos[skel.chamber2node[D]], skel.cornerShifts[D][0])
  );

  const cIdcs = [];
  cOrbs.forEach((orb, i) => {
    for (const D of orb)
      cIdcs[D] = i;
  });

  const faces = properties.orbits(cov, [0, 1], elms)
    .map(orb => ori[orb[0]] > 0 ? orb.reverse() : orb)
    .map(orb => orb.filter((D, i) => i % 2 == 0).map(D => cIdcs[D]));

  return (delaney.dim(cov) == 3 ? tileSurface3D : tileSurface2D)(cPos, faces);
};

const adjustedOrientation = (cov, pos) => {
  const D0 = nonDegenerateChamber(cov.elements(), pos);
  const sgn = opsR.sgn(chamberDeterminant(pos, D0));

  const ori = properties.partialOrientation(cov);
  if (sgn * ori[D0] < 0) {
    for (const D of cov.elements())
      ori[D] = -ori[D];
  }

  return ori;
};

export const tileSurfaces = (cov, skel, vertexPos, orbitReps) => {
  const dim = delaney.dim(cov);
  const idcs = range(dim);
  const pos = chamberPositions(cov, skel);
  const ori = adjustedOrientation(cov, pos);

  return orbitReps.map(D => tileSurface(
    cov, skel, vertexPos, ori, properties.orbit(cov, idcs, D), idcs));
};

const affineSymmetry = (D0, D1, pos) => {
  const bas = D => chamberBasis(pos, D);
  const linear = opsR.solve(bas(D0), bas(D1));
  const shift = opsR.minus(pos[D1][0], opsR.times(pos[D0][0], linear));

  return linear.map(r => r.concat(0)).concat([shift.concat([1])]);
};

export const tilesByTranslations = (ds, cov, skel) => {
  const dim = delaney.dim(cov);
  const pos = chamberPositions(cov, skel);
  const phi = properties.morphism(cov, 1, ds, 1);
  const idcs = range(dim);
  const tileOrbits = properties.orbits(cov, idcs);

  const orbitReps = [];
  const dsChamberToClassIndex = {};
  const covChamberToLatticeIndex = {};
  const tiles = [];

  for (const elms of tileOrbits) {
    const D0 = nonDegenerateChamber(elms, pos);
    const E0 = phi[D0];

    let classIndex = dsChamberToClassIndex[E0];
    let symmetry = opsR.identityMatrix(dim + 1);

    if (classIndex == null) {
      classIndex = orbitReps.length;

      for (const E of properties.orbit(ds, idcs, E0))
        dsChamberToClassIndex[E] = classIndex;

      orbitReps.push(D0);
    }
    else {
      const D0 = orbitReps[classIndex];
      const D1 = elms.find(D => phi[D] == phi[D0]);
      symmetry = affineSymmetry(D0, D1, pos);
    }

    for (const E of elms)
      covChamberToLatticeIndex[E] = tiles.length;

    tiles.push({ classIndex, symmetry, chambers: elms });
  }

  const e2t = _edgeTranslations(cov);
  const zero = Array(dim).fill(0);

  for (const tile of tiles) {
    const neighbors = [];
    for (const D of properties.orbitReps(cov, [0, 1], tile.chambers)) {
      const E = cov.s(dim, D);
      const latticeIndex = covChamberToLatticeIndex[E];
      const shift = e2t[D][dim] || zero;
      neighbors.push({ latticeIndex, shift });
    }

    tile.neighbors = neighbors;
  }

  const centers = orbitReps.map(D => pos[D][dim]);

  return { orbitReps, tiles, centers };
};

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

  const delaney = require('./delaney');
  const unitCells = require('../geometry/unitCells');

  const test = ds => {
    console.log(`ds = ${ds}`);

    const cov = makeCover(ds);
    console.log(`cover = ${cov}`);

    const skel = skeleton(cov);
    console.log(`skeleton = ${skel.graph}`);

    const embedding = embed(skel.graph).relaxed;
    const pos = embedding.positions;
    console.log(`vertex positions: ${JSON.stringify(pos)}`);

    const basis = unitCells.invariantBasis(embedding.gram);
    console.log(`invariant basis: ${basis}`);

    const rings = facialRings(cov, skel);
    console.log(`facial rings:`);
    for (const ring of rings)
      console.log(`  ${ring}`);

    const allSyms = symmetries.symmetries(skel.graph).symmetries;
    const goodSyms = facePreservingSymmetries(cov, skel);
    console.log(
      `skeleton has ${allSyms.length}, tiling ${goodSyms.length} symmetries`
    );
    const seeds = properties.orbitReps(cov, range(delaney.dim(cov)));
    const surfaces = tileSurfaces(cov, skel, pos, seeds);
    console.log(`tile surfaces:`);
    for (const surface of surfaces)
      console.log(`  ${JSON.stringify(surface)}`);

    console.log();
  }

  test(delaney.parse('<1.1:1 3:1,1,1,1:4,3,4>'));
  test(delaney.parse('<1.1:2 3:2,1 2,1 2,2:6,3 2,6>'));
  test(delaney.parse('<1.1:2 3:1 2,1 2,1 2,2:3 3,3 4,4>'));
  test(delaney.parse(
    '<1.1:6 3:2 4 6,1 2 3 5 6,3 4 5 6,2 3 4 5 6:6 4,2 3 3,8 4>'
  ));
  test(delaney.parse('<1.1:8:2 4 6 8,8 3 5 7,6 5 8 7:4,4>'));
  test(delaney.parse('<1.1:8:2 4 6 8,8 3 5 7,5 6 8 7:4,4>'));
}
<|endoftext|>


<commit_before>/**
 * Draggable unit
 *
 * Copyright (C) Nikolay V. Nemshilov aka St.
 */
var Draggable = new Class(Observer, {
  extend: {
    EVENTS: $w('start drag stop drop'),

    Options: {
      handle:            null,        // a handle element that will start the drag

      snap:              0,           // a number in pixels or [x,y]
      axis:              null,        // null or 'x' or 'y' or 'vertical' or 'horizontal'
      range:             null,        // {x: [min, max], y:[min, max]} or reference to another element

      dragClass:         'dragging',  // the in-process class name

      clone:             false,       // if should keep a clone in place
      revert:            false,       // marker if the object should be moved back on finish
      revertDuration:    'normal',    // the moving back fx duration

      scroll:            true,        // if it should automatically scroll
      scrollSensitivity: 32,          // the scrolling area size in pixels

      zIndex:            10000000,    // the element's z-index

      relName:           'draggable'  // the audodiscovery feature key
    },

    // referenece to the currently active draggable
    current: null,

    // scans the document for auto-processed draggables with the rel="draggable" attribute
    rescan: function() {
      var key = this.Options.relName;
      $$('*[rel^="'+key+'"]').each(function(element) {
        if (!element._draggable) {
          var data = element.get('data-'+key+'-options');
          new this(element, eval('('+data+')') || {});
        }
      }, this);
    }
  },

  /**
   * Basic controller
   *
   * @param mixed element reference
   * @param Object options
   */
  initialize: function(element, options) {
    this.element = $(element);
    this.$super(options);

    this.element._draggable = this.init();
  },

  /**
   * detaches the mouse observers out of the draggable element
   *
   * @return this
   */
  destroy: function() {
    this.handle.stopObserving('mousedown', this._dragStart);
    delete(this.element._draggable);

    return this;
  },

  // additional options processing
  setOptions: function(options) {
    this.$super(options);

    // checking the handle
    this.handle = this.options.handle ? $(this.options.handle) : this.element;

    // checking the spappings
    if (isArray(this.options.snap)) {
      this.snapX = this.options.snap[0];
      this.snapY = this.options.snap[1];
    } else {
      this.snapX = this.snapY = this.options.snap;
    }

    return this;
  },

  /**
   * Moves the element back to the original position
   *
   * @return this
   */
  revert: function() {
    var position  = this.clone.position();
    var end_style = {
      top:  position.y + 'px',
      left: position.x + 'px'
    };

    if (this.options.revertDuration && this.element.morph) {
      this.element.morph(end_style, {
        duration: this.options.revertDuration,
        onFinish: this.swapBack.bind(this)
      });
    } else {
      this.element.setStyle(end_style);
      this.swapBack();
    }

    return this;
  },

// protected

  init: function() {
    // caching the callback so that we could detach it later
    this._dragStart = this.dragStart.bind(this);

    this.handle.onMousedown(this._dragStart);

    return this;
  },

  // handles the event start
  dragStart: function(event) {
    event.stop(); // prevents the text selection

    // calculating the positions diff
    var position  = this.element.position();

    this.xDiff = event.pageX - position.x;
    this.yDiff = event.pageY - position.y;

    // preserving the element sizes
    var size = {
      x: this.element.getStyle('width'),
      y: this.element.getStyle('height')
    };

    if (size.x == 'auto') size.x = this.element.offsetWidth  + 'px';
    if (size.y == 'auto') size.y = this.element.offsetHeight + 'px';

    // building a clone element if necessary
    if (this.options.clone || this.options.revert) {
      this.clone = $(this.element.cloneNode(true)).setStyle({
        visibility: this.options.clone ? 'visible' : 'hidden'
      }).insertTo(this.element, 'before');
    }

    // reinserting the element to the body so it was over all the other elements
    this.element.setStyle({
      position: 'absolute',
      zIndex:   Draggable.Options.zIndex++,
      top:      position.y + 'px',
      left:     position.x + 'px',
      width:    size.x,
      height:   size.y
    }).addClass(this.options.dragClass);

    // caching the window scrolls
    this.winScrolls = window.scrolls();
    this.winSizes   = window.sizes();

    Draggable.current = this.calcConstraints().fire('start', this, event);
  },

  // catches the mouse move event
  dragProcess: function(event) {
    var page_x = event.pageX, page_y = event.pageY, x = page_x - this.xDiff, y = page_y - this.yDiff, position = {};

    // checking the range
    if (this.ranged) {
      if (this.minX > x) x = this.minX;
      if (this.maxX < x) x = this.maxX;
      if (this.minY > y) y = this.minY;
      if (this.maxY < y) y = this.maxY;
    }

    // checking the scrolls
    if (this.options.scroll) {
      var scrolls = {x: this.winScrolls.x, y: this.winScrolls.y},
        sensitivity = this.options.scrollSensitivity;

      if ((page_y - scrolls.y) < sensitivity) {
        scrolls.y = page_y - sensitivity;
      } else if ((scrolls.y + this.winSizes.y - page_y) < sensitivity){
        scrolls.y = page_y - this.winSizes.y + sensitivity;
      }

      if ((page_x - scrolls.x) < sensitivity) {
        scrolls.x = page_x - sensitivity;
      } else if ((scrolls.x + this.winSizes.x - page_x) < sensitivity){
        scrolls.x = page_x - this.winSizes.x + sensitivity;
      }

      if (scrolls.y < 0) scrolls.y = 0;
      if (scrolls.x < 0) scrolls.x = 0;

      if (scrolls.y < this.winScrolls.y || scrolls.y > this.winScrolls.y ||
        scrolls.x < this.winScrolls.x || scrolls.x > this.winScrolls.x) {

          window.scrollTo(this.winScrolls = scrolls);
      }
    }

    // checking the snaps
    if (this.snapX) x = x - x % this.snapX;
    if (this.snapY) y = y - y % this.snapY;

    // checking the constraints
    if (!this.axisY) position.left = x + 'px';
    if (!this.axisX) position.top  = y + 'px';

    this.element.setStyle(position);

    this.fire('drag', this, event);
  },

  // handles the event stop
  dragStop: function(event) {
    this.element.removeClass(this.options.dragClass);

    // notifying the droppables for the drop
    Droppable.checkDrop(event, this);

    if (this.options.revert) {
      this.revert();
    }

    Draggable.current = null;

    this.fire('stop', this, event);
  },

  // swaps the clone element to the actual element back
  swapBack: function() {
    if (this.clone) {
      this.clone.insert(
        this.element.setStyle({
          width:    this.clone.getStyle('width'),
          height:   this.clone.getStyle('height'),
          position: this.clone.getStyle('position'),
          zIndex:   this.clone.getStyle('zIndex')
        }), 'before'
      ).remove();
    }
  },

  // calculates the constraints
  calcConstraints: function() {
    var axis = this.options.axis;
    this.axisX = ['x', 'horizontal'].include(axis);
    this.axisY = ['y', 'vertical'].include(axis);

    this.ranged = false;
    var range = this.options.range;
    if (range) {
      this.ranged = true;

      // if the range is defined by another element
      var element = $(range);
      if (isElement(element)) {
        var dims = element.dimensions();

        range = {
          x: [dims.left, dims.left + dims.width],
          y: [dims.top,  dims.top + dims.height]
        };
      }

      if (isHash(range)) {
        var size = this.element.sizes();

        if (range.x) {
          this.minX = range.x[0];
          this.maxX = range.x[1] - size.x;
        }
        if (range.y) {
          this.minY = range.y[0];
          this.maxY = range.y[1] - size.y;
        }
      }
    }

    return this;
  }
});<commit_msg>Draggable the moveOut option<commit_after>/**
 * Draggable unit
 *
 * Copyright (C) Nikolay V. Nemshilov aka St.
 */
var Draggable = new Class(Observer, {
  extend: {
    EVENTS: $w('start drag stop drop'),

    Options: {
      handle:            null,        // a handle element that will start the drag

      snap:              0,           // a number in pixels or [x,y]
      axis:              null,        // null or 'x' or 'y' or 'vertical' or 'horizontal'
      range:             null,        // {x: [min, max], y:[min, max]} or reference to another element

      dragClass:         'dragging',  // the in-process class name

      clone:             false,       // if should keep a clone in place
      revert:            false,       // marker if the object should be moved back on finish
      revertDuration:    'normal',    // the moving back fx duration

      scroll:            true,        // if it should automatically scroll
      scrollSensitivity: 32,          // the scrolling area size in pixels

      zIndex:            10000000,    // the element's z-index
      moveOut:           false,       // marker if the draggable should be moved out of it's context (for overflown elements)

      relName:           'draggable'  // the audodiscovery feature key
    },

    // referenece to the currently active draggable
    current: null,

    // scans the document for auto-processed draggables with the rel="draggable" attribute
    rescan: function() {
      var key = this.Options.relName;
      $$('*[rel^="'+key+'"]').each(function(element) {
        if (!element._draggable) {
          var data = element.get('data-'+key+'-options');
          new this(element, eval('('+data+')') || {});
        }
      }, this);
    }
  },

  /**
   * Basic controller
   *
   * @param mixed element reference
   * @param Object options
   */
  initialize: function(element, options) {
    this.element = $(element);
    this.$super(options);

    this.element._draggable = this.init();
  },

  /**
   * detaches the mouse observers out of the draggable element
   *
   * @return this
   */
  destroy: function() {
    this.handle.stopObserving('mousedown', this._dragStart);
    delete(this.element._draggable);

    return this;
  },

  // additional options processing
  setOptions: function(options) {
    this.$super(options);

    // checking the handle
    this.handle = this.options.handle ? $(this.options.handle) : this.element;

    // checking the spappings
    if (isArray(this.options.snap)) {
      this.snapX = this.options.snap[0];
      this.snapY = this.options.snap[1];
    } else {
      this.snapX = this.snapY = this.options.snap;
    }

    return this;
  },

  /**
   * Moves the element back to the original position
   *
   * @return this
   */
  revert: function() {
    var position  = this.clone.position();
    var end_style = {
      top:  position.y + 'px',
      left: position.x + 'px'
    };

    if (this.options.revertDuration && this.element.morph) {
      this.element.morph(end_style, {
        duration: this.options.revertDuration,
        onFinish: this.swapBack.bind(this)
      });
    } else {
      this.element.setStyle(end_style);
      this.swapBack();
    }

    return this;
  },

// protected

  init: function() {
    // caching the callback so that we could detach it later
    this._dragStart = this.dragStart.bind(this);

    this.handle.onMousedown(this._dragStart);

    return this;
  },

  // handles the event start
  dragStart: function(event) {
    event.stop(); // prevents the text selection

    // calculating the positions diff
    var position  = this.element.position();

    this.xDiff = event.pageX - position.x;
    this.yDiff = event.pageY - position.y;

    // preserving the element sizes
    var size = {
      x: this.element.getStyle('width'),
      y: this.element.getStyle('height')
    };

    if (size.x == 'auto') size.x = this.element.offsetWidth  + 'px';
    if (size.y == 'auto') size.y = this.element.offsetHeight + 'px';

    // building a clone element if necessary
    if (this.options.clone || this.options.revert) {
      this.clone = $(this.element.cloneNode(true)).setStyle({
        visibility: this.options.clone ? 'visible' : 'hidden'
      }).insertTo(this.element, 'before');
    }

    // reinserting the element to the body so it was over all the other elements
    this.element.setStyle({
      position: 'absolute',
      zIndex:   Draggable.Options.zIndex++,
      top:      position.y + 'px',
      left:     position.x + 'px',
      width:    size.x,
      height:   size.y
    }).addClass(this.options.dragClass);

    if (this.options.moveOut) this.element.insertTo(document.body);

    // caching the window scrolls
    this.winScrolls = window.scrolls();
    this.winSizes   = window.sizes();

    Draggable.current = this.calcConstraints().fire('start', this, event);
  },

  // catches the mouse move event
  dragProcess: function(event) {
    var page_x = event.pageX, page_y = event.pageY, x = page_x - this.xDiff, y = page_y - this.yDiff, position = {};

    // checking the range
    if (this.ranged) {
      if (this.minX > x) x = this.minX;
      if (this.maxX < x) x = this.maxX;
      if (this.minY > y) y = this.minY;
      if (this.maxY < y) y = this.maxY;
    }

    // checking the scrolls
    if (this.options.scroll) {
      var scrolls = {x: this.winScrolls.x, y: this.winScrolls.y},
        sensitivity = this.options.scrollSensitivity;

      if ((page_y - scrolls.y) < sensitivity) {
        scrolls.y = page_y - sensitivity;
      } else if ((scrolls.y + this.winSizes.y - page_y) < sensitivity){
        scrolls.y = page_y - this.winSizes.y + sensitivity;
      }

      if ((page_x - scrolls.x) < sensitivity) {
        scrolls.x = page_x - sensitivity;
      } else if ((scrolls.x + this.winSizes.x - page_x) < sensitivity){
        scrolls.x = page_x - this.winSizes.x + sensitivity;
      }

      if (scrolls.y < 0) scrolls.y = 0;
      if (scrolls.x < 0) scrolls.x = 0;

      if (scrolls.y < this.winScrolls.y || scrolls.y > this.winScrolls.y ||
        scrolls.x < this.winScrolls.x || scrolls.x > this.winScrolls.x) {

          window.scrollTo(this.winScrolls = scrolls);
      }
    }

    // checking the snaps
    if (this.snapX) x = x - x % this.snapX;
    if (this.snapY) y = y - y % this.snapY;

    // checking the constraints
    if (!this.axisY) position.left = x + 'px';
    if (!this.axisX) position.top  = y + 'px';

    this.element.setStyle(position);

    this.fire('drag', this, event);
  },

  // handles the event stop
  dragStop: function(event) {
    this.element.removeClass(this.options.dragClass);

    // notifying the droppables for the drop
    Droppable.checkDrop(event, this);

    if (this.options.revert) {
      this.revert();
    }

    Draggable.current = null;

    this.fire('stop', this, event);
  },

  // swaps the clone element to the actual element back
  swapBack: function() {
    if (this.clone) {
      this.clone.insert(
        this.element.setStyle({
          width:    this.clone.getStyle('width'),
          height:   this.clone.getStyle('height'),
          position: this.clone.getStyle('position'),
          zIndex:   this.clone.getStyle('zIndex')
        }), 'before'
      ).remove();
    }
  },

  // calculates the constraints
  calcConstraints: function() {
    var axis = this.options.axis;
    this.axisX = ['x', 'horizontal'].include(axis);
    this.axisY = ['y', 'vertical'].include(axis);

    this.ranged = false;
    var range = this.options.range;
    if (range) {
      this.ranged = true;

      // if the range is defined by another element
      var element = $(range);
      if (isElement(element)) {
        var dims = element.dimensions();

        range = {
          x: [dims.left, dims.left + dims.width],
          y: [dims.top,  dims.top + dims.height]
        };
      }

      if (isHash(range)) {
        var size = this.element.sizes();

        if (range.x) {
          this.minX = range.x[0];
          this.maxX = range.x[1] - size.x;
        }
        if (range.y) {
          this.minY = range.y[0];
          this.maxY = range.y[1] - size.y;
        }
      }
    }

    return this;
  }
});<|endoftext|>


<commit_before>import React, {PropTypes} from 'react'
import styles from 'part:@sanity/components/edititem/popover-style'
import Button from 'part:@sanity/components/buttons/default'
import {debounce} from 'lodash'
import CloseIcon from 'part:@sanity/base/close-icon'

export default class EditItemPopOver extends React.Component {
  static propTypes = {
    title: PropTypes.string,
    children: PropTypes.node.isRequired,
    className: PropTypes.string,
    onClose: PropTypes.func,
    isCreatingNewItem: PropTypes.bool,
    actions: PropTypes.arrayOf(PropTypes.object),
    fullWidth: PropTypes.bool,
    onNeedScroll: PropTypes.func,
    scrollContainer: PropTypes.node,
    scrollContainerId: PropTypes.string
  }

  static defaultProps = {
    onClose() {},
    actions: []
  }

  constructor() {
    super()
    this.handleClose = this.handleClose.bind(this)
    this.handleClick = this.handleClick.bind(this)
    this.handleMouseDown = this.handleMouseDown.bind(this)
    this.setRootElement = this.setRootElement.bind(this)
    this.setInnerElement = this.setInnerElement.bind(this)
    this.setArrowElement = this.setArrowElement.bind(this)
    this.repositionElement = this.repositionElement.bind(this)
    this.handleResize = debounce(this.handleResize.bind(this), 17) // 60fps
    this.resetPosition = this.resetPosition.bind(this)
  }

  handleKeyDown = event => {
    if (event.key == 'Escape') {
      this.handleClose()
    }
  }

  handleClose() {
    this.props.onClose()
  }

  handleClick(event) {
    event.stopPropagation()
  }

  handleMouseDown(event) {
    event.stopPropagation()
  }

  repositionElement() {
    const rootElement = this._rootElement
    const innerElement = this._innerElement

    if (rootElement && innerElement) {

      const rootRects = rootElement.getClientRects()
      const width = rootRects[0].width
      const height = rootRects[0].height
      const left = rootRects[0].left
      const top = rootRects[0].top

      // we can use window since we don't support horizontal scrolling
      // and the backdrop is fixed
      const windowWidth = window.innerWidth
      const containerOffsetHeight = this.scrollContainer.offsetHeight

      const padding = 30

      const margin = parseInt(innerElement.style.marginLeft, 10) || 0
      const scrollTop = this.scrollContainer.scrollTop

      // Scroll container when there is no space
      if ((containerOffsetHeight + scrollTop) < (top + height)) {
        this.scrollContainer.scrollTop = (containerOffsetHeight - top - height - scrollTop) * -1
      }

      // Need more bottom space
      if (this.scrollContainer.scrollHeight < (scrollTop + top + height)) {
        this.scrollContainer.style.paddingBottom = this.scrollContainer.scrollHeight - scrollTop - height - top
        this.scrollContainer.scrollTop = (containerOffsetHeight - top - height - scrollTop) * -1
      }

      // Reposition horizon
      if ((width + left - margin + padding) > windowWidth) {
        const diff = windowWidth - width - padding - left + margin
        innerElement.style.marginLeft = `${diff}px`
        this._arrowElement.style.transform = `translateX(${diff * -1}px)`
      } else {
        this.resetPosition()
      }
    }
  }

  resetPosition() {
    this._innerElement.style.marginLeft = '0'
    this._arrowElement.style.transform = 'translateX(0px)'
  }

  handleResize() {
    // Uses the css the determine if it should reposition with an Media Query
    const computedStyle = window.getComputedStyle(this._rootElement, '::before')
    const contentValue = computedStyle.getPropertyValue('content')
    const shouldReposition = contentValue === '"shouldReposition"' // Is quoted

    if (shouldReposition) {
      this.repositionElement()
    } else {
      this.resetPosition()
    }
  }

  componentDidMount() {

    // Sets a scrollContainer with ID
    if (!this.props.scrollContainer && this.props.scrollContainerId) {
      this.scrollContainer = document.getElementById(this.props.scrollContainerId)
    } else {
      this.scrollContainer = this.props.scrollContainer
    }
    if (this.scrollContainer) {
      this.handleResize()
      window.addEventListener('resize', this.handleResize)
      window.addEventListener('keydown', this.handleKeyDown, false)
    }
  }

  componentWillUnmount() {
    window.removeEventListener('keydown', this.handleKeyDown, false)
    window.removeEventListener('resize', this.handleResize)
  }

  setRootElement(element) {
    this._rootElement = element
  }
  setInnerElement(element) {
    this._innerElement = element
  }

  setArrowElement(element) {
    this._arrowElement = element
  }

  handleBackdropClick = () => {
    this.handleClose()
  }

  handleInnerClick = event => {
    event.stopPropagation()
  }

  render() {
    const {title, children, className, isCreatingNewItem, actions, fullWidth} = this.props
    return (
      <div
        className={`${fullWidth ? styles.fullWidth : styles.autoWidth} ${className}`}
        onClick={this.handleClick}
        ref={this.setRootElement}
      >

          <button className={styles.close} type="button" onClick={this.handleClose}>
            <CloseIcon />
          </button>

              {
                isCreatingNewItem && 'New '
              }
              {title}

            {children}

          {
            actions.length > 0 &&

              {
                actions.map((action, i) => {
                  return (
                    <Button
                      key={i}
                      onClick={this.handleActionClick}
                      data-action-index={i}
                      kind={action.kind}
                      className={styles[`button_${action.kind}`] || styles.button}
                    >
                      {action.title}
                    </Button>
                  )
                })
              }

          }

    )
  }
}
<commit_msg>Adding fallback default id for scrollContainer<commit_after>import React, {PropTypes} from 'react'
import styles from 'part:@sanity/components/edititem/popover-style'
import Button from 'part:@sanity/components/buttons/default'
import {debounce} from 'lodash'
import CloseIcon from 'part:@sanity/base/close-icon'

export default class EditItemPopOver extends React.Component {
  static propTypes = {
    title: PropTypes.string,
    children: PropTypes.node.isRequired,
    className: PropTypes.string,
    onClose: PropTypes.func,
    isCreatingNewItem: PropTypes.bool,
    actions: PropTypes.arrayOf(PropTypes.object),
    fullWidth: PropTypes.bool,
    onNeedScroll: PropTypes.func,
    scrollContainer: PropTypes.node,
    scrollContainerId: PropTypes.string
  }

  static defaultProps = {
    onClose() {},
    actions: []
  }

  constructor() {
    super()
    this.handleClose = this.handleClose.bind(this)
    this.handleClick = this.handleClick.bind(this)
    this.handleMouseDown = this.handleMouseDown.bind(this)
    this.setRootElement = this.setRootElement.bind(this)
    this.setInnerElement = this.setInnerElement.bind(this)
    this.setArrowElement = this.setArrowElement.bind(this)
    this.repositionElement = this.repositionElement.bind(this)
    this.handleResize = debounce(this.handleResize.bind(this), 17) // 60fps
    this.resetPosition = this.resetPosition.bind(this)
  }

  handleKeyDown = event => {
    if (event.key == 'Escape') {
      this.handleClose()
    }
  }

  handleClose() {
    this.props.onClose()
  }

  handleClick(event) {
    event.stopPropagation()
  }

  handleMouseDown(event) {
    event.stopPropagation()
  }

  repositionElement() {
    const rootElement = this._rootElement
    const innerElement = this._innerElement

    if (rootElement && innerElement) {

      const rootRects = rootElement.getClientRects()
      const width = rootRects[0].width
      const height = rootRects[0].height
      const left = rootRects[0].left
      const top = rootRects[0].top

      // we can use window since we don't support horizontal scrolling
      // and the backdrop is fixed
      const windowWidth = window.innerWidth
      const containerOffsetHeight = this.scrollContainer.offsetHeight

      const padding = 30

      const margin = parseInt(innerElement.style.marginLeft, 10) || 0
      const scrollTop = this.scrollContainer.scrollTop

      // Scroll container when there is no space
      if ((containerOffsetHeight + scrollTop) < (top + height)) {
        this.scrollContainer.scrollTop = (containerOffsetHeight - top - height - scrollTop) * -1
      }

      // Need more bottom space
      if (this.scrollContainer.scrollHeight < (scrollTop + top + height)) {
        this.scrollContainer.style.paddingBottom = this.scrollContainer.scrollHeight - scrollTop - height - top
        this.scrollContainer.scrollTop = (containerOffsetHeight - top - height - scrollTop) * -1
      }

      // Reposition horizon
      if ((width + left - margin + padding) > windowWidth) {
        const diff = windowWidth - width - padding - left + margin
        innerElement.style.marginLeft = `${diff}px`
        this._arrowElement.style.transform = `translateX(${diff * -1}px)`
      } else {
        this.resetPosition()
      }
    }
  }

  resetPosition() {
    this._innerElement.style.marginLeft = '0'
    this._arrowElement.style.transform = 'translateX(0px)'
  }

  handleResize() {
    // Uses the css the determine if it should reposition with an Media Query
    const computedStyle = window.getComputedStyle(this._rootElement, '::before')
    const contentValue = computedStyle.getPropertyValue('content')
    const shouldReposition = contentValue === '"shouldReposition"' // Is quoted

    if (shouldReposition) {
      this.repositionElement()
    } else {
      this.resetPosition()
    }
  }

  componentDidMount() {
    // Sets a scrollContainer with ID
    if (!this.props.scrollContainer && this.props.scrollContainerId) {
      this.scrollContainer = document.getElementById(this.props.scrollContainerId)
    } else if (this.props.scrollContainer) {
      this.scrollContainer = this.props.scrollContainer
    } else {
      this.scrollContainer = document.getElementById('Sanity_Default_DeskTool_Editor_ScrollContainer')
    }
    if (this.scrollContainer) {
      this.initialScrollTop = this.scrollContainer.scrollTop
      this.handleResize()
      window.addEventListener('resize', this.handleResize)
      window.addEventListener('keydown', this.handleKeyDown, false)
    }
  }

  componentWillUnmount() {
    if (this.scrollContainer && this.initialScrollTop) {
      this.scrollContainer.scrollTop = this.initialScrollTop
    }
    window.removeEventListener('keydown', this.handleKeyDown, false)
    window.removeEventListener('resize', this.handleResize)
  }

  setRootElement(element) {
    this._rootElement = element
  }
  setInnerElement(element) {
    this._innerElement = element
  }

  setArrowElement(element) {
    this._arrowElement = element
  }

  handleBackdropClick = () => {
    this.handleClose()
  }

  handleInnerClick = event => {
    event.stopPropagation()
  }

  render() {
    const {title, children, className, isCreatingNewItem, actions, fullWidth} = this.props
    return (
      <div
        className={`${fullWidth ? styles.fullWidth : styles.autoWidth} ${className}`}
        onClick={this.handleClick}
        ref={this.setRootElement}
      >

          <button className={styles.close} type="button" onClick={this.handleClose}>
            <CloseIcon />
          </button>

              {
                isCreatingNewItem && 'New '
              }
              {title}

            {children}

          {
            actions.length > 0 &&

              {
                actions.map((action, i) => {
                  return (
                    <Button
                      key={i}
                      onClick={this.handleActionClick}
                      data-action-index={i}
                      kind={action.kind}
                      className={styles[`button_${action.kind}`] || styles.button}
                    >
                      {action.title}
                    </Button>
                  )
                })
              }

          }

    )
  }
}
<|endoftext|>


<commit_before>/*
 * L.Draggable allows you to add dragging capabilities to any element. Supports mobile devices too.
 */

L.Draggable = L.Class.extend({
	includes: L.Mixin.Events,

	statics: {
		START: L.Browser.mobileWebkit ? 'touchstart' : 'mousedown',
		END: L.Browser.mobileWebkit ? 'touchend' : 'mouseup',
		MOVE: L.Browser.mobileWebkit ? 'touchmove' : 'mousemove'
	},

	initialize: function(element, dragStartTarget) {
		this._element = element;
		this._dragStartTarget = dragStartTarget || element;
	},

	enable: function() {
		if (this._enabled) { return; }
		L.DomEvent.addListener(this._dragStartTarget, L.Draggable.START, this._onDown, this);
		this._enabled = true;
	},

	disable: function() {
		if (!this._enabled) { return; }
		L.DomEvent.removeListener(this._dragStartTarget, L.Draggable.START, this._onDown);
		this._enabled = false;
	},

	_onDown: function(e) {
		if (e.shiftKey || ((e.which != 1) && (e.button != 1) && !e.touches)) { return true; }

		if (e.touches && e.touches.length > 1) { return; }
		if (e.touches && e.touches.length == 1) { e = e.touches[0]; }

		this._dragStartPos = L.DomUtil.getPosition(this._element);
		this._startX = e.clientX;
		this._startY = e.clientY;

		this._disableTextSelection();

		this._setMovingCursor();

		L.DomEvent.addListener(document, L.Draggable.MOVE, this._onMove, this);
		L.DomEvent.addListener(document, L.Draggable.END, this._onUp, this);
	},

	_onMove: function(e) {
		L.DomEvent.preventDefault(e);

		if (e.touches && e.touches.length > 1) { return; }
		if (e.touches && e.touches.length == 1) { e = e.touches[0]; }

		var offset = new L.Point(e.clientX - this._startX, e.clientY - this._startY),
			newPos = this._dragStartPos.add(offset);

		if (!this._moved) {
			this.fire('dragstart');
			this._moved = true;
		}

		L.DomUtil.setPosition(this._element, newPos);

		this.fire('drag');
	},

	_onUp: function(e) {
		this._enableTextSelection();

		this._restoreCursor();

		L.DomEvent.removeListener(document, L.Draggable.MOVE, this._onMove);
		L.DomEvent.removeListener(document, L.Draggable.END, this._onUp);

		this.fire('dragend');
	},

	_setMovingCursor: function() {
		this._bodyCursor = document.body.style.cursor;
		document.body.style.cursor = 'move';
	},

	_restoreCursor: function() {
		document.body.style.cursor = this._bodyCursor;
	},

	_disableTextSelection: function() {
		if (document.selection && document.selection.empty) {
			document.selection.empty();
		}
		if (!this._onselectstart) {
			this._onselectstart = document.onselectstart;
			document.onselectstart = L.Util.falseFn;
		}
	},

	_enableTextSelection: function() {
		document.onselectstart = this._onselectstart;
		this._onselectstart = null;
	}
});<commit_msg>draggable fix<commit_after>/*
 * L.Draggable allows you to add dragging capabilities to any element. Supports mobile devices too.
 */

L.Draggable = L.Class.extend({
	includes: L.Mixin.Events,

	statics: {
		START: L.Browser.mobileWebkit ? 'touchstart' : 'mousedown',
		END: L.Browser.mobileWebkit ? 'touchend' : 'mouseup',
		MOVE: L.Browser.mobileWebkit ? 'touchmove' : 'mousemove'
	},

	initialize: function(element, dragStartTarget) {
		this._element = element;
		this._dragStartTarget = dragStartTarget || element;
	},

	enable: function() {
		if (this._enabled) { return; }
		L.DomEvent.addListener(this._dragStartTarget, L.Draggable.START, this._onDown, this);
		this._enabled = true;
	},

	disable: function() {
		if (!this._enabled) { return; }
		L.DomEvent.removeListener(this._dragStartTarget, L.Draggable.START, this._onDown);
		this._enabled = false;
	},

	_onDown: function(e) {
		if (e.shiftKey || ((e.which != 1) && (e.button != 1) && !e.touches)) { return; }

		if (!e.touches) {
			L.DomEvent.preventDefault(e);
		}
		if (e.touches && e.touches.length > 1) { return; }
		if (e.touches && e.touches.length == 1) { e = e.touches[0]; }

		this._dragStartPos = L.DomUtil.getPosition(this._element);
		this._startX = e.clientX;
		this._startY = e.clientY;

		this._disableTextSelection();

		this._setMovingCursor();

		L.DomEvent.addListener(document, L.Draggable.MOVE, this._onMove, this);
		L.DomEvent.addListener(document, L.Draggable.END, this._onUp, this);
	},

	_onMove: function(e) {
		L.DomEvent.preventDefault(e);

		if (e.touches && e.touches.length > 1) { return; }
		if (e.touches && e.touches.length == 1) { e = e.touches[0]; }

		var offset = new L.Point(e.clientX - this._startX, e.clientY - this._startY),
			newPos = this._dragStartPos.add(offset);

		if (!this._moved) {
			this.fire('dragstart');
			this._moved = true;
		}

		L.DomUtil.setPosition(this._element, newPos);

		this.fire('drag');
	},

	_onUp: function(e) {
		this._enableTextSelection();

		this._restoreCursor();

		L.DomEvent.removeListener(document, L.Draggable.MOVE, this._onMove);
		L.DomEvent.removeListener(document, L.Draggable.END, this._onUp);

		if (this._moved) {
			this.fire('dragend');
			this._moved = false;
		}
	},

	_setMovingCursor: function() {
		this._bodyCursor = document.body.style.cursor;
		document.body.style.cursor = 'move';
	},

	_restoreCursor: function() {
		document.body.style.cursor = this._bodyCursor;
	},

	_disableTextSelection: function() {
		if (document.selection && document.selection.empty) {
			document.selection.empty();
		}
		if (!this._onselectstart) {
			this._onselectstart = document.onselectstart;
			document.onselectstart = L.Util.falseFn;
		}
	},

	_enableTextSelection: function() {
		document.onselectstart = this._onselectstart;
		this._onselectstart = null;
	}
});<|endoftext|>


<commit_before>var ide = require('./ide.js');
var diffSystems = global.diffSystems;
var codeToSystem = global.codeToSystem;
var uiDiffRenderer = global.uiDiffRenderer;
var examples = global.examples;
var tests = global.tests || {};

//---------------------------------------------------------
// State
//---------------------------------------------------------

function getLocal(k, otherwise) {
  if(localStorage[k]) {
    return JSON.parse(localStorage[k])
  }
  return otherwise;
}

function setLocal(k, v) {
  localStorage[k] = JSON.stringify(v);
}

var prevVersion = getLocal("prevVersion");
var stacks = getLocal("stacks");
var exampleStacks = Object.keys(examples);
var testStacks = []; //Object.keys(tests);

//stacks = ["Tutorial", "Incrementer", "Net worth", "Department heads", "Graph paths", "TodoMVC", "Turing machine", "Clock", "Chat", "Game", "My Stack", "Editor", "Runtime", "Editor injection"];
stacks = (stacks || []).concat(exampleStacks);
stacks.sort();
var uniqueStacks = [];
var prev;
for(var stackIx = 0; stackIx < stacks.length; stackIx++) {
  var stack = stacks[stackIx];
  if(stack !== prev) {
    prev = stack;
    uniqueStacks.push(stack);
  }
}
stacks = uniqueStacks;
setLocal("stacks", stacks);
// setLocal("Editor-code", examples["Editor"]);

var client = getLocal("client", uuid());
global.client = client;
setLocal("client", client);

//---------------------------------------------------------
// renderer
//---------------------------------------------------------

var renderer = {"programQueue": [], "queued": false}

function drainRenderQueue() {
  var start = now();
  storage["rootParent"] = $(".uiCard").get(0);
  if(storage["rootParent"] && renderer["programQueue"].length > 0) {
    for(var i = 0, len = renderer["programQueue"].length; i < len; i++) {
      var queued = renderer["programQueue"][i];
      var program = queued[0];
      var diff = queued[1];
      uiDiffRenderer(diff, storage, program);
    }
    var eveRoot = $(storage["builtEls"]["eve-root"]);
    if(!eveRoot.closest(document.documentElement).size()) {
      storage["rootParent"].appendChild(eveRoot.get(0));
    }
    renderer["programQueue"] = [];
  }
  var end = now();
  if(end - start > 10) {
    console.error("Long render: " + (end - start));
  }
//   console.log("Render loop:", end - start);
  renderer["queued"] = false;
}

function queueRender() {
  if(!renderer["queued"]) {
    renderer["queued"] = true;
    requestAnimationFrame(drainRenderQueue);
  }
}

//---------------------------------------------------------
// worker
//---------------------------------------------------------

var system;
var storage = {};
var workers = {};

function onWorkerMessage(event) {
  switch(event.data.type) {
    case "log":
      console.log.apply(console, event.data.args);
      break;
    case "renderUI":
      renderer["programQueue"].push([event.data.from, event.data.diff]);
      queueRender();
      break;
    case "diffs":
      var diffs = event.data.diffs;
      applySystemDiff({system: system}, diffs);

      ide.render(diffs, system);
      programWorker.postMessage({type: "pull", runNumber: event.data.runNumber});
      break;
  }
}

function createWorker() {
  var worker = new Worker("../build/worker.js");
  worker.onmessage = onWorkerMessage;
  return worker;
}

var programWorker = global.programWorker = createWorker();

var system = codeToSystem( examples["Runtime"] + "\n\n" + examples["Clock"]);
programWorker.postMessage({type: "diffs", diffs: diffSystems(system, null, null)});

var workspaceViews = system.getStore("workspaceView").getFacts().map(function(row) {
  return row[0];
});
console.log(workspaceViews);

var initialDiff = system.getStore('view').getFacts().reduce(function(memo, view) {
  memo[view[0]] = {adds: system.getStore(view[0]).getFacts()};
  return memo;
}, {});

//---------------------------------------------------------
// socket.io
//---------------------------------------------------------

if(window["io"]) {
  var socket = io.connect('/');
  socket.on("message", function (data) {
    onWorkerMessage({data: data});
  });

  var server = {
    postMessage: function(data) {
      socket.emit("message", data);
    },
    terminate: function() {}
  };

  workers["server"] = server;
}

//---------------------------------------------------------
// Go!
//---------------------------------------------------------

ide.render(initialDiff, system);
programWorker.postMessage({type: "pull"});
<commit_msg>Removes debug log<commit_after>var ide = require('./ide.js');
var diffSystems = global.diffSystems;
var codeToSystem = global.codeToSystem;
var uiDiffRenderer = global.uiDiffRenderer;
var examples = global.examples;
var tests = global.tests || {};

//---------------------------------------------------------
// State
//---------------------------------------------------------

function getLocal(k, otherwise) {
  if(localStorage[k]) {
    return JSON.parse(localStorage[k])
  }
  return otherwise;
}

function setLocal(k, v) {
  localStorage[k] = JSON.stringify(v);
}

var prevVersion = getLocal("prevVersion");
var stacks = getLocal("stacks");
var exampleStacks = Object.keys(examples);
var testStacks = []; //Object.keys(tests);

//stacks = ["Tutorial", "Incrementer", "Net worth", "Department heads", "Graph paths", "TodoMVC", "Turing machine", "Clock", "Chat", "Game", "My Stack", "Editor", "Runtime", "Editor injection"];
stacks = (stacks || []).concat(exampleStacks);
stacks.sort();
var uniqueStacks = [];
var prev;
for(var stackIx = 0; stackIx < stacks.length; stackIx++) {
  var stack = stacks[stackIx];
  if(stack !== prev) {
    prev = stack;
    uniqueStacks.push(stack);
  }
}
stacks = uniqueStacks;
setLocal("stacks", stacks);
// setLocal("Editor-code", examples["Editor"]);

var client = getLocal("client", uuid());
global.client = client;
setLocal("client", client);

//---------------------------------------------------------
// renderer
//---------------------------------------------------------

var renderer = {"programQueue": [], "queued": false}

function drainRenderQueue() {
  var start = now();
  storage["rootParent"] = $(".uiCard").get(0);
  if(storage["rootParent"] && renderer["programQueue"].length > 0) {
    for(var i = 0, len = renderer["programQueue"].length; i < len; i++) {
      var queued = renderer["programQueue"][i];
      var program = queued[0];
      var diff = queued[1];
      uiDiffRenderer(diff, storage, program);
    }
    var eveRoot = $(storage["builtEls"]["eve-root"]);
    if(!eveRoot.closest(document.documentElement).size()) {
      storage["rootParent"].appendChild(eveRoot.get(0));
    }
    renderer["programQueue"] = [];
  }
  var end = now();
  if(end - start > 10) {
    console.error("Long render: " + (end - start));
  }
//   console.log("Render loop:", end - start);
  renderer["queued"] = false;
}

function queueRender() {
  if(!renderer["queued"]) {
    renderer["queued"] = true;
    requestAnimationFrame(drainRenderQueue);
  }
}

//---------------------------------------------------------
// worker
//---------------------------------------------------------

var system;
var storage = {};
var workers = {};

function onWorkerMessage(event) {
  switch(event.data.type) {
    case "log":
      console.log.apply(console, event.data.args);
      break;
    case "renderUI":
      renderer["programQueue"].push([event.data.from, event.data.diff]);
      queueRender();
      break;
    case "diffs":
      var diffs = event.data.diffs;
      applySystemDiff({system: system}, diffs);

      ide.render(diffs, system);
      programWorker.postMessage({type: "pull", runNumber: event.data.runNumber});
      break;
  }
}

function createWorker() {
  var worker = new Worker("../build/worker.js");
  worker.onmessage = onWorkerMessage;
  return worker;
}

var programWorker = global.programWorker = createWorker();

var system = codeToSystem( examples["Runtime"] + "\n\n" + examples["Clock"]);
programWorker.postMessage({type: "diffs", diffs: diffSystems(system, null, null)});

var workspaceViews = system.getStore("workspaceView").getFacts().map(function(row) {
  return row[0];
});

var initialDiff = system.getStore('view').getFacts().reduce(function(memo, view) {
  memo[view[0]] = {adds: system.getStore(view[0]).getFacts()};
  return memo;
}, {});

//---------------------------------------------------------
// socket.io
//---------------------------------------------------------

if(window["io"]) {
  var socket = io.connect('/');
  socket.on("message", function (data) {
    onWorkerMessage({data: data});
  });

  var server = {
    postMessage: function(data) {
      socket.emit("message", data);
    },
    terminate: function() {}
  };

  workers["server"] = server;
}

//---------------------------------------------------------
// Go!
//---------------------------------------------------------

ide.render(initialDiff, system);
programWorker.postMessage({type: "pull"});
<|endoftext|>


<commit_before>'use strict';

module.exports = {

  rules: {

    /* Possible errors */

    'comma-dangle': [1, {
      arrays: 'always-multiline',
      objects: 'always-multiline',
      imports: 'always-multiline',
      exports: 'always-multiline',
      functions: 'ignore',
    }],

    /* Best practices */

    'no-magic-numbers': [1, {
      enforceConst: true,
      ignore: [-1, 0, 1, 2, 7, 12, 24, 30, 60],
      ignoreArrayIndexes: true,
    }],

    /* Stylistic consistency */

    'array-bracket-spacing': 1,
    'block-spacing': 1,
    'brace-style': 1,
    'camelcase': 0,
    'comma-spacing': 1,
    'comma-style': 1,
    'computed-property-spacing': 1,
    'consistent-this': [1, 'self'],
    'eol-last': 1,
    'func-call-spacing': 1,
    'func-names': 1,
    'id-blacklist': [1,
      'obj', 'object',
      'num', 'number',
      'str', 'string',
      'bool', 'boolean',
      'val',
    ],
    'id-length': [1, {
      min: 3,
      exceptions: ['_', '$', '$q', 'cb', 'fs', 'i', 'id', 'x', 'y', 'S3'],
    }],
    'indent': [1,
      2,
      { SwitchCase: 1 },
    ],
    'jsx-quotes': 1,
    'key-spacing': [1, {
      afterColon: true,
      mode: 'minimum',
    }],
    'keyword-spacing': 1,
    'linebreak-style': 1,
    'lines-around-comment': [1, {
      beforeBlockComment: true,
      allowBlockStart: true,
      allowObjectStart: true,
      allowArrayStart: true,
    }],
    'lines-around-directive': 1,
    'line-comment-position': 0,
    'max-depth': [1, 5],
    'max-len': [1, {
      code: 120,
      ignoreComments: true,
      ignoreStrings: true,
      ignoreTemplateLiterals: true,
      ignoreTrailingComments: true,
      ignoreUrls: true,
    }],
    'max-lines': [1, {
      skipBlankLines: true,
      skipComments: true,
    }],
    'max-nested-callbacks': [1, 5],
    'max-params': [1, 5],
    'max-statements': [1, 16],
    'new-cap': 1,
    'new-parens': 1,
    'newline-per-chained-call': 0,
    'no-array-constructor': 2,
    'no-bitwise': 2,
    'no-continue': 1,
    'no-lonely-if': 1,
    'no-mixed-operators': 1,
    'no-mixed-spaces-and-tabs': 1,
    'no-multiple-empty-lines': 1,
    'no-nested-ternary': 2,
    'no-new-object': 2,
    'no-trailing-spaces': 1,
    'no-underscore-dangle': [1, { allow: ['_id'] }],
    'no-unneeded-ternary': 2,
    'no-whitespace-before-property': 1,
    'object-curly-newline': 0,
    'object-curly-spacing': [1, 'always'],
    'object-property-newline': [1, {
      allowMultiplePropertiesPerLine: true,
    }],
    'one-var-declaration-per-line': 1,
    'operator-linebreak': [1, 'before'],
    'quote-props': [1, 'consistent-as-needed'],
    'quotes': [1, 'single', 'avoid-escape'],
    'semi-spacing': 1,
    'sort-keys': 0,
    'space-before-blocks': 1,
    'space-before-function-paren': [1, {
      anonymous: 'always',
      named: 'never',
    }],
    'space-in-parens': 1,
    'space-infix-ops': 1,
    'space-unary-ops': 1,
    'spaced-comment': 1,
    'unicode-bom': 1,
    'wrap-regex': 1,
  },

};
<commit_msg>exception for 'db' in 'id-length'<commit_after>'use strict';

module.exports = {

  rules: {

    /* Possible errors */

    'comma-dangle': [1, {
      arrays: 'always-multiline',
      objects: 'always-multiline',
      imports: 'always-multiline',
      exports: 'always-multiline',
      functions: 'ignore',
    }],

    /* Best practices */

    'no-magic-numbers': [1, {
      enforceConst: true,
      ignore: [-1, 0, 1, 2, 7, 12, 24, 30, 60],
      ignoreArrayIndexes: true,
    }],

    /* Stylistic consistency */

    'array-bracket-spacing': 1,
    'block-spacing': 1,
    'brace-style': 1,
    'camelcase': 0,
    'comma-spacing': 1,
    'comma-style': 1,
    'computed-property-spacing': 1,
    'consistent-this': [1, 'self'],
    'eol-last': 1,
    'func-call-spacing': 1,
    'func-names': 1,
    'id-blacklist': [1,
      'obj', 'object',
      'num', 'number',
      'str', 'string',
      'bool', 'boolean',
      'val',
    ],
    'id-length': [1, {
      min: 3,
      exceptions: ['_', '$', '$q', 'cb', 'db', 'fs', 'i', 'id', 'x', 'y', 'S3'],
    }],
    'indent': [1,
      2,
      { SwitchCase: 1 },
    ],
    'jsx-quotes': 1,
    'key-spacing': [1, {
      afterColon: true,
      mode: 'minimum',
    }],
    'keyword-spacing': 1,
    'linebreak-style': 1,
    'lines-around-comment': [1, {
      beforeBlockComment: true,
      allowBlockStart: true,
      allowObjectStart: true,
      allowArrayStart: true,
    }],
    'lines-around-directive': 1,
    'line-comment-position': 0,
    'max-depth': [1, 5],
    'max-len': [1, {
      code: 120,
      ignoreComments: true,
      ignoreStrings: true,
      ignoreTemplateLiterals: true,
      ignoreTrailingComments: true,
      ignoreUrls: true,
    }],
    'max-lines': [1, {
      skipBlankLines: true,
      skipComments: true,
    }],
    'max-nested-callbacks': [1, 5],
    'max-params': [1, 5],
    'max-statements': [1, 16],
    'new-cap': 1,
    'new-parens': 1,
    'newline-per-chained-call': 0,
    'no-array-constructor': 2,
    'no-bitwise': 2,
    'no-continue': 1,
    'no-lonely-if': 1,
    'no-mixed-operators': 1,
    'no-mixed-spaces-and-tabs': 1,
    'no-multiple-empty-lines': 1,
    'no-nested-ternary': 2,
    'no-new-object': 2,
    'no-trailing-spaces': 1,
    'no-underscore-dangle': [1, { allow: ['_id'] }],
    'no-unneeded-ternary': 2,
    'no-whitespace-before-property': 1,
    'object-curly-newline': 0,
    'object-curly-spacing': [1, 'always'],
    'object-property-newline': [1, {
      allowMultiplePropertiesPerLine: true,
    }],
    'one-var-declaration-per-line': 1,
    'operator-linebreak': [1, 'before'],
    'quote-props': [1, 'consistent-as-needed'],
    'quotes': [1, 'single', 'avoid-escape'],
    'semi-spacing': 1,
    'sort-keys': 0,
    'space-before-blocks': 1,
    'space-before-function-paren': [1, {
      anonymous: 'always',
      named: 'never',
    }],
    'space-in-parens': 1,
    'space-infix-ops': 1,
    'space-unary-ops': 1,
    'spaced-comment': 1,
    'unicode-bom': 1,
    'wrap-regex': 1,
  },

};
<|endoftext|>


<commit_before>const { autoUpdater } = require('electron-updater');
const {
    dialog,
    ipcMain,
 } = require('electron');
const IPFSBox = require('./IPFSBox.js');
const logger = require('electron-log');
const menubar = require('menubar');
const fs = require('fs-extra');
const {
    basename,
    join,
} = require('path');
const {
    __DEV__,
    LIGHT_MENUBAR_ICON_PATH,
    DARK_MENUBAR_ICON_PATH,
    INDEX_PATH,
} = require('./constants');

autoUpdater.logger = logger;
autoUpdater.logger.transports.file.level = 'info';

const ipfsBox = new IPFSBox();

const mb = menubar({
    icon: DARK_MENUBAR_ICON_PATH,
    index: INDEX_PATH,
    preloadWindow: true,
    alwaysOnTop: __DEV__,
    width: 350,
    height: 400,
});

mb.on('ready', () => {

    // Menubar flashes, the closes on start without a slight timeout
    setTimeout(() => {
        mb.showWindow();
    }, 1000);

    // Setup highlighted icon
    mb.tray.setPressedImage(LIGHT_MENUBAR_ICON_PATH);

    // Copy files dragged to menu bar into partyshare
    mb.tray.on('drop-files', (event, files) => {

        if (!ipfsBox.state.connected) {
            return;
        }

        files.forEach((filePath) => {
            const fileName = basename(filePath);
            const newPath = join(ipfsBox.state.boxPath, fileName);
            fs.copy(filePath, newPath);
        });
    });

    if (__DEV__) {
        return;
    }

    autoUpdater.checkForUpdates();
});

mb.on('after-create-window', () => {
    mb.window.webContents.send('send-state', ipfsBox.state);
    mb.window.setMovable(false);

    if (__DEV__) {
        mb.window.openDevTools();
    }

});

mb.on('show', () => {
    mb.window.webContents.send('send-state', ipfsBox.state);
});

ipfsBox.on('state-change', () => {
    if (!mb.window) {
        logger.error('[ipcMain] No window to send event on');
        return;
    }

    mb.window.webContents.send('send-state', ipfsBox.state);
});

ipfsBox.on('files-added', () => {
    logger.info('[ipfsBox] files-added');
    mb.showWindow();
});

// Inter Process (Main <-> Render) Communication
// ___________________________________________________________________________

ipcMain.on('request-state', (event) => {
    event.sender.send('send-state', ipfsBox.state);
});

ipcMain.on('hide', () => {
    mb.hideWindow();
});

ipcMain.on('quit', () => {
    mb.app.quit();
});

ipcMain.on('notification', (event, text) => {
    const state = Object.assign(ipfsBox.state, {
        notification: text,
    });
    mb.window.webContents.send('send-state', state);
    setTimeout(() => {
        const clearedState = Object.assign(ipfsBox.state, {
            notification: false,
        });
        mb.window.webContents.send('send-state', clearedState);
    }, 3000);
});

// ---------------------------------------------------------------------------
// Auto updates
//
// For details about these events, see their Wiki:
// https://github.com/electron-userland/electron-builder/wiki/Auto-Update#events
// ___________________________________________________________________________

autoUpdater.on('update-downloaded', (ev, info) => {
    logger.info('[autoUpdate] Notifying user of update');
    dialog.showMessageBox({
        type: 'info',
        title: 'Update available',
        message: 'An update for Partyshare is available, ok to restart?',
        buttons: ['Restart', 'Ignore'],
    }, (buttonIndex) => {
        if (buttonIndex === 0) {
            logger.info('[autoUpdate] Quitting and installing....');
            autoUpdater.quitAndInstall();
        }
    });
});
<commit_msg>Pass this event to the front end for analytics<commit_after>const { autoUpdater } = require('electron-updater');
const {
    dialog,
    ipcMain,
 } = require('electron');
const IPFSBox = require('./IPFSBox.js');
const logger = require('electron-log');
const menubar = require('menubar');
const fs = require('fs-extra');
const {
    basename,
    join,
} = require('path');
const {
    __DEV__,
    LIGHT_MENUBAR_ICON_PATH,
    DARK_MENUBAR_ICON_PATH,
    INDEX_PATH,
} = require('./constants');

autoUpdater.logger = logger;
autoUpdater.logger.transports.file.level = 'info';

const ipfsBox = new IPFSBox();

const mb = menubar({
    icon: DARK_MENUBAR_ICON_PATH,
    index: INDEX_PATH,
    preloadWindow: true,
    alwaysOnTop: __DEV__,
    width: 350,
    height: 400,
});

mb.on('ready', () => {

    // Menubar flashes, the closes on start without a slight timeout
    setTimeout(() => {
        mb.showWindow();
    }, 1000);

    // Setup highlighted icon
    mb.tray.setPressedImage(LIGHT_MENUBAR_ICON_PATH);

    // Copy files dragged to menu bar into partyshare
    mb.tray.on('drop-files', (event, files) => {

        if (!ipfsBox.state.connected) {
            return;
        }

        files.forEach((filePath) => {
            const fileName = basename(filePath);
            const newPath = join(ipfsBox.state.boxPath, fileName);
            fs.copy(filePath, newPath);
        });
    });

    if (__DEV__) {
        return;
    }

    autoUpdater.checkForUpdates();
});

mb.on('after-create-window', () => {
    mb.window.webContents.send('send-state', ipfsBox.state);
    mb.window.setMovable(false);

    if (__DEV__) {
        mb.window.openDevTools();
    }

});

mb.on('show', () => {
    mb.window.webContents.send('send-state', ipfsBox.state);
});

ipfsBox.on('state-change', () => {
    if (!mb.window) {
        logger.error('[ipcMain] No window to send event on');
        return;
    }

    mb.window.webContents.send('send-state', ipfsBox.state);
});

ipfsBox.on('files-added', () => {
    logger.info('[ipfsBox] files-added');
    mb.showWindow();
    mb.window.webContents.send('files-added');
});

// Inter Process (Main <-> Render) Communication
// ___________________________________________________________________________

ipcMain.on('request-state', (event) => {
    event.sender.send('send-state', ipfsBox.state);
});

ipcMain.on('hide', () => {
    mb.hideWindow();
});

ipcMain.on('quit', () => {
    mb.app.quit();
});

ipcMain.on('notification', (event, text) => {
    const state = Object.assign(ipfsBox.state, {
        notification: text,
    });
    mb.window.webContents.send('send-state', state);
    setTimeout(() => {
        const clearedState = Object.assign(ipfsBox.state, {
            notification: false,
        });
        mb.window.webContents.send('send-state', clearedState);
    }, 3000);
});

// ---------------------------------------------------------------------------
// Auto updates
//
// For details about these events, see their Wiki:
// https://github.com/electron-userland/electron-builder/wiki/Auto-Update#events
// ___________________________________________________________________________

autoUpdater.on('update-downloaded', (ev, info) => {
    logger.info('[autoUpdate] Notifying user of update');
    dialog.showMessageBox({
        type: 'info',
        title: 'Update available',
        message: 'An update for Partyshare is available, ok to restart?',
        buttons: ['Restart', 'Ignore'],
    }, (buttonIndex) => {
        if (buttonIndex === 0) {
            logger.info('[autoUpdate] Quitting and installing....');
            autoUpdater.quitAndInstall();
        }
    });
});
<|endoftext|>


<commit_before>const express = require('express');
const path = require('path');
const auth = require('./auth.js');

const MENU_ITEMS = [
  {
    id: 'download',
    title: 'Download',
    url: '/download',
    icon: 'download',
    description: 'Get the BikeMoves Illinois mobile app.'
  },
  {
    id: 'demographics',
    title: 'Demographics',
    url: '/demographics',
    icon: 'bar-chart',
    description: "Who's using BikeMoves?"
  },
  {
    id: 'data',
    title: 'Data',
    url: '/data',
    icon: 'map',
    description: 'Explore cycling patterns in our community.'
  }
];
const MAP_VIEWS = [
  {
    id: 'users',
    title: 'Users',
    description: `This view shows the number of unique users that have
      traveled on each segment.`
  },
  {
    id: 'trips',
    title: 'Trips',
    description: `This view shows the number of trips that have traveled on
      on a given segment.`
  },
  {
    id: 'speed',
    title: 'Average Speed',
    description: `This view shows the average riding speed for segments.
      The speed includes delays, such as waiting for a traffic
      signal to change.`
  },
  {
    id: 'preference',
    title: 'Preference',
    description: `This view shows differences between the routes chosen by
      users and the fastest routes. The preference score indicates how often
      users have gone out of their way to use (positive score) or
      avoid (negative score) a segment. Only segments with a moderate
      or strong positive or negative preference are shown.`
  },
  {
    id: 'blts',
    title: 'Bicycle Level of Traffic Stress',
    description: `This view shows preliminary Bicycle Level of Traffic Stress
      (BLTS) scores. Lower scores indicate a segment that is preceived as
      more comfortable for cyclists. The BLTS scores are based on physical
      factors such as street width and lane configuration, not on data
      provided by BikeMoves Illinois users.`
  }
];
const PRIVACY_NOTICE = ` Segments ridden by only one user are excluded to
  protect privacy.`
const DATE_NOTICE = ` The data represent trips submitted
  from July 2016 to present.`
const MAP_LAYERS = [
  {
    id: 'bike-rack',
    title: 'Bicycle Parking',
    type: 'base'
  },
  {
    id: 'bike-path',
    title: 'Bicycle Facility',
    type: 'base'
  }
];
const USER_FILTERS = [
  {
    variables: ['age'],
    values: ['years'],
    examples: [
      'age>=35',
      'age<45'
    ]
  },
  {
    variables: ['experience'],
    values: ['beginner', 'intermediate', 'advanced', 'unspecified'],
    examples: [
      'experience=beginner',
      'experience=advanced'
    ]
  },
  {
    variables: ['gender'],
    values: ['male', 'female', 'other', 'unspecified'],
    examples: [
      'gender=female',
      'gender=male'
    ]
  },
  {
    variables: ['user'],
    values: ['id'],
    examples: [
      'user=1',
      'user=23'
    ]
  },
];
const TRIP_FILTERS = [
  {
    variables: ['date'],
    values: ['yyyy-mm-dd'],
    examples: [
      'date>=2017-01-01',
      'date<=2017-08-31'
    ]
  },
  {
    variables: ['distance'],
    values: ['miles'],
    examples: [
      'distance>2.5',
      'distance<=1.0'
    ]
  },
  {
    variables: ['duration'],
    values: ['hh:mm'],
    examples: [
      'duration>=01:00',
      'duration<=00:30'
    ]
  },
  {
    variables: ['origin', 'destination'],
    values: [
      'home',
      'work',
      'k12',
      'university',
      'shopping',
      'other',
      'unspecified'
    ],
    examples: [
      'origin=home',
      'destination=work'
    ]
  },
  {
    variables: ['start', 'end'],
    values: ['hh:mm'],
    examples: [
      'start>=08:00',
      'end<=15:45'
    ]
  },
  {
    variables: ['trip'],
    values: ['id'],
    examples: [
      'trip=10',
      'trip=55'
    ]
  }
];

function getStyles(req) {
  return [
    'https://fonts.googleapis.com/css?family=Montserrat:700%7COpen+Sans',
    '/explore.css'
  ];
}

function getScripts(req) {
  let view = req.path.replace('/', '');
  let scripts = [
    'https://use.fontawesome.com/e09acc63a3.js'
  ];

  if (view === 'demographics' || view === 'data') {
    scripts.push('/config.js');
    scripts.push('/explore.js');
  }

  return scripts;
}

function serveStatic(app) {
  app.use(express.static('dist/explore'));
}

function middleware(req, res, next) {
  res.locals.styles = getStyles(req);
  res.locals.scripts = getScripts(req);
  res.locals.user = (req.user) ? {
    username: req.user.username,
    role: req.user.role
  } : null;
  res.locals.permissions = {
    'view trip details': auth.checkPermission(req, auth.PERM_VIEW_TRIP_DETAILS)
  };
  res.locals.menuItems = MENU_ITEMS;
  next();
}

function getMapViews(req) {
  let views = MAP_VIEWS.map((view) => Object.assign({}, view));
  let canView = auth.checkPermission(req, auth.PERM_VIEW_TRIP_DETAILS);
  if (canView) {
    views.push({
      id: 'details',
      title: 'Trip Details',
      description: ''
    });
  } else {
    views.forEach((view, i) => {
      if (i < 3) view.description += PRIVACY_NOTICE;
      view.description += DATE_NOTICE;
    });
  }

  return views;
}

function getMapLayers(req) {
  let layers = MAP_LAYERS.slice();
  if (auth.checkPermission(req, auth.PERM_VIEW_TRIP_DETAILS))
    layers = [
      {
        id: 'tracepoint',
        title: 'Matched Tracepoint',
        type: 'trip'
      },
      {
        id: 'leg',
        title: 'Matched Route',
        type: 'trip',
        layers: [
          '0 to 3 MPH',
          '4 to 6 MPH',
          '7 to 9 MPH',
          '10 to 12 MPH',
          '13 MPH or higher'
        ]
      },
      {
        id: 'leg-fastest',
        title: 'Fastest Route',
        type: 'trip'
      },
      {
        id: 'point',
        title: 'GPS Points',
        type: 'trip'
      },
      {
        id: 'trip',
        title: 'GPS Trace',
        type: 'trip'
      }
    ].concat(layers);

  return layers;
}

module.exports.serveStatic = serveStatic;
module.exports.middleware = middleware;
module.exports.getMapViews = getMapViews;
module.exports.getMapLayers = getMapLayers;
module.exports.USER_FILTERS = USER_FILTERS;
module.exports.TRIP_FILTERS = TRIP_FILTERS;
<commit_msg>Don't show date notice for BLTS.<commit_after>const express = require('express');
const path = require('path');
const auth = require('./auth.js');

const MENU_ITEMS = [
  {
    id: 'download',
    title: 'Download',
    url: '/download',
    icon: 'download',
    description: 'Get the BikeMoves Illinois mobile app.'
  },
  {
    id: 'demographics',
    title: 'Demographics',
    url: '/demographics',
    icon: 'bar-chart',
    description: "Who's using BikeMoves?"
  },
  {
    id: 'data',
    title: 'Data',
    url: '/data',
    icon: 'map',
    description: 'Explore cycling patterns in our community.'
  }
];
const MAP_VIEWS = [
  {
    id: 'users',
    title: 'Users',
    description: `This view shows the number of unique users that have
      traveled on each segment.`
  },
  {
    id: 'trips',
    title: 'Trips',
    description: `This view shows the number of trips that have traveled on
      on a given segment.`
  },
  {
    id: 'speed',
    title: 'Average Speed',
    description: `This view shows the average riding speed for segments.
      The speed includes delays, such as waiting for a traffic
      signal to change.`
  },
  {
    id: 'preference',
    title: 'Preference',
    description: `This view shows differences between the routes chosen by
      users and the fastest routes. The preference score indicates how often
      users have gone out of their way to use (positive score) or
      avoid (negative score) a segment. Only segments with a moderate
      or strong positive or negative preference are shown.`
  },
  {
    id: 'blts',
    title: 'Bicycle Level of Traffic Stress',
    description: `This view shows preliminary Bicycle Level of Traffic Stress
      (BLTS) scores. Lower scores indicate a segment that is preceived as
      more comfortable for cyclists. The BLTS scores are based on physical
      factors such as street width and lane configuration, not on data
      provided by BikeMoves Illinois users.`
  }
];
const PRIVACY_NOTICE = ` Segments ridden by only one user are excluded to
  protect privacy.`
const DATE_NOTICE = ` The data represent trips submitted
  from July 2016 to present.`
const MAP_LAYERS = [
  {
    id: 'bike-rack',
    title: 'Bicycle Parking',
    type: 'base'
  },
  {
    id: 'bike-path',
    title: 'Bicycle Facility',
    type: 'base'
  }
];
const USER_FILTERS = [
  {
    variables: ['age'],
    values: ['years'],
    examples: [
      'age>=35',
      'age<45'
    ]
  },
  {
    variables: ['experience'],
    values: ['beginner', 'intermediate', 'advanced', 'unspecified'],
    examples: [
      'experience=beginner',
      'experience=advanced'
    ]
  },
  {
    variables: ['gender'],
    values: ['male', 'female', 'other', 'unspecified'],
    examples: [
      'gender=female',
      'gender=male'
    ]
  },
  {
    variables: ['user'],
    values: ['id'],
    examples: [
      'user=1',
      'user=23'
    ]
  },
];
const TRIP_FILTERS = [
  {
    variables: ['date'],
    values: ['yyyy-mm-dd'],
    examples: [
      'date>=2017-01-01',
      'date<=2017-08-31'
    ]
  },
  {
    variables: ['distance'],
    values: ['miles'],
    examples: [
      'distance>2.5',
      'distance<=1.0'
    ]
  },
  {
    variables: ['duration'],
    values: ['hh:mm'],
    examples: [
      'duration>=01:00',
      'duration<=00:30'
    ]
  },
  {
    variables: ['origin', 'destination'],
    values: [
      'home',
      'work',
      'k12',
      'university',
      'shopping',
      'other',
      'unspecified'
    ],
    examples: [
      'origin=home',
      'destination=work'
    ]
  },
  {
    variables: ['start', 'end'],
    values: ['hh:mm'],
    examples: [
      'start>=08:00',
      'end<=15:45'
    ]
  },
  {
    variables: ['trip'],
    values: ['id'],
    examples: [
      'trip=10',
      'trip=55'
    ]
  }
];

function getStyles(req) {
  return [
    'https://fonts.googleapis.com/css?family=Montserrat:700%7COpen+Sans',
    '/explore.css'
  ];
}

function getScripts(req) {
  let view = req.path.replace('/', '');
  let scripts = [
    'https://use.fontawesome.com/e09acc63a3.js'
  ];

  if (view === 'demographics' || view === 'data') {
    scripts.push('/config.js');
    scripts.push('/explore.js');
  }

  return scripts;
}

function serveStatic(app) {
  app.use(express.static('dist/explore'));
}

function middleware(req, res, next) {
  res.locals.styles = getStyles(req);
  res.locals.scripts = getScripts(req);
  res.locals.user = (req.user) ? {
    username: req.user.username,
    role: req.user.role
  } : null;
  res.locals.permissions = {
    'view trip details': auth.checkPermission(req, auth.PERM_VIEW_TRIP_DETAILS)
  };
  res.locals.menuItems = MENU_ITEMS;
  next();
}

function getMapViews(req) {
  let views = MAP_VIEWS.map((view) => Object.assign({}, view));
  let canView = auth.checkPermission(req, auth.PERM_VIEW_TRIP_DETAILS);
  if (canView) {
    views.push({
      id: 'details',
      title: 'Trip Details',
      description: ''
    });
  } else {
    views.forEach((view, i) => {
      if (i < 3) view.description += PRIVACY_NOTICE;
      if (i < 4) view.description += DATE_NOTICE;
    });
  }

  return views;
}

function getMapLayers(req) {
  let layers = MAP_LAYERS.slice();
  if (auth.checkPermission(req, auth.PERM_VIEW_TRIP_DETAILS))
    layers = [
      {
        id: 'tracepoint',
        title: 'Matched Tracepoint',
        type: 'trip'
      },
      {
        id: 'leg',
        title: 'Matched Route',
        type: 'trip',
        layers: [
          '0 to 3 MPH',
          '4 to 6 MPH',
          '7 to 9 MPH',
          '10 to 12 MPH',
          '13 MPH or higher'
        ]
      },
      {
        id: 'leg-fastest',
        title: 'Fastest Route',
        type: 'trip'
      },
      {
        id: 'point',
        title: 'GPS Points',
        type: 'trip'
      },
      {
        id: 'trip',
        title: 'GPS Trace',
        type: 'trip'
      }
    ].concat(layers);

  return layers;
}

module.exports.serveStatic = serveStatic;
module.exports.middleware = middleware;
module.exports.getMapViews = getMapViews;
module.exports.getMapLayers = getMapLayers;
module.exports.USER_FILTERS = USER_FILTERS;
module.exports.TRIP_FILTERS = TRIP_FILTERS;
<|endoftext|>