<commit_before>/*global declare, it, describe, beforeEach, afterEach*/
import { expect } from "mocha-es6";
import { promise } from "lively.lang";
import { pt, Color } from "lively.graphics";
import { createDOMEnvironment } from "../rendering/dom-helper.js";
import { EventDispatcher } from "../events.js";
import { morph, World, Renderer, show } from "../index.js";

function wait(n) {
  return n ? promise.delay(n*1000) : Promise.resolve();
}

var domEnv;

function installEventLogger(morph, log) {
  var loggedEvents = [
    "onMouseDown","onMouseUp","onMouseMove",
    "onDragStart", "onDrag", "onDragEnd",
    "onGrab", "onDrop",
    "onHoverIn", "onHoverOut",
    "onFocus", "onBlur",
    "onKeyDown", "onKeyUp"]
  loggedEvents.forEach(name => {
    morph[name] = function(evt) {
      log.push(name + "-" + morph.name);
      this.constructor.prototype[name].call(this, evt);
    }
  });
}

var world, submorph1, submorph2, submorph3, submorph4,
    eventLog, renderer, eventDispatcher;

async function setup() {
  domEnv = await createDOMEnvironment();
  world = new World({name: "world", extent: pt(300,300)})
  renderer = new Renderer(world, domEnv.document.body, domEnv);
  renderer.startRenderWorldLoop();
  eventDispatcher = new EventDispatcher(domEnv.window, world).install();

  world.submorphs = [{
      name: "submorph1", extent: pt(100,100), position: pt(10,10), fill: Color.red,
      submorphs: [{name: "submorph2", extent: pt(20,20), position: pt(5,10), fill: Color.green}]
    },
    {name: "submorph3", extent: pt(50,50), position: pt(200,20), fill: Color.yellow},
    {name: "submorph4", type: "text", extent: pt(50,50), position: pt(200,200), fill: Color.blue, textString: "old text"}];

  submorph1 = world.submorphs[0];
  submorph2 = world.submorphs[0].submorphs[0];
  submorph3 = world.submorphs[1];
  submorph4 = world.submorphs[2];

  eventLog = [];
  [world,submorph1,submorph2,submorph3,submorph4].forEach(ea => installEventLogger(ea, eventLog));

  await world.whenRendered();
}

function teardown() {
  eventDispatcher && eventDispatcher.uninstall();
  renderer && renderer.clear();
  domEnv && domEnv.destroy();
}

function assertEventLogContains(stuff) {
  expect(stuff).equals(eventLog)
  eventLog.length = 0;
}

describe("events", function() {

  // jsdom sometimes takes its time to initialize...
  if (System.get("@system-env").node)
    this.timeout(10000);

  beforeEach(async () => setup());
  afterEach(() => teardown());

  it("mousedown on submorph", () => {
    eventDispatcher.simulateDOMEvents({type: "pointerdown", target: submorph2});
    assertEventLogContains(["onMouseDown-world", "onMouseDown-submorph1", "onMouseDown-submorph2"]);
  });

  it("stop event", () => {
    submorph1.onMouseDown = function(evt) {
      evt.stop();
      eventLog.push("onMouseDown-submorph1");
    }
    eventDispatcher.simulateDOMEvents({type: "pointerdown", target: submorph2});
    assertEventLogContains(["onMouseDown-world", "onMouseDown-submorph1"]);
  });

  it("world has hand and moves it", () => {
    eventDispatcher.simulateDOMEvents({type: "pointermove", target: submorph2, position: pt(120,130)});
    expect(world.submorphs[0]).property("isHand", true);
  });

  it("drag morph", () => {
    submorph2.grabbable = false;
    eventDispatcher.simulateDOMEvents({type: "pointerdown", target: submorph2, position: pt(20, 25)});
    assertEventLogContains(["onMouseDown-world", "onMouseDown-submorph1", "onMouseDown-submorph2"]);

    eventDispatcher.simulateDOMEvents({type: "pointermove", target: submorph2, position: pt(30, 33)});
    assertEventLogContains(["onMouseMove-world", "onDragStart-submorph2"]);

    eventDispatcher.simulateDOMEvents({type: "pointermove", target: submorph2, position: pt(34, 36)});
    assertEventLogContains(["onMouseMove-world", "onDrag-submorph2"]);

    eventDispatcher.simulateDOMEvents({type: "pointerup", target: submorph2, position: pt(34, 36)});
    assertEventLogContains(["onMouseUp-world", "onDragEnd-submorph2"]);
  });

  it("drag computes drag delta", async () => {
    var m = world.addMorph(morph({extent: pt(50,50), fill: Color.pink, grabbable: false}));
    await m.whenRendered()
    var dragStartEvent, dragEvent, dragEndEvent;
    m.onDragStart = evt => dragStartEvent = evt;
    m.onDrag = evt => dragEvent = evt;
    m.onDragEnd = evt => dragEndEvent = evt;
    eventDispatcher.simulateDOMEvents(
      {type: "pointerdown", position: pt(20, 25)},
      {type: "pointermove", position: pt(20, 25)},
      {type: "pointermove", position: pt(30, 35)},
      {type: "pointermove", position: pt(40, 50)});
    expect(dragEvent.state.dragDelta).equals(pt(10,15))
    eventDispatcher.simulateDOMEvents({type: "pointerup", target: m, position: pt(40, 51)});
    expect(dragEndEvent.state.dragDelta).equals(pt(0, 1))
  });

  it("grab and drop morph", async () => {
    submorph2.grabbable = true;
    var morphPos = submorph2.globalPosition;

    // grab
    eventDispatcher.simulateDOMEvents(
      {type: "pointerdown", target: submorph2, position: morphPos.addXY(5,5)},
      {type: "pointermove", target: submorph2, position: morphPos.addXY(10,10)});
    assertEventLogContains([
      "onMouseDown-world", "onMouseDown-submorph1", "onMouseDown-submorph2",
      "onMouseMove-world", "onGrab-submorph2"]);
    expect(world.hands[0].carriesMorphs()).equals(true);
    var offsetWhenGrabbed = submorph2.position;

    // drop
    eventDispatcher.simulateDOMEvents(
      {type: "pointermove", target: submorph2, position: morphPos.addXY(15,15)},
      {type: "pointermove", target: submorph2, position: morphPos.addXY(20,20)},
      {type: "pointerup", target: world, position: morphPos.addXY(20,20)});
    assertEventLogContains(["onMouseMove-world", "onMouseMove-world", "onMouseUp-world", "onDrop-world"]);
    expect(world.hands[0].carriesMorphs()).equals(false);
    expect(submorph2.owner).equals(world);
    expect(submorph2.position).equals(morphPos.addXY(10,10));
  });

  it("dropped morph has correct position", async () => {
    world.submorphs = [
      {position: pt(10,10), extent: pt(100,100), fill: Color.red,
       rotation: -45,
       origin: pt(50,50)
       },
      {position: pt(60,60), extent: pt(20,20), fill: Color.green, grabbable: true, origin: pt(10,10)
      }];
    var [m1, m2] = world.submorphs;
    var prevGlobalPos = m2.globalPosition;

    world.renderAsRoot(renderer);
    eventDispatcher.simulateDOMEvents(
      {type: "pointerdown", target: m2, position: pt(60,60)},
      {type: "pointermove", target: m2, position: (pt(65,65))});
    expect(m2.globalPosition).equals(prevGlobalPos);
    expect(m2.owner).not.equals(world);
    eventDispatcher.simulateDOMEvents(
      {type: "pointermove", target: m2, position: pt(50,50)});
    expect(m2.globalPosition).equals(pt(45,45));
    eventDispatcher.simulateDOMEvents(
      {type: "pointerup", target: m1, position: (pt(2,2))});
    expect(m2.owner).equals(m1);
    expect(m2.globalPosition).equals(pt(45,45));
  });

  it("text input", () => {
    expect(submorph4).property("textString").equals("old text");
    domEnv.document.getElementById(submorph4.id).value = "new text";
    expect(submorph4).property("textString").equals("old text");
    eventDispatcher.simulateDOMEvents({type: "input", target: submorph4, position: pt(225, 225)});
    expect(submorph4).property("textString").equals("new text");
    domEnv.document.getElementById(submorph4.id).value = "really new text";
    expect(submorph4).property("textString").equals("new text");
    eventDispatcher.simulateDOMEvents({type: "input", target: submorph4, position: pt(225, 225)});
    expect(submorph4).property("textString").equals("really new text");
  });

  describe("hover", () => {

    it("into world", async () => {
      await eventDispatcher.simulateDOMEvents({target: world, type: "pointerover", position: pt(50,50)}).whenIdle();
      assertEventLogContains(["onHoverIn-world"]);
    });

    it("in and out world", async () => {
      await eventDispatcher.simulateDOMEvents(
        {target: world, type: "pointerover", position: pt(50,50)},
        {target: world, type: "pointerout", position: pt(50,50)}).whenIdle();
      assertEventLogContains(["onHoverIn-world", "onHoverOut-world"]);
    });

    it("in and out single morph", async () => {
      await eventDispatcher.simulateDOMEvents(
        {target: submorph3, type: "pointerover", position: pt(50,50)},
        {target: submorph3, type: "pointerout", position: pt(50,50)}).whenIdle();;
      assertEventLogContains(["onHoverIn-world", "onHoverIn-submorph3", "onHoverOut-world","onHoverOut-submorph3"]);
    });

    it("hover in and out with submorph", async () => {
      // simulate the over/out dom events when moving
      // - into submorph1 => into submorph2 (contained in 1) => out of submorph2 => out of submorph1
      await eventDispatcher.simulateDOMEvents({type: "pointerover", target: submorph1, position: pt(10,10)}).whenIdle();

      await eventDispatcher.simulateDOMEvents(
        {type: "pointerout", target: submorph1, position: pt(15,20)},
        {type: "pointerover", target: submorph2, position: pt(15,20)}).whenIdle();

      await eventDispatcher.simulateDOMEvents(
        {type: "pointerout", target: submorph2, position: pt(15,41)},
        {type: "pointerover", target: submorph1, position: pt(15,41)}).whenIdle();

      await eventDispatcher.simulateDOMEvents({type: "pointerout", target: submorph1, position: pt(9,9) }).whenIdle();

      assertEventLogContains([
        "onHoverIn-world", "onHoverIn-submorph1", "onHoverIn-submorph2", "onHoverOut-submorph2", "onHoverOut-world", "onHoverOut-submorph1"]);
    });

    it("hover in and out with submorph sticking out", async () => {

      var tl = submorph1.topLeft;
      submorph2.topRight = pt(submorph1.width + 10, 0);
      await eventDispatcher.simulateDOMEvents({type: "pointerover", target: submorph2, position: pt(109, 10)}).whenIdle();
      await eventDispatcher.simulateDOMEvents({type: "pointerout", target: submorph2, position: pt(111, 10)}).whenIdle();

      assertEventLogContains([
        "onHoverIn-world", "onHoverIn-submorph1", "onHoverIn-submorph2", "onHoverOut-world", "onHoverOut-submorph1", "onHoverOut-submorph2"]);
    });

  });

  describe("key events", () => {

    it("focus + blur", async () => {
      eventDispatcher.simulateDOMEvents(
        {target: submorph1, type: "focus"},
        {target: submorph1, type: "blur"});
      assertEventLogContains(["onFocus-submorph1", "onBlur-submorph1"]);
    });

    it("key down", async () => {
      eventDispatcher.simulateDOMEvents({target: submorph1, type: "keydown", ctrlKey: true, keyCode: 65});
      assertEventLogContains(["onKeyDown-world", "onKeyDown-submorph1"]);
    });

    it("key down keystring", async () => {
      var pressed; submorph1.onKeyDown = evt => pressed = evt.keyString();
      eventDispatcher.simulateDOMEvents({target: submorph1, type: "keydown", ctrlKey: true, keyCode: 65});
      expect(pressed).equals("Control-A")
    });

    it("key up keystring", async () => {
      var pressed; submorph1.onKeyUp = evt => pressed = evt.keyString();
      eventDispatcher.simulateDOMEvents({target: submorph1, type: "keyup", altKey: true, shiftKey: true, keyCode: 88});
      expect(pressed).equals("Alt-Shift-X")
    });

  });

  describe("simulation", () => {

    it("click", async () => {
      await eventDispatcher.simulateDOMEvents({type: "click", position: pt(25,25)});
      assertEventLogContains([
        "onMouseDown-world", "onMouseDown-submorph1", "onMouseDown-submorph2",
        "onMouseUp-world", "onMouseUp-submorph1", "onMouseUp-submorph2"]);
    });

  });

});
<commit_msg>test fix<commit_after>/*global declare, it, describe, beforeEach, afterEach*/
import { expect } from "mocha-es6";
import { promise } from "lively.lang";
import { pt, Color } from "lively.graphics";
import { createDOMEnvironment } from "../rendering/dom-helper.js";
import { EventDispatcher } from "../events.js";
import { morph, World, Renderer, show } from "../index.js";

