<commit_before>//Half-Edge mesh data structure
//Based on http://www.flipcode.com/archives/The_Half-Edge_Data_Structure.shtml
//and http://fgiesen.wordpress.com/2012/03/24/half-edge-based-mesh-representations-practice/
define([
  'pex/geom/Vec3',
  'pex/geom/hem/HEEdge',
  'pex/geom/hem/HEVertex',
  'pex/geom/hem/HEFace',
  'pex/geom/BoundingBox',
  'pex/geom/Octree'
],
function(Vec3, HEEdge, HEVertex, HEFace, BoundingBox, Octree) {
  function HEMesh() {
    this.vertices = new Array();
    this.faces = new Array();
    this.edges = new Array();
  }

  HEMesh.prototype.fixDuplicatedVertices = function() {
    var bbox = BoundingBox.fromPoints(this.vertices.map(function(v) { return v.position; }));
    var octree = Octree.fromBoundingBox(bbox);
    var dup = 0;
    for(var i=0; i<this.vertices.length; i++) {
      var v = this.vertices[i];
      var duplicate = octree.has(v.position);
      if (!duplicate) {
        v.position.vertex = v;
        octree.add(v.position);
      }
      else {
        this.vertices.splice(i, 1);
        i--;
        var duplicateVertex = duplicate.vertex;
        for(var j=0; j<this.edges.length; j++) {
          if (this.edges[j].vert == v) {
            this.edges[j].vert = duplicateVertex;
          }
        }
      }
    }
    return this;
  }

  HEMesh.prototype.faceHash = function(f) {
    var vertices = f.getAllVertices();
    var indices = vertices.map(function(v) {
      return this.vertices.indexOf(v);
    }.bind(this));
    indices = indices.sort();
    var hash = indices.join('_');
    if (indices[0] == indices[1] || indices[1] == indices[2]) {
      console.log(hash);
    }
    return hash;
  }

  HEMesh.prototype.edgeHash = function(e) {
    var i = this.vertices.indexOf(e.vert);
    var j = this.vertices.indexOf(e.next.vert);
    var hash = i + "_" + j;
    return hash;
  }

  HEMesh.prototype.fixDuplicatedEdges = function() {
    console.log('fixDuplicatedEdges', this.edges.length);

    var uniques = [];
    for(var i=0; i<this.edges.length; i++) {
      var edge = this.edges[i];
      var hash = this.edgeHash(edge);
      if (uniques.indexOf(hash) !== -1) {
        this.edges.splice(i, 1);
        i--;
      }
      else {
        uniques.push(hash);
      }
    }

    console.log('fixDuplicatedEdges end', this.edges.length);
  }

  HEMesh.prototype.fixVertexEdges = function() {
    this.vertices.forEach(function(v) {
      v.edge = null;
    });
    for(var i in this.edges) {
      var edge = this.edges[i];
      edge.vert.edge = edge;
    }
    return this;
  }

  var pairs = 0;
  HEMesh.prototype.fixEdgePairs = function() {
    for(var i=0; i<this.edges.length; i++) {
      this.edges[i].pair = null;
    }
    var numPairs = 0;
    var hash = {};
    for(var i=0; i<this.vertices.length; i++) {
      this.vertices[i].index = i;
    }
    for(var i=0; i<this.edges.length; i++) {
      var edge = this.edges[i];
      var edgeHash = edge.vert.index + "," + edge.next.vert.index;
      var pairEdgeHash = edge.next.vert.index + "," + edge.vert.index;
      hash[edgeHash] = edge;
      if (hash[pairEdgeHash]) {
        edge.pair = hash[pairEdgeHash];
        edge.pair.pair = edge;
      }
    }
    for(var i=0; i<this.vertices.length; i++) {
      this.vertices[i].index = -1;
    }
    return this;
  }

  // HEMesh.prototype.getEdgeBetween = function(a, b) {
  //       for(var i in this.edges) {
  //         var edge = this.edges[i];
  //         if (edge.vert == a && edge.next.vert == b) {
  //           return edge;
  //         }
  //       }
  //       return null;
  //   }

  HEMesh.prototype.check = function() {
    for(var i in this.vertices) {
      if (this.vertices[i].edge == null) {
        console.log("Missing vertex edge at ", i);
      }
      else if (this.vertices[i] != this.vertices[i].edge.vert) {
        console.log("Edge doesn't point to it's vertex at ", i);
      }
    }

    for(var i in this.faces) {
      if (this.faces[i].edge == null) {
        console.log("Missing faces edge at ", i);
      }
      else if (this.faces[i] != this.faces[i].edge.face) {
        console.log("Edge doesn't point to it's face at ", i);
      }
    }

    for(var i in this.edges) {
      var edge = this.edges[i];
      var e = edge;
      var watchDog = 0;

      if (edge.pair == null) {
        console.log("Edge doesn't have it's pair", i);
      }
      else if (edge.pair.pair != edge) {
        console.log("Edge pair doesn't match", i, this.edges.indexOf(edge), this.edges.indexOf(edge.pair), this.edges.indexOf(edge.pair.pair));
      }

      do {
        if (++watchDog > 100) {
          console.log("Edge watchDog break at", i, " . Wrong edge loop pointers?");
          break;
        }
        if (watchDog > 6) {
          console.log("Warning! Face with " + watchDog + " vertices");
        }
        if (e.next == null) {
          console.log("Missing edge next at ", i, ". Open loop.");
          break;
        }
        e = e.next;
      } while(e != edge)
    }

    return this;
  };

  HEMesh.prototype.splitVertex = function(vertex, newVertexPos, startEdge, endEdge) {
    var newVertex = new HEVertex(newVertexPos[0], newVertexPos[1], newVertexPos[2]);
    this.vertices.push(newVertex);
    if (startEdge != null && startEdge == endEdge) {
      //edge
      var e = startEdge;
      var e2 = new HEEdge();
      e2.vert = newVertex;
      e2.next = e.next;
      e.next = e2;
      e2.face = e.face;
      this.edges.push(e2);
      newVertex.edge = e2;

      //opposite edge
      var o = startEdge.pair;
      var o2 = new HEEdge();
      o2.vert = newVertex;
      o2.next = o.next;
      o.next = o2;
      o2.face = o.face;
      this.edges.push(o2);

      o2.pair = e;
      e.pair = o2;

      e2.pair = o;
      o.pair = e2;

      return e2;
    }
    else if (startEdge == null && endEdge == null) {
      var newEdge1 = new HEEdge();
      var newEdge2 = new HEEdge();

      var edge = vertex.edge;
      var prevEdge = edge.findPrev();

      newEdge1.vert = vertex;
      newEdge2.vert = newVertex;
      newEdge1.face = edge.face;
      newEdge2.face = edge.face;

      newEdge1.next = newEdge2;
      newEdge2.next = edge;

      newEdge1.pair = newEdge2;
      newEdge2.pair = newEdge1;

      vertex.edge = newEdge1;
      prevEdge.next = newEdge1;

      newVertex.edge = newEdge2;

      this.edges.push(newEdge1);
      this.edges.push(newEdge2);

      return newEdge1;
    }
  };

  HEMesh.prototype.splitFace = function(vert1Edge, vert2Edge) {
    var vert1EdgeNext = vert1Edge.next;
    var vert2EdgeNext = vert2Edge.next;
    var vert1EdgePrev = vert1Edge.findPrev();
    var vert2EdgePrev = vert2Edge.findPrev();
    var oldFace = vert1Edge.face;

    var splitEdge1 = new HEEdge();
    var splitEdge2 = new HEEdge();
    this.edges.push(splitEdge1);
    this.edges.push(splitEdge2);

    splitEdge1.pair = splitEdge2;
    splitEdge2.pair = splitEdge1;

    splitEdge1.vert = vert2Edge.vert;
    splitEdge2.vert = vert1Edge.vert;

    splitEdge1.next = vert1Edge;
    vert2EdgePrev.next = splitEdge1;

    splitEdge2.next = vert2Edge;
    vert1EdgePrev.next = splitEdge2;

    splitEdge1.face = vert1Edge.face;

    var newFace = new HEFace(splitEdge2);
    this.faces.push(newFace);

    var tmpEdge = splitEdge2;
    do {
      tmpEdge.face = newFace;
      tmpEdge = tmpEdge.next;
    } while(tmpEdge != splitEdge2);

    //just ot make sure we don't point to one of the splitten vertices
    oldFace.edge = vert1Edge;

    if (oldFace.color) newFace.color = oldFace.color;

    return newFace;
  };

  HEMesh.prototype.splitEdge = function(edge, ratio) {
    ratio = ratio || 0.5;

    //newVertPos = v + ratio * (nv - v)
    //newVertPos = add3(v, scale3(ratio, sub3(nv, v)))
    //newVertPos = nv.clone().sub(v).scale(ratio).add(v);
    var newVertPos = Vec3.clone(edge.next.vert.position);
    Vec3.sub(newVertPos, newVertPos, edge.vert.position);
    Vec3.scale(newVertPos, newVertPos, ratio);
    Vec3.add(newVertPos, newVertPos, edge.vert.position);

    this.splitVertex(edge.vert, newVertPos, edge, edge);
  };

  HEMesh.prototype.splitFaceAtPoint = function(face, newPoint) {
    var vert = face.edge.vert;
    var edge = face.edge;
    vert.edge = edge; //to make sure we split the right face
    var newEdge = this.splitVertex(vert, newPoint); //new edges will be added before 'edge'
    var from = newEdge.next;
    var to = from.next.next; //next corner afther the old first
    //split the face from the new vertex to the next corner
    //and move one corner further
    var i = 0;
    do {
      this.splitFace(from, to);
      from = to.findPrev();
      to = from.next.next;
    } while (to != newEdge);

    return newEdge.next;
  };

  return HEMesh;
});
<commit_msg>Added invalid edge face in HEMesh check<commit_after>//Half-Edge mesh data structure
//Based on http://www.flipcode.com/archives/The_Half-Edge_Data_Structure.shtml
//and http://fgiesen.wordpress.com/2012/03/24/half-edge-based-mesh-representations-practice/
define([
  'pex/geom/Vec3',
  'pex/geom/hem/HEEdge',
  'pex/geom/hem/HEVertex',
  'pex/geom/hem/HEFace',
  'pex/geom/BoundingBox',
  'pex/geom/Octree'
],
function(Vec3, HEEdge, HEVertex, HEFace, BoundingBox, Octree) {
  function HEMesh() {
    this.vertices = new Array();
    this.faces = new Array();
    this.edges = new Array();
  }

  HEMesh.prototype.fixDuplicatedVertices = function() {
    var bbox = BoundingBox.fromPoints(this.vertices.map(function(v) { return v.position; }));
    var octree = Octree.fromBoundingBox(bbox);
    var dup = 0;
    for(var i=0; i<this.vertices.length; i++) {
      var v = this.vertices[i];
      var duplicate = octree.has(v.position);
      if (!duplicate) {
        v.position.vertex = v;
        octree.add(v.position);
      }
      else {
        this.vertices.splice(i, 1);
        i--;
        var duplicateVertex = duplicate.vertex;
        for(var j=0; j<this.edges.length; j++) {
          if (this.edges[j].vert == v) {
            this.edges[j].vert = duplicateVertex;
          }
        }
      }
    }
    return this;
  }

  HEMesh.prototype.faceHash = function(f) {
    var vertices = f.getAllVertices();
    var indices = vertices.map(function(v) {
      return this.vertices.indexOf(v);
    }.bind(this));
    indices = indices.sort();
    var hash = indices.join('_');
    if (indices[0] == indices[1] || indices[1] == indices[2]) {
      console.log(hash);
    }
    return hash;
  }

  HEMesh.prototype.edgeHash = function(e) {
    var i = this.vertices.indexOf(e.vert);
    var j = this.vertices.indexOf(e.next.vert);
    var hash = i + "_" + j;
    return hash;
  }

  HEMesh.prototype.fixDuplicatedEdges = function() {
    console.log('fixDuplicatedEdges', this.edges.length);

    var uniques = [];
    for(var i=0; i<this.edges.length; i++) {
      var edge = this.edges[i];
      var hash = this.edgeHash(edge);
      if (uniques.indexOf(hash) !== -1) {
        this.edges.splice(i, 1);
        i--;
      }
      else {
        uniques.push(hash);
      }
    }

    console.log('fixDuplicatedEdges end', this.edges.length);
  }

  HEMesh.prototype.fixVertexEdges = function() {
    this.vertices.forEach(function(v) {
      v.edge = null;
    });
    for(var i in this.edges) {
      var edge = this.edges[i];
      edge.vert.edge = edge;
    }
    return this;
  }

  var pairs = 0;
  HEMesh.prototype.fixEdgePairs = function() {
    for(var i=0; i<this.edges.length; i++) {
      this.edges[i].pair = null;
    }
    var numPairs = 0;
    var hash = {};
    for(var i=0; i<this.vertices.length; i++) {
      this.vertices[i].index = i;
    }
    for(var i=0; i<this.edges.length; i++) {
      var edge = this.edges[i];
      var edgeHash = edge.vert.index + "," + edge.next.vert.index;
      var pairEdgeHash = edge.next.vert.index + "," + edge.vert.index;
      hash[edgeHash] = edge;
      if (hash[pairEdgeHash]) {
        edge.pair = hash[pairEdgeHash];
        edge.pair.pair = edge;
      }
    }
    for(var i=0; i<this.vertices.length; i++) {
      this.vertices[i].index = -1;
    }
    return this;
  }

  // HEMesh.prototype.getEdgeBetween = function(a, b) {
  //       for(var i in this.edges) {
  //         var edge = this.edges[i];
  //         if (edge.vert == a && edge.next.vert == b) {
  //           return edge;
  //         }
  //       }
  //       return null;
  //   }

  HEMesh.prototype.check = function() {
    for(var i in this.vertices) {
      if (this.vertices[i].edge == null) {
        console.log("Missing vertex edge at ", i);
      }
      else if (this.vertices[i] != this.vertices[i].edge.vert) {
        console.log("Edge doesn't point to it's vertex at ", i);
      }
    }

    for(var i in this.faces) {
      if (this.faces[i].edge == null) {
        console.log("Missing faces edge at ", i);
      }
      else if (this.faces[i] != this.faces[i].edge.face) {
        console.log("Edge doesn't point to it's face at ", i);
      }

      if (this.edges.indexOf(this.faces[i].edge) === -1) {
        console.log("Invalid face edge at ", i);
      }
    }

    for(var i in this.edges) {
      var edge = this.edges[i];
      var e = edge;
      var watchDog = 0;

      if (edge.pair == null) {
        console.log("Edge doesn't have it's pair", i);
      }
      else if (edge.pair.pair != edge) {
        console.log("Edge pair doesn't match", i, this.edges.indexOf(edge), this.edges.indexOf(edge.pair), this.edges.indexOf(edge.pair.pair));
      }

      do {
        if (++watchDog > 100) {
          console.log("Edge watchDog break at", i, " . Wrong edge loop pointers?");
          break;
        }
        if (watchDog > 6) {
          console.log("Warning! Face with " + watchDog + " vertices");
        }
        if (e.next == null) {
          console.log("Missing edge next at ", i, ". Open loop.");
          break;
        }
        e = e.next;
      } while(e != edge)
    }

    return this;
  };

  HEMesh.prototype.splitVertex = function(vertex, newVertexPos, startEdge, endEdge) {
    var newVertex = new HEVertex(newVertexPos[0], newVertexPos[1], newVertexPos[2]);
    this.vertices.push(newVertex);
    if (startEdge != null && startEdge == endEdge) {
      //edge
      var e = startEdge;
      var e2 = new HEEdge();
      e2.vert = newVertex;
      e2.next = e.next;
      e.next = e2;
      e2.face = e.face;
      this.edges.push(e2);
      newVertex.edge = e2;

      //opposite edge
      var o = startEdge.pair;
      var o2 = new HEEdge();
      o2.vert = newVertex;
      o2.next = o.next;
      o.next = o2;
      o2.face = o.face;
      this.edges.push(o2);

      o2.pair = e;
      e.pair = o2;

      e2.pair = o;
      o.pair = e2;

      return e2;
    }
    else if (startEdge == null && endEdge == null) {
      var newEdge1 = new HEEdge();
      var newEdge2 = new HEEdge();

      var edge = vertex.edge;
      var prevEdge = edge.findPrev();

      newEdge1.vert = vertex;
      newEdge2.vert = newVertex;
      newEdge1.face = edge.face;
      newEdge2.face = edge.face;

      newEdge1.next = newEdge2;
      newEdge2.next = edge;

      newEdge1.pair = newEdge2;
      newEdge2.pair = newEdge1;

      vertex.edge = newEdge1;
      prevEdge.next = newEdge1;

      newVertex.edge = newEdge2;

      this.edges.push(newEdge1);
      this.edges.push(newEdge2);

      return newEdge1;
    }
  };

  HEMesh.prototype.splitFace = function(vert1Edge, vert2Edge) {
    var vert1EdgeNext = vert1Edge.next;
    var vert2EdgeNext = vert2Edge.next;
    var vert1EdgePrev = vert1Edge.findPrev();
    var vert2EdgePrev = vert2Edge.findPrev();
    var oldFace = vert1Edge.face;

    var splitEdge1 = new HEEdge();
    var splitEdge2 = new HEEdge();
    this.edges.push(splitEdge1);
    this.edges.push(splitEdge2);

    splitEdge1.pair = splitEdge2;
    splitEdge2.pair = splitEdge1;

    splitEdge1.vert = vert2Edge.vert;
    splitEdge2.vert = vert1Edge.vert;

    splitEdge1.next = vert1Edge;
    vert2EdgePrev.next = splitEdge1;

    splitEdge2.next = vert2Edge;
    vert1EdgePrev.next = splitEdge2;

    splitEdge1.face = vert1Edge.face;

    var newFace = new HEFace(splitEdge2);
    this.faces.push(newFace);

    var tmpEdge = splitEdge2;
    do {
      tmpEdge.face = newFace;
      tmpEdge = tmpEdge.next;
    } while(tmpEdge != splitEdge2);

    //just ot make sure we don't point to one of the splitten vertices
    oldFace.edge = vert1Edge;

    if (oldFace.color) newFace.color = oldFace.color;

    return newFace;
  };

  HEMesh.prototype.splitEdge = function(edge, ratio) {
    ratio = ratio || 0.5;

    //newVertPos = v + ratio * (nv - v)
    //newVertPos = add3(v, scale3(ratio, sub3(nv, v)))
    //newVertPos = nv.clone().sub(v).scale(ratio).add(v);
    var newVertPos = Vec3.clone(edge.next.vert.position);
    Vec3.sub(newVertPos, newVertPos, edge.vert.position);
    Vec3.scale(newVertPos, newVertPos, ratio);
    Vec3.add(newVertPos, newVertPos, edge.vert.position);

    this.splitVertex(edge.vert, newVertPos, edge, edge);
  };

  HEMesh.prototype.splitFaceAtPoint = function(face, newPoint) {
    var vert = face.edge.vert;
    var edge = face.edge;
    vert.edge = edge; //to make sure we split the right face
    var newEdge = this.splitVertex(vert, newPoint); //new edges will be added before 'edge'
    var from = newEdge.next;
    var to = from.next.next; //next corner afther the old first
    //split the face from the new vertex to the next corner
    //and move one corner further
    var i = 0;
    do {
      this.splitFace(from, to);
      from = to.findPrev();
      to = from.next.next;
    } while (to != newEdge);

    return newEdge.next;
  };

  return HEMesh;
});
<|endoftext|>


