<commit_before>
(function() {
'use strict';

if (!window.location.origin) {
  window.location.origin =
      window.location.protocol + "//" +
      window.location.hostname +
      (window.location.port ? (':' + window.location.port) : '');
}

Elm.Debugger = null;
Elm.debuggerAttach = function(module, hotSwapState) {
  return {
    make: function(runtime) {
      var wrappedModule = debugModule(module, runtime);
      Elm.Debugger = debuggerInit(wrappedModule, runtime, hotSwapState);
      dispatchElmDebuggerInit();
      return wrappedModule.debuggedModule;
    }
  };
};

function dispatchElmDebuggerInit() {
  if (parent.window) {
    var dispatch = function() {
      parent.window.postMessage("elmDebuggerInit", window.location.origin);
    };
    if (parent.window.document.readyState === "complete") {
      dispatch();
    }
    else {
      parent.window.addEventListener("load", dispatch);
    }
  }
}

function debugModule(module, runtime) {
  var programPaused = false;
  var recordedEvents = [];
  var asyncCallbacks = [];
  var watchTracker = Elm.Native.Debug.make(runtime).watchTracker;
  var pauseTime = 0;

  function wrapNotify(id, v) {
    var timestep = runtime.timer.now();

    if (programPaused) {
      // ignore async events generated while playing back
      // or user events while program is paused
      return false;
    }
    else {
      var changed = runtime.notify(id, v, timestep);
      recordEvent(id, v, timestep);
      if (parent.window) {
        parent.window.postMessage("elmNotify", window.location.origin);
      }
      return changed;
    }
  };

  function wrapRunDelayed(func, delayMs) {
    if (programPaused) {
      // Don't push timers and such to the callback stack while we're paused.
      // It causes too many callbacks to be fired during unpausing.
      return 0;
    }
    var cbObj = { func:func, delayMs:delayMs, timerId:0, executed:false };
    var timerId = setTimeout(function() {
        cbObj.executed = true;
        func();
      }, delayMs);
    cbObj.timerId = timerId;
    asyncCallbacks.push(cbObj);
    return timerId;
  }

  function recordEvent(id, v, timestep) {
    watchTracker.pushFrame();
    recordedEvents.push({ id:id, value:v, timestep:timestep });
  }

  function clearAsyncCallbacks() {
    asyncCallbacks.forEach(function(timer) {
      if (!timer.executed) {
        clearTimeout(timer.timerId);
      }
    });
  }

  function clearRecordedEvents() {
    recordedEvents = [];
  }

  function getRecordedEventsLength() {
    return recordedEvents.length;
  }

  function getRecordedEventAt(i) {
    return recordedEvents[i];
  }

  function copyRecordedEvents() {
    return recordedEvents.slice();
  }

  function loadRecordedEvents(events) {
    recordedEvents = events.slice();
  }

  function setPaused() {
    programPaused = true;
    clearAsyncCallbacks();
    tracePath.stopRecording();
    pauseTime = Date.now();
  }

  function setContinue(position) {
    var timerDelay = Date.now() - pauseTime;
    runtime.timer.addDelay(timerDelay);
    programPaused = false;
    if (position > 0) {
      // If we're not unpausing at the head, then we need to dump the
      // events that are ahead of where we're continuing.
      recordedEvents = recordedEvents.slice(0, position);
      executeCallbacks(asyncCallbacks, false);
    }
    tracePath.startRecording();
  }

  function getPaused() {
    return programPaused;
  }

  // runtime is the prototype of wrappedRuntime
  // so we can access all runtime properties too
  var wrappedRuntime = Object.create(runtime);
  wrappedRuntime.notify = wrapNotify;
  wrappedRuntime.runDelayed = wrapRunDelayed;

  var assignedPropTracker = Object.create(wrappedRuntime);
  var debuggedModule = module.make(assignedPropTracker);

  // make sure the signal graph is actually a signal & extract the visual model
  if ( !('recv' in debuggedModule.main) ) {
      debuggedModule.main = Elm.Signal.make(runtime).constant(debuggedModule.main);
  }

  // The main module stores imported modules onto the runtime.
  // To ensure only one instance of each module is created,
  // we assign them back on the original runtime object.
  Object.keys(assignedPropTracker).forEach(function(key) {
    runtime[key] = assignedPropTracker[key];
  });

  var signalGraphNodes = flattenSignalGraph(wrappedRuntime.inputs);
  var tracePath = tracePathInit(runtime, debuggedModule.main);

  return {
    debuggedModule: debuggedModule,
    signalGraphNodes: signalGraphNodes,
    initialSnapshot: snapshotSignalGraph(signalGraphNodes),
    initialAsyncCallbacks: asyncCallbacks.slice(),
    // API functions
    clearAsyncCallbacks: clearAsyncCallbacks,
    clearRecordedEvents: clearRecordedEvents,
    getRecordedEventsLength: getRecordedEventsLength,
    getRecordedEventAt: getRecordedEventAt,
    copyRecordedEvents: copyRecordedEvents,
    loadRecordedEvents: loadRecordedEvents,
    getPaused: getPaused,
    setPaused: setPaused,
    setContinue: setContinue,
    tracePath: tracePath,
    watchTracker: watchTracker
  };
}

function debuggerInit(debugModule, runtime, hotSwapState) {
  var currentEventIndex = 0;

  function resetProgram() {
    debugModule.clearAsyncCallbacks();
    restoreSnapshot(debugModule.signalGraphNodes, debugModule.initialSnapshot);
    redrawGraphics();
  }

  function restartProgram() {
    resetProgram();
    debugModule.watchTracker.clear();
    debugModule.tracePath.clearTraces();
    debugModule.clearRecordedEvents();
    debugModule.setContinue(0);
    executeCallbacks(debugModule.initialAsyncCallbacks, true);
  }

  function pauseProgram() {
    debugModule.setPaused();
    currentEventIndex = debugModule.getRecordedEventsLength();
  }

  function continueProgram() {
    if (debugModule.getPaused())
    {
      if (currentEventIndex === 0) {
        restartProgram();
        return;
      }
      resetProgram();
      var continueIndex = currentEventIndex;
      currentEventIndex = 0;
      debugModule.tracePath.clearTraces();
      debugModule.tracePath.startRecording();
      stepTo(continueIndex);
      debugModule.setContinue(currentEventIndex);
    }
  }

  function stepTo(index) {
    if (!debugModule.getPaused()) {
      debugModule.setPaused();
      resetProgram();
    }

    if (index < 0 || index > getMaxSteps()) {
      throw "Index out of bounds: " + index;
    }

    if (index < currentEventIndex) {
      resetProgram();
      currentEventIndex = 0;
    }

    while (currentEventIndex < index) {
      var nextEvent = debugModule.getRecordedEventAt(currentEventIndex);
      runtime.notify(nextEvent.id, nextEvent.value, nextEvent.timestep);

      currentEventIndex += 1;
    }
  }

  function getMaxSteps() {
    return debugModule.getRecordedEventsLength();
  }

  function redrawGraphics() {
    var main = debugModule.debuggedModule.main
    for (var i = main.kids.length ; i-- ; ) {
      main.kids[i].recv(runtime.timer.now(), true, main.id);
    }
  }

  function getHotSwapState() {
    var continueIndex = currentEventIndex;
    if (!debugModule.getPaused()) {
      continueIndex = getMaxSteps();
    }
    return {
      recordedEvents: debugModule.copyRecordedEvents(),
      currentEventIndex: continueIndex
    };
  }

  function dispose() {
    var parentNode = runtime.node.parentNode;
    parentNode.removeChild(debugModule.tracePath.canvas);
    parentNode.removeChild(runtime.node);
  }

  if (hotSwapState) {
    // The problem is that we want to previous paused state. But
    // by the time JS reaches here, the old code has been swapped out
    // and the new modules are being generated. So we can ask the
    // debugging console what it thinks the pause state is and go
    // from there.
    var paused = top.debug.paused;
    debugModule.setPaused();
    debugModule.loadRecordedEvents(hotSwapState.recordedEvents);

    // draw new trace path
    debugModule.tracePath.startRecording();
    stepTo(getMaxSteps());
    debugModule.tracePath.stopRecording();

    stepTo(hotSwapState.currentEventIndex);
    if (paused) {
      debugModule.setPaused();
    } else {
      debugModule.setContinue(hotSwapState.currentEventIndex);
    }
  }

  runtime.node.parentNode.appendChild(debugModule.tracePath.canvas);

  var elmDebugger = {
      restart: restartProgram,
      pause: pauseProgram,
      kontinue: continueProgram,
      getMaxSteps: getMaxSteps,
      stepTo: stepTo,
      getPaused: debugModule.getPaused,
      getHotSwapState: getHotSwapState,
      dispose: dispose,
      allNodes: debugModule.signalGraphNodes,
      watchTracker: debugModule.watchTracker,
      signalGraphMain: debugModule.debuggedModule.main
  };

  return elmDebugger;
}

function Point(x, y) {
  this.x = x;
  this.y = y;

  this.translate = function(x, y) {
    return new Point(this.x + x, this.y + y);
  }

  this.equals = function(p) {
    return this.x == p.x && this.y == p.y;
  }
}

function tracePathInit(runtime, signalGraphMain) {
  var List = Elm.List.make(runtime);
  var Signal = Elm.Signal.make(runtime);
  var tracePathNode = A2(Signal.lift, graphicsUpdate, signalGraphMain);
  var tracePathCanvas = createCanvas();
  var tracePositions = {};
  var recordingTraces = true;

  function findPositions(currentScene) {
    var positions = {};
    function processElement(elem, offset) {
      if (elem.element.ctor == "Custom" && elem.element.type == "Collage")
      {
        List.map(F2(processForm)(offset))(elem.element.model.forms);
      }
      if (elem.props.debugTracePathId)
      {
        positions[elem.props.debugTracePathId] = new Point(offset.x, offset.y);
      }
    }

    function processForm(offset, form) {
      if (form.form.ctor == "FElement")
      {
        processElement(form.form._0, offset.translate(form.x, -form.y));
      }
    }

    processElement(currentScene, new Point(0, 0));
    return positions;
  }

  function appendPositions(positions) {
    for (var id in positions) {
      var pos = positions[id];
      if (tracePositions.hasOwnProperty(id)) {
        var points = tracePositions[id];
        if (!pos.equals(points[points.length-1])) {
          points.push(pos);
        }
      }
      else {
        tracePositions[id] = [pos];
      }
    }
  }

  function graphicsUpdate(currentScene) {
    if (!recordingTraces) {
      return;
    }

    var ctx = tracePathCanvas.getContext('2d');
    ctx.clearRect(0, 0, tracePathCanvas.width, tracePathCanvas.height);

    ctx.save();
    ctx.translate(ctx.canvas.width/2, ctx.canvas.height/2);
    appendPositions(findPositions(currentScene));
    for (var id in tracePositions)
    {
      ctx.beginPath();
      var points = tracePositions[id];
      for (var i=0; i < points.length; i++)
      {
        var p = points[i];
        if (i == 0) {
          ctx.moveTo(p.x, p.y);
        }
        else {
          ctx.lineTo(p.x, p.y);
        }
      }
      ctx.lineWidth = 1;
      ctx.strokeStyle = "rgba(50, 50, 50, 0.4)";
      ctx.stroke();

      for (var i=0; i < points.length; i++)
      {
        var p = points[i];
        ctx.beginPath();
        ctx.arc(p.x, p.y, 2, 0, Math.PI * 2);
        ctx.fillStyle = "rgba(50, 50, 50, 0.4)";
        ctx.fill();
      }
    }

    ctx.restore();
  }

  function clearTraces() {
    tracePositions = {};
  }

  function stopRecording() {
    recordingTraces = false;
  }

  function startRecording() {
    recordingTraces = true;
  }

  return {
    graphicsUpdate: graphicsUpdate,
    canvas: tracePathCanvas,
    clearTraces: clearTraces,
    stopRecording: stopRecording,
    startRecording: startRecording
  }
}

function executeCallbacks(callbacks, reexecute) {
  callbacks.forEach(function(timer) {
    if (reexecute || !timer.executed) {
      var func = timer.func;
      timer.executed = true;
      func();
    }
  });
}

function createCanvas() {
  var c = document.createElement('canvas');
  c.width = window.innerWidth;
  c.height = window.innerHeight;
  c.style.position = "absolute";
  c.style.top = "0";
  c.style.left = "0";
  c.style.pointerEvents = "none";
  return c;
}

function assert(bool, msg) {
  if (!bool) {
    throw "Assertion error: " + msg;
  }
}

function snapshotSignalGraph(signalGraphNodes) {
  var nodeValues = [];

  signalGraphNodes.forEach(function(node) {
    nodeValues.push({ value: node.value, id: node.id });
  });

  return nodeValues;
};

function restoreSnapshot(signalGraphNodes, snapshot) {
  assert(signalGraphNodes.length == snapshot.length, "saved program state has wrong length");
  for (var i=0; i < signalGraphNodes.length; i++) {
    var node = signalGraphNodes[i];
    var state = snapshot[i];
    assert(node.id == state.id, "the nodes moved position");

    node.value = state.value;
  }
}

function flattenSignalGraph(nodes) {
  var nodesById = {};

  function addAllToDict(node) {
    nodesById[node.id] = node;
    node.kids.forEach(addAllToDict);
  }
  nodes.forEach(addAllToDict);

  var allNodes = Object.keys(nodesById).sort().map(function(key) {
    return nodesById[key];
  });
  return allNodes;
};

}());
<commit_msg>two-space indent!<commit_after>
(function() {
'use strict';

if (!window.location.origin) {
  window.location.origin =
      window.location.protocol + "//" +
      window.location.hostname +
      (window.location.port ? (':' + window.location.port) : '');
}

Elm.Debugger = null;
Elm.debuggerAttach = function(module, hotSwapState) {
  return {
    make: function(runtime) {
      var wrappedModule = debugModule(module, runtime);
      Elm.Debugger = debuggerInit(wrappedModule, runtime, hotSwapState);
      dispatchElmDebuggerInit();
      return wrappedModule.debuggedModule;
    }
  };
};

function dispatchElmDebuggerInit() {
  if (parent.window) {
    var dispatch = function() {
      parent.window.postMessage("elmDebuggerInit", window.location.origin);
    };
    if (parent.window.document.readyState === "complete") {
      dispatch();
    }
    else {
      parent.window.addEventListener("load", dispatch);
    }
  }
}

function debugModule(module, runtime) {
  var programPaused = false;
  var recordedEvents = [];
  var asyncCallbacks = [];
  var watchTracker = Elm.Native.Debug.make(runtime).watchTracker;
  var pauseTime = 0;

  function wrapNotify(id, v) {
    var timestep = runtime.timer.now();

    if (programPaused) {
      // ignore async events generated while playing back
      // or user events while program is paused
      return false;
    }
    else {
      var changed = runtime.notify(id, v, timestep);
      recordEvent(id, v, timestep);
      if (parent.window) {
        parent.window.postMessage("elmNotify", window.location.origin);
      }
      return changed;
    }
  };

  function wrapRunDelayed(func, delayMs) {
    if (programPaused) {
      // Don't push timers and such to the callback stack while we're paused.
      // It causes too many callbacks to be fired during unpausing.
      return 0;
    }
    var cbObj = { func:func, delayMs:delayMs, timerId:0, executed:false };
    var timerId = setTimeout(function() {
        cbObj.executed = true;
        func();
      }, delayMs);
    cbObj.timerId = timerId;
    asyncCallbacks.push(cbObj);
    return timerId;
  }

  function recordEvent(id, v, timestep) {
    watchTracker.pushFrame();
    recordedEvents.push({ id:id, value:v, timestep:timestep });
  }

  function clearAsyncCallbacks() {
    asyncCallbacks.forEach(function(timer) {
      if (!timer.executed) {
        clearTimeout(timer.timerId);
      }
    });
  }

  function clearRecordedEvents() {
    recordedEvents = [];
  }

  function getRecordedEventsLength() {
    return recordedEvents.length;
  }

  function getRecordedEventAt(i) {
    return recordedEvents[i];
  }

  function copyRecordedEvents() {
    return recordedEvents.slice();
  }

  function loadRecordedEvents(events) {
    recordedEvents = events.slice();
  }

  function setPaused() {
    programPaused = true;
    clearAsyncCallbacks();
    tracePath.stopRecording();
    pauseTime = Date.now();
  }

  function setContinue(position) {
    var timerDelay = Date.now() - pauseTime;
    runtime.timer.addDelay(timerDelay);
    programPaused = false;
    if (position > 0) {
      // If we're not unpausing at the head, then we need to dump the
      // events that are ahead of where we're continuing.
      recordedEvents = recordedEvents.slice(0, position);
      executeCallbacks(asyncCallbacks, false);
    }
    tracePath.startRecording();
  }

  function getPaused() {
    return programPaused;
  }

  // runtime is the prototype of wrappedRuntime
  // so we can access all runtime properties too
  var wrappedRuntime = Object.create(runtime);
  wrappedRuntime.notify = wrapNotify;
  wrappedRuntime.runDelayed = wrapRunDelayed;

  var assignedPropTracker = Object.create(wrappedRuntime);
  var debuggedModule = module.make(assignedPropTracker);

  // make sure the signal graph is actually a signal & extract the visual model
  if ( !('recv' in debuggedModule.main) ) {
    debuggedModule.main = Elm.Signal.make(runtime).constant(debuggedModule.main);
  }

  // The main module stores imported modules onto the runtime.
  // To ensure only one instance of each module is created,
  // we assign them back on the original runtime object.
  Object.keys(assignedPropTracker).forEach(function(key) {
    runtime[key] = assignedPropTracker[key];
  });

  var signalGraphNodes = flattenSignalGraph(wrappedRuntime.inputs);
  var tracePath = tracePathInit(runtime, debuggedModule.main);

  return {
    debuggedModule: debuggedModule,
    signalGraphNodes: signalGraphNodes,
    initialSnapshot: snapshotSignalGraph(signalGraphNodes),
    initialAsyncCallbacks: asyncCallbacks.slice(),
    // API functions
    clearAsyncCallbacks: clearAsyncCallbacks,
    clearRecordedEvents: clearRecordedEvents,
    getRecordedEventsLength: getRecordedEventsLength,
    getRecordedEventAt: getRecordedEventAt,
    copyRecordedEvents: copyRecordedEvents,
    loadRecordedEvents: loadRecordedEvents,
    getPaused: getPaused,
    setPaused: setPaused,
    setContinue: setContinue,
    tracePath: tracePath,
    watchTracker: watchTracker
  };
}

function debuggerInit(debugModule, runtime, hotSwapState) {
  var currentEventIndex = 0;

  function resetProgram() {
    debugModule.clearAsyncCallbacks();
    restoreSnapshot(debugModule.signalGraphNodes, debugModule.initialSnapshot);
    redrawGraphics();
  }

  function restartProgram() {
    resetProgram();
    debugModule.watchTracker.clear();
    debugModule.tracePath.clearTraces();
    debugModule.clearRecordedEvents();
    debugModule.setContinue(0);
    executeCallbacks(debugModule.initialAsyncCallbacks, true);
  }

  function pauseProgram() {
    debugModule.setPaused();
    currentEventIndex = debugModule.getRecordedEventsLength();
  }

  function continueProgram() {
    if (debugModule.getPaused())
    {
      if (currentEventIndex === 0) {
        restartProgram();
        return;
      }
      resetProgram();
      var continueIndex = currentEventIndex;
      currentEventIndex = 0;
      debugModule.tracePath.clearTraces();
      debugModule.tracePath.startRecording();
      stepTo(continueIndex);
      debugModule.setContinue(currentEventIndex);
    }
  }

  function stepTo(index) {
    if (!debugModule.getPaused()) {
      debugModule.setPaused();
      resetProgram();
    }

    if (index < 0 || index > getMaxSteps()) {
      throw "Index out of bounds: " + index;
    }

    if (index < currentEventIndex) {
      resetProgram();
      currentEventIndex = 0;
    }

    while (currentEventIndex < index) {
      var nextEvent = debugModule.getRecordedEventAt(currentEventIndex);
      runtime.notify(nextEvent.id, nextEvent.value, nextEvent.timestep);

      currentEventIndex += 1;
    }
  }

  function getMaxSteps() {
    return debugModule.getRecordedEventsLength();
  }

  function redrawGraphics() {
    var main = debugModule.debuggedModule.main
    for (var i = main.kids.length ; i-- ; ) {
      main.kids[i].recv(runtime.timer.now(), true, main.id);
    }
  }

  function getHotSwapState() {
    var continueIndex = currentEventIndex;
    if (!debugModule.getPaused()) {
      continueIndex = getMaxSteps();
    }
    return {
      recordedEvents: debugModule.copyRecordedEvents(),
      currentEventIndex: continueIndex
    };
  }

  function dispose() {
    var parentNode = runtime.node.parentNode;
    parentNode.removeChild(debugModule.tracePath.canvas);
    parentNode.removeChild(runtime.node);
  }

  if (hotSwapState) {
    // The problem is that we want to previous paused state. But
    // by the time JS reaches here, the old code has been swapped out
    // and the new modules are being generated. So we can ask the
    // debugging console what it thinks the pause state is and go
    // from there.
    var paused = top.debug.paused;
    debugModule.setPaused();
    debugModule.loadRecordedEvents(hotSwapState.recordedEvents);

    // draw new trace path
    debugModule.tracePath.startRecording();
    stepTo(getMaxSteps());
    debugModule.tracePath.stopRecording();

    stepTo(hotSwapState.currentEventIndex);
    if (paused) {
      debugModule.setPaused();
    } else {
      debugModule.setContinue(hotSwapState.currentEventIndex);
    }
  }

  runtime.node.parentNode.appendChild(debugModule.tracePath.canvas);

  var elmDebugger = {
      restart: restartProgram,
      pause: pauseProgram,
      kontinue: continueProgram,
      getMaxSteps: getMaxSteps,
      stepTo: stepTo,
      getPaused: debugModule.getPaused,
      getHotSwapState: getHotSwapState,
      dispose: dispose,
      allNodes: debugModule.signalGraphNodes,
      watchTracker: debugModule.watchTracker,
      signalGraphMain: debugModule.debuggedModule.main
  };

  return elmDebugger;
}

function Point(x, y) {
  this.x = x;
  this.y = y;

  this.translate = function(x, y) {
    return new Point(this.x + x, this.y + y);
  }

  this.equals = function(p) {
    return this.x == p.x && this.y == p.y;
  }
}

function tracePathInit(runtime, signalGraphMain) {
  var List = Elm.List.make(runtime);
  var Signal = Elm.Signal.make(runtime);
  var tracePathNode = A2(Signal.lift, graphicsUpdate, signalGraphMain);
  var tracePathCanvas = createCanvas();
  var tracePositions = {};
  var recordingTraces = true;

  function findPositions(currentScene) {
    var positions = {};
    function processElement(elem, offset) {
      if (elem.element.ctor == "Custom" && elem.element.type == "Collage")
      {
        List.map(F2(processForm)(offset))(elem.element.model.forms);
      }
      if (elem.props.debugTracePathId)
      {
        positions[elem.props.debugTracePathId] = new Point(offset.x, offset.y);
      }
    }

    function processForm(offset, form) {
      if (form.form.ctor == "FElement")
      {
        processElement(form.form._0, offset.translate(form.x, -form.y));
      }
    }

    processElement(currentScene, new Point(0, 0));
    return positions;
  }

  function appendPositions(positions) {
    for (var id in positions) {
      var pos = positions[id];
      if (tracePositions.hasOwnProperty(id)) {
        var points = tracePositions[id];
        if (!pos.equals(points[points.length-1])) {
          points.push(pos);
        }
      }
      else {
        tracePositions[id] = [pos];
      }
    }
  }

  function graphicsUpdate(currentScene) {
    if (!recordingTraces) {
      return;
    }

    var ctx = tracePathCanvas.getContext('2d');
    ctx.clearRect(0, 0, tracePathCanvas.width, tracePathCanvas.height);

    ctx.save();
    ctx.translate(ctx.canvas.width/2, ctx.canvas.height/2);
    appendPositions(findPositions(currentScene));
    for (var id in tracePositions)
    {
      ctx.beginPath();
      var points = tracePositions[id];
      for (var i=0; i < points.length; i++)
      {
        var p = points[i];
        if (i == 0) {
          ctx.moveTo(p.x, p.y);
        }
        else {
          ctx.lineTo(p.x, p.y);
        }
      }
      ctx.lineWidth = 1;
      ctx.strokeStyle = "rgba(50, 50, 50, 0.4)";
      ctx.stroke();

      for (var i=0; i < points.length; i++)
      {
        var p = points[i];
        ctx.beginPath();
        ctx.arc(p.x, p.y, 2, 0, Math.PI * 2);
        ctx.fillStyle = "rgba(50, 50, 50, 0.4)";
        ctx.fill();
      }
    }

    ctx.restore();
  }

  function clearTraces() {
    tracePositions = {};
  }

  function stopRecording() {
    recordingTraces = false;
  }

  function startRecording() {
    recordingTraces = true;
  }

  return {
    graphicsUpdate: graphicsUpdate,
    canvas: tracePathCanvas,
    clearTraces: clearTraces,
    stopRecording: stopRecording,
    startRecording: startRecording
  }
}

function executeCallbacks(callbacks, reexecute) {
  callbacks.forEach(function(timer) {
    if (reexecute || !timer.executed) {
      var func = timer.func;
      timer.executed = true;
      func();
    }
  });
}

function createCanvas() {
  var c = document.createElement('canvas');
  c.width = window.innerWidth;
  c.height = window.innerHeight;
  c.style.position = "absolute";
  c.style.top = "0";
  c.style.left = "0";
  c.style.pointerEvents = "none";
  return c;
}

function assert(bool, msg) {
  if (!bool) {
    throw "Assertion error: " + msg;
  }
}

function snapshotSignalGraph(signalGraphNodes) {
  var nodeValues = [];

  signalGraphNodes.forEach(function(node) {
    nodeValues.push({ value: node.value, id: node.id });
  });

  return nodeValues;
};

function restoreSnapshot(signalGraphNodes, snapshot) {
  assert(signalGraphNodes.length == snapshot.length, "saved program state has wrong length");
  for (var i=0; i < signalGraphNodes.length; i++) {
    var node = signalGraphNodes[i];
    var state = snapshot[i];
    assert(node.id == state.id, "the nodes moved position");

    node.value = state.value;
  }
}

function flattenSignalGraph(nodes) {
  var nodesById = {};

  function addAllToDict(node) {
    nodesById[node.id] = node;
    node.kids.forEach(addAllToDict);
  }
  nodes.forEach(addAllToDict);

  var allNodes = Object.keys(nodesById).sort().map(function(key) {
    return nodesById[key];
  });
  return allNodes;
};

}());
<|endoftext|>