function wait(n) {
  return n ? promise.delay(n*1000) : Promise.resolve();
}

var domEnv;

function installEventLogger(morph, log) {
  var loggedEvents = [
    "onMouseDown","onMouseUp","onMouseMove",
    "onDragStart", "onDrag", "onDragEnd",
    "onGrab", "onDrop",
    "onHoverIn", "onHoverOut",
    "onFocus", "onBlur",
    "onKeyDown", "onKeyUp"]
  loggedEvents.forEach(name => {
    morph[name] = function(evt) {
      log.push(name + "-" + morph.name);
      this.constructor.prototype[name].call(this, evt);
    }
  });
}

var world, submorph1, submorph2, submorph3, submorph4,
    eventLog, renderer, eventDispatcher;

async function setup() {
  domEnv = await createDOMEnvironment();
  world = new World({name: "world", extent: pt(300,300)})
  renderer = new Renderer(world, domEnv.document.body, domEnv);
  renderer.startRenderWorldLoop();
  eventDispatcher = new EventDispatcher(domEnv.window, world).install();

  world.submorphs = [{
      name: "submorph1", extent: pt(100,100), position: pt(10,10), fill: Color.red,
      submorphs: [{name: "submorph2", extent: pt(20,20), position: pt(5,10), fill: Color.green}]
    },
    {name: "submorph3", extent: pt(50,50), position: pt(200,20), fill: Color.yellow},
    {name: "submorph4", type: "text", extent: pt(50,50), position: pt(200,200), fill: Color.blue, textString: "old text"}];

  submorph1 = world.submorphs[0];
  submorph2 = world.submorphs[0].submorphs[0];
  submorph3 = world.submorphs[1];
  submorph4 = world.submorphs[2];

  eventLog = [];
  [world,submorph1,submorph2,submorph3,submorph4].forEach(ea => installEventLogger(ea, eventLog));

  await world.whenRendered();
}

function teardown() {
  eventDispatcher && eventDispatcher.uninstall();
  renderer && renderer.clear();
  domEnv && domEnv.destroy();
}

function assertEventLogContains(stuff) {
  expect(stuff).equals(eventLog)
  eventLog.length = 0;
}