<commit_before>import ArrayUtil from '../../util/array-util.js';
import ParseUtil from '../../util/parse-util.js';

export default class UpdateNote {
    /**
     * 1ms毎処理用の変数を初期化
     */
    static init(picoAudio, currentTime) {
        this.updatePreTime = performance.now();
        this.pPreTime = performance.now();
        this.cPreTime = picoAudio.context.currentTime * 1000;
        this.pTimeSum = 0;
        this.cTimeSum = 0;
        this.cnt = 0;
        this.initCurrentTime = currentTime;
    }

    /**
     * 再生中、1ms毎に呼ばれるコールバック
     * （ブラウザの制限で実際は最短4ms毎に呼ばれる）
     * @returns {number} 現在の時間
     */
    static update(picoAudio) {
        const context = picoAudio.context;
        const settings = picoAudio.settings;
        const states = picoAudio.states;
        let updateNowTime = performance.now();
        let updatePreTime = this.updatePreTime;
        let pPreTime = this.pPreTime;
        let cPreTime = this.cPreTime;
        let pTimeSum = this.pTimeSum;
        let cTimeSum = this.cTimeSum;
        let cnt = this.cnt;

        // サウンドが重くないか監視（フリーズ対策） //
        //   performance.now()とAudioContext.currentTimeの時間差を計算し
        //   AudioContext.currentTimeが遅れていたら処理落ちしていると判断する
        let updateBufTime = updateNowTime - updatePreTime;
        const pTime = updateNowTime;
        const cTime = context.currentTime * 1000;
        pTimeSum += pTime - pPreTime;
        cTimeSum += cTime - cPreTime;
        pPreTime = pTime;
        cPreTime = cTime;
        const latencyTime = pTimeSum - cTimeSum;
        states.latencyTime = latencyTime;

        // サウンドが重い場合、負荷軽減処理を発動するリミットを上げていく //
        if (latencyTime >= 100) { // currentTimeが遅い（サウンドが重い）
            states.latencyLimitTime += latencyTime;
            cTimeSum += 100;
        } else if (latencyTime <= -100) { // currentTimeが速い（誤差）
            cTimeSum = pTimeSum;
        } else {
            if (states.latencyLimitTime > 0) { // currentTimeが丁度いい
                states.latencyLimitTime -= updateBufTime*0.003;
                if (states.latencyLimitTime < 0) states.latencyLimitTime = 0;
            }
        }

        // ノートを先読み度合いを自動調整（予約しすぎると重くなる） //
        states.updateIntervalTime = updateBufTime;
        if (states.updateBufTime < updateBufTime) { // 先読み遅れている場合
            states.updateBufTime = updateBufTime;
        } else { // 先読み量に余裕がある場合
            // 先読み量を少しずつ減らす //
            if (states.updateBufMaxTime > 350) {
                states.updateBufMaxTime -= states.updateBufMaxTime*0.002;
            }
            // 先読み量を少しずつ増やす //
            if (states.updateBufTime < 20) {
                states.updateBufTime += states.updateBufTime*0.0005;
            }
            if (states.updateBufMaxTime >= 10 && states.updateBufMaxTime < 340) {
                states.updateBufMaxTime += states.updateBufMaxTime*0.002;
            }
        }
        // 先読み量が足りなくなった場合
        if (states.updateBufTime > states.updateBufMaxTime) {
            if (updateBufTime >= 900 && states.latencyLimitTime <= 150) {
                // バックグラウンドっぽくて重くない場合、バックグラウンド再生
                states.updateBufMaxTime += updateBufTime;
            } else { // 通常
                const tempTime = updateBufTime - states.updateBufMaxTime;
                states.updateBufTime = states.updateBufMaxTime;

                // 先読み量が小さい場合大きくする
                if (states.updateBufMaxTime < 10) {
                    states.updateBufTime = states.updateBufMaxTime;
                    states.updateBufMaxTime *= 1.25;
                } else {
                    states.updateBufMaxTime += tempTime / 2;
                }
            }
            if (states.updateBufMaxTime > 1100) states.updateBufMaxTime = 1100;
        }

        // サウンドが重すぎる場合、先読み度合いを小さくして負荷軽減 //
        if (states.latencyLimitTime > 150) {
            cTimeSum = pTimeSum;
            states.latencyLimitTime -= 5;
            if (states.latencyLimitTime > 1000) states.latencyLimitTime = 1000;
            // ノート先読みをかなり小さくする（フリーズ対策）
            states.updateBufMaxTime = 1;
            states.updateBufTime = 1;
            updateBufTime = 1;
        }

        // 再生処理 //
        for (let ch=0; ch<16; ch++) {
            const notes = picoAudio.playData.channels[ch].notes;
            let idx = states.playIndices[ch];
            for (; idx<notes.length; idx++) {
                const note = notes[idx];
                const curTime = cnt == 0 ? this.initCurrentTime - states.startTime
                    : context.currentTime - states.startTime;

                // 終わったノートは演奏せずにスキップ
                if (curTime >= note.stopTime) continue;
                // （シークバーで途中から再生時）startTimeが過ぎたものは鳴らさない
                if (cnt == 0 && curTime > note.startTime) continue;
                // AudioParam.setValueAtTime()等でマイナスが入るとエラーになるので対策
                if (curTime + note.startTime < 0) continue;
                // 演奏開始時間 - 先読み時間(ノート予約) になると演奏予約or演奏開始
                if (curTime < note.startTime - states.updateBufTime/1000) break;

                // PicoAudio音源の再生処理 //
                if (!settings.isWebMIDI) {
                    // 予約ノート数が急激に増えそうな時、先読み量を小さくしておく //
                    if (states.stopFuncs.length >= 350 && states.updateBufTime < 1000) {
                        states.updateBufTime = 12;
                        states.updateBufMaxTime = states.updateBufTime;
                    }

                    // レトロモード（和音制限モード） //
                    if (settings.maxPoly != -1 || settings.maxPercPoly != -1) {
                        let polyCnt = 0;
                        let percCnt = 0;
                        states.stopFuncs.forEach((tar) => {
                            if (!tar.note) return;
                            if (tar.note.channel != 9) {
                                if (note.start >= tar.note.start && note.start < tar.note.stop) {
                                    polyCnt++;
                                }
                            } else {
                                if (note.start == tar.note.start) {
                                    percCnt++;
                                }
                            }
                        });
                        if ((note.channel != 9 && polyCnt >= settings.maxPoly)
                            || (note.channel == 9 && percCnt >= settings.maxPercPoly)) {
                            continue;
                        }
                    }

                    // １ノート分の再生処理（WebAudioで再生） //
                    const stopFunc =
                        note.channel != 9 ? picoAudio.createNote(note)
                        : picoAudio.createPercussionNote(note);
                    if (!stopFunc) continue; // 無音の場合、処理しない
                    picoAudio.pushFunc({
                        note: note,
                        stopFunc: stopFunc
                    });
                }
                states.noteOnAry.push(note);
            }
            // notesのどこまで再生したかを記憶して、次回コールバック時にそこから処理を始める
            states.playIndices[ch] = idx;
        }

        // noteOnの時間になったか監視 //
        this.checkNoteOn(picoAudio);

        // noteOffの時間になったか監視 //
        this.checkNoteOff(picoAudio);

        // WebMIDIの再生処理 //
        if (settings.isWebMIDI && settings.WebMIDIPortOutput != null) {
            const messages = picoAudio.playData.messages;
            const smfData = picoAudio.playData.smfData;
            let idx = states.playIndices[16]; // 17chはWebMIDI用
            for (; idx<messages.length; idx++) {
                const message = messages[idx];
                const curTime = context.currentTime - states.startTime;

                // 終わったノートは演奏せずにスキップ
                if (curTime > message.time + 1) continue;
                // 演奏開始時間 - 先読み時間(ノート予約) になると演奏予約or演奏開始
                if (curTime < message.time - 1) break;

                // WebMIDIでMIDIメッセージを送信する処理 //
                const pLen = message.smfPtrLen;
                const p = message.smfPtr;
                const time = message.time;
                const state = smfData[p];
                if (state!=0xff) {
                    try {
                        if (state==0xF0 || state==0xF7) {
                            // sysExのMIDIメッセージ
                            if (settings.WebMIDIPortSysEx) {
                                // 長さ情報を取り除いて純粋なSysExメッセージにする
                                const lengthAry = ParseUtil.variableLengthToInt(smfData, p+1, p+1+4);
                                const sysExStartP = p+1+lengthAry[1];
                                const sysExEndP = sysExStartP+lengthAry[0];
                                const webMIDIMes = new Uint8Array(1 + lengthAry[0]);
                                webMIDIMes[0] = state;
                                const size = sysExEndP - sysExStartP;
                                for (let i=0; i<size; i++)
                                    webMIDIMes[i+1] = smfData[sysExStartP + i];
                                settings.WebMIDIPortOutput.send(webMIDIMes,
                                    (time - context.currentTime + window.performance.now()/1000 + states.startTime) * 1000);
                            }
                        } else {
                            // sysEx以外のMIDIメッセージ
                            const sendMes = [];
                            for (let i=0; i<pLen; i++) sendMes.push(smfData[p+i]);
                            settings.WebMIDIPortOutput.send(sendMes,
                                (time - context.currentTime + window.performance.now()/1000 + states.startTime) * 1000);
                        }
                    } catch(e) {
                        console.log(e, p, pLen, time, state);
                    }
                }
            }
            // messagesのどこまで送信したかを記憶して、次回コールバック時にそこから処理を始める
            states.playIndices[16] = idx;
        }

        // 1msコールバックが呼ばれた回数をカウント
        cnt ++;

        // 変数を反映 //
        this.updatePreTime = updateNowTime;
        this.pPreTime = pPreTime;
        this.cPreTime = cPreTime;
        this.pTimeSum = pTimeSum;
        this.cTimeSum = cTimeSum;
        this.cnt = cnt;
    }