<commit_before>// Documentation available at https://theintern.github.io

define({
	proxyPort: 9010,
	proxyUrl: 'http://localhost:9010/',
	excludeInstrumentation: /^(?:node_modules|public)\//,
	tunnel: 'NullTunnel',
	loaderOptions: {
		packages: [
			{
				name: 'Script',
				location: 'public/assets/js',
				main: 'script.min.js'
			}
		]
	},
	suites: [
		'source/js/tests/unit/example'
	],
	functionalSuites: [

	],
	environments: [
		{
			browserName: 'chrome'
		}
	]
});<commit_msg>Tweak test config formatting<commit_after>// Documentation available at https://theintern.github.io

define({
	proxyPort: 9010,
	proxyUrl: 'https://localhost:9010/',
	excludeInstrumentation: /^(?:node_modules|public)\//,
	tunnel: 'NullTunnel',
	loaderOptions: {
		packages: [{
			name: 'Script',
			location: 'public/assets/js',
			main: 'script.min.js'
		}]
	},
	suites: [
		'source/js/tests/unit/example'
	],
	functionalSuites: [

	],
	environments: [{
		browserName: 'chrome'
	}]
});<|endoftext|>


<commit_before>/**
 * @license
 * Copyright (c) 2017 Google Inc. All rights reserved.
 * This code may only be used under the BSD style license found at
 * http://polymer.github.io/LICENSE.txt
 * Code distributed by Google as part of this project is also
 * subject to an additional IP rights grant found at
 * http://polymer.github.io/PATENTS.txt
 */