describe("events", function() {

  // jsdom sometimes takes its time to initialize...
  if (System.get("@system-env").node)
    this.timeout(10000);

  beforeEach(async () => setup());
  afterEach(() => teardown());

  it("mousedown on submorph", () => {
    eventDispatcher.simulateDOMEvents({type: "pointerdown", target: submorph2});
    assertEventLogContains(["onMouseDown-world", "onMouseDown-submorph1", "onMouseDown-submorph2"]);
  });

  it("stop event", () => {
    submorph1.onMouseDown = function(evt) {
      evt.stop();
      eventLog.push("onMouseDown-submorph1");
    }
    eventDispatcher.simulateDOMEvents({type: "pointerdown", target: submorph2});
    assertEventLogContains(["onMouseDown-world", "onMouseDown-submorph1"]);
  });

  it("world has hand and moves it", () => {
    eventDispatcher.simulateDOMEvents({type: "pointermove", target: submorph2, position: pt(120,130)});
    expect(world.submorphs[0]).property("isHand", true);
  });

  it("drag morph", () => {
    submorph2.grabbable = false;
    eventDispatcher.simulateDOMEvents({type: "pointerdown", target: submorph2, position: pt(20, 25)});
    assertEventLogContains(["onMouseDown-world", "onMouseDown-submorph1", "onMouseDown-submorph2"]);

    eventDispatcher.simulateDOMEvents({type: "pointermove", target: submorph2, position: pt(30, 33)});
    assertEventLogContains(["onMouseMove-world", "onDragStart-submorph2"]);

    eventDispatcher.simulateDOMEvents({type: "pointermove", target: submorph2, position: pt(34, 36)});
    assertEventLogContains(["onMouseMove-world", "onDrag-submorph2"]);

    eventDispatcher.simulateDOMEvents({type: "pointerup", target: submorph2, position: pt(34, 36)});
    assertEventLogContains(["onMouseUp-world", "onDragEnd-submorph2"]);
  });

  it("drag computes drag delta", async () => {
    var m = world.addMorph(morph({extent: pt(50,50), fill: Color.pink, grabbable: false}));
    await m.whenRendered()
    var dragStartEvent, dragEvent, dragEndEvent;
    m.onDragStart = evt => dragStartEvent = evt;
    m.onDrag = evt => dragEvent = evt;
    m.onDragEnd = evt => dragEndEvent = evt;
    eventDispatcher.simulateDOMEvents(
      {type: "pointerdown", position: pt(20, 25)},
      {type: "pointermove", position: pt(20, 25)},
      {type: "pointermove", position: pt(30, 35)},
      {type: "pointermove", position: pt(40, 50)});
    expect(dragEvent.state.dragDelta).equals(pt(10,15))
    eventDispatcher.simulateDOMEvents({type: "pointerup", target: m, position: pt(40, 51)});
    expect(dragEndEvent.state.dragDelta).equals(pt(0, 1))
  });

  it("grab and drop morph", async () => {
    submorph2.grabbable = true;
    var morphPos = submorph2.globalPosition;

    // grab
    eventDispatcher.simulateDOMEvents(
      {type: "pointerdown", target: submorph2, position: morphPos.addXY(5,5)},
      {type: "pointermove", target: submorph2, position: morphPos.addXY(10,10)});
    assertEventLogContains([
      "onMouseDown-world", "onMouseDown-submorph1", "onMouseDown-submorph2",
      "onMouseMove-world", "onGrab-submorph2"]);
    expect(world.hands[0].carriesMorphs()).equals(true);
    var offsetWhenGrabbed = submorph2.position;

    // drop
    eventDispatcher.simulateDOMEvents(
      {type: "pointermove", target: submorph2, position: morphPos.addXY(15,15)},
      {type: "pointermove", target: submorph2, position: morphPos.addXY(20,20)},
      {type: "pointerup", target: world, position: morphPos.addXY(20,20)});
    assertEventLogContains(["onMouseMove-world", "onMouseMove-world", "onMouseUp-world", "onDrop-world"]);
    expect(world.hands[0].carriesMorphs()).equals(false);
    expect(submorph2.owner).equals(world);
    expect(submorph2.position).equals(morphPos.addXY(10,10));
  });

  it("dropped morph has correct position", async () => {
    world.submorphs = [
      {position: pt(10,10), extent: pt(100,100), fill: Color.red,
       rotation: -45,
       origin: pt(50,50)
       },
      {position: pt(60,60), extent: pt(20,20), fill: Color.green, grabbable: true, origin: pt(10,10)
      }];
    var [m1, m2] = world.submorphs;
    var prevGlobalPos = m2.globalPosition;

    world.renderAsRoot(renderer);
    eventDispatcher.simulateDOMEvents(
      {type: "pointerdown", target: m2, position: pt(60,60)},
      {type: "pointermove", target: m2, position: (pt(65,65))});
    expect(m2.globalPosition).equals(prevGlobalPos);
    expect(m2.owner).not.equals(world);
    eventDispatcher.simulateDOMEvents(
      {type: "pointermove", target: m2, position: pt(50,50)});
    expect(m2.globalPosition).equals(pt(45,45));
    eventDispatcher.simulateDOMEvents(
      {type: "pointerup", target: m1, position: (pt(2,2))});
    expect(m2.owner).equals(m1);
    expect(m2.globalPosition).equals(pt(45,45));
  });

  it("text input", () => {
    expect(submorph4).property("textString").equals("old text");
    domEnv.document.getElementById(submorph4.id).value = "new text";
    expect(submorph4).property("textString").equals("old text");
    eventDispatcher.simulateDOMEvents({type: "input", target: submorph4, position: pt(225, 225)});
    expect(submorph4).property("textString").equals("new text");
    domEnv.document.getElementById(submorph4.id).value = "really new text";
    expect(submorph4).property("textString").equals("new text");
    eventDispatcher.simulateDOMEvents({type: "input", target: submorph4, position: pt(225, 225)});
    expect(submorph4).property("textString").equals("really new text");
  });

  describe("hover", () => {

    it("into world", async () => {
      await eventDispatcher.simulateDOMEvents({target: world, type: "pointerover", position: pt(50,50)}).whenIdle();
      assertEventLogContains(["onHoverIn-world"]);
    });

    it("in and out world", async () => {
      await eventDispatcher.simulateDOMEvents(
        {target: world, type: "pointerover", position: pt(50,50)},
        {target: world, type: "pointerout", position: pt(50,50)}).whenIdle();
      assertEventLogContains(["onHoverIn-world", "onHoverOut-world"]);
    });

    it("in and out single morph", async () => {
      await eventDispatcher.simulateDOMEvents(
        {target: submorph3, type: "pointerover", position: pt(50,50)},
        {target: submorph3, type: "pointerout", position: pt(50,50)}).whenIdle();;
      assertEventLogContains(["onHoverIn-world", "onHoverIn-submorph3", "onHoverOut-world","onHoverOut-submorph3"]);
    });

    it("hover in and out with submorph", async () => {
      // simulate the over/out dom events when moving
      // - into submorph1 => into submorph2 (contained in 1) => out of submorph2 => out of submorph1
      await eventDispatcher.simulateDOMEvents({type: "pointerover", target: submorph1, position: pt(10,10)}).whenIdle();

      await eventDispatcher.simulateDOMEvents(
        {type: "pointerout", target: submorph1, position: pt(15,20)},
        {type: "pointerover", target: submorph2, position: pt(15,20)}).whenIdle();

      await eventDispatcher.simulateDOMEvents(
        {type: "pointerout", target: submorph2, position: pt(15,41)},
        {type: "pointerover", target: submorph1, position: pt(15,41)}).whenIdle();

      await eventDispatcher.simulateDOMEvents({type: "pointerout", target: submorph1, position: pt(9,9) }).whenIdle();

      assertEventLogContains([
        "onHoverIn-world", "onHoverIn-submorph1", "onHoverIn-submorph2", "onHoverOut-submorph2", "onHoverOut-world", "onHoverOut-submorph1"]);
    });

    it("hover in and out with submorph sticking out", async () => {

      var tl = submorph1.topLeft;
      submorph2.topRight = pt(submorph1.width + 10, 0);
      await eventDispatcher.simulateDOMEvents({type: "pointerover", target: submorph2, position: pt(109, 10)}).whenIdle();
      await eventDispatcher.simulateDOMEvents({type: "pointerout", target: submorph2, position: pt(111, 10)}).whenIdle();

      assertEventLogContains([
        "onHoverIn-world", "onHoverIn-submorph1", "onHoverIn-submorph2", "onHoverOut-world", "onHoverOut-submorph1", "onHoverOut-submorph2"]);
    });

  });

  describe("key events", () => {

    it("focus + blur", async () => {
      eventDispatcher.simulateDOMEvents(
        {target: submorph1, type: "focus"},
        {target: submorph1, type: "blur"});
      assertEventLogContains(["onFocus-submorph1", "onBlur-submorph1"]);
    });

    it("key down", async () => {
      eventDispatcher.simulateDOMEvents({target: submorph1, type: "keydown", ctrlKey: true, keyCode: 65});
      assertEventLogContains(["onKeyDown-world", "onKeyDown-submorph1"]);
    });

    it("key down keystring", async () => {
      var pressed; submorph1.onKeyDown = evt => pressed = evt.keyString();
      eventDispatcher.simulateDOMEvents({target: submorph1, type: "keydown", ctrlKey: true, keyCode: 65});
      expect(pressed).match(/Control-A/)
    });

    it("key up keystring", async () => {
      var pressed; submorph1.onKeyUp = evt => pressed = evt.keyString();
      eventDispatcher.simulateDOMEvents({target: submorph1, type: "keyup", altKey: true, shiftKey: true, keyCode: 88});
      expect(pressed).equals("Alt-Shift-X")
    });

  });

  describe("simulation", () => {

    it("click", async () => {
      await eventDispatcher.simulateDOMEvents({type: "click", position: pt(25,25)});
      assertEventLogContains([
        "onMouseDown-world", "onMouseDown-submorph1", "onMouseDown-submorph2",
        "onMouseUp-world", "onMouseUp-submorph1", "onMouseUp-submorph2"]);
    });

  });

});
<|endoftext|>


<commit_before>import * as d3selection from 'd3-selection'
import * as d3scale from 'd3-scale'

import {TimeLine, class_name} from './TimeLine'

export class TimeLineLegend {
    /**
     * legend for time line chart
     * @param context
     * @param config
     *      data: {
     *          label: {
     *              text: ''
     *          },
     *          info:{
     *              text: ''
     *          },
     *          class_styles: [
     *              {class_name:'unknown', color: '#bdbdbd'},
     *          ]
     *      },
     *      options: {
     *          size: {
     *              height: 50,
     *              width: 1800
     *          },
     *          start_point: {
     *              x: 250
     *          }
     *      }
     */
    constructor(context, config){
        this.context = context;
        this.config = {};

        this.color_scale = null;
        this.color_domain = [];
        this.color_range = [];

        // merge config
        Object.assign(this.config, TimeLineLegend.default);

        if(config.hasOwnProperty('options')){
            let options = this.config.options;
            let user_options = config.options;

            if(user_options.hasOwnProperty("size")) {
                Object.keys(user_options.size).forEach(function (key) {
                    options.size[key] = user_options.size[key];
                });
            }

            if(user_options.hasOwnProperty("start_point")) {
                Object.keys(user_options.start_point).forEach(function (key) {
                    options.start_point[key] = user_options.start_point[key];
                });
            }
        }

        if(config.hasOwnProperty('data')){
            let data = this.config.data;
            let user_data = config.data;

            if(user_data.hasOwnProperty("class_styles")) {
                data.class_styles = user_data.class_styles;

                let color_domain = this.color_domain;
                let color_range = this.color_range;
                data.class_styles.forEach(function(a_class){
                    color_domain.push(a_class.class_name);
                    color_range.push(a_class.color);
                });
                this.color_scale = d3scale.scaleOrdinal()
                    .domain(color_domain)
                    .range(color_range);
            }

            if(user_data.hasOwnProperty("label")) {
                Object.keys(user_data.label).forEach(function (key) {
                    data.label[key] = user_data.label[key];
                });
            }

            if(user_data.hasOwnProperty("info")) {
                Object.keys(user_data.info).forEach(function (key) {
                    data.info[key] = user_data.info[key];
                });
            }
        }

        this.drawLegend();
    }

    drawLegend() {
        let context = this.context;
        let data = this.config.data;
        let options = this.config.options;

        let color_domain = this.color_domain;
        let color_scale = this.color_scale;

        let legend_svg = d3selection.select(context)
            .append("svg")
            .classed(class_name, true)
            .attr('width', options.size.width)
            .attr('height', options.size.height);

        let legend_bar = legend_svg.append('g')
            .attr('transform', 'translate('+ options.start_point.x +',0)');

        let legend_enter = legend_bar.selectAll('.legend-item')
            .data(color_domain)
            .enter()
            .append('g')
            .classed('legend-item', true);

        legend_enter.append('rect')
            .attr('x', function(d,i){
                return 120*i;
            })
            .attr('y', function(d,i){
                return 5;
            })
            .attr('width', 20)
            .attr('height', 20)
            .style('fill', function(d,i){
                return color_scale(d)
            });

        legend_enter.append('text')
            .attr('x', function(d,i){
                return 120*i+25;
            })
            .attr('y', function(d,i){
                return 15;
            })
            .attr("dominant-baseline", "central")
            .text(function(d,i){
                return d
            });

        let legend_bar_label = legend_bar.append('text')
            .attr('x', -15)
            .attr('y', 15)
            .attr("dominant-baseline", "central")
            .attr("text-anchor", "end")
            .text(data.label.text);

        legend_bar.append('text')
            .attr('x', 0)
            .attr('y', 40)
            .attr("dominant-baseline", "central")
            .attr("text-anchor", "start")
            .text(data.info.text);
    }
}

TimeLineLegend.default = {
    data: {
        label: {
            text: ''
        },
        info:{
            text: ''
        },
        class_styles: [
            {class_name:'unknown', color: '#bdbdbd'}
        ]
    },
    options: {
        size: {
            height: 50,
            width: TimeLine.default.options.size.width
        },
        start_point: {
            x: TimeLine.default.options.start_point.x
        }
    }
};
<commit_msg>use mergeConfig in TimeLineLegend.<commit_after>import * as d3selection from 'd3-selection'
import * as d3scale from 'd3-scale'

import {TimeLine, class_name} from './TimeLine'
import {mergeConfig} from './config'