    /**
     * noteOnの時間になったか監視
     * @param {PicoAudio} picoAudio PicoAudioインスタンス
     */
    static checkNoteOn(picoAudio) {
        const context = picoAudio.context;
        const trigger = picoAudio.trigger;
        const states = picoAudio.states;
        const noteOnAry = picoAudio.states.noteOnAry;
        const noteOffAry = picoAudio.states.noteOffAry;

        for (let i=0; i<noteOnAry.length; i++) {
            const tempNote = noteOnAry[i];
            const nowTime = context.currentTime - states.startTime;
            if (tempNote.startTime - nowTime <= 0) {
                ArrayUtil.delete(noteOnAry, i); // noteOnAry.splice(i, 1); の高速化
                noteOffAry.push(tempNote);
                // noteOnにコールバックを通知
                if (trigger.isNoteTrigger) trigger.noteOn(tempNote);
                i--;
            }
        }
    }

    /**
     * noteOffの時間になったか監視
     * @param {PicoAudio} picoAudio PicoAudioインスタンス
     */
    static checkNoteOff(picoAudio) {
        const context = picoAudio.context;
        const trigger = picoAudio.trigger;
        const states = picoAudio.states;
        const noteOffAry = picoAudio.states.noteOffAry;

        for (let i=0; i<noteOffAry.length; i++) {
            const tempNote = noteOffAry[i];
            const nowTime = context.currentTime - states.startTime;
            if ((tempNote.channel != 9 && tempNote.stopTime - nowTime <= 0)
                || (tempNote.channel == 9 && tempNote.drumStopTime - nowTime <= 0)) {
                ArrayUtil.delete(noteOffAry, i); // noteOffAry.splice(i, 1); の高速化
                picoAudio.clearFunc("note", tempNote);
                // noteOffにコールバックを通知
                if (trigger.isNoteTrigger) trigger.noteOff(tempNote);
                i--;
            }
        }
    }
}<commit_msg>最初途切れ軽減<commit_after>import ArrayUtil from '../../util/array-util.js';
import ParseUtil from '../../util/parse-util.js';

export default class UpdateNote {
    /**
     * 1ms毎処理用の変数を初期化
     */
    static init(picoAudio, currentTime) {
        this.updatePreTime = performance.now();
        this.pPreTime = performance.now();
        this.cPreTime = picoAudio.context.currentTime * 1000;
        this.pTimeSum = 0;
        this.cTimeSum = 0;
        this.cnt = 0;
        this.initCurrentTime = currentTime;
    }