const assert = require('assert');
const parser = require('./build/manifest-parser.js');
const Recipe = require('./recipe/recipe.js');
const ParticleParser = require('./build/particle-parser.js');
const ParticleSpec = require('./particle-spec.js')
const Schema = require('./schema.js')

class Manifest {
  constructor() {
    this._recipes = [];
    this._imports = [];
    this._particles = {};
    this._schemas = {};
    this._fileName = null;
  }
  get recipes() {
    return this._recipes;
  }
  get particles() {
    return this._particles;
  }
  get imports() {
    return this._imports;
  }
  get schemas() {
    return this._schemas;
  }
  get fileName() {
    return this._fileName;
  }
  findSchemaByName(name) {
    let schema = this._schemas[name];
    if (!schema) {
      // TODO: flatten or index imports?
      for (let importedManifest of this._imports) {
        schema = importedManifest.findSchemaByName(name);
        if (schema) {
          break;
        }
      }
    }
    return schema;
  }
  static async load(fileName, loader, registry) {
    if (registry && registry[fileName]) {
      return registry[fileName];
    }
    let content = await loader.loadFile(fileName);
    let manifest = await Manifest.parse(content, {fileName, loader, registry, position: {line: 1, column: 0}});
    if (manifest && registry) {
      registry[fileName] = manifest;
    }
    return manifest;
  }
  static async parse(content, options) {
    options = options || {};
    let {fileName, position, loader, registry} = options;
    registry = registry || {};
    position = position || {line: 1, column: 0};

    let items = parser.parse(content);
    let manifest = new Manifest();
    manifest._fileName = fileName;
    // TODO: This should be written to process in dependency order.
    // 1. imports
    // 2. schemas
    // 3. particles, TODO: entities => views
    // 4. recipes
    for (let item of items.filter(item => item.kind == 'import')) {
      let path = loader.path(manifest.fileName);
      let target = loader.join(path, item.path);
      manifest._imports.push(await Manifest.load(target, loader, registry));
    }
    for (let item of items.filter(item => item.kind == 'schema')) {
      this._processSchema(manifest, item);
    }
    for (let item of items.filter(item => item.kind == 'particle')) {
      this._processParticle(manifest, item);
    }
    for (let item of items.filter(item => item.kind == 'recipe')) {
      this._processRecipe(manifest, item);
    }
    return manifest;
  }
  static _processSchema(manifest, schemaItem) {
    let parent;
    if (schemaItem.parent) {
      let parent = manifest.findSchemaByName(schemaItem.parent);
      // TODO: error handling
      assert(parent);
      schemaItem.parent = parent;
    }
    manifest._schemas[schemaItem.name] = new Schema(schemaItem);
  }
  static _processParticle(manifest, particleItem) {
    let particleSpec = new ParticleSpec(ParticleParser.parse(particleItem.body));
    manifest._particles[particleItem.name] = particleSpec;
  }
  static _processRecipe(manifest, recipeItem) {
    let recipe = manifest._newRecipe(recipeItem.name);
    let items = {
      views: recipeItem.items.filter(item => item.kind == 'view'),
      byView: new Map(),
      particles: recipeItem.items.filter(item => item.kind == 'particle'),
      byParticle: new Map(),
      byName: new Map(),
      connections: recipeItem.items.filter(item => item.kind == 'connection')
    };

    for (let connection of items.connections) {
      recipe.newConnectionConstraint(connection.from.particle, connection.from.param,
                                     connection.to.particle, connection.to.param);
    }

    for (let item of items.views) {
      let view = recipe.newView();
      if (item.ref.id) {
        view.id = item.ref.id;
      }
      view.tags = item.ref.tags;
      if (item.name) {
        assert(!items.byName.has(item.name));
        view.localName = item.name;
        items.byName.set(item.name, {item: item, view: view});
      }
      view.create = item.create;
      items.byView.set(view, item);
    }

    // TODO: disambiguate.
    let particlesByName = {};
    for (let item of items.particles) {
      let particle = recipe.newParticle(item.ref.name);
      particle.tags = item.ref.tags;
      if (item.ref.name) {
        let spec = manifest.particles[item.ref.name];
        // TODO: factor out import lookups
        if (!spec) {
          for (let importManifest of manifest.imports) {
            if (importManifest.particles[item.ref.name]) {
              spec = importManifest.particles[item.ref.name];
              break;
            }
          }
        }
        if (spec) {
          particle.spec = spec;
        }
        particlesByName[item.ref.name] = particle;
      }
      if (item.name) {
        // TODO: errors.
        assert(!items.byName.has(item.name));
        particle.localName = item.name;
        items.byName.set(item.name, {item: item, particle: particle});
      }
      items.byParticle.set(particle, item);
    }

    for (let [particle, item] of items.byParticle) {
      for (let connectionItem of item.connections) {
        let connection;
        if (connectionItem.param == '*') {
          connection = particle.addUnnamedConnection();
        } else {
          connection = particle.connections[connectionItem.param];
          if (!connection) {
            connection = particle.addConnectionName(connectionItem.param);
          }
          // TODO: else, merge tags? merge directions?
        }
        connection.tags = connectionItem.target.tags;
        connection.direction = {'->': 'out', '<-': 'in', '=': 'inout'}[connectionItem.dir];

        let targetView;
        let targetParticle;

        if (connectionItem.target.name) {
          let entry = items.byName.get(connectionItem.target.name);
          assert(entry, `could not find ${connectionItem.target.name}`);
          if (entry.item.kind == 'view') {
            targetView = entry.view;
          } else if (entry.item.kind == 'particle') {
            targetParticle = entry.particle;
          } else {
            assert(false, `did not expect ${entry.item.kind}`);
          }
        }

        if (connectionItem.target.particle) {
          targetParticle = particlesByName[connectionItem.target.particle];
          // TODO: error reporting
          assert(targetParticle);
        }

        if (targetParticle) {
          let targetConnection;
          if (connectionItem.target.param) {
            targetConnection = targetParticle.connections[connectionItem.target.param];
            if (!targetConnection) {
              targetConnection = targetParticle.addConnectionName(connectionItem.target.param);
              // TODO: direction?
            }
          } else {
            targetConnection = targetParticle.addUnnamedConnection();
            // TODO: direction?
          }

          targetView = targetConnection.view;
          if (!targetView) {
            // TODO: tags?
            targetView = recipe.newView();
            targetConnection.connectToView(targetView)
          }
        }

        if (targetView) {
          connection.connectToView(targetView);
        }
      }
      for (let slotConnectionItem of item.slotConnections) {
        let slotConn = particle.addSlotConnection(slotConnectionItem.param, slotConnectionItem.dir);
        if (slotConnectionItem.name) {
          let slot = items.byName.get(slotConnectionItem.name);
          if (!slot) {
            slot = recipe.newSlot();
            slot.localName = slotConnectionItem.name;
            items.byName.set(slotConnectionItem.name, slot);
          }
          slotConn.connectToSlot(slot);
        }
        if (slotConnectionItem.viewRef) {
          assert(slotConnectionItem.dir == "provide");
          if (slotConnectionItem.viewRef.name) {
            assert(slotConnectionItem.viewRef.tags.length == 0);
            let viewConn = particle.connections[slotConnectionItem.viewRef.name];
            assert(viewConn, `Slot ${slotConnectionItem.name} cannot use nonexistent view connection ${slotConnectionItem.viewRef.name}`);
            slotConn.viewConnections.push(viewConn);
          } else if (slotConnectionItem.viewRef.tags.length > 0) {
            slotConnectionItem.viewRef.tags.forEach(t => slotConn.tags.push(t));
          } else {
            fail(`Unsupported view ref for slot ${slotConnectionItem.name}`);
          }
        }
      }
    }
  }
  _newRecipe(name) {
    // TODO: use name
    let recipe = new Recipe();
    this._recipes.push(recipe);
    return recipe;
  }
}