export class TimeLineLegend {
    /**
     * legend for time line chart
     * @param context
     * @param config
     *      data: {
     *          label: {
     *              text: ''
     *          },
     *          info:{
     *              text: ''
     *          },
     *          class_styles: [
     *              {class_name:'unknown', color: '#bdbdbd'},
     *          ]
     *      },
     *      options: {
     *          size: {
     *              height: 50,
     *              width: 1800
     *          },
     *          start_point: {
     *              x: 250
     *          }
     *      }
     */
    constructor(context, config){
        this.context = context;

        this.color_domain = [];
        this.color_range = [];

        this.config = mergeConfig(TimeLineLegend.default, config);

        let color_domain = this.color_domain;
        let color_range = this.color_range;
        this.config.data.class_styles.forEach(function(a_class){
            color_domain.push(a_class.class_name);
            color_range.push(a_class.color);
        });
        this.color_scale = d3scale.scaleOrdinal()
            .domain(color_domain)
            .range(color_range);

        this.drawLegend();
    }

    drawLegend() {
        let context = this.context;
        let data = this.config.data;
        let options = this.config.options;

        let color_domain = this.color_domain;
        let color_scale = this.color_scale;

        let legend_svg = d3selection.select(context)
            .append("svg")
            .classed(class_name, true)
            .attr('width', options.size.width)
            .attr('height', options.size.height);

        let legend_bar = legend_svg.append('g')
            .attr('transform', 'translate('+ options.start_point.x +',0)');

        let legend_enter = legend_bar.selectAll('.legend-item')
            .data(color_domain)
            .enter()
            .append('g')
            .classed('legend-item', true);

        legend_enter.append('rect')
            .attr('x', function(d,i){
                return 120*i;
            })
            .attr('y', function(d,i){
                return 5;
            })
            .attr('width', 20)
            .attr('height', 20)
            .style('fill', function(d,i){
                return color_scale(d)
            });

        legend_enter.append('text')
            .attr('x', function(d,i){
                return 120*i+25;
            })
            .attr('y', function(d,i){
                return 15;
            })
            .attr("dominant-baseline", "central")
            .text(function(d,i){
                return d
            });

        let legend_bar_label = legend_bar.append('text')
            .attr('x', -15)
            .attr('y', 15)
            .attr("dominant-baseline", "central")
            .attr("text-anchor", "end")
            .text(data.label.text);

        legend_bar.append('text')
            .attr('x', 0)
            .attr('y', 40)
            .attr("dominant-baseline", "central")
            .attr("text-anchor", "start")
            .text(data.info.text);
    }
}

TimeLineLegend.default = {
    data: {
        label: {
            text: ''
        },
        info:{
            text: ''
        },
        class_styles: [
            {class_name:'unknown', color: '#bdbdbd'}
        ]
    },
    options: {
        size: {
            height: 50,
            width: TimeLine.default.options.size.width
        },
        start_point: {
            x: TimeLine.default.options.start_point.x
        }
    }
};
<|endoftext|>


<commit_before>/**
 * @file Ripple component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import CSSTransition from 'react-transition-group/CSSTransition';
import classNames from 'classnames';

import PureRender from '../_vendors/PureRender';

@PureRender
class Ripple extends Component {

    constructor(props, ...restArgs) {
        super(props, ...restArgs);
    }

    render() {

        const {className, style, duration, ...restProps} = this.props,

            rippleClassName = classNames('ripple', {
                [className]: className
            });

        return (
            <CSSTransition {...restProps}
                           classNames="ripple"
                           timeout={{enter: duration, exit: duration * 2}}>

            </CSSTransition>
        );

    }
};

process.env.NODE_ENV !== 'production' && (Ripple.propTypes = {

    className: PropTypes.string,
    style: PropTypes.object,

    duration: PropTypes.number

});

Ripple.defaultProps = {
    duration: 500
};

export default Ripple;<commit_msg>update Ripple<commit_after>/**
 * @file Ripple component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import CSSTransition from 'react-transition-group/CSSTransition';
import classNames from 'classnames';

import PureRender from '../_vendors/PureRender';

@PureRender
class Ripple extends Component {

    constructor(props, ...restArgs) {
        super(props, ...restArgs);
    }

    render() {

        const {className, style, duration, ...restProps} = this.props,

            rippleClassName = classNames('ripple', {
                [className]: className
            });

        return (
            <CSSTransition {...restProps}
                           classNames="ripple"
                           timeout={{enter: duration, exit: duration * 2}}>

            </CSSTransition>
        );

    }
};

if (process.env.NODE_ENV === 'development') {

    const PropTypes = require('prop-types');

    Ripple.propTypes = {

        className: PropTypes.string,
        style: PropTypes.object,

        duration: PropTypes.number

    };

}

Ripple.defaultProps = {
    duration: 500
};

export default Ripple;<|endoftext|>


<commit_before>import React, {Component} from 'react';
import PropTypes from 'prop-types';
import ReactDOM from 'react-dom';

export default class Ripple extends Component {

    constructor(props) {

        super(props);

        this.hasMounted = false;

    }

    initializeAnimation(callback) {

        ReactDOM.findDOMNode(this).style.transform = 'scale(0)';

        setTimeout(() => {
            this.hasMounted && callback();
        }, 0);

    }

    animate() {
        ReactDOM.findDOMNode(this).style.transform = 'scale(1)';
    }

    componentDidMount() {
        this.hasMounted = true;
    }

    componentWillAppear(callback) {
        this.initializeAnimation(callback);
    }

    componentWillEnter(callback) {
        this.initializeAnimation(callback);
    }

    componentDidAppear() {
        this.animate();
    }

    componentDidEnter() {
        this.animate();
    }

    componentWillLeave(callback) {

        ReactDOM.findDOMNode(this).style.opacity = 0;

        this.timeout = setTimeout(() => {
            this.hasMounted && callback();
        }, 2000);

    }

    componentWillUnmount() {
        this.timeout && clearTimeout(this.timeout);
    }

    render() {

        const {className, style, children} = this.props;

        return (
            <div className={`ripple ${className}`}
                 style={style}>
                {children}

        );

    }
};

Ripple.propTypes = {
    className: PropTypes.string,
    style: PropTypes.object
};

Ripple.defaultProps = {
    className: '',
    style: null
};<commit_msg>optimize Ripple<commit_after>import React, {Component} from 'react';
import PropTypes from 'prop-types';
import ReactDOM from 'react-dom';

export default class Ripple extends Component {

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.hasMounted = false;

    }

    initializeAnimation(callback) {

        ReactDOM.findDOMNode(this).style.transform = 'scale(0)';

        setTimeout(() => {
            this.hasMounted && callback();
        }, 0);

    }

    animate() {
        ReactDOM.findDOMNode(this).style.transform = 'scale(1)';
    }

    componentDidMount() {
        this.hasMounted = true;
    }

    componentWillAppear(callback) {
        this.initializeAnimation(callback);
    }

    componentWillEnter(callback) {
        this.initializeAnimation(callback);
    }

    componentDidAppear() {
        this.animate();
    }

    componentDidEnter() {
        this.animate();
    }

    componentWillLeave(callback) {

        ReactDOM.findDOMNode(this).style.opacity = 0;

        this.timeout = setTimeout(() => {
            this.hasMounted && callback();
        }, 2000);

    }

    componentWillUnmount() {
        this.timeout && clearTimeout(this.timeout);
    }

    render() {

        const {className, style, children} = this.props;

        return (
            <div className={`ripple ${className}`}
                 style={style}>
                {children}

        );

    }
};

Ripple.propTypes = {
    className: PropTypes.string,
    style: PropTypes.object
};

Ripple.defaultProps = {
    className: '',
    style: null
};<|endoftext|>


<commit_before>/**
 * angular-locker
 *
 * A simple & configurable abstraction for local/session storage in angular projects.
 *
 * @link https://github.com/tymondesigns/angular-locker
 * @author Sean Tymon @tymondesigns
 * @license MIT License, http://www.opensource.org/licenses/MIT
 */