    /**
     * 再生中、1ms毎に呼ばれるコールバック
     * （ブラウザの制限で実際は最短4ms毎に呼ばれる）
     * @returns {number} 現在の時間
     */
    static update(picoAudio) {
        const context = picoAudio.context;
        const settings = picoAudio.settings;
        const states = picoAudio.states;
        let updateNowTime = performance.now();
        let updatePreTime = this.updatePreTime;
        let pPreTime = this.pPreTime;
        let cPreTime = this.cPreTime;
        let pTimeSum = this.pTimeSum;
        let cTimeSum = this.cTimeSum;
        let cnt = this.cnt;

        // サウンドが重くないか監視（フリーズ対策） //
        //   performance.now()とAudioContext.currentTimeの時間差を計算し
        //   AudioContext.currentTimeが遅れていたら処理落ちしていると判断する
        let updateBufTime = updateNowTime - updatePreTime;
        const pTime = updateNowTime;
        const cTime = context.currentTime * 1000;
        pTimeSum += pTime - pPreTime;
        cTimeSum += cTime - cPreTime;
        pPreTime = pTime;
        cPreTime = cTime;
        const latencyTime = pTimeSum - cTimeSum;
        states.latencyTime = latencyTime;

        // サウンドが重い場合、負荷軽減処理を発動するリミットを上げていく //
        if (latencyTime >= 100) { // currentTimeが遅い（サウンドが重い）
            states.latencyLimitTime += latencyTime;
            cTimeSum += 100;
        } else if (latencyTime <= -100) { // currentTimeが速い（誤差）
            cTimeSum = pTimeSum;
        } else {
            if (states.latencyLimitTime > 0) { // currentTimeが丁度いい
                states.latencyLimitTime -= updateBufTime*0.003;
                if (states.latencyLimitTime < 0) states.latencyLimitTime = 0;
            }
        }

        // ノートを先読み度合いを自動調整（予約しすぎると重くなる） //
        states.updateIntervalTime = updateBufTime;
        if (states.updateBufTime < updateBufTime) { // 先読み遅れている場合
            states.updateBufTime = updateBufTime;
        } else { // 先読み量に余裕がある場合
            // 先読み量を少しずつ減らす //
            if (states.updateBufMaxTime > 350) {
                states.updateBufMaxTime -= states.updateBufMaxTime*0.002;
            }
            // 先読み量を少しずつ増やす //
            if (states.updateBufTime < 20) {
                states.updateBufTime += states.updateBufTime*0.0005;
            }
            if (states.updateBufMaxTime >= 10 && states.updateBufMaxTime < 340) {
                states.updateBufMaxTime += states.updateBufMaxTime*0.002;
            }
        }
        // 先読み量が足りなくなった場合
        if (states.updateBufTime > states.updateBufMaxTime) {
            if (updateBufTime >= 900 && states.latencyLimitTime <= 150) {
                // バックグラウンドっぽくて重くない場合、バックグラウンド再生
                states.updateBufMaxTime += updateBufTime;
            } else { // 通常
                const tempTime = updateBufTime - states.updateBufMaxTime;
                states.updateBufTime = states.updateBufMaxTime;

                // 先読み量が小さい場合大きくする
                if (states.updateBufMaxTime < 10) {
                    states.updateBufTime = states.updateBufMaxTime;
                    states.updateBufMaxTime *= 1.25;
                } else {
                    states.updateBufMaxTime += tempTime / 2;
                }
            }
            if (states.updateBufMaxTime > 1100) states.updateBufMaxTime = 1100;
        }

        // サウンドが重すぎる場合、先読み度合いを小さくして負荷軽減 //
        if (states.latencyLimitTime > 150) {
            cTimeSum = pTimeSum;
            states.latencyLimitTime -= 5;
            if (states.latencyLimitTime > 1000) states.latencyLimitTime = 1000;
            // ノート先読みをかなり小さくする（フリーズ対策）
            states.updateBufMaxTime = 1;
            states.updateBufTime = 1;
            updateBufTime = 1;
        }

        // 再生処理 //
        for (let ch=0; ch<16; ch++) {
            const notes = picoAudio.playData.channels[ch].notes;
            let idx = states.playIndices[ch];
            for (; idx<notes.length; idx++) {
                const note = notes[idx];
                const curTime = cnt == 0 ? this.initCurrentTime - states.startTime
                    : context.currentTime - states.startTime;

                // 終わったノートは演奏せずにスキップ
                if (curTime >= note.stopTime) continue;
                // （シークバーで途中から再生時）startTimeが過ぎたものは鳴らさない
                if (cnt == 0 && curTime > note.startTime+0.05) continue;
                // AudioParam.setValueAtTime()等でマイナスが入るとエラーになるので対策
                if (curTime + note.startTime < 0) continue;
                // 演奏開始時間 - 先読み時間(ノート予約) になると演奏予約or演奏開始
                if (curTime < note.startTime - states.updateBufTime/1000) break;

                // PicoAudio音源の再生処理 //
                if (!settings.isWebMIDI) {
                    // 予約ノート数が急激に増えそうな時、先読み量を小さくしておく //
                    if (states.stopFuncs.length >= 350 && states.updateBufTime < 1000) {
                        states.updateBufTime = 12;
                        states.updateBufMaxTime = states.updateBufTime;
                    }

                    // レトロモード（和音制限モード） //
                    if (settings.maxPoly != -1 || settings.maxPercPoly != -1) {
                        let polyCnt = 0;
                        let percCnt = 0;
                        states.stopFuncs.forEach((tar) => {
                            if (!tar.note) return;
                            if (tar.note.channel != 9) {
                                if (note.start >= tar.note.start && note.start < tar.note.stop) {
                                    polyCnt++;
                                }
                            } else {
                                if (note.start == tar.note.start) {
                                    percCnt++;
                                }
                            }
                        });
                        if ((note.channel != 9 && polyCnt >= settings.maxPoly)
                            || (note.channel == 9 && percCnt >= settings.maxPercPoly)) {
                            continue;
                        }
                    }

                    // １ノート分の再生処理（WebAudioで再生） //
                    const stopFunc =
                        note.channel != 9 ? picoAudio.createNote(note)
                        : picoAudio.createPercussionNote(note);
                    if (!stopFunc) continue; // 無音の場合、処理しない
                    picoAudio.pushFunc({
                        note: note,
                        stopFunc: stopFunc
                    });
                }
                states.noteOnAry.push(note);
            }
            // notesのどこまで再生したかを記憶して、次回コールバック時にそこから処理を始める
            states.playIndices[ch] = idx;
        }

        // noteOnの時間になったか監視 //
        this.checkNoteOn(picoAudio);

        // noteOffの時間になったか監視 //
        this.checkNoteOff(picoAudio);

        // WebMIDIの再生処理 //
        if (settings.isWebMIDI && settings.WebMIDIPortOutput != null) {
            const messages = picoAudio.playData.messages;
            const smfData = picoAudio.playData.smfData;
            let idx = states.playIndices[16]; // 17chはWebMIDI用
            for (; idx<messages.length; idx++) {
                const message = messages[idx];
                const curTime = context.currentTime - states.startTime;

                // 終わったノートは演奏せずにスキップ
                if (curTime > message.time + 1) continue;
                // 演奏開始時間 - 先読み時間(ノート予約) になると演奏予約or演奏開始
                if (curTime < message.time - 1) break;

                // WebMIDIでMIDIメッセージを送信する処理 //
                const pLen = message.smfPtrLen;
                const p = message.smfPtr;
                const time = message.time;
                const state = smfData[p];
                if (state!=0xff) {
                    try {
                        if (state==0xF0 || state==0xF7) {
                            // sysExのMIDIメッセージ
                            if (settings.WebMIDIPortSysEx) {
                                // 長さ情報を取り除いて純粋なSysExメッセージにする
                                const lengthAry = ParseUtil.variableLengthToInt(smfData, p+1, p+1+4);
                                const sysExStartP = p+1+lengthAry[1];
                                const sysExEndP = sysExStartP+lengthAry[0];
                                const webMIDIMes = new Uint8Array(1 + lengthAry[0]);
                                webMIDIMes[0] = state;
                                const size = sysExEndP - sysExStartP;
                                for (let i=0; i<size; i++)
                                    webMIDIMes[i+1] = smfData[sysExStartP + i];
                                settings.WebMIDIPortOutput.send(webMIDIMes,
                                    (time - context.currentTime + window.performance.now()/1000 + states.startTime) * 1000);
                            }
                        } else {
                            // sysEx以外のMIDIメッセージ
                            const sendMes = [];
                            for (let i=0; i<pLen; i++) sendMes.push(smfData[p+i]);
                            settings.WebMIDIPortOutput.send(sendMes,
                                (time - context.currentTime + window.performance.now()/1000 + states.startTime) * 1000);
                        }
                    } catch(e) {
                        console.log(e, p, pLen, time, state);
                    }
                }
            }
            // messagesのどこまで送信したかを記憶して、次回コールバック時にそこから処理を始める
            states.playIndices[16] = idx;
        }

        // 1msコールバックが呼ばれた回数をカウント
        cnt ++;

        // 変数を反映 //
        this.updatePreTime = updateNowTime;
        this.pPreTime = pPreTime;
        this.cPreTime = cPreTime;
        this.pTimeSum = pTimeSum;
        this.cTimeSum = cTimeSum;
        this.cnt = cnt;
    }

    /**
     * noteOnの時間になったか監視
     * @param {PicoAudio} picoAudio PicoAudioインスタンス
     */
    static checkNoteOn(picoAudio) {
        const context = picoAudio.context;
        const trigger = picoAudio.trigger;
        const states = picoAudio.states;
        const noteOnAry = picoAudio.states.noteOnAry;
        const noteOffAry = picoAudio.states.noteOffAry;

        for (let i=0; i<noteOnAry.length; i++) {
            const tempNote = noteOnAry[i];
            const nowTime = context.currentTime - states.startTime;
            if (tempNote.startTime - nowTime <= 0) {
                ArrayUtil.delete(noteOnAry, i); // noteOnAry.splice(i, 1); の高速化
                noteOffAry.push(tempNote);
                // noteOnにコールバックを通知
                if (trigger.isNoteTrigger) trigger.noteOn(tempNote);
                i--;
            }
        }
    }

    /**
     * noteOffの時間になったか監視
     * @param {PicoAudio} picoAudio PicoAudioインスタンス
     */
    static checkNoteOff(picoAudio) {
        const context = picoAudio.context;
        const trigger = picoAudio.trigger;
        const states = picoAudio.states;
        const noteOffAry = picoAudio.states.noteOffAry;

        for (let i=0; i<noteOffAry.length; i++) {
            const tempNote = noteOffAry[i];
            const nowTime = context.currentTime - states.startTime;
            if ((tempNote.channel != 9 && tempNote.stopTime - nowTime <= 0)
                || (tempNote.channel == 9 && tempNote.drumStopTime - nowTime <= 0)) {
                ArrayUtil.delete(noteOffAry, i); // noteOffAry.splice(i, 1); の高速化
                picoAudio.clearFunc("note", tempNote);
                // noteOffにコールバックを通知
                if (trigger.isNoteTrigger) trigger.noteOff(tempNote);
                i--;
            }
        }
    }
}<|endoftext|>


<commit_before>import { combineReducers } from 'redux';
import items from './items';
import groups from './groups';
import { choices } from './choices';
import general from './general';
import { cloneObject } from '../lib/utils';

const appReducer = combineReducers({
  items,
  groups,
  choices,
  general,
});

const rootReducer = (passedState, action) => {
  let state = passedState;
  // If we are clearing all items, groups and options we reassign
  // state and then pass that state to our proper reducer. This isn't
  // mutating our actual state
  // See: http://stackoverflow.com/a/35641992
  if (action.type === 'CLEAR_ALL') {
    state = undefined;
  } else if (action.type === 'RESET_TO') {
    return cloneObject(action.state);
  }

  return appReducer(state, action);
};

export default rootReducer;
<commit_msg>Update index.js<commit_after>import { combineReducers } from 'redux';
import items from './items';
import groups from './groups';
import choices from './choices';
import general from './general';
import { cloneObject } from '../lib/utils';

const appReducer = combineReducers({
  items,
  groups,
  choices,
  general,
});

const rootReducer = (passedState, action) => {
  let state = passedState;
  // If we are clearing all items, groups and options we reassign
  // state and then pass that state to our proper reducer. This isn't
  // mutating our actual state
  // See: http://stackoverflow.com/a/35641992
  if (action.type === 'CLEAR_ALL') {
    state = undefined;
  } else if (action.type === 'RESET_TO') {
    return cloneObject(action.state);
  }

  return appReducer(state, action);
};

export default rootReducer;
<|endoftext|>


<commit_before>import * from '../constants/ActionTypes';

function requestLogin() {

}
<commit_msg>Started an exported reducer for login status cases<commit_after>import * from '../constants/ActionTypes';

export function login(state = { isAttemptingLogin: false, authenticated: false }, action) {
    switch(action.type) {
        case LOGIN_REQUEST:
            return Object.assign({}, state, {
                isAttemptingLogin: true
            });
        case LOGIN_FAIL:
            return Object.assign({}, state, {
                isAttemptingLogin: false,
                error: action.error
                id: action.id
            });
        case LOGIN_SUCCESS:
            return Object.assign({}, state, {
                isAttemptingLogin: false,
                authenticated: true,
                id: action.id
            });
        default: return state;
    }
}
<|endoftext|>


<commit_before>/*
 * grunt
 * http://gruntjs.com/
 *
 * Copyright (c) 2013 "Cowboy" Ben Alman
 * Licensed under the MIT license.
 * https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
 */

'use strict';

module.exports = function(grunt) {

  // Run sub-grunt files, because right now, testing tasks is a pain.
  grunt.registerMultiTask('subgrunt', 'Run a sub-gruntfile.', function() {
    var path = require('path');
    grunt.util.async.forEachSeries(this.filesSrc, function(gruntfile, next) {
      grunt.util.spawn({
        grunt: true,
        args: ['--gruntfile', path.resolve(gruntfile)],
      }, function(error, result) {
        if (error) {
          grunt.log.error(result.stdout).writeln();
          next(new Error('Error running sub-gruntfile "' + gruntfile + '".'));
        } else {
          grunt.verbose.ok(result.stdout);
          next();
        }
      });
    }, this.async());
  });

};
<commit_msg>Slightly better output for internal subgrunt task.<commit_after>/*
 * grunt
 * http://gruntjs.com/
 *
 * Copyright (c) 2013 "Cowboy" Ben Alman
 * Licensed under the MIT license.
 * https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
 */

'use strict';

module.exports = function(grunt) {

  // Run sub-grunt files, because right now, testing tasks is a pain.
  grunt.registerMultiTask('subgrunt', 'Run a sub-gruntfile.', function() {
    var path = require('path');
    grunt.util.async.forEachSeries(this.filesSrc, function(gruntfile, next) {
      grunt.log.write('Loading ' + gruntfile + '...');
      grunt.util.spawn({
        grunt: true,
        args: ['--gruntfile', path.resolve(gruntfile)],
      }, function(error, result) {
        if (error) {
          grunt.log.error().error(result.stdout).writeln();
          next(new Error('Error running sub-gruntfile "' + gruntfile + '".'));
        } else {
          grunt.log.ok().verbose.ok(result.stdout);
          next();
        }
      });
    }, this.async());
  });

};
<|endoftext|>


<commit_before>import Prefab from './entityPrefab'