module.exports = Manifest;
<commit_msg>Remove redundant declaration<commit_after>/**
 * @license
 * Copyright (c) 2017 Google Inc. All rights reserved.
 * This code may only be used under the BSD style license found at
 * http://polymer.github.io/LICENSE.txt
 * Code distributed by Google as part of this project is also
 * subject to an additional IP rights grant found at
 * http://polymer.github.io/PATENTS.txt
 */

const assert = require('assert');
const parser = require('./build/manifest-parser.js');
const Recipe = require('./recipe/recipe.js');
const ParticleParser = require('./build/particle-parser.js');
const ParticleSpec = require('./particle-spec.js')
const Schema = require('./schema.js')

class Manifest {
  constructor() {
    this._recipes = [];
    this._imports = [];
    this._particles = {};
    this._schemas = {};
    this._fileName = null;
  }
  get recipes() {
    return this._recipes;
  }
  get particles() {
    return this._particles;
  }
  get imports() {
    return this._imports;
  }
  get schemas() {
    return this._schemas;
  }
  get fileName() {
    return this._fileName;
  }
  findSchemaByName(name) {
    let schema = this._schemas[name];
    if (!schema) {
      // TODO: flatten or index imports?
      for (let importedManifest of this._imports) {
        schema = importedManifest.findSchemaByName(name);
        if (schema) {
          break;
        }
      }
    }
    return schema;
  }
  static async load(fileName, loader, registry) {
    if (registry && registry[fileName]) {
      return registry[fileName];
    }
    let content = await loader.loadFile(fileName);
    let manifest = await Manifest.parse(content, {fileName, loader, registry, position: {line: 1, column: 0}});
    if (manifest && registry) {
      registry[fileName] = manifest;
    }
    return manifest;
  }
  static async parse(content, options) {
    options = options || {};
    let {fileName, position, loader, registry} = options;
    registry = registry || {};
    position = position || {line: 1, column: 0};

    let items = parser.parse(content);
    let manifest = new Manifest();
    manifest._fileName = fileName;
    // TODO: This should be written to process in dependency order.
    // 1. imports
    // 2. schemas
    // 3. particles, TODO: entities => views
    // 4. recipes
    for (let item of items.filter(item => item.kind == 'import')) {
      let path = loader.path(manifest.fileName);
      let target = loader.join(path, item.path);
      manifest._imports.push(await Manifest.load(target, loader, registry));
    }
    for (let item of items.filter(item => item.kind == 'schema')) {
      this._processSchema(manifest, item);
    }
    for (let item of items.filter(item => item.kind == 'particle')) {
      this._processParticle(manifest, item);
    }
    for (let item of items.filter(item => item.kind == 'recipe')) {
      this._processRecipe(manifest, item);
    }
    return manifest;
  }
  static _processSchema(manifest, schemaItem) {
    if (schemaItem.parent) {
      let parent = manifest.findSchemaByName(schemaItem.parent);
      // TODO: error handling
      assert(parent);
      schemaItem.parent = parent;
    }
    manifest._schemas[schemaItem.name] = new Schema(schemaItem);
  }
  static _processParticle(manifest, particleItem) {
    let particleSpec = new ParticleSpec(ParticleParser.parse(particleItem.body));
    manifest._particles[particleItem.name] = particleSpec;
  }
  static _processRecipe(manifest, recipeItem) {
    let recipe = manifest._newRecipe(recipeItem.name);
    let items = {
      views: recipeItem.items.filter(item => item.kind == 'view'),
      byView: new Map(),
      particles: recipeItem.items.filter(item => item.kind == 'particle'),
      byParticle: new Map(),
      byName: new Map(),
      connections: recipeItem.items.filter(item => item.kind == 'connection')
    };

    for (let connection of items.connections) {
      recipe.newConnectionConstraint(connection.from.particle, connection.from.param,
                                     connection.to.particle, connection.to.param);
    }

    for (let item of items.views) {
      let view = recipe.newView();
      if (item.ref.id) {
        view.id = item.ref.id;
      }
      view.tags = item.ref.tags;
      if (item.name) {
        assert(!items.byName.has(item.name));
        view.localName = item.name;
        items.byName.set(item.name, {item: item, view: view});
      }
      view.create = item.create;
      items.byView.set(view, item);
    }

    // TODO: disambiguate.
    let particlesByName = {};
    for (let item of items.particles) {
      let particle = recipe.newParticle(item.ref.name);
      particle.tags = item.ref.tags;
      if (item.ref.name) {
        let spec = manifest.particles[item.ref.name];
        // TODO: factor out import lookups
        if (!spec) {
          for (let importManifest of manifest.imports) {
            if (importManifest.particles[item.ref.name]) {
              spec = importManifest.particles[item.ref.name];
              break;
            }
          }
        }
        if (spec) {
          particle.spec = spec;
        }
        particlesByName[item.ref.name] = particle;
      }
      if (item.name) {
        // TODO: errors.
        assert(!items.byName.has(item.name));
        particle.localName = item.name;
        items.byName.set(item.name, {item: item, particle: particle});
      }
      items.byParticle.set(particle, item);
    }

    for (let [particle, item] of items.byParticle) {
      for (let connectionItem of item.connections) {
        let connection;
        if (connectionItem.param == '*') {
          connection = particle.addUnnamedConnection();
        } else {
          connection = particle.connections[connectionItem.param];
          if (!connection) {
            connection = particle.addConnectionName(connectionItem.param);
          }
          // TODO: else, merge tags? merge directions?
        }
        connection.tags = connectionItem.target.tags;
        connection.direction = {'->': 'out', '<-': 'in', '=': 'inout'}[connectionItem.dir];

        let targetView;
        let targetParticle;

        if (connectionItem.target.name) {
          let entry = items.byName.get(connectionItem.target.name);
          assert(entry, `could not find ${connectionItem.target.name}`);
          if (entry.item.kind == 'view') {
            targetView = entry.view;
          } else if (entry.item.kind == 'particle') {
            targetParticle = entry.particle;
          } else {
            assert(false, `did not expect ${entry.item.kind}`);
          }
        }

        if (connectionItem.target.particle) {
          targetParticle = particlesByName[connectionItem.target.particle];
          // TODO: error reporting
          assert(targetParticle);
        }

        if (targetParticle) {
          let targetConnection;
          if (connectionItem.target.param) {
            targetConnection = targetParticle.connections[connectionItem.target.param];
            if (!targetConnection) {
              targetConnection = targetParticle.addConnectionName(connectionItem.target.param);
              // TODO: direction?
            }
          } else {
            targetConnection = targetParticle.addUnnamedConnection();
            // TODO: direction?
          }

          targetView = targetConnection.view;
          if (!targetView) {
            // TODO: tags?
            targetView = recipe.newView();
            targetConnection.connectToView(targetView)
          }
        }

        if (targetView) {
          connection.connectToView(targetView);
        }
      }
      for (let slotConnectionItem of item.slotConnections) {
        let slotConn = particle.addSlotConnection(slotConnectionItem.param, slotConnectionItem.dir);
        if (slotConnectionItem.name) {
          let slot = items.byName.get(slotConnectionItem.name);
          if (!slot) {
            slot = recipe.newSlot();
            slot.localName = slotConnectionItem.name;
            items.byName.set(slotConnectionItem.name, slot);
          }
          slotConn.connectToSlot(slot);
        }
        if (slotConnectionItem.viewRef) {
          assert(slotConnectionItem.dir == "provide");
          if (slotConnectionItem.viewRef.name) {
            assert(slotConnectionItem.viewRef.tags.length == 0);
            let viewConn = particle.connections[slotConnectionItem.viewRef.name];
            assert(viewConn, `Slot ${slotConnectionItem.name} cannot use nonexistent view connection ${slotConnectionItem.viewRef.name}`);
            slotConn.viewConnections.push(viewConn);
          } else if (slotConnectionItem.viewRef.tags.length > 0) {
            slotConnectionItem.viewRef.tags.forEach(t => slotConn.tags.push(t));
          } else {
            fail(`Unsupported view ref for slot ${slotConnectionItem.name}`);
          }
        }
      }
    }
  }
  _newRecipe(name) {
    // TODO: use name
    let recipe = new Recipe();
    this._recipes.push(recipe);
    return recipe;
  }
}

module.exports = Manifest;
<|endoftext|>