(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        define(function () {
            return factory(root.angular);
        });
    } else if (typeof exports === 'object') {
        module.exports = factory(root.angular || (window && window.angular));
    } else {
        factory(root.angular);
    }
})(this, function (angular) {

    'use strict';

    angular.module('angular-locker', [])

    .provider('locker', function () {

        /**
         * If value is a function then execute, otherwise return
         *
         * @param  {Mixed}  value
         * @return {Mixed}
         */
        var _value = function (value) {
            return angular.isFunction(value) ? value() : value;
        };

        /**
         * Get the key of an object by the value
         *
         * @param  {Object}  object
         * @param  {Mixed}   value
         * @return {String}
         */
        var _keyByVal = function (object, value) {
            return Object.keys(object).filter(function (key) { return object[key] === value; })[0];
        };

        /**
         * Trigger an error
         *
         * @param  {String}  msg
         * @return {void}
         */
        var _error = function (msg) {
            throw new Error('[angular-locker] ' + msg);
        };

        /**
         * Set the default driver and namespace
         *
         * @type {Object}
         */
        var defaults = {
            driver: 'local',
            namespace: 'locker',
            eventsEnabled: true,
            separator: '.'
        };

        return {

            /**
             * Allow setting of default storage driver via `lockerProvider`
             * e.g. lockerProvider.setDefaultDriver('session');
             *
             * @param {String}  driver
             */
            setDefaultDriver: function (driver) {
                defaults.driver = _value(driver);

                return this;
            },

            /**
             * Get the default driver
             */
            getDefaultDriver: function () {
                return defaults.driver;
            },

            /**
             * Allow setting of default namespace via `lockerProvider`
             * e.g. lockerProvider.setDefaultNamespace('myAppName');
             *
             * @param {String}  namespace
             */
            setDefaultNamespace: function (namespace) {
                defaults.namespace = _value(namespace);

                return this;
            },

            /**
             * Get the default namespace
             */
            getDefaultNamespace: function () {
                return defaults.namespace;
            },

            /**
             * Set whether the events are enabled
             *
             * @param {Boolean}  enabled
             */
            setEventsEnabled: function (enabled) {
                defaults.eventsEnabled = _value(enabled);

                return this;
            },

            /**
             * Get whether the events are enabled
             */
            getEventsEnabled: function () {
                return defaults.eventsEnabled;
            },

            /**
             * Set the separator to use with namespace in keys
             *
             * @param {String} separator
             */
            setSeparator: function (separator) {
                defaults.separator = _value(separator);

                return this;
            },

            /**
             * Get the separator
             */
            getSeparator: function () {
                return defaults.separator;
            },

            /**
             * The locker service
             */
            $get: ['$window', '$rootScope', '$parse', function ($window, $rootScope, $parse) {

                /**
                 * Define the Locker class
                 *
                 * @param {Storage}  driver
                 * @param {String}   namespace
                 */
                function Locker (driver, namespace, eventsEnabled, separator) {

                    /**
                     * @type {Object}
                     */
                    this._registeredDrivers = {
                        local: $window.localStorage,
                        session: $window.sessionStorage
                    };

                    /**
                     * Get the Storage instance from the key
                     *
                     * @param  {String}  driver
                     * @return {Storage}
                     */
                    this._resolveDriver = function (driver) {
                        if (! this._registeredDrivers.hasOwnProperty(driver)) {
                            _error('The driver "' + driver + '" was not found.');
                        }

                        return this._registeredDrivers[driver];
                    };

                    /**
                     * Get the driver key (local/session) by the Storage instance
                     *
                     * @param  {Storage}  driver
                     * @return {String}
                     */
                    this._deriveDriver = function (driver) {
                        return _keyByVal(this._registeredDrivers, driver);
                    };

                    /**
                     * @type {Storage}
                     */
                    this._driver = this._resolveDriver(driver);

                    /**
                     * @type {String}
                     */
                    this._namespace = namespace;

                    /**
                     * @type {Boolean}
                     */
                    this._eventsEnabled = eventsEnabled;

                    /**
                     * @type {String}
                     */
                    this._separator = separator;

                    /**
                     * @type {Object}
                     */
                    this._watchers = {};

                    /**
                     * Check browser support
                     *
                     * @see https://github.com/Modernizr/Modernizr/blob/master/feature-detects/storage/localstorage.js#L38-L47
                     * @param  {String}  driver
                     * @return {Boolean}
                     */
                    this._checkSupport = function (driver) {
                        if (angular.isUndefined(this._supported)) {
                            var l = 'l';
                            try {
                                this._resolveDriver(driver || 'local').setItem(l, l);
                                this._resolveDriver(driver || 'local').removeItem(l);
                                this._supported = true;
                            } catch (e) {
                                this._supported = false;
                            }
                        }

                        return this._supported;
                    };

                    /**
                     * Build the storage key from the namspace
                     *
                     * @param  {String}  key
                     * @return {String}
                     */
                    this._getPrefix = function (key) {
                        if (! this._namespace) return key;

                        return this._namespace + this._separator + key;
                    };

                    /**
                     * Try to encode value as json, or just return the value upon failure
                     *
                     * @param  {Mixed}  value
                     * @return {Mixed}
                     */
                    this._serialize = function (value) {
                        try {
                            return angular.toJson(value);
                        } catch (e) {
                            return value;
                        }
                    };

                    /**
                     * Try to parse value as json, if it fails then it probably isn't json so just return it
                     *
                     * @param  {String}  value
                     * @return {Object|String}
                     */
                    this._unserialize = function (value) {
                        try {
                            return angular.fromJson(value);
                        } catch (e) {
                            return value;
                        }
                    };

                    /**
                     * Trigger an event
                     *
                     * @param  {String} name
                     * @param  {Object} payload
                     * @return {void}
                     */
                    this._event = function (name, payload) {
                        if (this._eventsEnabled) {
                            $rootScope.$emit(name, angular.extend(payload, {
                                driver: this._deriveDriver(this._driver),
                                namespace: this._namespace,
                            }));
                        }
                    };

                    /**
                     * Add to storage
                     *
                     * @param {String}  key
                     * @param {Mixed}  value
                     */
                    this._setItem = function (key, value) {
                        if (this._checkSupport()) {
                            try {
                                var oldVal = this._getItem(key);
                                this._driver.setItem(this._getPrefix(key), this._serialize(value));
                                if (this._exists(key) && ! angular.equals(oldVal, value)) {
                                    this._event('locker.item.updated', { key: key, oldValue: oldVal, newValue: value });
                                } else {
                                    this._event('locker.item.added', { key: key, value: value });
                                }
                            } catch (e) {
                                if (['QUOTA_EXCEEDED_ERR', 'NS_ERROR_DOM_QUOTA_REACHED', 'QuotaExceededError'].indexOf(e.name) !== -1) {
                                    _error('The browser storage quota has been exceeded');
                                } else {
                                    _error('Could not add item with key "' + key + '"');
                                }
                            }
                        } else {
                            _error('The browser does not support localStorage');
                        }
                    };

                    /**
                     * Get from storage
                     *
                     * @param  {String}  key
                     * @return {Mixed}
                     */
                    this._getItem = function (key) {
                        if (this._checkSupport()) {
                            return this._unserialize(this._driver.getItem(this._getPrefix(key)));
                        } else {
                            _error('The browser does not support localStorage');
                        }
                    };

                    /**
                     * Exists in storage
                     *
                     * @param  {String}  key
                     * @return {Boolean}
                     */
                    this._exists = function (key) {
                        if (this._checkSupport()) {
                            return this._driver.hasOwnProperty(this._getPrefix(_value(key)));
                        } else {
                            _error('The browser does not support localStorage');
                        }
                    };

                    /**
                     * Remove from storage
                     *
                     * @param  {String}  key
                     * @return {Boolean}
                     */
                    this._removeItem = function (key) {
                        if (this._checkSupport()) {
                            if (! this._exists(key)) return false;
                            this._driver.removeItem(this._getPrefix(key));

                            this._event('locker.item.forgotten', { key: key });

                            return true;
                        } else {
                            _error('The browser does not support localStorage');
                        }
                    };
                }

                /**
                 * Define the public api
                 *
                 * @type {Object}
                 */
                Locker.prototype = {

                    /**
                     * Add a new item to storage (even if it already exists)
                     *
                     * @param  {Mixed}  key
                     * @param  {Mixed}  value
                     * @return {self}
                     */
                    put: function (key, value) {
                        if (! key) return false;
                        key = _value(key);

                        if (angular.isObject(key)) {
                            angular.forEach(key, function (value, key) {
                                this._setItem(key, value);
                            }, this);
                        } else {
                            if (! angular.isDefined(value)) return false;
                            this._setItem(key, _value(value));
                        }

                        return this;
                    },

                    /**
                     * Add an item to storage if it doesn't already exist
                     *
                     * @param  {Mixed}  key
                     * @param  {Mixed}  value
                     * @return {Boolean}
                     */
                    add: function (key, value) {
                        if (! this.has(key)) {
                            this.put(key, value);
                            return true;
                        }

                        return false;
                    },

                    /**
                     * Retrieve the specified item from storage
                     *
                     * @param  {String|Array}  key
                     * @param  {Mixed}  def
                     * @return {Mixed}
                     */
                    get: function (key, def) {
                        if (angular.isArray(key)) {
                            var items = {};
                            angular.forEach(key, function (k) {
                                if (this.has(k)) items[k] = this._getItem(k);
                            }, this);

                            return items;
                        }

                        if (! this.has(key)) return arguments.length === 2 ? def : void 0;

                        return this._getItem(key);
                    },

                    /**
                     * Determine whether the item exists in storage
                     *
                     * @param  {String|Function}  key
                     * @return {Boolean}
                     */
                    has: function (key) {
                        return this._exists(key);
                    },

                    /**
                     * Remove specified item(s) from storage
                     *
                     * @param  {Mixed}  key
                     * @return {Object}
                     */
                    forget: function (key) {
                        key = _value(key);

                        if (angular.isArray(key)) {
                            angular.forEach(key, function (key) {
                                this._removeItem(key);
                            }, this);
                        } else {
                            this._removeItem(key);
                        }

                        return this;
                    },

                    /**
                     * Retrieve the specified item from storage and then remove it
                     *
                     * @param  {String|Array}  key
                     * @param  {Mixed}  def
                     * @return {Mixed}
                     */
                    pull: function (key, def) {
                        var value = this.get(key, def);
                        this.forget(key);

                        return value;
                    },

                    /**
                     * Return all items in storage within the current namespace
                     *
                     * @return {Object}
                     */
                    all: function () {
                        var items = {};
                        angular.forEach(this._driver, function (value, key) {
                            var split = key.split(this._separator);
                            if (split.length > 1 && split[0] === this._namespace) {
                                split.splice(0, 1);
                                key = split.join(this._separator);
                            }
                            if (this.has(key)) items[key] = this.get(key);
                        }, this);

                        return items;
                    },

                    /**
                     * Remove all items set within the current namespace
                     *
                     * @return {self}
                     */
                    clean: function () {
                        this.forget(Object.keys(this.all()));

                        return this;
                    },

                    /**
                     * Empty the current storage driver completely. careful now.
                     *
                     * @return {self}
                     */
                    empty: function () {
                        this._driver.clear();

                        return this;
                    },

                    /**
                     * Get the total number of items within the current namespace
                     *
                     * @return {Integer}
                     */
                    count: function () {
                        return Object.keys(this.all()).length;
                    },

                    /**
                     * Bind a storage key to a $scope property
                     *
                     * @param  {Object}  $scope
                     * @param  {String}  key
                     * @param  {Mixed}   def
                     * @return {self}
                     */
                    bind: function ($scope, key, def) {
                        if (angular.isUndefined( $scope.$eval(key) )) {
                            $parse(key).assign($scope, this.get(key, def));
                            if (! this.has(key)) this.put(key, def);
                        }

                        var self = this;
                        this._watchers[key + $scope.$id] = $scope.$watch(key, function (newVal) {
                            if (angular.isDefined(newVal)) self.put(key, newVal);
                        }, angular.isObject($scope[key]));

                        return this;
                    },

                    /**
                     * Unbind a storage key from a $scope property
                     *
                     * @param  {Object}  $scope
                     * @param  {String}  key
                     * @return {self}
                     */
                    unbind: function ($scope, key) {
                        $parse(key).assign($scope, void 0);
                        this.forget(key);
                        if (this._watchers[key + $scope.$id]) {
                            // execute the de-registration function
                            this._watchers[key + $scope.$id]();
                            delete this._watchers[key + $scope.$id];
                        }

                        return this;
                    },

                    /**
                     * Set the storage driver on a new instance to enable overriding defaults
                     *
                     * @param  {String}  driver
                     * @return {self}
                     */
                    driver: function (driver) {
                        return new Locker(driver, this._namespace, this._eventsEnabled);
                    },

                    /**
                     * Get the currently set driver
                     *
                     * @return {Storage}
                     */
                    getDriver: function () {
                        return this._driver;
                    },

                    /**
                     * Set the namespace on a new instance to enable overriding defaults
                     *
                     * @param  {String}  namespace
                     * @return {self}
                     */
                    namespace: function (namespace) {
                        return new Locker(this._deriveDriver(this._driver), namespace, this._eventsEnabled);
                    },

                    /**
                     * Get the currently set namespace
                     *
                     * @return {String}
                     */
                    getNamespace: function () {
                        return this._namespace;
                    },

                    /**
                     * Check browser support
                     *
                     * @see https://github.com/Modernizr/Modernizr/blob/master/feature-detects/storage/localstorage.js#L38-L47
                     * @param  {String}  driver
                     * @return {Boolean}
                     */
                    supported: function (driver) {
                        return this._checkSupport(driver);
                    }
                };

                /**
                 * Create the driver instances
                 *
                 * @type {Object}
                 */
                var drivers = {
                    local: new Locker('local', defaults.namespace, defaults.eventsEnabled, defaults.separator),
                    session: new Locker('session', defaults.namespace, defaults.eventsEnabled, defaults.separator)
                };

                return drivers[defaults.driver];
            }]
        };

    });

});
<commit_msg>fixing new instances<commit_after>/**
 * angular-locker
 *
 * A simple & configurable abstraction for local/session storage in angular projects.
 *
 * @link https://github.com/tymondesigns/angular-locker
 * @author Sean Tymon @tymondesigns
 * @license MIT License, http://www.opensource.org/licenses/MIT
 */