// client side class for Playable Characters
export default class Player extends Prefab {
  constructor(x, y, key) {
    // call it's superclass constructor
    super(x, y, key)
    // Send context as first argument!!
    this.anchor.set(0.25, 0.35)
    this.orientation = 4 // down
    this.speed = this.this.game.playerSpeed
    // TODO create dialogue logic.. maybe
    // this.dialoguesMemory = {}
    // this.maxLife = this.this.game.playerLife
    // this.life = this.maxLife
    this.inFight = false
    this.defaultFrames = {
        // the third value is the frame to come back to at the end of the animation
        // TODO reassign frame values based on the sprite.
      'attack_right': [0, 4, 9],
      'right': [5, 8],
      'idle_right': [9, 10],
      'attack_up': [11, 15, 20],
      'up': [16, 19],
      'idle_up': [20, 21],
      'attack_down': [22, 26, 31],
      'down': [27, 30],
      'idle_down': [31, 32],
      'attack_left': [33, 37, 42],
      'left': [38, 41],
      'idle_left': [42, 43]
    }
    // TODO reassign these children based on our assets
    // this.addChild(this.weapon = this.this.game.add.sprite(0, 0, 'atlas3'))
    // this.addChild(this.shadow = this.this.game.add.sprite(0, 5, 'atlas1', 'shadow'))
    this.addChild(this.nameHolder = this.this.game.add.text(0, -30, '', {
      font: '14px pixel',
      fill: '#ffffff',
      stroke: '#000000',
      strokeThickness: 1
    }))
    this.events.onKilled.add(function(player) {
      this.this.game.displayedPlayers.delete(player.id)
    }, this)
  }
  // changes color of name text if Player is the main Player
  setIsPlayer(flag) {
    this.isPlayer = flag
    if (this.isPlayer) this.nameHolder.addColor("#f4d442", 0)
  }
  // sets the player's name
  setName(name) {
    this.nameHolder.text = name
    this.nameHolder.x = Math.floor(16 - (this.nameHolder.width/2))
  }

  prepareMovement(end, finalOrientation, action, delta, sendToServer) {
    if (!this.alive) return
    if (!end) return
    let start = Game.computeTileCoords(this.x, this.y)
    if (start.x === end.x && start.y === end.y) {
      if (action.action === 1) this.finishMovement(finalOrientation, action)
      return
    }
    if (this.isPlayer) Game.manageMoveTarget(end.x,end.y)
    if (this.tween) {
        this.stopMovement(false)
        start = this.adjustStartPosition(start)
    }
    if (this.isPlayer && this.inFight && action.action != 3) this.endFight()
    Game.easystar.findPath(start.x, start.y, end.x, end.y, this.pathfindingCallback.bind(this, finalOrientation, action, delta, sendToServer))
    Game.easystar.calculate()
  }

  equipWeapon(key){
    this.weapon.name = key
    this.weapon.frameName = key + '_0' //sets initial weapon animation frame
    this.weapon.absorbProperties(Game.itemsInfo[key]) //assigns stats to weapon
    this.atk = this.weapon.attack
    this.adjustWeapon()
    this.setAnimations(this.weapon)
    if (this.isPlayer) {
      Game.weaponIcon.frameName = this.weapon.icon+'_0'
      Client.setWeapon(key)
    }
    return true
  }

  adjustWeapon() {
    this.weapon.position.set(this.weapon.offsets.x, this.weapon.offsets.y)
  }

  equipArmor(key) {
    const armorInfo = Game.itemsInfo[key]
    this.def = armorInfo.def
    this.armorName = key
    this.frameName = key+'_0'
    if (this.isPlayer) {
      Game.armorIcon.frameName = armorInfo.icon+'_0'
      Client.setArmor(key)
      Game.armorIcon.anchor.set(0,0)
      if (armorInfo.iconAnchor) Game.armorIcon.anchor.set(armorInfo.iconAnchor.x,armorInfo.iconAnchor.y)
    }
    const animationFrames = (armorInfo.hasOwnProperty('frames')? armorInfo.frames : null)
    this.frames = animationFrames
    this.setAnimations(this)
    return true
  }
  // TODO reimplement this fn when we manage life
  updateLife() {
    if (this.life < 0) this.life = 0
    let width = Game.computeLifeBarWidth()
    let tweenWidth = this.game.add.tween(Game.health.getChildAt(0)) // tween for the "body" of the bar
    let tweenEnd = this.game.add.tween(Game.health.getChildAt(1)) // tween for the curved tip
    tweenWidth.to({width: width }, 200,null, false, 200)
    tweenEnd.to({x: width }, 200,null, false, 200)
    tweenWidth.start()
    tweenEnd.start()
  }

  fight() {
    if (!this.target) return
    this.inFight = true
    this.fightTween = this.game.add.tween(this)
    this.fightTween.to({}, Phaser.Timer.SECOND, null, false, 0, -1)
    this.fightTween.onStart.add(function(){this.fightAction()}, this)
    this.fightTween.onLoop.add(function(){this.fightAction()}, this)
    this.fightTween.start()
  }

  fightAction() {
    if (this.isPlayer) return // For the main player, attack animations are handled differently, see updateSelf()
    let direction = Game.adjacent(this,this.target)
    if (direction > 0){ // Target is on adjacent cell
        if (this.tween) {
            this.tween.stop()
            this.tween = null
        }
        this.orientation = direction
        this.attack()
    }
  }

  die(animate) {
    if (this.tween) this.stopMovement(false)
    this.endFight()
    this.target = null
    this.life = 0
    if (this.isPlayer) {
        Game.moveTarget.visible = false
        this.updateLife()
        setTimeout(Game.displayDeathScroll,Phaser.Timer.SECOND*2)
    }
    if (animate && this.inCamera) {
        this.frameName = 'death_0'
        this.animate('death', false)
        Game.sounds.play('death')
    }
    this.delayedKill(750)
  }
  respawn() {
    this.revive() // method from the Phaser Sprite class
    this.orientation = this.game.rnd.between(1,4)
    if (this.isPlayer) {
      this.life = this.maxLife
      this.updateLife()
    }
    this.idle(true)
  }
}
<commit_msg>ref: fix linter errors in playerRefactor.js<commit_after>import Prefab from './entityPrefab'

// client side class for Playable Characters
export default class Player extends Prefab {
  constructor(x, y, key) {
    // call it's superclass constructor
    super(x, y, key)
    // Send context as first argument!!
    this.anchor.set(0.25, 0.35)
    this.orientation = 4 // down
    this.speed = this.this.game.playerSpeed
    // TODO create dialogue logic.. maybe
    // this.dialoguesMemory = {}
    // this.maxLife = this.this.game.playerLife
    // this.life = this.maxLife
    this.inFight = false
    this.defaultFrames = {
        // the third value is the frame to come back to at the end of the animation
        // TODO reassign frame values based on the sprite.
      'attack_right': [0, 4, 9],
      'right': [5, 8],
      'idle_right': [9, 10],
      'attack_up': [11, 15, 20],
      'up': [16, 19],
      'idle_up': [20, 21],
      'attack_down': [22, 26, 31],
      'down': [27, 30],
      'idle_down': [31, 32],
      'attack_left': [33, 37, 42],
      'left': [38, 41],
      'idle_left': [42, 43]
    }
    // TODO reassign these children based on our assets
    // this.addChild(this.weapon = this.this.game.add.sprite(0, 0, 'atlas3'))
    // this.addChild(this.shadow = this.this.game.add.sprite(0, 5, 'atlas1', 'shadow'))
    this.addChild(this.nameHolder = this.this.game.add.text(0, -30, '', {
      font: '14px pixel',
      fill: '#ffffff',
      stroke: '#000000',
      strokeThickness: 1
    }))
    this.events.onKilled.add(function(player) {
      this.this.game.displayedPlayers.delete(player.id)
    }, this)
  }
  // changes color of name text if Player is the main Player
  setIsPlayer(flag) {
    this.isPlayer = flag
    if (this.isPlayer) this.nameHolder.addColor('#f4d442', 0)
  }
  // sets the player's name
  setName(name) {
    this.nameHolder.text = name
    this.nameHolder.x = Math.floor(16 - (this.nameHolder.width/2))
  }

  // TODO refactor this to use cursor keys (since we won't be using clicks for movement)
  prepareMovement(end, finalOrientation, action, delta, sendToServer) {
    if (!this.alive || !end) return
    let start = Game.computeTileCoords(this.x, this.y)
    if (start.x === end.x && start.y === end.y) {
      if (action.action === 1) this.finishMovement(finalOrientation, action)
      return
    }
    if (this.isPlayer) Game.manageMoveTarget(end.x,end.y)
    if (this.tween) {
      this.stopMovement(false)
      start = this.adjustStartPosition(start)
    }
    if (this.isPlayer && this.inFight && action.action != 3) this.endFight()
    Game.easystar.findPath(start.x, start.y, end.x, end.y, this.pathfindingCallback.bind(this, finalOrientation, action, delta, sendToServer))
    Game.easystar.calculate()
  }

  equipWeapon(key) {
    this.weapon.name = key
    this.weapon.frameName = key + '_0' // sets initial weapon animation frame
    this.weapon.absorbProperties(Game.itemsInfo[key]) // assigns stats to weapon
    this.atk = this.weapon.attack
    this.adjustWeapon() // will change weapons position on char sprite
    this.setAnimations(this.weapon)
    if (this.isPlayer) {
      Game.weaponIcon.frameName = this.weapon.icon+'_0' // weapons icon on screen?
      Client.setWeapon(key)
    }
    return true
  }

  adjustWeapon() {
    this.weapon.position.set(this.weapon.offsets.x, this.weapon.offsets.y)
  }

  equipArmor(key) {
    const armorInfo = Game.itemsInfo[key]
    this.def = armorInfo.def
    this.armorName = key
    this.frameName = key+'_0'
    if (this.isPlayer) {
      Game.armorIcon.frameName = armorInfo.icon+'_0'
      Client.setArmor(key)
      Game.armorIcon.anchor.set(0,0)
      if (armorInfo.iconAnchor) Game.armorIcon.anchor.set(armorInfo.iconAnchor.x,armorInfo.iconAnchor.y)
    }
    const animationFrames = (armorInfo.hasOwnProperty('frames')? armorInfo.frames : null)
    this.frames = animationFrames
    this.setAnimations(this)
    return true
  }
  // TODO reimplement this fn when we manage life
  updateLife() {
    if (this.life < 0) this.life = 0
    const width = Game.computeLifeBarWidth()
    const tweenWidth = this.game.add.tween(Game.health.getChildAt(0)) // tween for the "body" of the bar
    const tweenEnd = this.game.add.tween(Game.health.getChildAt(1)) // tween for the curved tip
    tweenWidth.to({ width: width }, 200, null, false, 200)
    tweenEnd.to({ x: width }, 200, null, false, 200)
    tweenWidth.start()
    tweenEnd.start()
  }

  fight() {
    if (!this.target) return
    this.inFight = true
    this.fightTween = this.game.add.tween(this) // adds a tween to the game state
    this.fightTween.to({}, Phaser.Timer.SECOND, null, false, 0, -1)
    this.fightTween.onStart.add(function() { this.fightAction() }, this)
    this.fightTween.onLoop.add(function() { this.fightAction() }, this)
    this.fightTween.start()
  }

  fightAction() {
    if (this.isPlayer) return // For the main player, attack animations are handled differently, see updateSelf()
    const direction = Game.adjacent(this, this.target)
    if (direction > 0){ //Target is on adjacent cell
      if (this.tween) {
        this.tween.stop()
        this.tween = null
      }
      this.orientation = direction
      this.attack()
    }
  }

  die(animate) {
    if (this.tween) this.stopMovement(false)
    this.endFight()
    this.target = null
    this.life = 0
    if (this.isPlayer) {
      Game.moveTarget.visible = false
      this.updateLife()
      setTimeout(Game.displayDeathScroll, Phaser.Timer.SECOND*2)
    }
    if (animate && this.inCamera) {
      this.frameName = 'death_0'
      this.animate('death', false)
      Game.sounds.play('death')
    }
    this.delayedKill(750)
  }
  respawn() {
    this.revive() // method from the Phaser Sprite class
    this.orientation = this.game.rnd.between(1, 4)
    if (this.isPlayer) {
      this.life = this.maxLife
      this.updateLife()
    }
    this.idle(true)
  }
}
<|endoftext|>


<commit_before>module.exports = {
  name: 'Entry',
  schema: {
    id: {type: 'increments', primary: true},
    folderId: {type: Number},
    sourceEntry: {type: String, nullable: false},
    pageNum: {type: Number},
    data: {type: 'json', defaultTo: '{}'}
  },
  options: {
    unique: ['folderId', 'sourceEntry']
  }
};
<commit_msg>add id as unique key for sorting performance<commit_after>module.exports = {
  name: 'Entry',
  schema: {
    id: {type: 'increments', primary: true},
    folderId: {type: Number},
    sourceEntry: {type: String, nullable: false},
    pageNum: {type: Number},
    data: {type: 'json', defaultTo: '{}'}
  },
  options: {
    unique: ['id', 'folderId', 'sourceEntry']
  }
};
<|endoftext|>