<commit_before>enyo.kind({
	name: "TodayView",
	noStretch: true,
	components: [
		{layoutKind: "FittableRowsLayout", noStretch: true, components: [
			{name: "sensors", layoutKind: "FittableColumnsLayout", noStretch: true, components: [
				{layoutKind: "FittableRowsLayout", classes: "today_sensor_element", noStretch: true, components: [
					{content: "Temperatur", classes: "name", tag: "h4"},
					{id: "temperatureChart", classes: "temperature", tag: "canvas"}
				]}
			]}
		]}
	],
	loadChart: function(record) {
		// temperature 1 sensor
		$temperature1Data = record.attributes.t1;
		$temperature1 = Object.keys($temperature1Data).map(function (key) {
    		return $temperature1Data[key];
		});
    	// temperature 2 sensor
		$temperature2 = Object.keys(record.attributes.t2).map(function (key) {
			return record.attributes.t2[key];
		});
		// data
		var data = {
			labels : Object.keys($temperature1Data).map(function (key) {
					if( key.substr(-2) === "00" ) {
						return key;
					} else {
						return "";
					}
			}),
			datasets : [
				{
					fillColor : "rgba(220,220,220,0.5)",
					strokeColor : "rgba(220,220,220,1)",
					pointColor : "rgba(220,220,220,1)",
					pointStrokeColor : "#fff",
					data : $temperature1
				},
				{
					fillColor : "rgba(151,187,205,0.5)",
					strokeColor : "rgba(151,187,205,1)",
					pointColor : "rgba(151,187,205,1)",
					pointStrokeColor : "#fff",
					data : $temperature2
				}
			]
		}
		// options
		var options = {

			skipLabels : true,

			//Boolean - If we show the scale above the chart data
			scaleOverlay : false,

			//Boolean - If we want to override with a hard coded scale
			scaleOverride : false,

			//** Required if scaleOverride is true **
			//Number - The number of steps in a hard coded scale
			scaleSteps : null,
			//Number - The value jump in the hard coded scale
			scaleStepWidth : null,
			//Number - The scale starting value
			scaleStartValue : null,

			//String - Colour of the scale line
			scaleLineColor : "rgba(0,0,0,.1)",

			//Number - Pixel width of the scale line
			scaleLineWidth : 1,

			//Boolean - Whether to show labels on the scale
			scaleShowLabels : true,

			//Interpolated JS string - can access value
			scaleLabel : "<%=value%>",

			//String - Scale label font declaration for the scale label
			scaleFontFamily : "'Arial'",

			//Number - Scale label font size in pixels
			scaleFontSize : 12,

			//String - Scale label font weight style
			scaleFontStyle : "normal",

			//String - Scale label font colour
			scaleFontColor : "#666",

			///Boolean - Whether grid lines are shown across the chart
			scaleShowGridLines : true,

			//String - Colour of the grid lines
			scaleGridLineColor : "rgba(0,0,0,.05)",

			//Number - Width of the grid lines
			scaleGridLineWidth : 1,

			//Boolean - Whether the line is curved between points
			bezierCurve : true,

			//Boolean - Whether to show a dot for each point
			pointDot : false,

			//Number - Radius of each point dot in pixels
			pointDotRadius : 3,

			//Number - Pixel width of point dot stroke
			pointDotStrokeWidth : 1,

			//Boolean - Whether to show a stroke for datasets
			datasetStroke : true,

			//Number - Pixel width of dataset stroke
			datasetStrokeWidth : 2,

			//Boolean - Whether to fill the dataset with a colour
			datasetFill : true,

			//Boolean - Whether to animate the chart
			animation : true,

			//Number - Number of animation steps
			animationSteps : 60,

			//String - Animation easing effect
			animationEasing : "easeOutQuart",

			//Function - Fires when the animation is complete
			onAnimationComplete : null

		}
		// chart
		var ctx = document.getElementById("temperatureChart").getContext("2d");
		appModel = new AppModel();
    	if (appModel.get("existsBigScreen")) {
    		ctx.canvas.width = 640;
			ctx.canvas.height = 480;
    	} else {
    		ctx.canvas.width = 320;
			ctx.canvas.height = 240;
    	}
		var temperatureChart = new Chart(ctx).Line(data,options);
	}
});<commit_msg>bugfix (parse error)<commit_after>enyo.kind({
	name: "TodayView",
	noStretch: true,
	components: [
		{layoutKind: "FittableRowsLayout", noStretch: true, components: [
			{name: "sensors", layoutKind: "FittableColumnsLayout", noStretch: true, components: [
				{layoutKind: "FittableRowsLayout", classes: "today_sensor_element", noStretch: true, components: [
					{content: "Temperatur", classes: "name", tag: "h4"},
					{id: "temperatureChart", classes: "temperature", tag: "canvas"}
				]}
			]}
		]}
	],
	loadChart: function(record) {
		// temperature 1 sensor
		$temperature1Data = record.attributes.t1;
		$temperature1 = Object.keys($temperature1Data).map(function (key) {
    		return parseFloat($temperature1Data[key]);
		});
    	// temperature 2 sensor
    	$temperature2Data = record.attributes.t2;
		$temperature2 = Object.keys($temperature2Data).map(function (key) {
			return parseFloat($temperature2Data[key]);
		});
		// data
		var data = {
			labels : Object.keys(record.attributes.t1).map(function (key) {
					if( key.substr(-2) === "00" ) {
						return key;
					} else {
						return "";
					}
			}),
			datasets : [
				{
					fillColor : "rgba(220,220,220,0.5)",
					strokeColor : "rgba(220,220,220,1)",
					pointColor : "rgba(220,220,220,1)",
					pointStrokeColor : "#fff",
					data : $temperature1
				},
				{
					fillColor : "rgba(151,187,205,0.5)",
					strokeColor : "rgba(151,187,205,1)",
					pointColor : "rgba(151,187,205,1)",
					pointStrokeColor : "#fff",
					data : $temperature2
				}
			]
		}
		// options
		var options = {

			skipLabels : true,

			//Boolean - If we show the scale above the chart data
			scaleOverlay : false,

			//Boolean - If we want to override with a hard coded scale
			scaleOverride : false,

			//** Required if scaleOverride is true **
			//Number - The number of steps in a hard coded scale
			scaleSteps : null,
			//Number - The value jump in the hard coded scale
			scaleStepWidth : null,
			//Number - The scale starting value
			scaleStartValue : null,

			//String - Colour of the scale line
			scaleLineColor : "rgba(0,0,0,.1)",

			//Number - Pixel width of the scale line
			scaleLineWidth : 1,

			//Boolean - Whether to show labels on the scale
			scaleShowLabels : true,

			//Interpolated JS string - can access value
			scaleLabel : "<%=value%>",

			//String - Scale label font declaration for the scale label
			scaleFontFamily : "'Arial'",

			//Number - Scale label font size in pixels
			scaleFontSize : 12,

			//String - Scale label font weight style
			scaleFontStyle : "normal",

			//String - Scale label font colour
			scaleFontColor : "#666",

			///Boolean - Whether grid lines are shown across the chart
			scaleShowGridLines : true,

			//String - Colour of the grid lines
			scaleGridLineColor : "rgba(0,0,0,.05)",

			//Number - Width of the grid lines
			scaleGridLineWidth : 1,

			//Boolean - Whether the line is curved between points
			bezierCurve : true,

			//Boolean - Whether to show a dot for each point
			pointDot : false,

			//Number - Radius of each point dot in pixels
			pointDotRadius : 3,

			//Number - Pixel width of point dot stroke
			pointDotStrokeWidth : 1,

			//Boolean - Whether to show a stroke for datasets
			datasetStroke : true,

			//Number - Pixel width of dataset stroke
			datasetStrokeWidth : 2,

			//Boolean - Whether to fill the dataset with a colour
			datasetFill : true,

			//Boolean - Whether to animate the chart
			animation : true,

			//Number - Number of animation steps
			animationSteps : 60,

			//String - Animation easing effect
			animationEasing : "easeOutQuart",

			//Function - Fires when the animation is complete
			onAnimationComplete : null

		}
		// chart
		var ctx = document.getElementById("temperatureChart").getContext("2d");
		appModel = new AppModel();
    	if (appModel.get("existsBigScreen")) {
    		ctx.canvas.width = 640;
			ctx.canvas.height = 480;
    	} else {
    		ctx.canvas.width = 320;
			ctx.canvas.height = 240;
    	}
		var temperatureChart = new Chart(ctx).Line(data,options);
	}
});<|endoftext|>


<commit_before>'use strict';
/* Chat filter plugin
 * By: jd, panpawn
 * Caps/stretching moderating from: https://github.com/Ecuacion/Pokemon-Showdown-Node-Bot/blob/master/features/moderation/index.js
 */

const fs = require('fs');

let adWhitelist = Config.adWhitelist ? Config.adWhitelist : [];
let adRegex = new RegExp("(play.pokemonshowdown.com\\/~~)(?!(" + adWhitelist.join('|') + "))", "g");

let bannedMessages = Config.bannedMessages ? Config.bannedMessages : [];

let watchPhrases = Config.watchPhrases ? Config.watchPhrases : [];
let watchUsers = Config.watchUsers ? Config.watchUsers : [];

let MIN_CAPS_LENGTH = 18;
let MIN_CAPS_PROPORTION = 0.8;
let MAX_STRETCH = 7;
let MAX_REPEAT = 4;