(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        define(function () {
            return factory(root.angular);
        });
    } else if (typeof exports === 'object') {
        module.exports = factory(root.angular || (window && window.angular));
    } else {
        factory(root.angular);
    }
})(this, function (angular) {

    'use strict';

    angular.module('angular-locker', [])

    .provider('locker', function () {

        /**
         * If value is a function then execute, otherwise return
         *
         * @param  {Mixed}  value
         * @return {Mixed}
         */
        var _value = function (value) {
            return angular.isFunction(value) ? value() : value;
        };

        /**
         * Get the key of an object by the value
         *
         * @param  {Object}  object
         * @param  {Mixed}   value
         * @return {String}
         */
        var _keyByVal = function (object, value) {
            return Object.keys(object).filter(function (key) { return object[key] === value; })[0];
        };

        /**
         * Trigger an error
         *
         * @param  {String}  msg
         * @return {void}
         */
        var _error = function (msg) {
            throw new Error('[angular-locker] ' + msg);
        };

        /**
         * Set the default driver and namespace
         *
         * @type {Object}
         */
        var defaults = {
            driver: 'local',
            namespace: 'locker',
            eventsEnabled: true,
            separator: '.'
        };

        return {

            /**
             * Allow setting of default storage driver via `lockerProvider`
             * e.g. lockerProvider.setDefaultDriver('session');
             *
             * @param {String}  driver
             */
            setDefaultDriver: function (driver) {
                defaults.driver = _value(driver);

                return this;
            },

            /**
             * Get the default driver
             */
            getDefaultDriver: function () {
                return defaults.driver;
            },

            /**
             * Allow setting of default namespace via `lockerProvider`
             * e.g. lockerProvider.setDefaultNamespace('myAppName');
             *
             * @param {String}  namespace
             */
            setDefaultNamespace: function (namespace) {
                defaults.namespace = _value(namespace);

                return this;
            },

            /**
             * Get the default namespace
             */
            getDefaultNamespace: function () {
                return defaults.namespace;
            },

            /**
             * Set whether the events are enabled
             *
             * @param {Boolean}  enabled
             */
            setEventsEnabled: function (enabled) {
                defaults.eventsEnabled = _value(enabled);

                return this;
            },

            /**
             * Get whether the events are enabled
             */
            getEventsEnabled: function () {
                return defaults.eventsEnabled;
            },

            /**
             * Set the separator to use with namespace in keys
             *
             * @param {String} separator
             */
            setSeparator: function (separator) {
                defaults.separator = _value(separator);

                return this;
            },

            /**
             * Get the separator
             */
            getSeparator: function () {
                return defaults.separator;
            },

            /**
             * The locker service
             */
            $get: ['$window', '$rootScope', '$parse', function ($window, $rootScope, $parse) {

                /**
                 * Define the Locker class
                 *
                 * @param {Storage}  driver
                 * @param {String}   namespace
                 */
                function Locker (driver, namespace, eventsEnabled, separator) {

                    /**
                     * @type {Object}
                     */
                    this._registeredDrivers = {
                        local: $window.localStorage,
                        session: $window.sessionStorage
                    };

                    /**
                     * Get the Storage instance from the key
                     *
                     * @param  {String}  driver
                     * @return {Storage}
                     */
                    this._resolveDriver = function (driver) {
                        if (! this._registeredDrivers.hasOwnProperty(driver)) {
                            _error('The driver "' + driver + '" was not found.');
                        }

                        return this._registeredDrivers[driver];
                    };

                    /**
                     * Get the driver key (local/session) by the Storage instance
                     *
                     * @param  {Storage}  driver
                     * @return {String}
                     */
                    this._deriveDriver = function (driver) {
                        return _keyByVal(this._registeredDrivers, driver);
                    };

                    /**
                     * @type {Storage}
                     */
                    this._driver = this._resolveDriver(driver);

                    /**
                     * @type {String}
                     */
                    this._namespace = namespace;

                    /**
                     * @type {Boolean}
                     */
                    this._eventsEnabled = eventsEnabled;

                    /**
                     * @type {String}
                     */
                    this._separator = separator;

                    /**
                     * @type {Object}
                     */
                    this._watchers = {};

                    /**
                     * Check browser support
                     *
                     * @see https://github.com/Modernizr/Modernizr/blob/master/feature-detects/storage/localstorage.js#L38-L47
                     * @param  {String}  driver
                     * @return {Boolean}
                     */
                    this._checkSupport = function (driver) {
                        if (angular.isUndefined(this._supported)) {
                            var l = 'l';
                            try {
                                this._resolveDriver(driver || 'local').setItem(l, l);
                                this._resolveDriver(driver || 'local').removeItem(l);
                                this._supported = true;
                            } catch (e) {
                                this._supported = false;
                            }
                        }

                        return this._supported;
                    };

                    /**
                     * Build the storage key from the namspace
                     *
                     * @param  {String}  key
                     * @return {String}
                     */
                    this._getPrefix = function (key) {
                        if (! this._namespace) return key;

                        return this._namespace + this._separator + key;
                    };

                    /**
                     * Try to encode value as json, or just return the value upon failure
                     *
                     * @param  {Mixed}  value
                     * @return {Mixed}
                     */
                    this._serialize = function (value) {
                        try {
                            return angular.toJson(value);
                        } catch (e) {
                            return value;
                        }
                    };

                    /**
                     * Try to parse value as json, if it fails then it probably isn't json so just return it
                     *
                     * @param  {String}  value
                     * @return {Object|String}
                     */
                    this._unserialize = function (value) {
                        try {
                            return angular.fromJson(value);
                        } catch (e) {
                            return value;
                        }
                    };

                    /**
                     * Trigger an event
                     *
                     * @param  {String} name
                     * @param  {Object} payload
                     * @return {void}
                     */
                    this._event = function (name, payload) {
                        if (this._eventsEnabled) {
                            $rootScope.$emit(name, angular.extend(payload, {
                                driver: this._deriveDriver(this._driver),
                                namespace: this._namespace,
                            }));
                        }
                    };

                    /**
                     * Add to storage
                     *
                     * @param {String}  key
                     * @param {Mixed}  value
                     */
                    this._setItem = function (key, value) {
                        if (this._checkSupport()) {
                            try {
                                var oldVal = this._getItem(key);
                                this._driver.setItem(this._getPrefix(key), this._serialize(value));
                                if (this._exists(key) && ! angular.equals(oldVal, value)) {
                                    this._event('locker.item.updated', { key: key, oldValue: oldVal, newValue: value });
                                } else {
                                    this._event('locker.item.added', { key: key, value: value });
                                }
                            } catch (e) {
                                if (['QUOTA_EXCEEDED_ERR', 'NS_ERROR_DOM_QUOTA_REACHED', 'QuotaExceededError'].indexOf(e.name) !== -1) {
                                    _error('The browser storage quota has been exceeded');
                                } else {
                                    _error('Could not add item with key "' + key + '"');
                                }
                            }
                        } else {
                            _error('The browser does not support localStorage');
                        }
                    };

                    /**
                     * Get from storage
                     *
                     * @param  {String}  key
                     * @return {Mixed}
                     */
                    this._getItem = function (key) {
                        if (this._checkSupport()) {
                            return this._unserialize(this._driver.getItem(this._getPrefix(key)));
                        } else {
                            _error('The browser does not support localStorage');
                        }
                    };

                    /**
                     * Exists in storage
                     *
                     * @param  {String}  key
                     * @return {Boolean}
                     */
                    this._exists = function (key) {
                        if (this._checkSupport()) {
                            return this._driver.hasOwnProperty(this._getPrefix(_value(key)));
                        } else {
                            _error('The browser does not support localStorage');
                        }
                    };

                    /**
                     * Remove from storage
                     *
                     * @param  {String}  key
                     * @return {Boolean}
                     */
                    this._removeItem = function (key) {
                        if (this._checkSupport()) {
                            if (! this._exists(key)) return false;
                            this._driver.removeItem(this._getPrefix(key));

                            this._event('locker.item.forgotten', { key: key });

                            return true;
                        } else {
                            _error('The browser does not support localStorage');
                        }
                    };
                }

                /**
                 * Define the public api
                 *
                 * @type {Object}
                 */
                Locker.prototype = {

                    /**
                     * Add a new item to storage (even if it already exists)
                     *
                     * @param  {Mixed}  key
                     * @param  {Mixed}  value
                     * @return {self}
                     */
                    put: function (key, value) {
                        if (! key) return false;
                        key = _value(key);

                        if (angular.isObject(key)) {
                            angular.forEach(key, function (value, key) {
                                this._setItem(key, value);
                            }, this);
                        } else {
                            if (! angular.isDefined(value)) return false;
                            this._setItem(key, _value(value));
                        }

                        return this;
                    },

                    /**
                     * Add an item to storage if it doesn't already exist
                     *
                     * @param  {Mixed}  key
                     * @param  {Mixed}  value
                     * @return {Boolean}
                     */
                    add: function (key, value) {
                        if (! this.has(key)) {
                            this.put(key, value);
                            return true;
                        }

                        return false;
                    },

                    /**
                     * Retrieve the specified item from storage
                     *
                     * @param  {String|Array}  key
                     * @param  {Mixed}  def
                     * @return {Mixed}
                     */
                    get: function (key, def) {
                        if (angular.isArray(key)) {
                            var items = {};
                            angular.forEach(key, function (k) {
                                if (this.has(k)) items[k] = this._getItem(k);
                            }, this);

                            return items;
                        }

                        if (! this.has(key)) return arguments.length === 2 ? def : void 0;

                        return this._getItem(key);
                    },

                    /**
                     * Determine whether the item exists in storage
                     *
                     * @param  {String|Function}  key
                     * @return {Boolean}
                     */
                    has: function (key) {
                        return this._exists(key);
                    },

                    /**
                     * Remove specified item(s) from storage
                     *
                     * @param  {Mixed}  key
                     * @return {Object}
                     */
                    forget: function (key) {
                        key = _value(key);

                        if (angular.isArray(key)) {
                            angular.forEach(key, function (key) {
                                this._removeItem(key);
                            }, this);
                        } else {
                            this._removeItem(key);
                        }

                        return this;
                    },

                    /**
                     * Retrieve the specified item from storage and then remove it
                     *
                     * @param  {String|Array}  key
                     * @param  {Mixed}  def
                     * @return {Mixed}
                     */
                    pull: function (key, def) {
                        var value = this.get(key, def);
                        this.forget(key);

                        return value;
                    },

                    /**
                     * Return all items in storage within the current namespace
                     *
                     * @return {Object}
                     */
                    all: function () {
                        var items = {};
                        angular.forEach(this._driver, function (value, key) {
                            var split = key.split(this._separator);
                            if (split.length > 1 && split[0] === this._namespace) {
                                split.splice(0, 1);
                                key = split.join(this._separator);
                            }
                            if (this.has(key)) items[key] = this.get(key);
                        }, this);

                        return items;
                    },

                    /**
                     * Remove all items set within the current namespace
                     *
                     * @return {self}
                     */
                    clean: function () {
                        this.forget(Object.keys(this.all()));

                        return this;
                    },

                    /**
                     * Empty the current storage driver completely. careful now.
                     *
                     * @return {self}
                     */
                    empty: function () {
                        this._driver.clear();

                        return this;
                    },

                    /**
                     * Get the total number of items within the current namespace
                     *
                     * @return {Integer}
                     */
                    count: function () {
                        return Object.keys(this.all()).length;
                    },

                    /**
                     * Bind a storage key to a $scope property
                     *
                     * @param  {Object}  $scope
                     * @param  {String}  key
                     * @param  {Mixed}   def
                     * @return {self}
                     */
                    bind: function ($scope, key, def) {
                        if (angular.isUndefined( $scope.$eval(key) )) {
                            $parse(key).assign($scope, this.get(key, def));
                            if (! this.has(key)) this.put(key, def);
                        }

                        var self = this;
                        this._watchers[key + $scope.$id] = $scope.$watch(key, function (newVal) {
                            if (angular.isDefined(newVal)) self.put(key, newVal);
                        }, angular.isObject($scope[key]));

                        return this;
                    },

                    /**
                     * Unbind a storage key from a $scope property
                     *
                     * @param  {Object}  $scope
                     * @param  {String}  key
                     * @return {self}
                     */
                    unbind: function ($scope, key) {
                        $parse(key).assign($scope, void 0);
                        this.forget(key);
                        if (this._watchers[key + $scope.$id]) {
                            // execute the de-registration function
                            this._watchers[key + $scope.$id]();
                            delete this._watchers[key + $scope.$id];
                        }

                        return this;
                    },

                    /**
                     * Set the storage driver on a new instance to enable overriding defaults
                     *
                     * @param  {String}  driver
                     * @return {self}
                     */
                    driver: function (driver) {
                        return new Locker(driver, this._namespace, this._eventsEnabled, this._separator);
                    },

                    /**
                     * Get the currently set driver
                     *
                     * @return {Storage}
                     */
                    getDriver: function () {
                        return this._driver;
                    },

                    /**
                     * Set the namespace on a new instance to enable overriding defaults
                     *
                     * @param  {String}  namespace
                     * @return {self}
                     */
                    namespace: function (namespace) {
                        return new Locker(this._deriveDriver(this._driver), namespace, this._eventsEnabled, this._separator);
                    },

                    /**
                     * Get the currently set namespace
                     *
                     * @return {String}
                     */
                    getNamespace: function () {
                        return this._namespace;
                    },

                    /**
                     * Check browser support
                     *
                     * @see https://github.com/Modernizr/Modernizr/blob/master/feature-detects/storage/localstorage.js#L38-L47
                     * @param  {String}  driver
                     * @return {Boolean}
                     */
                    supported: function (driver) {
                        return this._checkSupport(driver);
                    }
                };

                /**
                 * Create the driver instances
                 *
                 * @type {Object}
                 */
                var drivers = {
                    local: new Locker('local', defaults.namespace, defaults.eventsEnabled, defaults.separator),
                    session: new Locker('session', defaults.namespace, defaults.eventsEnabled, defaults.separator)
                };

                return drivers[defaults.driver];
            }]
        };

    });

});
<|endoftext|>