<commit_before>// # Ghost Configuration
// Setup your Ghost install for various [environments](http://support.ghost.org/config/#about-environments).

// Ghost runs in `development` mode by default. Full documentation can be found at http://support.ghost.org/config/

var path = require('path'),
config;

var mailgunApiTransport = require('nodemailer-mailgunapi-transport');

var auth = {
  auth: {
    api_key: 'SMTP_PASSWORD',
    domain: 'MAIN_DOMAIN'
  }
}

config = {
    // ### Production
    // When running Ghost in the wild, use the production environment.
    // Configure your URL and mail settings here
    production: {
        url: 'https://MAIN_DOMAIN',
        mail: {
            from: 'SMTP_USER',
            transport: mailgunApiTransport(auth),
        },
        database: {
            client: 'sqlite3',
            connection: {
                filename: path.join(__dirname, '/content/data/ghost.db')
            },
            debug: false
        },

        server: {
            host: '127.0.0.1',
            port: '2368'
        }
    },

    // ### Development **(default)**
    development: {
        // The url to use when providing links to the site, E.g. in RSS and email.
        // Change this to your Ghost blog's published URL.
        url: 'http://localhost:2368',

        // Example mail config
        // Visit http://support.ghost.org/mail for instructions
        // ```
        //  mail: {
        //      transport: 'SMTP',
        //      options: {
        //          service: 'Mailgun',
        //          auth: {
        //              user: '', // mailgun username
        //              pass: ''  // mailgun password
        //          }
        //      }
        //  },
        // ```

        // #### Database
        // Ghost supports sqlite3 (default), MySQL & PostgreSQL
        database: {
            client: 'sqlite3',
            connection: {
                filename: path.join(__dirname, '/content/data/ghost-dev.db')
            },
            debug: false
        },
        // #### Server
        // Can be host & port (default), or socket
        server: {
            // Host to be passed to node's `net.Server#listen()`
            host: '127.0.0.1',
            // Port to be passed to node's `net.Server#listen()`, for iisnode set this to `process.env.PORT`
            port: '2368'
        },
        // #### Paths
        // Specify where your content directory lives
        paths: {
            contentPath: path.join(__dirname, '/content/')
        }
    },

    // **Developers only need to edit below here**

    // ### Testing
    // Used when developing Ghost to run tests and check the health of Ghost
    // Uses a different port number
    testing: {
        url: 'http://127.0.0.1:2369',
        database: {
            client: 'sqlite3',
            connection: {
                filename: path.join(__dirname, '/content/data/ghost-test.db')
            },
            pool: {
                afterCreate: function (conn, done) {
                    conn.run('PRAGMA synchronous=OFF;' +
                        'PRAGMA journal_mode=MEMORY;' +
                        'PRAGMA locking_mode=EXCLUSIVE;' +
                        'BEGIN EXCLUSIVE; COMMIT;', done);
                }
            }
        },
        server: {
            host: '127.0.0.1',
            port: '2369'
        },
        logging: false
    },

    // ### Testing MySQL
    // Used by Travis - Automated testing run through GitHub
    'testing-mysql': {
        url: 'http://127.0.0.1:2369',
        database: {
            client: 'mysql',
            connection: {
                host     : '127.0.0.1',
                user     : 'root',
                password : '',
                database : 'ghost_testing',
                charset  : 'utf8'
            }
        },
        server: {
            host: '127.0.0.1',
            port: '2369'
        },
        logging: false
    },

    // ### Testing pg
    // Used by Travis - Automated testing run through GitHub
    'testing-pg': {
        url: 'http://127.0.0.1:2369',
        database: {
            client: 'pg',
            connection: {
                host     : '127.0.0.1',
                user     : 'postgres',
                password : '',
                database : 'ghost_testing',
                charset  : 'utf8'
            }
        },
        server: {
            host: '127.0.0.1',
            port: '2369'
        },
        logging: false
    }
};

module.exports = config;
<commit_msg>redirect failure<commit_after>// # Ghost Configuration
// Setup your Ghost install for various [environments](http://support.ghost.org/config/#about-environments).

// Ghost runs in `development` mode by default. Full documentation can be found at http://support.ghost.org/config/

var path = require('path'),
config;

var mailgunApiTransport = require('nodemailer-mailgunapi-transport');

var auth = {
  auth: {
    api_key: 'SMTP_PASSWORD',
    domain: 'MAIN_DOMAIN'
  }
}

config = {
    // ### Production
    // When running Ghost in the wild, use the production environment.
    // Configure your URL and mail settings here
    production: {
        url: 'http://MAIN_DOMAIN',
        mail: {
            from: 'SMTP_USER',
            transport: mailgunApiTransport(auth),
        },
        database: {
            client: 'sqlite3',
            connection: {
                filename: path.join(__dirname, '/content/data/ghost.db')
            },
            debug: false
        },

        server: {
            host: '127.0.0.1',
            port: '2368'
        }
    },

    // ### Development **(default)**
    development: {
        // The url to use when providing links to the site, E.g. in RSS and email.
        // Change this to your Ghost blog's published URL.
        url: 'http://localhost:2368',

        // Example mail config
        // Visit http://support.ghost.org/mail for instructions
        // ```
        //  mail: {
        //      transport: 'SMTP',
        //      options: {
        //          service: 'Mailgun',
        //          auth: {
        //              user: '', // mailgun username
        //              pass: ''  // mailgun password
        //          }
        //      }
        //  },
        // ```

        // #### Database
        // Ghost supports sqlite3 (default), MySQL & PostgreSQL
        database: {
            client: 'sqlite3',
            connection: {
                filename: path.join(__dirname, '/content/data/ghost-dev.db')
            },
            debug: false
        },
        // #### Server
        // Can be host & port (default), or socket
        server: {
            // Host to be passed to node's `net.Server#listen()`
            host: '127.0.0.1',
            // Port to be passed to node's `net.Server#listen()`, for iisnode set this to `process.env.PORT`
            port: '2368'
        },
        // #### Paths
        // Specify where your content directory lives
        paths: {
            contentPath: path.join(__dirname, '/content/')
        }
    },

    // **Developers only need to edit below here**

    // ### Testing
    // Used when developing Ghost to run tests and check the health of Ghost
    // Uses a different port number
    testing: {
        url: 'http://127.0.0.1:2369',
        database: {
            client: 'sqlite3',
            connection: {
                filename: path.join(__dirname, '/content/data/ghost-test.db')
            },
            pool: {
                afterCreate: function (conn, done) {
                    conn.run('PRAGMA synchronous=OFF;' +
                        'PRAGMA journal_mode=MEMORY;' +
                        'PRAGMA locking_mode=EXCLUSIVE;' +
                        'BEGIN EXCLUSIVE; COMMIT;', done);
                }
            }
        },
        server: {
            host: '127.0.0.1',
            port: '2369'
        },
        logging: false
    },

    // ### Testing MySQL
    // Used by Travis - Automated testing run through GitHub
    'testing-mysql': {
        url: 'http://127.0.0.1:2369',
        database: {
            client: 'mysql',
            connection: {
                host     : '127.0.0.1',
                user     : 'root',
                password : '',
                database : 'ghost_testing',
                charset  : 'utf8'
            }
        },
        server: {
            host: '127.0.0.1',
            port: '2369'
        },
        logging: false
    },

    // ### Testing pg
    // Used by Travis - Automated testing run through GitHub
    'testing-pg': {
        url: 'http://127.0.0.1:2369',
        database: {
            client: 'pg',
            connection: {
                host     : '127.0.0.1',
                user     : 'postgres',
                password : '',
                database : 'ghost_testing',
                charset  : 'utf8'
            }
        },
        server: {
            host: '127.0.0.1',
            port: '2369'
        },
        logging: false
    }
};

module.exports = config;
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

const pgp = require('pg-promise')({
	// This sets the style of returned durations so that Moment can parse them
	connect: (client, dc, fresh) => {
		// Only set the style on fresh connections
		if (fresh === true || fresh === undefined) {
			client.query('SET intervalStyle = iso_8601');
		}
	}
});

const path = require('path');
const patchMomentType = require('./patch-moment-type');

patchMomentType(pgp);

/**
 * Create a new connection to the database.
 * @return {pgPromise.IDatabase}
 */
function getDB(connectionParameters) {
	return pgp(connectionParameters);
}

/**
 * Get the name of the database current being worked on.
 * @return {string}
 */
function getCurrentDB() {
	return currentDB;
}

/**
 * Closes the connection pool and stops pg-promise
 * Only call this to avoid the 30 second script timeout before pg-promise closes connections.
 */
function stopDB() {
	pgp.end();
}

const sqlFilesDir = path.join(__dirname, '..', 'sql');
const loadedSqlFiles = {};

/**
 * Load a QueryFile from the sql directory.
 * The QueryFile is loaded once and subsequently cached, so repeated calls to this method will only create a single
 * QueryFile object. See {@link https://github.com/vitaly-t/pg-promise#query-files}
 * @example
 * 	sqlFile('meter/get_all_meters.sql') // Returns an sql file for getting all meters.
 * @param filePath the path to the sql file, relative to the sql directory
 * @returns {pgPromise.QueryFile}
 */
function sqlFile(filePath) {
	const sqlFilePath = path.join(sqlFilesDir, filePath);
	if (loadedSqlFiles[sqlFilePath] === undefined) {
		loadedSqlFiles[sqlFilePath] = new pgp.QueryFile(path.join(sqlFilesDir, filePath), { minify: true });
	}
	return loadedSqlFiles[sqlFilePath];
}

/**
 * Returns a promise to create the database schema.
 * @param conn the connection to be used.
 * @return {Promise<void>}
 */
async function createSchema(conn) {
	// We need to require these here instead of at the top to prevent circular dependency issues.
	/* eslint-disable global-require */
	const Meter = require('./Meter');
	const Reading = require('./Reading');
	const User = require('./User');
	const Group = require('./Group');
	const Preferences = require('./Preferences');
	const Configfile = require('./obvius/Configfile');
	const Migration = require('./Migration');
	const LogEmail = require('./LogEmail');
	const Baseline = require('./Baseline');

	/* eslint-enable global-require */
	await Meter.createMeterTypesEnum(conn);
	await Meter.createTable(conn);
	await Reading.createTable(conn);
	await Reading.createCompressedReadingsFunction(conn);
	await Reading.createCompressedGroupsReadingsFunction(conn);
	await Reading.createBarchartReadingsFunction(conn);
	await Reading.createCompressedGroupsBarchartReadingsFunction(conn);
	await User.createTable(conn);
	await Preferences.createTable(conn);
	await Group.createTables(conn);
	await Migration.createTable(conn);
	await LogEmail.createTable(conn);
	await conn.none(sqlFile('reading/create_function_get_compressed_readings.sql'));
	await Reading.createCompressedReadingsMaterializedViews(conn);
	await Reading.createCompareReadingsFunction(conn);
	await Baseline.createTable(conn);
	await conn.none(sqlFile('baseline/create_function_get_average_reading.sql'));
	await Configfile.createTable(conn);
}

module.exports = {
	getDB,
	currentDB: getCurrentDB,
	sqlFile,
	createSchema,
	pgp,
	stopDB
};
<commit_msg>fix pg-promise connect usage<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

const pgp = require('pg-promise')({
	// This sets the style of returned durations so that Moment can parse them
	connect(client, dc, useCount) {
		// Only set the style on fresh connections
		if (useCount === 0) {
			client.query('SET intervalStyle = iso_8601');
		}
	}
});

const path = require('path');
const patchMomentType = require('./patch-moment-type');

patchMomentType(pgp);

/**
 * Create a new connection to the database.
 * @return {pgPromise.IDatabase}
 */
function getDB(connectionParameters) {
	return pgp(connectionParameters);
}

/**
 * Get the name of the database current being worked on.
 * @return {string}
 */
function getCurrentDB() {
	return currentDB;
}

/**
 * Closes the connection pool and stops pg-promise
 * Only call this to avoid the 30 second script timeout before pg-promise closes connections.
 */