Config.chatfilter = function (message, user, room, connection) {
	user.lastActive = Date.now();

	// caps and stretching
	let capsMatch = message.replace(/[^A-Za-z]/g, '').match(/[A-Z]/g);
	capsMatch = capsMatch && toId(message).length > MIN_CAPS_LENGTH && (capsMatch.length >= Math.floor(toId(message).length * MIN_CAPS_PROPORTION));
	let stretchRegExp = new RegExp('(.)\\1{' + MAX_STRETCH.toString() + ',}', 'g');
	let repeatRegExp = new RegExp('(..+)\\1{' + MAX_REPEAT.toString() + ',}', 'g');
	let stretchMatch = message.toLowerCase().match(stretchRegExp);
	let formatError = capsMatch ? "too many capital letters" : "too much stretching";
	if (capsMatch && stretchMatch) formatError = "too many capital letters and too much streching";
	if (!user.can('mute', null, room) && room && room.id === 'lobby') {
		if (capsMatch || stretchMatch) {
			this.privateModCommand("(" + user.name + "'s message was not sent because it contained: " + formatError + ".  Message: " + message + ")");
			return this.errorReply("Your message was not sent because it contained " + formatError + ".");
			return false;
		}
	}

	// watch phrases and watch users
	let watchWords = watchPhrases.filter(phrase => { return ~toId(message).indexOf(phrase); }).length;
	let watchUserslist = watchUsers.filter(name => { return ~user.userid.indexOf(name); }).length;
	if (watchWords >= 1 || watchUserslist >= 1) {
		Rooms('upperstaff').add('|c|' + user.getIdentity() + '| __(' + (room ? "To " + room.id : "Private message") + ")__ " + message).update();
	}

	// global banned messages
	for (let x in bannedMessages) {
		if (message.toLowerCase().indexOf(bannedMessages[x]) > -1 && bannedMessages[x] !== '' && message.substr(0, 1) !== '/') {
			if (user.locked) return false;
			Punishments.lock(user, Date.now() + 7 * 24 * 60 * 60 * 1000, "Said a banned word: " + bannedMessages[x]);
			user.popup('You have been automatically locked for sending a message containing a banned word.');
			Rooms('staff').add('[PornMonitor] ' + (room ? '(' + room + ') ' : '') + Tools.escapeHTML(user.name) +
			' was automatically locked for trying to say "' + message + '"').update();
			fs.appendFile('logs/modlog/modlog_staff.txt', '[' + (new Date().toJSON()) + '] (staff) ' + user.name + ' was locked from talking by the Server (' +
			bannedMessages[x] + ') (' + connection.ip + ')\n');
			Gold.pmUpperStaff(user.name + ' has been automatically locked for sending a message containing a banned word' +
			(room ? ". **Room:**" + room.id : " in a private message.") + ' **Message:** ' + message, '~Server');
			return false;
		}
	}

	// advertising
	let pre_matches = (message.match(/psim|psim.us|psim us|psm.us|psm us/g) || []).length;
	let final_check = (pre_matches >= 1 ? adWhitelist.filter(server => { return ~message.indexOf(server); }).length : 0);
	if (!user.can('hotpatch') && (pre_matches >= 1 && final_check === 0 || pre_matches >= 2 && final_check >= 1 || message.match(adRegex))) {
		if (user.locked) return false;
		if (!user.advWarns) user.advWarns = 0;
		user.advWarns++;
		if (user.advWarns > 1) {
			Punishments.lock(user, Date.now() + 7 * 24 * 60 * 60 * 1000, "Advertising");
			fs.appendFile('logs/modlog/modlog_staff.txt', '[' + (new Date().toJSON()) + '] (staff) ' + user.name +
				' was locked from talking by the Server. (Advertising) (' + connection.ip + ')\n');
			connection.sendTo(room, '|raw|You have been locked for attempting to advertise.');
			Gold.pmUpperStaff(user.name + " has been locked for attempting to advertise" + (room ? ". **Room:**" + room.id : " in a private message.") + " **Message:** " + message, "~Server");
			Rooms('staff').add('|raw|' + Gold.nameColor(user.name, false) + " has been locked for attempting to advertise" + (room ? ". Room: " + room.id : " in a private message.") + " Message: " + message).update();
			return false;
		}
		Gold.pmUpperStaff(user.name + " has attempted to advertise" + (room ? ". **Room:** " + room.id : " in a private message.") + " **Message:** " + message, "~Server");
		Rooms('staff').add('|raw|' + Gold.nameColor(user.name, false) + " has attempted to advertise" + (room ? ". Room: " + room.id : " in a private message.") + " Message: " + message).update();
		connection.sendTo(room, '|raw|Advertising detected, your message has not been sent and upper staff has been notified.' +
			'
Further attempts to advertise will result in being locked');
		connection.user.popup("Advertising detected, your message has not been sent and upper staff has been notified.\n" +
			"Further attempts to advertise will result in being locked");
		return false;
	}
	return message;
};
<commit_msg>Chatfilter: use Monitor.log and make advertising warn popups |modal|<commit_after>'use strict';
/* Chat filter plugin
 * By: jd, panpawn
 * Caps/stretching moderating from: https://github.com/Ecuacion/Pokemon-Showdown-Node-Bot/blob/master/features/moderation/index.js
 */

const fs = require('fs');

let adWhitelist = Config.adWhitelist ? Config.adWhitelist : [];
let adRegex = new RegExp("(play.pokemonshowdown.com\\/~~)(?!(" + adWhitelist.join('|') + "))", "g");

let bannedMessages = Config.bannedMessages ? Config.bannedMessages : [];

let watchPhrases = Config.watchPhrases ? Config.watchPhrases : [];
let watchUsers = Config.watchUsers ? Config.watchUsers : [];

let MIN_CAPS_LENGTH = 18;
let MIN_CAPS_PROPORTION = 0.8;
let MAX_STRETCH = 7;
let MAX_REPEAT = 4;

Config.chatfilter = function (message, user, room, connection) {
	user.lastActive = Date.now();

	// caps and stretching
	let capsMatch = message.replace(/[^A-Za-z]/g, '').match(/[A-Z]/g);
	capsMatch = capsMatch && toId(message).length > MIN_CAPS_LENGTH && (capsMatch.length >= Math.floor(toId(message).length * MIN_CAPS_PROPORTION));
	let stretchRegExp = new RegExp('(.)\\1{' + MAX_STRETCH.toString() + ',}', 'g');
	let repeatRegExp = new RegExp('(..+)\\1{' + MAX_REPEAT.toString() + ',}', 'g');
	let stretchMatch = message.toLowerCase().match(stretchRegExp);
	let formatError = capsMatch ? "too many capital letters" : "too much stretching";
	if (capsMatch && stretchMatch) formatError = "too many capital letters and too much streching";
	if (!user.can('mute', null, room) && room && room.id === 'lobby') {
		if (capsMatch || stretchMatch) {
			this.privateModCommand("(" + user.name + "'s message was not sent because it contained: " + formatError + ".  Message: " + message + ")");
			return this.errorReply("Your message was not sent because it contained " + formatError + ".");
			return false;
		}
	}

	// watch phrases and watch users
	let watchWords = watchPhrases.filter(phrase => { return ~toId(message).indexOf(phrase); }).length;
	let watchUserslist = watchUsers.filter(name => { return ~user.userid.indexOf(name); }).length;
	if (watchWords >= 1 || watchUserslist >= 1) {
		Rooms('upperstaff').add('|c|' + user.getIdentity() + '| __(' + (room ? "To " + room.id : "Private message") + ")__ " + message).update();
	}

	// global banned messages
	for (let x in bannedMessages) {
		if (message.toLowerCase().indexOf(bannedMessages[x]) > -1 && bannedMessages[x] !== '' && message.substr(0, 1) !== '/') {
			if (user.locked) return false;
			Punishments.lock(user, Date.now() + 7 * 24 * 60 * 60 * 1000, "Said a banned word: " + bannedMessages[x]);
			user.popup('You have been automatically locked for sending a message containing a banned word.');
			Monitor.log('[PornMonitor] ' + (room ? '(' + room + ') ' : '') + Tools.escapeHTML(user.name) + ' was automatically locked for trying to say "' + message + '"');
			fs.appendFile('logs/modlog/modlog_staff.txt', '[' + (new Date().toJSON()) + '] (staff) ' + user.name + ' was locked from talking by the Server (' +
			bannedMessages[x] + ') (' + connection.ip + ')\n');
			Gold.pmUpperStaff(user.name + ' has been automatically locked for sending a message containing a banned word' +
			(room ? ". **Room:**" + room.id : " in a private message.") + ' **Message:** ' + message, '~Server');
			return false;
		}
	}

	// advertising
	let pre_matches = (message.match(/psim|psim.us|psim us|psm.us|psm us/g) || []).length;
	let final_check = (pre_matches >= 1 ? adWhitelist.filter(server => { return ~message.indexOf(server); }).length : 0);
	if (!user.can('hotpatch') && (pre_matches >= 1 && final_check === 0 || pre_matches >= 2 && final_check >= 1 || message.match(adRegex))) {
		if (user.locked) return false;
		if (!user.advWarns) user.advWarns = 0;
		user.advWarns++;
		if (user.advWarns > 1) {
			Punishments.lock(user, Date.now() + 7 * 24 * 60 * 60 * 1000, "Advertising");
			fs.appendFile('logs/modlog/modlog_staff.txt', '[' + (new Date().toJSON()) + '] (staff) ' + user.name + ' was locked from talking by the Server. (Advertising) (' + connection.ip + ')\n');
			connection.sendTo(room, '|raw|You have been locked for attempting to advertise.');
			Gold.pmUpperStaff(user.name + " has been locked for attempting to advertise" + (room ? ". **Room:**" + room.id : " in a private message.") + " **Message:** " + message, "~Server");
			Monitor.log(Tools.escapeHTML(user.name) + " has been locked for attempting to advertise" + (room ? ". **Room:** " + room.id : " in a private message.") + " **Message:** " + Tools.escapeHTML(message));
			return false;
		}
		Gold.pmUpperStaff(user.name + " has attempted to advertise" + (room ? ". **Room:** " + room.id : " in a private message.") + " **Message:** " + message, "~Server");
		Monitor.log(Tools.escapeHTML(user.name) + " has attempted to advertise" + (room ? ". **Room:** " + room.id : " in a private message.") + " **Message:** " + Tools.escapeHTML(message));
		connection.sendTo(room, '|raw|Advertising detected, your message has not been sent and upper staff has been notified.' + '
Further attempts to advertise will result in being locked');
		connection.user.popup("|modal|Advertising detected, your message has not been sent and upper staff has been notified.\n" + "Further attempts to advertise will result in being locked");
		return false;
	}
	return message;
};
<|endoftext|>


<commit_before>"use babel";

describe("The Clang Provider for AtomLinter", () => {

  beforeEach(() => {
    provider = require("../lib/main").provideLinter();

    waitsForPromise(() => {
      return atom.packages.activatePackage("linter-clang")
    });
  });

  describe("finds issue with the code", () => {
    describe("in 'missing_import.c'", () => {
      waitsForPromise(() => {
        return atom.workspace.open("./files/missing_import.c").then((editor) => {
          console.log(editor);
        });
      });
    });

    describe("in 'missing_import.cpp'", () => {
      beforeEach(() => {
        waitsForPromise(() => {
          atom.workspace.open("missing_import.cpp");
        });
      });
    });

    describe("in 'missing_import.m'", () => {
      beforeEach(() => {
        waitsForPromise(() => {
          atom.workspace.open("missing_import.m");
        });
      });
    });

    describe("in 'missing_import.mm'", () => {
      beforeEach(() => {
        waitsForPromise(() => {
          atom.workspace.open("missing_import.mm");
        });
      });
    });
  });
});
<commit_msg>while I pondered weak and weary<commit_after>"use babel";

describe("The Clang Provider for AtomLinter", () => {

  beforeEach(() => {
    provider = require("../lib/main").provideLinter();

    waitsForPromise(() => {
      return atom.packages.activatePackage("linter-clang")
    });
  });

  describe("finds issue with the code", () => {
    describe("in 'missing_import.c'", () => {
      waitsForPromise(() => {
        return atom.workspace.open("./files/missing_import.c").then((editor) => {
          console.log(editor);
        });
      });
    });
  });
});
<|endoftext|>


<commit_before>import { parserUtils } from '../src/parser/parser_utils.js';

describe('ParserUtils', function() {
  describe('splitVAST', function() {
    it('should parse normally defined vast pods', () => {
      const input = [
        { id: 2, sequence: 1 },
        { id: 3, sequence: 2 },
        { id: 4 },
        { id: 5, sequence: 1 },
        { id: 6, sequence: 2 },
        { id: 7, sequence: 3 },
        { id: 8, sequence: 1 },
        { id: 9, sequence: 2 },
        { id: 10 },
        { id: 11, sequence: 1 },
        { id: 12 }
      ];

      const expectedOutput = [
        [{ id: 2, sequence: 1 }, { id: 3, sequence: 2 }],
        [{ id: 4 }],
        [
          { id: 5, sequence: 1 },
          { id: 6, sequence: 2 },
          { id: 7, sequence: 3 }
        ],
        [{ id: 8, sequence: 1 }, { id: 9, sequence: 2 }],
        [{ id: 10 }],
        [{ id: 11, sequence: 1 }],
        [{ id: 12 }]
      ];

      const output = parserUtils.splitVAST(input);

      expect(output).toEqual(expectedOutput);
    });

    it('should parse vast pods with single sequence', () => {
      const input = [
        { id: 1, sequence: 1 },
        { id: 2, sequence: 1 },
        { id: 3, sequence: 1 }
      ];

      const expectedOutput = [
        [{ id: 1, sequence: 1 }],
        [{ id: 2, sequence: 1 }],
        [{ id: 3, sequence: 1 }]
      ];

      const output = parserUtils.splitVAST(input);

      expect(output).toEqual(expectedOutput);
    });

    it('should parse vast pods with no pods', () => {
      const input = [{ id: 1 }, { id: 2 }, { id: 3 }];

      const expectedOutput = [[{ id: 1 }], [{ id: 2 }], [{ id: 3 }]];

      const output = parserUtils.splitVAST(input);

      expect(output).toEqual(expectedOutput);
    });

    it('should parse vast pods with weird sequences', () => {
      const input = [
        { id: 1, sequence: 99 },
        { id: 2, sequence: 99 },
        { id: 3, sequence: 99 }
      ];

      const expectedOutput = [[{ id: 1 }], [{ id: 2 }], [{ id: 3 }]];

      const output = parserUtils.splitVAST(input);

      expect(output).toEqual(expectedOutput);
    });

    it('should parse vast pods with sequences that not start with index = 1', () => {
      const input = [
        { id: 1, sequence: 2 },
        { id: 4 },
        { id: 98, sequence: 3 },
        { id: 99, sequence: 4 },
        { id: 5, sequence: 1 },
        { id: 6, sequence: 2 },
        { id: 7, sequence: 3 },
        { id: 8, sequence: 1 },
        { id: 9, sequence: 2 },
        { id: 10 },
        { id: 11, sequence: 1 },
        { id: 12 }
      ];

      const expectedOutput = [
        [{ id: 1 }],
        [{ id: 4 }],
        [{ id: 98 }],
        [{ id: 99 }],
        [
          { id: 5, sequence: 1 },
          { id: 6, sequence: 2 },
          { id: 7, sequence: 3 }
        ],
        [{ id: 8, sequence: 1 }, { id: 9, sequence: 2 }],
        [{ id: 10 }],
        [{ id: 11, sequence: 1 }],
        [{ id: 12 }]
      ];

      const output = parserUtils.splitVAST(input);

      expect(output).toEqual(expectedOutput);
    });

    it('should parse vast pods with sequences that not start with index = 1, and not following incrementally', () => {
      const input = [
        { id: 1, sequence: 2 },
        { id: 2, sequence: 4 },
        { id: 4 },
        { id: 98, sequence: 3 },
        { id: 99, sequence: 4 },
        { id: 100, sequence: 17 },
        { id: 101, sequence: 18 },
        { id: 5, sequence: 1 },
        { id: 6, sequence: 2 },
        { id: 7, sequence: 3 },
        { id: 8, sequence: 1 },
        { id: 9, sequence: 2 },
        { id: 10 },
        { id: 11, sequence: 1 },
        { id: 12 }
      ];

      const expectedOutput = [
        [{ id: 1 }],
        [{ id: 2 }],
        [{ id: 4 }],
        [{ id: 98 }],
        [{ id: 99 }],
        [{ id: 100 }],
        [{ id: 101 }],
        [
          { id: 5, sequence: 1 },
          { id: 6, sequence: 2 },
          { id: 7, sequence: 3 }
        ],
        [{ id: 8, sequence: 1 }, { id: 9, sequence: 2 }],
        [{ id: 10 }],
        [{ id: 11, sequence: 1 }],
        [{ id: 12 }]
      ];

      const output = parserUtils.splitVAST(input);

      expect(output).toEqual(expectedOutput);
    });
  });

  describe('parseDuration', function() {
    [
      null,
      undefined,
      -1,
      0,
      1,
      '1',
      '00:00',
      '00:00:00:00',
      'test',
      '00:test:01',
      '00:00:01.001',
      '00:00:01.test'
    ].map(item =>
      it(`should not return NaN for \`${item}\``, function() {
        expect(parserUtils.parseDuration(item)).not.toBeNaN();
      })
    );
  });

  describe('parseAttributes', function() {
    document.body.innerHTML = `<foo id='1234' width='400' height='250'></foo>`;
    const element = document.getElementById('1234');

    it('parses attributes correctly', function() {
      expect(parserUtils.parseAttributes(element)).toEqual({
        id: '1234',
        width: '400',
        height: '250'
      });
    });

    afterAll(function() {
      document.body.innerHTML = '';
    });
  });
});
<commit_msg>[test] add missing tests for mergeWrapperAdData<commit_after>import { parserUtils } from '../src/parser/parser_utils.js';

describe('ParserUtils', function() {
  describe('splitVAST', function() {
    it('should parse normally defined vast pods', () => {
      const input = [
        { id: 2, sequence: 1 },
        { id: 3, sequence: 2 },
        { id: 4 },
        { id: 5, sequence: 1 },
        { id: 6, sequence: 2 },
        { id: 7, sequence: 3 },
        { id: 8, sequence: 1 },
        { id: 9, sequence: 2 },
        { id: 10 },
        { id: 11, sequence: 1 },
        { id: 12 }
      ];

      const expectedOutput = [
        [{ id: 2, sequence: 1 }, { id: 3, sequence: 2 }],
        [{ id: 4 }],
        [
          { id: 5, sequence: 1 },
          { id: 6, sequence: 2 },
          { id: 7, sequence: 3 }
        ],
        [{ id: 8, sequence: 1 }, { id: 9, sequence: 2 }],
        [{ id: 10 }],
        [{ id: 11, sequence: 1 }],
        [{ id: 12 }]
      ];

      const output = parserUtils.splitVAST(input);

      expect(output).toEqual(expectedOutput);
    });

    it('should parse vast pods with single sequence', () => {
      const input = [
        { id: 1, sequence: 1 },
        { id: 2, sequence: 1 },
        { id: 3, sequence: 1 }
      ];

      const expectedOutput = [
        [{ id: 1, sequence: 1 }],
        [{ id: 2, sequence: 1 }],
        [{ id: 3, sequence: 1 }]
      ];

      const output = parserUtils.splitVAST(input);

      expect(output).toEqual(expectedOutput);
    });

    it('should parse vast pods with no pods', () => {
      const input = [{ id: 1 }, { id: 2 }, { id: 3 }];

      const expectedOutput = [[{ id: 1 }], [{ id: 2 }], [{ id: 3 }]];

      const output = parserUtils.splitVAST(input);

      expect(output).toEqual(expectedOutput);
    });

    it('should parse vast pods with weird sequences', () => {
      const input = [
        { id: 1, sequence: 99 },
        { id: 2, sequence: 99 },
        { id: 3, sequence: 99 }
      ];

      const expectedOutput = [[{ id: 1 }], [{ id: 2 }], [{ id: 3 }]];

      const output = parserUtils.splitVAST(input);

      expect(output).toEqual(expectedOutput);
    });

    it('should parse vast pods with sequences that not start with index = 1', () => {
      const input = [
        { id: 1, sequence: 2 },
        { id: 4 },
        { id: 98, sequence: 3 },
        { id: 99, sequence: 4 },
        { id: 5, sequence: 1 },
        { id: 6, sequence: 2 },
        { id: 7, sequence: 3 },
        { id: 8, sequence: 1 },
        { id: 9, sequence: 2 },
        { id: 10 },
        { id: 11, sequence: 1 },
        { id: 12 }
      ];

      const expectedOutput = [
        [{ id: 1 }],
        [{ id: 4 }],
        [{ id: 98 }],
        [{ id: 99 }],
        [
          { id: 5, sequence: 1 },
          { id: 6, sequence: 2 },
          { id: 7, sequence: 3 }
        ],
        [{ id: 8, sequence: 1 }, { id: 9, sequence: 2 }],
        [{ id: 10 }],
        [{ id: 11, sequence: 1 }],
        [{ id: 12 }]
      ];

      const output = parserUtils.splitVAST(input);

      expect(output).toEqual(expectedOutput);
    });

    it('should parse vast pods with sequences that not start with index = 1, and not following incrementally', () => {
      const input = [
        { id: 1, sequence: 2 },
        { id: 2, sequence: 4 },
        { id: 4 },
        { id: 98, sequence: 3 },
        { id: 99, sequence: 4 },
        { id: 100, sequence: 17 },
        { id: 101, sequence: 18 },
        { id: 5, sequence: 1 },
        { id: 6, sequence: 2 },
        { id: 7, sequence: 3 },
        { id: 8, sequence: 1 },
        { id: 9, sequence: 2 },
        { id: 10 },
        { id: 11, sequence: 1 },
        { id: 12 }
      ];

      const expectedOutput = [
        [{ id: 1 }],
        [{ id: 2 }],
        [{ id: 4 }],
        [{ id: 98 }],
        [{ id: 99 }],
        [{ id: 100 }],
        [{ id: 101 }],
        [
          { id: 5, sequence: 1 },
          { id: 6, sequence: 2 },
          { id: 7, sequence: 3 }
        ],
        [{ id: 8, sequence: 1 }, { id: 9, sequence: 2 }],
        [{ id: 10 }],
        [{ id: 11, sequence: 1 }],
        [{ id: 12 }]
      ];

      const output = parserUtils.splitVAST(input);

      expect(output).toEqual(expectedOutput);
    });
  });

  describe('parseDuration', function() {
    [
      null,
      undefined,
      -1,
      0,
      1,
      '1',
      '00:00',
      '00:00:00:00',
      'test',
      '00:test:01',
      '00:00:01.001',
      '00:00:01.test'
    ].map(item =>
      it(`should not return NaN for \`${item}\``, function() {
        expect(parserUtils.parseDuration(item)).not.toBeNaN();
      })
    );
  });

  describe('parseAttributes', function() {
    document.body.innerHTML = `<foo id='1234' width='400' height='250'></foo>`;
    const element = document.getElementById('1234');

    it('parses attributes correctly', function() {
      expect(parserUtils.parseAttributes(element)).toEqual({
        id: '1234',
        width: '400',
        height: '250'
      });
    });

    afterAll(function() {
      document.body.innerHTML = '';
    });
  });

  describe('mergeWrapperAdData', function() {
    let unwrappedAd = {
      id: null,
      sequence: 1,
      title: null,
      description: null,
      advertiser: null,
      pricing: null,
      survey: null,
      errorURLTemplates: ['unwrappedAd1', 'unwrappedAd2'],
      impressionURLTemplates: [
        {
          id: 'unwrappedAd',
          url: 'https://unwrappedAd.com'
        }
      ],
      creatives: [
        {
          type: 'companion',
          trackingEvents: {
            creativeView: [
              'http://example.com/companion1-unwrappedAd-creativeview'
            ]
          },
          variations: [
            {
              adType: 'companionAd',
              companionClickTrackingURLTemplates: [
                {
                  id: '1',
                  url:
                    'http://example.com/companion1-unwrappedAd-clicktracking-first'
                },
                {
                  id: '2',
                  url:
                    'http://example.com/companion1-unwrappedAd-clicktracking-second'
                }
              ]
            },
            {
              type: 'linear',
              videoClickThroughURLTemplate: null,
              videoClickTrackingURLTemplates: [
                {
                  id: 'video-click-1',
                  url:
                    'http://example.com/linear-clicktracking1_ts:[TIMESTAMP]_adplayhead:[ADPLAYHEAD]'
                }
              ],
              videoCustomClickURLTemplates: [
                {
                  id: 'custom-click-1',
                  url: 'http://example.com/linear-customclick'
                }
              ]
            }
          ]
        }
      ],
      extensions: [
        {
          name: 'Extension',
          value: null,
          attributes: {
            type: 'geo'
          },
          children: [
            {
              name: 'Country',
              value: 'FR',
              attributes: {},
              children: []
            },
            {
              name: 'Bandwidth',
              value: '5',
              attributes: {},
              children: []
            }
          ]
        }
      ],
      adVerifications: ['unwrappedAd'],
      blockedAdCategories: ['unwrappedAd'],
      trackingEvents: { nonlinear: [], linear: [] },
      videoClickTrackingURLTemplates: [],
      videoCustomClickURLTemplates: [],
      followAdditionalWrappers: null,
      allowMultipleAds: null,
      fallbackOnNoAd: null
    };
    let wrapper = {
      id: null,
      sequence: 2,
      title: null,
      description: null,
      advertiser: null,
      pricing: null,
      survey: null,
      errorURLTemplates: ['wrapper1', 'wrapper2'],
      impressionURLTemplates: [
        {
          id: 'wrapper1',
          url: 'https://wrapper1.com'
        },
        {
          id: 'wrapper2',
          url: 'https://wrapper2.com'
        }
      ],
      creatives: [
        {
          type: 'companion',
          trackingEvents: {
            creativeView: ['http://example.com/companion1-wrapper-creativeview']
          },
          variations: [
            {
              companionClickTrackingURLTemplates: [
                {
                  id: '1',
                  url:
                    'http://example.com/companion1-wrapper-clicktracking-first'
                },
                {
                  id: '2',
                  url:
                    'http://example.com/companion1-wrapper-clicktracking-second'
                }
              ]
            }
          ]
        },
        {
          type: 'linear',
          videoClickThroughURLTemplate: {
            id: 'click-through',
            url:
              'http://example.com/linear-clickthrough_adplayhead:[ADPLAYHEAD]'
          },
          videoClickTrackingURLTemplates: [
            {
              id: 'video-click-1',
              url:
                'http://example.com/linear-clicktracking1_ts:[TIMESTAMP]_adplayhead:[ADPLAYHEAD]'
            },
            {
              id: 'video-click-2',
              url: 'http://example.com/linear-clicktracking2'
            }
          ],
          videoCustomClickURLTemplates: [
            {
              id: 'custom-click-1',
              url: 'http://example.com/linear-customclick'
            }
          ]
        }
      ],
      extensions: [
        {
          name: 'Extension',
          value: null,
          attributes: {
            type: 'geo'
          },
          children: [
            {
              name: 'Country',
              value: 'US',
              attributes: {},
              children: []
            },
            {
              name: 'Bandwidth',
              value: '4',
              attributes: {},
              children: []
            }
          ]
        }
      ],
      adVerifications: ['wrapper1', 'wrapper2'],
      blockedAdCategories: ['wrapper1', 'wrapper2'],
      trackingEvents: { nonlinear: [], linear: [] },
      videoClickTrackingURLTemplates: [],
      videoCustomClickURLTemplates: [],
      followAdditionalWrappers: true,
      allowMultipleAds: true,
      fallbackOnNoAd: true
    };

    beforeAll(() => {
      return parserUtils.mergeWrapperAdData(unwrappedAd, wrapper);
    });

    it('merge the wrapper and unwrappedAd errorURLTemplates together', function() {
      expect(unwrappedAd.errorURLTemplates).toEqual([
        'wrapper1',
        'wrapper2',
        'unwrappedAd1',
        'unwrappedAd2'
      ]);
    });

    it('merge the wrapper and unwrappedAd impressionURLTemplates together', function() {
      expect(unwrappedAd.impressionURLTemplates).toEqual([
        {
          id: 'wrapper1',
          url: 'https://wrapper1.com'
        },
        {
          id: 'wrapper2',
          url: 'https://wrapper2.com'
        },
        {
          id: 'unwrappedAd',
          url: 'https://unwrappedAd.com'
        }
      ]);
    });

    it('merge the wrapper and unwrappedAd extensions together', function() {
      expect(unwrappedAd.extensions).toEqual([
        {
          attributes: {
            type: 'geo'
          },
          children: [
            {
              attributes: {},
              children: [],
              name: 'Country',
              value: 'US'
            },
            {
              attributes: {},
              children: [],
              name: 'Bandwidth',
              value: '4'
            }
          ],
          name: 'Extension',
          value: null
        },
        {
          attributes: {
            type: 'geo'
          },
          children: [
            {
              attributes: {},
              children: [],
              name: 'Country',
              value: 'FR'
            },
            {
              attributes: {},
              children: [],
              name: 'Bandwidth',
              value: '5'
            }
          ],
          name: 'Extension',
          value: null
        }
      ]);
    });

    it('override unwrapped followAdditionalWrappers with wrapper one', function() {
      expect(unwrappedAd.followAdditionalWrappers).toEqual(
        wrapper.followAdditionalWrappers
      );
    });

    it('override unwrapped allowMultipleAds with wrapper one', function() {
      expect(unwrappedAd.allowMultipleAds).toEqual(wrapper.allowMultipleAds);
    });

    it('override unwrapped fallbackOnNoAd with wrapper one', function() {
      expect(unwrappedAd.fallbackOnNoAd).toEqual(wrapper.fallbackOnNoAd);
    });

    it('merge the wrapper and unwrappedAd creatives together', function() {
      expect(unwrappedAd.creatives).toEqual([
        {
          trackingEvents: {
            creativeView: ['http://example.com/companion1-wrapper-creativeview']
          },
          type: 'companion',
          variations: [
            {
              companionClickTrackingURLTemplates: [
                {
                  id: '1',
                  url:
                    'http://example.com/companion1-wrapper-clicktracking-first'
                },
                {
                  id: '2',
                  url:
                    'http://example.com/companion1-wrapper-clicktracking-second'
                }
              ]
            }
          ]
        },
        {
          trackingEvents: {
            creativeView: [
              'http://example.com/companion1-unwrappedAd-creativeview'
            ]
          },
          type: 'companion',
          variations: [
            {
              adType: 'companionAd',
              companionClickTrackingURLTemplates: [
                {
                  id: '1',
                  url:
                    'http://example.com/companion1-unwrappedAd-clicktracking-first'
                },
                {
                  id: '2',
                  url:
                    'http://example.com/companion1-unwrappedAd-clicktracking-second'
                },
                {
                  id: '1',
                  url:
                    'http://example.com/companion1-wrapper-clicktracking-first'
                },
                {
                  id: '2',
                  url:
                    'http://example.com/companion1-wrapper-clicktracking-second'
                }
              ]
            },
            {
              companionClickTrackingURLTemplates: [
                {
                  id: '1',
                  url:
                    'http://example.com/companion1-wrapper-clicktracking-first'
                },
                {
                  id: '2',
                  url:
                    'http://example.com/companion1-wrapper-clicktracking-second'
                }
              ],
              type: 'linear',
              videoClickThroughURLTemplate: null,
              videoClickTrackingURLTemplates: [
                {
                  id: 'video-click-1',
                  url:
                    'http://example.com/linear-clicktracking1_ts:[TIMESTAMP]_adplayhead:[ADPLAYHEAD]'
                }
              ],
              videoCustomClickURLTemplates: [
                {
                  id: 'custom-click-1',
                  url: 'http://example.com/linear-customclick'
                }
              ]
            }
          ]
        }
      ]);
    });
  });
});
<|endoftext|>


<commit_before>describe('dc.seriesChart', function() {

    var chart;
    var jsonColorData = JSON.parse("[" +
        "{\"colData\":\"1\", \"rowData\": \"1\", \"colorData\": \"1\"}," +
        "{\"colData\":\"1\", \"rowData\": \"2\", \"colorData\": \"2\"}," +
        "{\"colData\":\"2\", \"rowData\": \"1\", \"colorData\": \"3\"}," +
        "{\"colData\":\"2\", \"rowData\": \"2\", \"colorData\": \"4\"}" +
        "]");
    var colorData = crossfilter(jsonColorData);

    beforeEach(function() {
        var dimensionColorData = colorData.dimension(function (d) { return [+d.colData, +d.rowData]; });
        var groupColorData = dimensionColorData.group().reduceSum(function(d) { return +d.colorData; });

        id = "series-chart";
        appendChartID(id);
        chart = dc.seriesChart("#" + id);

        chart
            .width(210)
            .height(210)
            .x(d3.scale.linear().domain([1,2]))
            .dimension(dimensionColorData)
            .group(groupColorData)
            .ordinalColors(["#000001", "#000002"])
            .seriesAccessor(function(d) { return +d.key[0];})
            .keyAccessor(function(d) { return +d.key[1];})
            .valueAccessor(function(d) { return +d.value ;})
            .childOptions({renderArea: true, dashStyle: [3, 1, 1]})
            .transitionDuration(0);
    });

    describe('#render', function() {
        beforeEach(function() {
            chart.render();
        });

        it('should create the svg', function() {
            expect(chart.svg()).not.toBeNull();
        });

        it('should position generated lineCharts using the data', function() {
            var lines = chart.selectAll("path.line");

            expect(d3.select(lines[0][0]).attr("d")).toMatchPath("M0,128L130,85");
            expect(d3.select(lines[0][1]).attr("d")).toMatchPath("M0,43L130,0");
        });

        it('should color lines using the colors in the data', function() {
            var lines = chart.selectAll("path.line");

            expect(d3.select(lines[0][0]).attr("stroke")).toBe("#000001");
            expect(d3.select(lines[0][1]).attr("stroke")).toBe("#000002");
        });

        describe('with brush off', function () {
          it('should create line chart dots', function () {
            chart.brushOn(false).render();
            var dots = chart.selectAll('circle.dot');
            expect(dots[0].length).toEqual(4);
            chart.brushOn(true);
          });
        });
    });

    describe('series sorting', function() {
        beforeEach(function() {
            chart
                .seriesSort(d3.descending)
                .render();
        });

        it('should order lineCharts in the order specified', function() {
            var lines = chart.selectAll("path.line");

            expect(d3.select(lines[0][1]).attr("d")).toMatchPath("M0,128L130,85");
            expect(d3.select(lines[0][0]).attr("d")).toMatchPath("M0,43L130,0");
        });
    });

    describe('chart options', function () {
        beforeEach(function() {
            chart.render();
        });

        it('should apply options to all lines in the chart', function () {
            var lines = chart.selectAll("path.line");
            var areas = chart.selectAll("path.area");

            expect(d3.select(lines[0][0]).attr("stroke-dasharray")).toBe("3,1,1");
            expect(d3.select(lines[0][1]).attr("stroke-dasharray")).toBe("3,1,1");

            expect(d3.select(areas[0][0]).attr("fill")).toBe("#000001");
            expect(d3.select(areas[0][1]).attr("fill")).toBe("#000002");
        });
    });

    describe('#redraw', function () {
      var jsonData = JSON.parse("[" +
        "{\"colData\":\"1\", \"rowData\": \"1\", \"colorData\": \"1\"}," +
        "{\"colData\":\"1\", \"rowData\": \"2\", \"colorData\": \"2\"}," +
        "{\"colData\":\"2\", \"rowData\": \"1\", \"colorData\": \"3\"}," +
        "{\"colData\":\"2\", \"rowData\": \"2\", \"colorData\": \"4\"}," +
        "{\"colData\":\"3\", \"rowData\": \"1\", \"colorData\": \"5\"}," +
        "{\"colData\":\"3\", \"rowData\": \"2\", \"colorData\": \"6\"}" +
      "]");
      var data = crossfilter(jsonData);
      beforeEach(function () {
        chart.brushOn(false);
        chart.render();

        var dimensionData = data.dimension(function (d) { return [+d.colData, +d.rowData]; });
        var groupData = dimensionData.group().reduceSum(function(d) { return +d.colorData; });

        chart.dimension(dimensionData).group(groupData);

        chart.redraw();
      });

      afterEach(function () {
        chart.brushOn(true);
      });

      it ('is redrawn with dots', function () {
        var dots = chart.selectAll('circle.dot');
        expect(dots[0].length).toEqual(6);
      });
    });
});
<commit_msg>nix fugly json; fix indentation<commit_after>describe('dc.seriesChart', function() {

    var chart;
    var colorRows = [
        {colData:1, rowData: 1, colorData: 1},
        {colData:1, rowData: 2, colorData: 2},
        {colData:2, rowData: 1, colorData: 3},
        {colData:2, rowData: 2, colorData: 4}
    ];
    var colorData = crossfilter(colorRows);

    beforeEach(function() {
        var dimensionColorData = colorData.dimension(function (d) { return [+d.colData, +d.rowData]; });
        var groupColorData = dimensionColorData.group().reduceSum(function(d) { return +d.colorData; });

        id = "series-chart";
        appendChartID(id);
        chart = dc.seriesChart("#" + id);

        chart
            .width(210)
            .height(210)
            .x(d3.scale.linear().domain([1,2]))
            .dimension(dimensionColorData)
            .group(groupColorData)
            .ordinalColors(["#000001", "#000002"])
            .seriesAccessor(function(d) { return +d.key[0];})
            .keyAccessor(function(d) { return +d.key[1];})
            .valueAccessor(function(d) { return +d.value ;})
            .childOptions({renderArea: true, dashStyle: [3, 1, 1]})
            .transitionDuration(0);
    });

    describe('#render', function() {
        beforeEach(function() {
            chart.render();
        });

        it('should create the svg', function() {
            expect(chart.svg()).not.toBeNull();
        });

        it('should position generated lineCharts using the data', function() {
            var lines = chart.selectAll("path.line");

            expect(d3.select(lines[0][0]).attr("d")).toMatchPath("M0,128L130,85");
            expect(d3.select(lines[0][1]).attr("d")).toMatchPath("M0,43L130,0");
        });

        it('should color lines using the colors in the data', function() {
            var lines = chart.selectAll("path.line");

            expect(d3.select(lines[0][0]).attr("stroke")).toBe("#000001");
            expect(d3.select(lines[0][1]).attr("stroke")).toBe("#000002");
        });

        describe('with brush off', function () {
            it('should create line chart dots', function () {
                chart.brushOn(false).render();
                var dots = chart.selectAll('circle.dot');
                expect(dots[0].length).toEqual(4);
                chart.brushOn(true);
            });
        });
    });

    describe('series sorting', function() {
        beforeEach(function() {
            chart
                .seriesSort(d3.descending)
                .render();
        });

        it('should order lineCharts in the order specified', function() {
            var lines = chart.selectAll("path.line");

            expect(d3.select(lines[0][1]).attr("d")).toMatchPath("M0,128L130,85");
            expect(d3.select(lines[0][0]).attr("d")).toMatchPath("M0,43L130,0");
        });
    });

    describe('chart options', function () {
        beforeEach(function() {
            chart.render();
        });

        it('should apply options to all lines in the chart', function () {
            var lines = chart.selectAll("path.line");
            var areas = chart.selectAll("path.area");

            expect(d3.select(lines[0][0]).attr("stroke-dasharray")).toBe("3,1,1");
            expect(d3.select(lines[0][1]).attr("stroke-dasharray")).toBe("3,1,1");

            expect(d3.select(areas[0][0]).attr("fill")).toBe("#000001");
            expect(d3.select(areas[0][1]).attr("fill")).toBe("#000002");
        });
    });

    describe('#redraw', function () {
        var colorRows2 = [
            {colData:1, rowData: 1, colorData: 1},
            {colData:1, rowData: 2, colorData: 2},
            {colData:2, rowData: 1, colorData: 3},
            {colData:2, rowData: 2, colorData: 4},
            {colData:3, rowData: 1, colorData: 5},
            {colData:3, rowData: 2, colorData: 6}
        ];
        var colorData2 = crossfilter(colorRows2);
        beforeEach(function () {
            chart.brushOn(false);
            chart.render();

            var dimensionData = colorData2.dimension(function (d) { return [+d.colData, +d.rowData]; });
            var groupData = dimensionData.group().reduceSum(function(d) { return +d.colorData; });

            chart.dimension(dimensionData).group(groupData);

            chart.redraw();
        });

        afterEach(function () {
            chart.brushOn(true);
        });

        it ('is redrawn with dots', function () {
            var dots = chart.selectAll('circle.dot');
            expect(dots[0].length).toEqual(6);
        });
    });
});
<|endoftext|>