<commit_before>var Opbeat = require('./opbeat')
var utils = require('./instrumentation/utils')
var logger = require('./lib/logger')

function ngOpbeatProvider () {
  this.config = function config (properties) {
    Opbeat.config(properties)
  }

  this.install = function install () {
    Opbeat.install()
  }

  this.$get = [function () {
    return {
      captureException: function captureException (exception, cause) {
        Opbeat.captureException(exception, cause)
      },

      setUserContext: function setUser (user) {
        Opbeat.setUserContext(user)
      },

      setExtraContext: function setExtraContext (data) {
        Opbeat.setExtraContext(data)
      }
    }
  }]
}

function $opbeatErrorProvider ($provide) {
  $provide.decorator('$exceptionHandler', ['$delegate', '$opbeat', function $ExceptionHandlerDecorator ($delegate, $opbeat) {
    return function $ExceptionHandler (exception, cause) {
      $opbeat.captureException(exception)
      return $delegate(exception, cause)
    }
  }])
}

function $opbeatInstrumentationProvider ($provide) {

  // Route controller Instrumentation
  $provide.decorator('$controller', function ($delegate, $location, $rootScope) {
    $rootScope._opbeatTransactions = {}

    var onRouteChange = function(e, current) {
      var routeControllerTarget = current.controller
      logger.log('opbeat.decorator.controller.onRouteChange')
      var transaction = $rootScope._opbeatTransactions[$location.absUrl()]
      if (!transaction) {
        transaction = Opbeat.startTransaction('app.angular.controller.' + routeControllerTarget, 'transaction')
        transaction.metadata.controllerName = routeControllerTarget

        $rootScope._opbeatTransactions[$location.absUrl()] = transaction
      }

    }

    $rootScope.$on('$routeChangeStart', onRouteChange) // ng-router
    $rootScope.$on('$stateChangeSuccess', onRouteChange) // ui-router

    return function () {
      logger.log('opbeat.decorator.controller.ctor')

      var args = Array.prototype.slice.call(arguments)
      var transaction = $rootScope._opbeatTransactions[$location.absUrl()]
      var controllerInfo = utils.getControllerInfoFromArgs(args)
      var controllerName = controllerInfo.name
      var controllerScope = controllerInfo.scope
      var isRouteController = controllerName && transaction && transaction.metadata.controllerName === controllerName

      var result = $delegate.apply(this, args)

      if (isRouteController && controllerScope) {
        logger.log('opbeat.angular.controller', controllerName)

        // Instrument controller scope functions
        utils.getObjectFunctions(controllerScope).forEach(function (funcScope) {
          utils.instrumentMethod(funcScope.scope, funcScope.property, transaction, 'app.angular.controller', {
            override: true
          })
        })

        controllerScope.$on('$destroy', function () {
          logger.log('opbeat.angular.controller.destroy')
        })

        controllerScope.$on('$viewContentLoaded', function (event) {

          logger.log('opbeat.angular.controller.$viewContentLoaded')

          // Transaction clean up
          transaction.end()
          $rootScope._opbeatTransactions[$location.absUrl()] = null

          if (controllerScope) {
            // Uninstrument controller scope functions
            utils.getObjectFunctions(controllerScope).forEach(function (funcScope) {
              utils.uninstrumentMethod(funcScope.scope, funcScope.property)
            })
          }

        })
      }

      logger.log('opbeat.decorator.controller.end')
      return result
    }
  })

  // Controller Instrumentation
  $provide.decorator('$controller', function ($delegate, $injector) {

    return function () {
      var $rootScope = $injector.get('$rootScope')
      var $location = $injector.get('$location')

      var args = Array.prototype.slice.call(arguments)
      var controllerInfo = utils.getControllerInfoFromArgs(args)
      var transaction = $rootScope._opbeatTransactions && $rootScope._opbeatTransactions[$location.absUrl()]

      if (controllerInfo.name) {
        if (transaction && transaction.metadata.controllerName !== controllerInfo.name) {
          return utils.instrumentModule($delegate, $injector, {
            type: 'template.angular.controller',
            prefix: 'controller.' + controllerInfo.name
          }).apply(this, arguments)
        }
      }

      return $delegate.apply(this, args)
    }

  })

  // Template Compile Instrumentation
  $provide.decorator('$compile', function ($delegate, $injector) {
    return utils.instrumentModule($delegate, $injector, {
      type: 'template.angular.$compile',
      prefix: '$compile'
    })
  })

  // Template Request Instrumentation
  $provide.decorator('$templateRequest', function ($delegate, $injector) {
    return utils.instrumentModule($delegate, $injector, {
      type: 'template.angular.request',
      prefix: '$templateRequest'
    })
  })

  // HTTP Instrumentation
  $provide.decorator('$http', function ($delegate, $injector) {
    return utils.instrumentModule($delegate, $injector, {
      type: 'ext.http.request',
      prefix: 'angular.$http',
      signatureFormatter: function(key, args) {
        var text = []
        // $http used directly
        if(key && args) {
          text = [key.toUpperCase(), args]
        } else if(!key && typeof args === 'object') {
          // $http used from $resource
          var req = args
          text = [req.method, req.url]
        }

        return text.join(' ')
      }
    })
  })

  // Core directive instrumentation
  var coreDirectives = ['ngBind', 'ngClass', 'ngModel', 'ngIf', 'ngInclude', 'ngRepeat', 'ngSrc', 'ngStyle', 'ngSwitch', 'ngTransclude']
  coreDirectives.forEach(function(name) {
    var directiveName = name + 'Directive'
    $provide.decorator(directiveName, function ($delegate, $injector) {
      utils.instrumentObject($delegate[0], $injector, {
        type: 'template.angular.directive',
        prefix: directiveName
      })
      return $delegate
    })
  })

  // ngResource Instrumentation
  $provide.decorator('$resource', function ($delegate, $injector) {
    return function () {
      var args = Array.prototype.slice.call(arguments)
      var result = $delegate.apply(this, args)
      utils.instrumentObject(result, $injector, {
        type: 'http.$resource',
        prefix: '$resource'
      })
      return result
    };
  })

}