function stopDB() {
	pgp.end();
}

const sqlFilesDir = path.join(__dirname, '..', 'sql');
const loadedSqlFiles = {};

/**
 * Load a QueryFile from the sql directory.
 * The QueryFile is loaded once and subsequently cached, so repeated calls to this method will only create a single
 * QueryFile object. See {@link https://github.com/vitaly-t/pg-promise#query-files}
 * @example
 * 	sqlFile('meter/get_all_meters.sql') // Returns an sql file for getting all meters.
 * @param filePath the path to the sql file, relative to the sql directory
 * @returns {pgPromise.QueryFile}
 */
function sqlFile(filePath) {
	const sqlFilePath = path.join(sqlFilesDir, filePath);
	if (loadedSqlFiles[sqlFilePath] === undefined) {
		loadedSqlFiles[sqlFilePath] = new pgp.QueryFile(path.join(sqlFilesDir, filePath), { minify: true });
	}
	return loadedSqlFiles[sqlFilePath];
}

/**
 * Returns a promise to create the database schema.
 * @param conn the connection to be used.
 * @return {Promise<void>}
 */
async function createSchema(conn) {
	// We need to require these here instead of at the top to prevent circular dependency issues.
	/* eslint-disable global-require */
	const Meter = require('./Meter');
	const Reading = require('./Reading');
	const User = require('./User');
	const Group = require('./Group');
	const Preferences = require('./Preferences');
	const Configfile = require('./obvius/Configfile');
	const Migration = require('./Migration');
	const LogEmail = require('./LogEmail');
	const Baseline = require('./Baseline');

	/* eslint-enable global-require */
	await Meter.createMeterTypesEnum(conn);
	await Meter.createTable(conn);
	await Reading.createTable(conn);
	await Reading.createCompressedReadingsFunction(conn);
	await Reading.createCompressedGroupsReadingsFunction(conn);
	await Reading.createBarchartReadingsFunction(conn);
	await Reading.createCompressedGroupsBarchartReadingsFunction(conn);
	await User.createTable(conn);
	await Preferences.createTable(conn);
	await Group.createTables(conn);
	await Migration.createTable(conn);
	await LogEmail.createTable(conn);
	await conn.none(sqlFile('reading/create_function_get_compressed_readings.sql'));
	await Reading.createCompressedReadingsMaterializedViews(conn);
	await Reading.createCompareReadingsFunction(conn);
	await Baseline.createTable(conn);
	await conn.none(sqlFile('baseline/create_function_get_average_reading.sql'));
	await Configfile.createTable(conn);
}

module.exports = {
	getDB,
	currentDB: getCurrentDB,
	sqlFile,
	createSchema,
	pgp,
	stopDB
};
<|endoftext|>


<commit_before>define(["config",
        "app/app",
        "ember"], function(config, App, Ember) {
  "use strict";

  // "Abstract" generic controller for posts
  App.PostGenericController = Ember.Controller.extend({
    // NOTE: this code doesn't work reliably, see
    // https://github.com/emberjs/ember.js/issues/10343
    //commentSortProperties: ['createdAt:asc'],
    //comments: Ember.computed.sort('content.comments', 'commentSortProperties'),

    comments: function() {
      return Ember.ArrayProxy.createWithMixins(Ember.SortableMixin, {
        sortProperties: ['createdAt'],
        sortAscending: true,
        content: this.get('content.comments')
      })
    }.property('model.posts'),

    isLiked: function() {
      return this.get('model.likes').isAny('id', this.get('session.currentUser.id'))
    }.property('model.likes', 'session.currentUser.id'),

    hasOmittedComments: function() {
      return this.get('model.omittedComments') > 0
        || this.get('isLoadingComments') === true
    }.property('model.omittedComments', 'isLoadingComments'),

    omittedComments: function() {
      if (this.get('isLoadingComments')) {
        return this.get('omittedCommentsOld')
      } else {
        if (this.get('model.omittedComments') > 0)
          return this.get('model.omittedComments') + this.get('model.comments.length') - 2
      }
    }.property('model.omittedComments', 'model.comments.length', 'isLoadingComments'),

    hasOmittedLikes: function() {
      return this.get('maxLikes') != 'all' &&
        (this.get('model.omittedLikes') != 0 ||
         this.get('model.likes.length') > 4)
        || this.get('isLoadingLikes') === true
    }.property('maxLikes', 'model.omittedLikes', 'model.likes.length', 'isLoadingLikes'),

    omittedLikes: function() {
      var likes = this.get('model.likes')
      return likes.get('length') + this.get('model.omittedLikes') - 3
    }.property('model.omittedLikes', 'model.likes', 'model.likes.length'),

    isEdit: false,
    isFormVisible: false,
    maxComments: 2,
    maxLikes: 4,
    isLoadingLikes: false,
    isLoadingComments: false,

    body: Ember.computed.oneWay('model.body'),

    firstComments: function() {
      return this.get('model.comments').slice(0, 1)
    }.property('model.comments', 'model.comments.length'),

    lastComments: function() {
      return this.get('model.comments').slice(this.get('model.comments.length') - 1, this.get('model.comments.length'))
    }.property('model.comments', 'model.comments.length'),

    allLikes: function() {
      var user_id = this.get('session.currentUser.id')
      var likes = this.get('model.likes').toArray().sort(function(a, b) {
        if (a.id == user_id) return -1
        if (b.id == user_id) return 1
      });

      return likes
    }.property('model.likes', 'session.currentUser.id'),

    firstLikes: function() {
      var likes = this.get('allLikes')
      var omittedLikes = this.get('model.omittedLikes') || 0

      if (likes.get('length') < 5 && omittedLikes == 0)
        return likes
      else
        return likes.slice(0, 3)
    }.property('model.omittedLikes', 'allLikes'),

    actions: {
      toggleCommentForm: function() {
        this.toggleProperty('isFormVisible')

        if (!this.get('isFormVisible'))
          this.set('newComment', '')
      },

      toggleEditability: function() {
        this.toggleProperty('isEdit')
      },

      showAllComments: function() {
        var that = this

        // NOTE: we are setting omittedCommentsOld because for UX we
        // are going to show throbber for extra 0.25s, however new data
        // will be already loaded at that time, so we must show old
        // data to simulate loading process
        this.set('omittedCommentsOld', this.get('omittedComments'))
        this.set('isLoadingComments', true)
        this.set('maxComments', 'all')
        this.store.findOneQuery('post', this.get('model.id'), {
          maxComments: this.get('maxComments'),
          maxLikes: this.get('maxLikes')
        }).then(function() {
          Ember.run.later(function() {
            that.set('isLoadingComments', false)
          }, 250)
        })
      },

      showAllLikes: function() {
        var that = this

        this.set('isLoadingLikes', true)
        this.set('maxLikes', 'all')
        this.store.findOneQuery('post', this.get('model.id'), {
          maxComments: this.get('maxComments'),
          maxLikes: this.get('maxLikes')
        }).then(function() {
          Ember.run.later(function() {
            that.set('isLoadingLikes', false)
          }, 250)
        })
      },

      create: function() {
        var comment = this.store.createRecord('comment', {
          body: this.get('newComment'),
          postId: this.get('content.id')
        })

        this.set('newComment', '')
        comment.save()
          .then(function(comment) {
            this.set('isFormVisible', false)
            var object = this.get('content.comments').findProperty('id', comment.get('id'))
            if (!object) {
              this.get('content.comments').pushObject(comment)
            }
          }.bind(this))
      },

      update: function() {
        var post = this.get('model')
        var body = this.get('body')

        post.set('body', body)
        post.save()
          .then(function(newComment) {
            this.set('isEdit', false)
          }.bind(this))
      },

      destroy: function() {
        var post = this.get('model')

        post.destroyRecord()
          .then(function(post) {
          })
      },

      like: function() {
        var post = this.get('model')

        post.like()
          .then(function() {
            var user = this.get('session.currentUser')
            this.get('content.likes').addObject(user)
          }.bind(this))
      },

      unlike: function() {
        var post = this.get('model')

        post.unlike()
          .then(function() {
            var like = this.get('content.likes').findProperty('id', this.get('session.currentUser.id'))
            this.get('content.likes').removeObject(like)
          }.bind(this))
      }
    }
  })
})
<commit_msg>Does force post to bump on update, showComments, and showLikes actions. Fixes #422.<commit_after>define(["config",
        "app/app",
        "ember"], function(config, App, Ember) {
  "use strict";

  // "Abstract" generic controller for posts
  App.PostGenericController = Ember.Controller.extend({
    // NOTE: this code doesn't work reliably, see
    // https://github.com/emberjs/ember.js/issues/10343
    //commentSortProperties: ['createdAt:asc'],
    //comments: Ember.computed.sort('content.comments', 'commentSortProperties'),

    comments: function() {
      return Ember.ArrayProxy.createWithMixins(Ember.SortableMixin, {
        sortProperties: ['createdAt'],
        sortAscending: true,
        content: this.get('content.comments')
      })
    }.property('model.posts'),

    isLiked: function() {
      return this.get('model.likes').isAny('id', this.get('session.currentUser.id'))
    }.property('model.likes', 'session.currentUser.id'),

    hasOmittedComments: function() {
      return this.get('model.omittedComments') > 0
        || this.get('isLoadingComments') === true
    }.property('model.omittedComments', 'isLoadingComments'),

    omittedComments: function() {
      if (this.get('isLoadingComments')) {
        return this.get('omittedCommentsOld')
      } else {
        if (this.get('model.omittedComments') > 0)
          return this.get('model.omittedComments') + this.get('model.comments.length') - 2
      }
    }.property('model.omittedComments', 'model.comments.length', 'isLoadingComments'),

    hasOmittedLikes: function() {
      return this.get('maxLikes') != 'all' &&
        (this.get('model.omittedLikes') != 0 ||
         this.get('model.likes.length') > 4)
        || this.get('isLoadingLikes') === true
    }.property('maxLikes', 'model.omittedLikes', 'model.likes.length', 'isLoadingLikes'),

    omittedLikes: function() {
      var likes = this.get('model.likes')
      return likes.get('length') + this.get('model.omittedLikes') - 3
    }.property('model.omittedLikes', 'model.likes', 'model.likes.length'),

    isEdit: false,
    isFormVisible: false,
    maxComments: 2,
    maxLikes: 4,
    isLoadingLikes: false,
    isLoadingComments: false,

    body: Ember.computed.oneWay('model.body'),

    firstComments: function() {
      return this.get('model.comments').slice(0, 1)
    }.property('model.comments', 'model.comments.length'),

    lastComments: function() {
      return this.get('model.comments').slice(this.get('model.comments.length') - 1, this.get('model.comments.length'))
    }.property('model.comments', 'model.comments.length'),

    allLikes: function() {
      var user_id = this.get('session.currentUser.id')
      var likes = this.get('model.likes').toArray().sort(function(a, b) {
        if (a.id == user_id) return -1
        if (b.id == user_id) return 1
      });

      return likes
    }.property('model.likes', 'session.currentUser.id'),

    firstLikes: function() {
      var likes = this.get('allLikes')
      var omittedLikes = this.get('model.omittedLikes') || 0

      if (likes.get('length') < 5 && omittedLikes == 0)
        return likes
      else
        return likes.slice(0, 3)
    }.property('model.omittedLikes', 'allLikes'),

    actions: {
      toggleCommentForm: function() {
        this.toggleProperty('isFormVisible')

        if (!this.get('isFormVisible'))
          this.set('newComment', '')
      },

      toggleEditability: function() {
        this.toggleProperty('isEdit')
      },

      showAllComments: function() {
        var that = this
        var oldUpdatedAt = this.get('model.updatedAt')

        // NOTE: we are setting omittedCommentsOld because for UX we
        // are going to show throbber for extra 0.25s, however new data
        // will be already loaded at that time, so we must show old
        // data to simulate loading process
        this.set('omittedCommentsOld', this.get('omittedComments'))
        this.set('isLoadingComments', true)
        this.set('maxComments', 'all')
        this.store.findOneQuery('post', this.get('model.id'), {
          maxComments: this.get('maxComments'),
          maxLikes: this.get('maxLikes')
        }).then(function(post) {
          post.set('updatedAt', oldUpdatedAt)
          Ember.run.later(function() {
            that.set('isLoadingComments', false)
          }, 250)
        })
      },

      showAllLikes: function() {
        var that = this
        var oldUpdatedAt = this.get('model.updatedAt')

        this.set('isLoadingLikes', true)
        this.set('maxLikes', 'all')
        this.store.findOneQuery('post', this.get('model.id'), {
          maxComments: this.get('maxComments'),
          maxLikes: this.get('maxLikes')
        }).then(function(post) {
          post.set('updatedAt', oldUpdatedAt)
          Ember.run.later(function() {
            that.set('isLoadingLikes', false)
          }, 250)
        })
      },

      create: function() {
        var comment = this.store.createRecord('comment', {
          body: this.get('newComment'),
          postId: this.get('content.id')
        })

        this.set('newComment', '')
        comment.save()
          .then(function(comment) {
            this.set('isFormVisible', false)
            var object = this.get('content.comments').findProperty('id', comment.get('id'))
            if (!object) {
              this.get('content.comments').pushObject(comment)
            }
          }.bind(this))
      },

      update: function() {
        var post = this.get('model')
        var oldUpdatedAt = this.get('model.updatedAt')
        var body = this.get('body')

        post.set('body', body)
        post.save()
          .then(function(post) {
            post.set('updatedAt', oldUpdatedAt)
            this.set('isEdit', false)
          }.bind(this))
      },

      destroy: function() {
        var post = this.get('model')

        post.destroyRecord()
          .then(function(post) {
          })
      },

      like: function() {
        var post = this.get('model')

        post.like()
          .then(function() {
            var user = this.get('session.currentUser')
            this.get('content.likes').addObject(user)
          }.bind(this))
      },

      unlike: function() {
        var post = this.get('model')

        post.unlike()
          .then(function() {
            var like = this.get('content.likes').findProperty('id', this.get('session.currentUser.id'))
            this.get('content.likes').removeObject(like)
          }.bind(this))
      }
    }
  })
})
<|endoftext|>