window.angular.module('ngOpbeat', [])
  .provider('$opbeat', ngOpbeatProvider)
  .config(['$provide', $opbeatErrorProvider])
  .config(['$provide', $opbeatInstrumentationProvider])

window.angular.module('angular-opbeat', ['ngOpbeat'])
<commit_msg>Comment tweaks<commit_after>var Opbeat = require('./opbeat')
var utils = require('./instrumentation/utils')
var logger = require('./lib/logger')

function ngOpbeatProvider () {
  this.config = function config (properties) {
    Opbeat.config(properties)
  }

  this.install = function install () {
    Opbeat.install()
  }

  this.$get = [function () {
    return {
      captureException: function captureException (exception, cause) {
        Opbeat.captureException(exception, cause)
      },

      setUserContext: function setUser (user) {
        Opbeat.setUserContext(user)
      },

      setExtraContext: function setExtraContext (data) {
        Opbeat.setExtraContext(data)
      }
    }
  }]
}

function $opbeatErrorProvider ($provide) {
  $provide.decorator('$exceptionHandler', ['$delegate', '$opbeat', function $ExceptionHandlerDecorator ($delegate, $opbeat) {
    return function $ExceptionHandler (exception, cause) {
      $opbeat.captureException(exception)
      return $delegate(exception, cause)
    }
  }])
}

function $opbeatInstrumentationProvider ($provide) {

  // Route controller Instrumentation
  $provide.decorator('$controller', function ($delegate, $location, $rootScope) {
    $rootScope._opbeatTransactions = {}

    var onRouteChange = function(e, current) {
      var routeControllerTarget = current.controller
      logger.log('opbeat.decorator.controller.onRouteChange')
      var transaction = $rootScope._opbeatTransactions[$location.absUrl()]
      if (!transaction) {
        transaction = Opbeat.startTransaction('app.angular.controller.' + routeControllerTarget, 'transaction')
        transaction.metadata.controllerName = routeControllerTarget

        $rootScope._opbeatTransactions[$location.absUrl()] = transaction
      }

    }

    $rootScope.$on('$routeChangeStart', onRouteChange) // ng-router
    $rootScope.$on('$stateChangeSuccess', onRouteChange) // ui-router

    return function () {
      logger.log('opbeat.decorator.controller.ctor')

      var args = Array.prototype.slice.call(arguments)
      var transaction = $rootScope._opbeatTransactions[$location.absUrl()]
      var controllerInfo = utils.getControllerInfoFromArgs(args)
      var controllerName = controllerInfo.name
      var controllerScope = controllerInfo.scope
      var isRouteController = controllerName && transaction && transaction.metadata.controllerName === controllerName

      var result = $delegate.apply(this, args)

      if (isRouteController && controllerScope) {
        logger.log('opbeat.angular.controller', controllerName)

        // Instrument controller scope functions
        utils.getObjectFunctions(controllerScope).forEach(function (funcScope) {
          utils.instrumentMethod(funcScope.scope, funcScope.property, transaction, 'app.angular.controller', {
            override: true
          })
        })

        controllerScope.$on('$destroy', function () {
          logger.log('opbeat.angular.controller.destroy')
        })

        controllerScope.$on('$viewContentLoaded', function (event) {

          logger.log('opbeat.angular.controller.$viewContentLoaded')

          // Transaction clean up
          transaction.end()
          $rootScope._opbeatTransactions[$location.absUrl()] = null

          if (controllerScope) {
            // Uninstrument controller scope functions
            utils.getObjectFunctions(controllerScope).forEach(function (funcScope) {
              utils.uninstrumentMethod(funcScope.scope, funcScope.property)
            })
          }

        })
      }

      logger.log('opbeat.decorator.controller.end')
      return result
    }
  })

  // Controller Instrumentation
  $provide.decorator('$controller', function ($delegate, $injector) {

    return function () {
      var $rootScope = $injector.get('$rootScope')
      var $location = $injector.get('$location')

      var args = Array.prototype.slice.call(arguments)
      var controllerInfo = utils.getControllerInfoFromArgs(args)
      var transaction = $rootScope._opbeatTransactions && $rootScope._opbeatTransactions[$location.absUrl()]

      if (controllerInfo.name) {
        if (transaction && transaction.metadata.controllerName !== controllerInfo.name) {
          return utils.instrumentModule($delegate, $injector, {
            type: 'template.angular.controller',
            prefix: 'controller.' + controllerInfo.name
          }).apply(this, arguments)
        }
      }

      return $delegate.apply(this, args)
    }

  })

  // Template Compile Instrumentation
  $provide.decorator('$compile', function ($delegate, $injector) {
    return utils.instrumentModule($delegate, $injector, {
      type: 'template.angular.$compile',
      prefix: '$compile'
    })
  })

  // Template Request Instrumentation
  $provide.decorator('$templateRequest', function ($delegate, $injector) {
    return utils.instrumentModule($delegate, $injector, {
      type: 'template.angular.request',
      prefix: '$templateRequest'
    })
  })

  // HTTP Instrumentation
  $provide.decorator('$http', function ($delegate, $injector) {
    return utils.instrumentModule($delegate, $injector, {
      type: 'ext.http.request',
      prefix: 'angular.$http',
      signatureFormatter: function(key, args) {
        var text = []
        // $http used directly
        if(key && args) {
          text = [key.toUpperCase(), args]
        } else if(!key && typeof args === 'object') {
          // $http used from $resource
          var req = args
          text = [req.method, req.url]
        }

        return text.join(' ')
      }
    })
  })

  // Core directive instrumentation
  var coreDirectives = ['ngBind', 'ngClass', 'ngModel', 'ngIf', 'ngInclude', 'ngRepeat', 'ngSrc', 'ngStyle', 'ngSwitch', 'ngTransclude']
  coreDirectives.forEach(function(name) {
    var directiveName = name + 'Directive'
    $provide.decorator(directiveName, function ($delegate, $injector) {
      utils.instrumentObject($delegate[0], $injector, {
        type: 'template.angular.directive',
        prefix: directiveName
      })
      return $delegate
    })
  })

  // ngResource instrumentation
  $provide.decorator('$resource', function ($delegate, $injector) {
    return function () {
      var args = Array.prototype.slice.call(arguments)
      var result = $delegate.apply(this, args)
      utils.instrumentObject(result, $injector, {
        type: 'http.$resource',
        prefix: '$resource'
      })
      return result
    };
  })

}

window.angular.module('ngOpbeat', [])
  .provider('$opbeat', ngOpbeatProvider)
  .config(['$provide', $opbeatErrorProvider])
  .config(['$provide', $opbeatInstrumentationProvider])

window.angular.module('angular-opbeat', ['ngOpbeat'])
<|endoftext|>


<commit_before>var test = require('tape');
var MoedCore = require('../..');

function setup() {
	var engine = new MoedCore();
	var s = engine.context.settings;
	var win = s.get('windows.current');

	return {
		engine: engine,
		c: engine.context.commands,
		s: s,
		win: win // :D
	};
}

test('can move the cursor to the specified position', function (t) {
	t.plan(2);
	var position = {
		x: 5,
		y: 0
	};
	this.c.moveCursor(this.win, position);
	t.strictEqual(this.win.cursor.x, position.x, 'x is correct');
	t.strictEqual(this.win.cursor.y, position.y, 'y is correct');
}.bind(setup()));
<commit_msg>Made the cursor move test update the y as well.<commit_after>var test = require('tape');
var MoedCore = require('../..');

function setup() {
	var engine = new MoedCore();
	var s = engine.context.settings;
	var win = s.get('windows.current');

	return {
		engine: engine,
		c: engine.context.commands,
		s: s,
		win: win // :D
	};
}

test('can move the cursor to the specified position', function (t) {
	t.plan(2);
	var position = {
		x: 5,
		y: 2
	};
	this.c.moveCursor(this.win, position);
	t.strictEqual(this.win.cursor.x, position.x, 'x is correct');
	t.strictEqual(this.win.cursor.y, position.y, 'y is correct');
}.bind(setup()));
<|endoftext|>