<commit_before>import config from '../../../data/__config.json';
var selectedThemeModule = './themes/'+config.currentTheme+'/boot.js';
const Theme = require(selectedThemeModule);
export default Theme.default;
<commit_msg>Simplify syntax and use ES6 features<commit_after>import config from '../../../data/__config.json';

const theme = require(`./themes/${config.currentTheme}/boot.js`);

export default theme.default;
<|endoftext|>


<commit_before>var columnDefs = [
    { headerName: '#', colId: 'rowNum', valueGetter: 'node.id', width: 80 },
    { headerName: 'Athlete', field: 'athlete', width: 150 },
    { headerName: 'Age', field: 'age', width: 90 },
    { headerName: 'Country', field: 'country', width: 150 },
    { headerName: 'Year', field: 'year', width: 90 },
    { headerName: 'Date', field: 'date', width: 110,
        editable: true,
        cellEditor: 'agDateTimeCellEditor',
        cellEditorParams: {
            valueToDate: function(value) {
                const parts = value.split("/");
                const date = parts[0];
                const month = parts[1];
                const year = parts[2];
                return new Date(parseFloat(year), parseFloat(month) - 1, parseFloat(date));
            },
            dateToValue: function(date) {
                function pad(number) {
                    return number < 10 ? "0" + number : "" + number;
                }
                return pad(date.getDate()) + '/' + pad(date.getMonth() + 1) + '/' + date.getFullYear();
            }
        }
    },
    { headerName: 'Sport', field: 'sport', width: 150 },
    { headerName: 'Gold', field: 'gold', width: 100 },
    { headerName: 'Silver', field: 'silver', width: 100 },
    { headerName: 'Bronze', field: 'bronze', width: 100 },
    { headerName: 'Total', field: 'total', width: 100 },
];

var gridOptions = {
    defaultColDef: {
        resizable: true,
    },
    columnDefs: columnDefs,
    debug: true,
    rowData: null,
    onGridReady: function() {
        gridOptions.api.sizeColumnsToFit()
    }
};

// setup the grid after the page has finished loading
document.addEventListener('DOMContentLoaded', function() {
    var gridDiv = document.querySelector('#myGrid');
    new agGrid.Grid(gridDiv, gridOptions);

    // do http request to get our sample data - not using any framework to keep the example self contained.
    // you will probably use a framework like JQuery, Angular or something else to do your HTTP calls.
    var httpRequest = new XMLHttpRequest();
    httpRequest.open(
        'GET',
        'https://raw.githubusercontent.com/ag-grid/ag-grid/master/grid-packages/ag-grid-docs/src/olympicWinnersSmall.json'
    );
    httpRequest.send();
    httpRequest.onreadystatechange = function() {
        if (httpRequest.readyState === 4 && httpRequest.status === 200) {
            var httpResult = JSON.parse(httpRequest.responseText);
            gridOptions.api.setRowData(httpResult);
        }
    };
});
<commit_msg>AG-4037 - remove excess columns in example<commit_after>var columnDefs = [
    { headerName: '#', colId: 'rowNum', valueGetter: 'node.id', width: 80 },
    { headerName: 'Athlete', field: 'athlete', width: 150 },
    { headerName: 'Age', field: 'age', width: 90 },
    { headerName: 'Country', field: 'country', width: 150 },
    { headerName: 'Year', field: 'year', width: 90 },
    { headerName: 'Date', field: 'date', width: 110,
        editable: true,
        cellEditor: 'agDateTimeCellEditor',
        cellEditorParams: {
            valueToDate: function(value) {
                const parts = value.split("/");
                const date = parts[0];
                const month = parts[1];
                const year = parts[2];
                return new Date(parseFloat(year), parseFloat(month) - 1, parseFloat(date));
            },
            dateToValue: function(date) {
                function pad(number) {
                    return number < 10 ? "0" + number : "" + number;
                }
                return pad(date.getDate()) + '/' + pad(date.getMonth() + 1) + '/' + date.getFullYear();
            }
        }
    }
];

var gridOptions = {
    defaultColDef: {
        resizable: true,
    },
    columnDefs: columnDefs,
    debug: true,
    rowData: null,
    onGridReady: function() {
        gridOptions.api.sizeColumnsToFit()
    }
};

// setup the grid after the page has finished loading
document.addEventListener('DOMContentLoaded', function() {
    var gridDiv = document.querySelector('#myGrid');
    new agGrid.Grid(gridDiv, gridOptions);

    // do http request to get our sample data - not using any framework to keep the example self contained.
    // you will probably use a framework like JQuery, Angular or something else to do your HTTP calls.
    var httpRequest = new XMLHttpRequest();
    httpRequest.open(
        'GET',
        'https://raw.githubusercontent.com/ag-grid/ag-grid/master/grid-packages/ag-grid-docs/src/olympicWinnersSmall.json'
    );
    httpRequest.send();
    httpRequest.onreadystatechange = function() {
        if (httpRequest.readyState === 4 && httpRequest.status === 200) {
            var httpResult = JSON.parse(httpRequest.responseText);
            gridOptions.api.setRowData(httpResult);
        }
    };
});
<|endoftext|>


<commit_before>const Plugin = require('../Plugin')

module.exports = class AwsS3 extends Plugin {
  constructor (core, opts) {
    super(core, opts)
    this.type = 'uploader'
    this.id = 'AwsS3'
    this.title = 'AWS S3'

    const defaultOptions = {
      getUploadParameters: this.getUploadParameters.bind(this)
    }

    this.opts = Object.assign({}, defaultOptions, opts)

    this.prepareUpload = this.prepareUpload.bind(this)
  }

  getFile (id) {
    return this.core.getState().files[id]
  }

  getUploadParameters (file) {
    if (!this.opts.host) {
      throw new Error('Expected a `host` option containing an uppy-server address.')
    }

    const filename = encodeURIComponent(file.name)
    const type = encodeURIComponent(`${file.type.general}/${file.type.specific}`)
    return fetch(`${this.opts.host}/s3/credentials?filename=${filename}&type=${type}`, {
      method: 'get',
      headers: { accept: 'application/json' }
    }).then((response) => response.json())
  }

  prepareUpload (fileIDs) {
    fileIDs.forEach((id) => {
      this.core.emit('core:preprocess-progress', id, {
        mode: 'determinate',
        message: 'Preparing upload...',
        value: 0
      })
    })

    return Promise.all(
      fileIDs.map((id) => {
        const file = this.getFile(id)
        return this.opts.getUploadParameters(file).then((params) => {
          this.core.emit('core:preprocess-progress', file.id, {
            mode: 'determinate',
            message: 'Preparing upload...',
            value: 1
          })
          return params
        })
      })
    ).then((responses) => {
      const updatedFiles = {}
      fileIDs.forEach((id, index) => {
        const file = this.getFile(id)
        const { endpoint, params } = responses[index]
        const updatedFile = Object.assign({}, file, {
          meta: Object.assign({}, file.meta, params),
          multipart: {
            endpoint,
            fieldName: 'file',
            metaFields: Object.keys(params),
            getUploadUrl (xhr) {
              const locationEl = xhr.responseXML.querySelector('Location')
              return locationEl.textContent
            }
          }
        })

        updatedFiles[id] = updatedFile
      })

      this.core.setState({
        files: Object.assign({}, this.core.getState().files, updatedFiles)
      })

      fileIDs.forEach((id) => {
        this.core.emit('core:preprocess-complete', id)
      })
    })
  }

  install () {
    this.core.addPreProcessor(this.prepareUpload)
  }

  uninstall () {
    this.core.removePreProcessor(this.prepareUpload)
  }
}
<commit_msg>s3: rename /credentials route to /params<commit_after>const Plugin = require('../Plugin')

module.exports = class AwsS3 extends Plugin {
  constructor (core, opts) {
    super(core, opts)
    this.type = 'uploader'
    this.id = 'AwsS3'
    this.title = 'AWS S3'

    const defaultOptions = {
      getUploadParameters: this.getUploadParameters.bind(this)
    }

    this.opts = Object.assign({}, defaultOptions, opts)

    this.prepareUpload = this.prepareUpload.bind(this)
  }

  getFile (id) {
    return this.core.getState().files[id]
  }

  getUploadParameters (file) {
    if (!this.opts.host) {
      throw new Error('Expected a `host` option containing an uppy-server address.')
    }

    const filename = encodeURIComponent(file.name)
    const type = encodeURIComponent(`${file.type.general}/${file.type.specific}`)
    return fetch(`${this.opts.host}/s3/params?filename=${filename}&type=${type}`, {
      method: 'get',
      headers: { accept: 'application/json' }
    }).then((response) => response.json())
  }

  prepareUpload (fileIDs) {
    fileIDs.forEach((id) => {
      this.core.emit('core:preprocess-progress', id, {
        mode: 'determinate',
        message: 'Preparing upload...',
        value: 0
      })
    })

    return Promise.all(
      fileIDs.map((id) => {
        const file = this.getFile(id)
        return this.opts.getUploadParameters(file).then((params) => {
          this.core.emit('core:preprocess-progress', file.id, {
            mode: 'determinate',
            message: 'Preparing upload...',
            value: 1
          })
          return params
        })
      })
    ).then((responses) => {
      const updatedFiles = {}
      fileIDs.forEach((id, index) => {
        const file = this.getFile(id)
        const { endpoint, params } = responses[index]
        const updatedFile = Object.assign({}, file, {
          meta: Object.assign({}, file.meta, params),
          multipart: {
            endpoint,
            fieldName: 'file',
            metaFields: Object.keys(params),
            getUploadUrl (xhr) {
              const locationEl = xhr.responseXML.querySelector('Location')
              return locationEl.textContent
            }
          }
        })

        updatedFiles[id] = updatedFile
      })

      this.core.setState({
        files: Object.assign({}, this.core.getState().files, updatedFiles)
      })

      fileIDs.forEach((id) => {
        this.core.emit('core:preprocess-complete', id)
      })
    })
  }

  install () {
    this.core.addPreProcessor(this.prepareUpload)
  }

  uninstall () {
    this.core.removePreProcessor(this.prepareUpload)
  }
}
<|endoftext|>