<commit_before>/*
	GEIESPICTURES - The picture language from chapter 2 of SICP
	Author: Marco Faustinelli (contacts@faustinelli.net)
	Web: http://faustinelli.net/
	     http://faustinelli.wordpress.com/
	Version: 0.1 - Requires Geieslists 1.1 and Geiesvectors 0.1

	The MIT License - Copyright (c) 2016 Geiespictures Project
*/

var expect = chai.expect;
//		expect(head(head(listlA_BA))).to.be.equal('a');
//		expect(isEmpty(tail(head(listlA_BA)))).to.be.ok;
//		expect(isEmpty(tail(EMPTY))).to.be.not.ok;

var probe = () => { return {}; }

var fakeCtx = () => probe => { return {
    canvas: {
        clientWidth: 400,
        clientHeight: 200
    },
    resetTransform: () => {},
    beginPath: () => {},
    moveTo: (x,y) => { probe.startX = x; probe.startY = y; },
    lineTo: (x,y) => { probe.endX = x; probe.endY = y; },
    stroke: () => {},
    transform: (m11, m21, m12, m22, m13, m23) => {
        probe._11 = m11;
        probe._21 = m21;
        probe._12 = m12;
        probe._22 = m22;
        probe._13 = m13;
        probe._23 = m23;
      }
    };
};

describe('a sound picture system requires', function () {

  it('a series of vector operations that allow to sum, subtract and scale vectors', function() {

    var pippo = V.make_vect(-3,1);
    var pluto = V.make_vect(-2,3);
    var luigi = V.add_vect(pippo, pluto);
    var luigi2 = V.sub_vect(pippo, pluto);
    expect(luigi.c).to.be.equal('[-5,4]');
    expect(luigi2.c).to.be.equal('[-1,-2]');
    expect(V.length_vect(pippo)).to.be.gt(3.16);
    expect(V.length_vect(pippo)).to.be.lt(3.17);

  });
  it('a series of matrix operations for HTML5 canvases', function() {

    var pippo = V.transf_matrix(11,12,13,21,22,23,31,32,33);
    expect(pippo._11()).to.be.equal(11);
    expect(pippo._12()).to.be.equal(12);
    expect(pippo._13()).to.be.equal(13);
    expect(pippo._21()).to.be.equal(21);
    expect(pippo._22()).to.be.equal(22);
    expect(pippo._23()).to.be.equal(23);
    expect(pippo._31()).to.be.equal(31);
    expect(pippo._32()).to.be.equal(32);
    expect(pippo._33()).to.be.equal(33);

    var x = V.transf_matrix(1,2,3,4,5,6,7,8,9);
    var y = V.transf_matrix(5,6,7,8,9,10,11,12,13);
    var xy = V.mult_matrix(x,y);
    expect(xy._11()).to.be.equal(54);
    expect(xy._12()).to.be.equal(60);
    expect(xy._13()).to.be.equal(66);
    expect(xy._21()).to.be.equal(126);
    expect(xy._22()).to.be.equal(141);
    expect(xy._23()).to.be.equal(156);
    expect(xy._31()).to.be.equal(198);
    expect(xy._32()).to.be.equal(222);
    expect(xy._33()).to.be.equal(246);

    var x_plus_y = V.add_matrix(x,y);
    expect(x_plus_y._11()).to.be.equal(6);
    expect(x_plus_y._12()).to.be.equal(8);
    expect(x_plus_y._13()).to.be.equal(10);
    expect(x_plus_y._21()).to.be.equal(12);
    expect(x_plus_y._22()).to.be.equal(14);
    expect(x_plus_y._23()).to.be.equal(16);
    expect(x_plus_y._31()).to.be.equal(18);
    expect(x_plus_y._32()).to.be.equal(20);
    expect(x_plus_y._33()).to.be.equal(22);

    var translated = V.translation_matrix(2,5);
    expect(translated._11()).to.be.equal(1);
    expect(translated._12()).to.be.equal(0);
    expect(translated._13()).to.be.equal(2);
    expect(translated._21()).to.be.equal(0);
    expect(translated._22()).to.be.equal(1);
    expect(translated._23()).to.be.equal(5);
    expect(translated._31()).to.be.equal(0);
    expect(translated._32()).to.be.equal(0);
    expect(translated._33()).to.be.equal(1);

    var scaled = V.scaling_matrix(2,5);
    expect(scaled._11()).to.be.equal(2);
    expect(scaled._12()).to.be.equal(0);
    expect(scaled._13()).to.be.equal(0);
    expect(scaled._21()).to.be.equal(0);
    expect(scaled._22()).to.be.equal(5);
    expect(scaled._23()).to.be.equal(0);
    expect(scaled._31()).to.be.equal(0);
    expect(scaled._32()).to.be.equal(0);
    expect(scaled._33()).to.be.equal(1);

    var rot30 = V.rotation_matrix(Math.PI/6);
    expect(Math.round(100*rot30._11())).to.be.equal(87);
    expect(Math.round(100*rot30._12())).to.be.equal(-50);
    expect(Math.round(100*rot30._13())).to.be.equal(0);
    expect(Math.round(100*rot30._21())).to.be.equal(50);
    expect(Math.round(100*rot30._22())).to.be.equal(87);
    expect(Math.round(100*rot30._23())).to.be.equal(0);
    expect(Math.round(100*rot30._31())).to.be.equal(0);
    expect(Math.round(100*rot30._32())).to.be.equal(0);
    expect(Math.round(100*rot30._33())).to.be.equal(100);

    var trasl200x = V.translation_matrix(200,0);
    var scale2x = V.scaling_matrix(2,1);

    // translate then rotate then scale
    var traslRotoScale = V.mult_matrix(V.mult_matrix(trasl200x,rot30),scale2x);
    // next one is (1.73,1,-.5,.87,200,0)
    expect(Math.round(100*traslRotoScale._11())).to.be.equal(173);
    expect(Math.round(100*traslRotoScale._12())).to.be.equal(-50);
    expect(Math.round(100*traslRotoScale._13())).to.be.equal(20000);
    expect(Math.round(100*traslRotoScale._21())).to.be.equal(100);
    expect(Math.round(100*traslRotoScale._22())).to.be.equal(87);
    expect(Math.round(100*traslRotoScale._23())).to.be.equal(0);
    expect(Math.round(100*traslRotoScale._31())).to.be.equal(0);
    expect(Math.round(100*traslRotoScale._32())).to.be.equal(0);
    expect(Math.round(100*traslRotoScale._33())).to.be.equal(100);

    // rotate then translate then scale
    var rotoTraslScale = V.mult_matrix(V.mult_matrix(rot30,trasl200x),scale2x);
    // next one is (1.73,1,-.5,.87,174,100) <-- !!
    expect(Math.round(100*rotoTraslScale._11())).to.be.equal(173);
    expect(Math.round(100*rotoTraslScale._12())).to.be.equal(-50);
    expect(Math.round(100*rotoTraslScale._13())).to.be.equal(17321);
    expect(Math.round(100*rotoTraslScale._21())).to.be.equal(100);
    expect(Math.round(100*rotoTraslScale._22())).to.be.equal(87);
    expect(Math.round(100*rotoTraslScale._23())).to.be.equal(10000);
    expect(Math.round(100*rotoTraslScale._31())).to.be.equal(0);
    expect(Math.round(100*rotoTraslScale._32())).to.be.equal(0);
    expect(Math.round(100*rotoTraslScale._33())).to.be.equal(100);

    var deg30 = V.make_vect(.87,.5);
    expect(Math.round(100*V.angle_vect(deg30))).to.be.equal(52);
    var minusDeg30 = V.make_vect(.87,-.5);
    expect(Math.round(100*V.angle_vect(minusDeg30))).to.be.equal(-52);
    var minusDeg60 = V.make_vect(.5,-.87);
    expect(Math.round(100*V.angle_vect(minusDeg60))).to.be.equal(-105);
    var deg120 = V.make_vect(-.5,.87);
    expect(Math.round(100*V.angle_vect(deg120))).to.be.equal(209);

    var testProbe = probe();
    var fakeContext = fakeCtx()(testProbe);
    var transfMatrix = V.transf_matrix(11,12,13,21,22,23,31,32,33);
    P.transform_ctx(fakeContext, transfMatrix);
    expect(testProbe._11).to.be.equal(11);
    expect(testProbe._21).to.be.equal(21);
    expect(testProbe._12).to.be.equal(12);
    expect(testProbe._22).to.be.equal(22);
    expect(testProbe._13).to.be.equal(13);
    expect(testProbe._23).to.be.equal(23);
  });
  it('definitions for fold left and right', function() {

    expect(L.fold((x,ys)=>x+ys,0,L.ArrayToList([1,3,5]))).to.be.equal(9);
    expect(L.foldl((acc,x)=>acc+x,0,L.ArrayToList([1,3,6]))).to.be.equal(10);

  });

});
describe('a sound picture system entails', function () {

  it('functions to handle frames', function() {

    var origin = V.make_vect(-3,1);
    var edge1 = V.make_vect(5,3);
    var edge2 = V.make_vect(-2,3);
    var fram1 = P.make_frame(origin, edge1, edge2);

    expect(P.origin_frame(fram1).c).to.be.equal('[-3,1]');
    expect(P.edge1_frame(fram1).c).to.be.equal('[5,3]');
    expect(P.edge2_frame(fram1).c).to.be.equal('[-2,3]');

  });
  it('a coordinateMapper, measuring pixels relative to the canvas origin', function() {

    var origin = V.make_vect(-3,1);
    var edge1 = V.make_vect(5,3);
    var edge2 = V.make_vect(-2,3);
    var fram1 = P.make_frame(origin, edge1, edge2);

    var coordinateMapper1 = P.frame_coord_map(fram1);
    var unitVec = V.make_vect(1,1);
    var testVec = V.make_vect(2,2);
    var mappedUnit = coordinateMapper1(unitVec);
    var mappedTest = coordinateMapper1(testVec);

    expect(mappedUnit.c).to.be.equal('[0,7]');
    expect(mappedTest.c).to.be.equal('[3,13]');

  });
  it('a relative coordinateMapper, measuring pixels relative to the frame origin', function() {

    var origin = V.make_vect(-3,1);
    var edge1 = V.make_vect(5,3);
    var edge2 = V.make_vect(-2,3);
    var fram1 = P.make_frame(origin, edge1, edge2);

    var relCoordinateMapper = P.rel_frame_coord_map(fram1);
    var unitVec = V.make_vect(1,1);
    var testVec = V.make_vect(2,2);
    var mappedUnit = relCoordinateMapper(unitVec);
    var mappedTest = relCoordinateMapper(testVec);

    expect(mappedUnit.c).to.be.equal('[3,6]');
    expect(mappedTest.c).to.be.equal('[6,12]');

  });
  it('a segments painter that maps frames into DOM canvas, adapting the coordinates', function() {

    var origin = V.make_vect(-100,25);
    var edge1 = V.make_vect(0,50);
    var edge2 = V.make_vect(-100,0);
    var fram1 = P.make_frame(origin, edge1, edge2);

    var testProbe = probe();
    var fakeContext = fakeCtx()(testProbe)

    // drawing a diagonal from frame origin to opposite corner
    P.segments_painter([P.make_segment(V.make_vect(0,0),V.make_vect(1,1))])(fram1)(fakeContext);

    expect(testProbe.startX).to.be.equal(100);
    expect(testProbe.startY).to.be.equal(75);
    expect(testProbe.endX).to.be.equal(0);
    expect(testProbe.endY).to.be.equal(25);

  });
});
<commit_msg>last touches<commit_after>/*
	GEIESPICTURES - The picture language from chapter 2 of SICP
	Author: Marco Faustinelli (contacts@faustinelli.net)
	Web: http://faustinelli.net/
	     http://faustinelli.wordpress.com/
	Version: 0.1 - Requires Geieslists 1.1 and Geiesvectors 0.1

	The MIT License - Copyright (c) 2016 Geiespictures Project
*/

var expect = chai.expect;
//		expect(head(head(listlA_BA))).to.be.equal('a');
//		expect(isEmpty(tail(head(listlA_BA)))).to.be.ok;
//		expect(isEmpty(tail(EMPTY))).to.be.not.ok;

var probe = () => { return {}; }

var fakeCtx = () => probe => { return {
    canvas: {
        clientWidth: 400,
        clientHeight: 200
    },
    resetTransform: () => {},
    beginPath: () => {},
    moveTo: (x,y) => { probe.startX = x; probe.startY = y; },
    lineTo: (x,y) => { probe.endX = x; probe.endY = y; },
    stroke: () => {},
    transform: (m11, m21, m12, m22, m13, m23) => {
        probe._11 = m11;
        probe._21 = m21;
        probe._12 = m12;
        probe._22 = m22;
        probe._13 = m13;
        probe._23 = m23;
      }
    };
};

describe('a sound picture system requires', function () {

  it('a series of vector operations that allow to sum, subtract and scale vectors', function() {

    var pippo = V.make_vect(-3,1);
    var pluto = V.make_vect(-2,3);
    var luigi = V.add_vect(pippo, pluto);
    var luigi2 = V.sub_vect(pippo, pluto);
    expect(luigi.c).to.be.equal('[-5,4]');
    expect(luigi2.c).to.be.equal('[-1,-2]');
    expect(V.length_vect(pippo)).to.be.gt(3.16);
    expect(V.length_vect(pippo)).to.be.lt(3.17);

  });
  it('a series of matrix operations for HTML5 canvases', function() {

    var pippo = V.transf_matrix(11,12,13,21,22,23,31,32,33);
    expect(pippo._11()).to.be.equal(11);
    expect(pippo._12()).to.be.equal(12);
    expect(pippo._13()).to.be.equal(13);
    expect(pippo._21()).to.be.equal(21);
    expect(pippo._22()).to.be.equal(22);
    expect(pippo._23()).to.be.equal(23);
    expect(pippo._31()).to.be.equal(31);
    expect(pippo._32()).to.be.equal(32);
    expect(pippo._33()).to.be.equal(33);

    var x = V.transf_matrix(1,2,3,4,5,6,7,8,9);
    var y = V.transf_matrix(5,6,7,8,9,10,11,12,13);
    var xy = V.mult_matrix(x,y);
    expect(xy._11()).to.be.equal(54);
    expect(xy._12()).to.be.equal(60);
    expect(xy._13()).to.be.equal(66);
    expect(xy._21()).to.be.equal(126);
    expect(xy._22()).to.be.equal(141);
    expect(xy._23()).to.be.equal(156);
    expect(xy._31()).to.be.equal(198);
    expect(xy._32()).to.be.equal(222);
    expect(xy._33()).to.be.equal(246);

    var x_plus_y = V.add_matrix(x,y);
    expect(x_plus_y._11()).to.be.equal(6);
    expect(x_plus_y._12()).to.be.equal(8);
    expect(x_plus_y._13()).to.be.equal(10);
    expect(x_plus_y._21()).to.be.equal(12);
    expect(x_plus_y._22()).to.be.equal(14);
    expect(x_plus_y._23()).to.be.equal(16);
    expect(x_plus_y._31()).to.be.equal(18);
    expect(x_plus_y._32()).to.be.equal(20);
    expect(x_plus_y._33()).to.be.equal(22);

    var translated = V.translation_matrix(2,5);
    expect(translated._11()).to.be.equal(1);
    expect(translated._12()).to.be.equal(0);
    expect(translated._13()).to.be.equal(2);
    expect(translated._21()).to.be.equal(0);
    expect(translated._22()).to.be.equal(1);
    expect(translated._23()).to.be.equal(5);
    expect(translated._31()).to.be.equal(0);
    expect(translated._32()).to.be.equal(0);
    expect(translated._33()).to.be.equal(1);

    var scaled = V.scaling_matrix(2,5);
    expect(scaled._11()).to.be.equal(2);
    expect(scaled._12()).to.be.equal(0);
    expect(scaled._13()).to.be.equal(0);
    expect(scaled._21()).to.be.equal(0);
    expect(scaled._22()).to.be.equal(5);
    expect(scaled._23()).to.be.equal(0);
    expect(scaled._31()).to.be.equal(0);
    expect(scaled._32()).to.be.equal(0);
    expect(scaled._33()).to.be.equal(1);

    var rot30 = V.rotation_matrix(Math.PI/6);
    expect(Math.round(100*rot30._11())).to.be.equal(87);
    expect(Math.round(100*rot30._12())).to.be.equal(-50);
    expect(Math.round(100*rot30._13())).to.be.equal(0);
    expect(Math.round(100*rot30._21())).to.be.equal(50);
    expect(Math.round(100*rot30._22())).to.be.equal(87);
    expect(Math.round(100*rot30._23())).to.be.equal(0);
    expect(Math.round(100*rot30._31())).to.be.equal(0);
    expect(Math.round(100*rot30._32())).to.be.equal(0);
    expect(Math.round(100*rot30._33())).to.be.equal(100);

    var trasl200x = V.translation_matrix(200,0);
    var scale2x = V.scaling_matrix(2,1);

    // translate then rotate then scale
    var traslRotoScale = V.mult_matrix(V.mult_matrix(trasl200x,rot30),scale2x);
    // next one is (1.73,1,-.5,.87,200,0)
    expect(Math.round(100*traslRotoScale._11())).to.be.equal(173);
    expect(Math.round(100*traslRotoScale._12())).to.be.equal(-50);
    expect(Math.round(100*traslRotoScale._13())).to.be.equal(20000);
    expect(Math.round(100*traslRotoScale._21())).to.be.equal(100);
    expect(Math.round(100*traslRotoScale._22())).to.be.equal(87);
    expect(Math.round(100*traslRotoScale._23())).to.be.equal(0);
    expect(Math.round(100*traslRotoScale._31())).to.be.equal(0);
    expect(Math.round(100*traslRotoScale._32())).to.be.equal(0);
    expect(Math.round(100*traslRotoScale._33())).to.be.equal(100);

    // rotate then translate then scale
    var rotoTraslScale = V.mult_matrix(V.mult_matrix(rot30,trasl200x),scale2x);
    // next one is (1.73,1,-.5,.87,174,100) <-- !!
    expect(Math.round(100*rotoTraslScale._11())).to.be.equal(173);
    expect(Math.round(100*rotoTraslScale._12())).to.be.equal(-50);
    expect(Math.round(100*rotoTraslScale._13())).to.be.equal(17321);
    expect(Math.round(100*rotoTraslScale._21())).to.be.equal(100);
    expect(Math.round(100*rotoTraslScale._22())).to.be.equal(87);
    expect(Math.round(100*rotoTraslScale._23())).to.be.equal(10000);
    expect(Math.round(100*rotoTraslScale._31())).to.be.equal(0);
    expect(Math.round(100*rotoTraslScale._32())).to.be.equal(0);
    expect(Math.round(100*rotoTraslScale._33())).to.be.equal(100);

    var deg30 = V.make_vect(.87,.5);
    expect(Math.round(100*V.angle_vect(deg30))).to.be.equal(52);
    var minusDeg30 = V.make_vect(.87,-.5);
    expect(Math.round(100*V.angle_vect(minusDeg30))).to.be.equal(-52);
    var minusDeg60 = V.make_vect(.5,-.87);
    expect(Math.round(100*V.angle_vect(minusDeg60))).to.be.equal(-105);
    var deg120 = V.make_vect(-.5,.87);
    expect(Math.round(100*V.angle_vect(deg120))).to.be.equal(209);

    var testProbe = probe();
    var fakeContext = fakeCtx()(testProbe);
    var transfMatrix = V.transf_matrix(11,12,13,21,22,23,31,32,33);
    P.transform_ctx(fakeContext, transfMatrix);
    expect(testProbe._11).to.be.equal(11);
    expect(testProbe._21).to.be.equal(21);
    expect(testProbe._12).to.be.equal(12);
    expect(testProbe._22).to.be.equal(22);
    expect(testProbe._13).to.be.equal(13);
    expect(testProbe._23).to.be.equal(23);
  });
  it('definitions for fold left and right', function() {

    expect(L.fold((x,ys)=>x+ys,0,L.ArrayToList([1,3,5]))).to.be.equal(9);
    expect(L.foldl((acc,x)=>acc+x,0,L.ArrayToList([1,3,6]))).to.be.equal(10);

  });

});
describe('a sound picture system entails', function () {

  it('functions to handle frames', function() {

    var origin = V.make_vect(-3,1);
    var edge1 = V.make_vect(5,3);
    var edge2 = V.make_vect(-2,3);
    var fram1 = P.make_frame(origin, edge1, edge2);

    expect(P.origin_frame(fram1).c).to.be.equal('[-3,1]');
    expect(P.edge1_frame(fram1).c).to.be.equal('[5,3]');
    expect(P.edge2_frame(fram1).c).to.be.equal('[-2,3]');

  });
  it('a coordinateMapper, measuring pixels relative to the canvas origin', function() {

    var origin = V.make_vect(-3,1);
    var edge1 = V.make_vect(5,3);
    var edge2 = V.make_vect(-2,3);
    var fram1 = P.make_frame(origin, edge1, edge2);

    var coordinateMapper1 = P.frame_coord_map(fram1);
    var unitVec = V.make_vect(1,1);
    var testVec = V.make_vect(2,2);
    var mappedUnit = coordinateMapper1(unitVec);
    var mappedTest = coordinateMapper1(testVec);

    expect(mappedUnit.c).to.be.equal('[0,7]');
    expect(mappedTest.c).to.be.equal('[3,13]');

  });
  it('a segments painter that maps frames into DOM canvas, adapting the coordinates', function() {

    var origin = V.make_vect(-100,25);
    var edge1 = V.make_vect(0,50);
    var edge2 = V.make_vect(-100,0);
    var fram1 = P.make_frame(origin, edge1, edge2);

    var testProbe = probe();
    var fakeContext = fakeCtx()(testProbe)

    // drawing a diagonal from frame origin to opposite corner
    P.segments_painter([P.make_segment(V.make_vect(0,0),V.make_vect(1,1))])(fram1)(fakeContext);

    expect(testProbe.startX).to.be.equal(100);
    expect(testProbe.startY).to.be.equal(75);
    expect(testProbe.endX).to.be.equal(0);
    expect(testProbe.endY).to.be.equal(25);

  });
});
<|endoftext|>


<commit_before>/*
Gordon Williams (gw@pur3.co.uk)

If we're running in an iframe, this gets enabled and allows the IDE
to work by passing messages using window.postMessage.

Use embed.js on the client side to link this in.
*/

  var emu;

(function() {
  var callbacks = {};

  function post(msg) {
    if (!emu) return;
    msg.for="emu";
    emu.postMessage(msg,"*");
  }

  window.addEventListener('message', function(e) {
    if (!emu) return;
    var event = e.data;
    //if (typeof event!="object" || event.from!="emu") return;
    console.log("EMU HOST MESSAGE", event);
    switch (event.type) {
      case "init":
        console.log("EMU frame initialised");
        emu.addEventListener('unload', function(e) {
          console.log("EMU frame closed");
          callbacks.disconnected();
          callbacks = {};
        });
        callbacks.open("success");
        break;
      case "tx": {
        var d = event.data;
        var a = new Uint8Array(d.length);
        for (var i=0;i<d.length;i++) a[i]=d.charCodeAt(i);
        callbacks.receive(a.buffer);
        break;
      }
    }
  });

  var device = {
    "name" : "Emulator",
    "init" : function() {
      Espruino.Core.Config.add("EMULATOR_BANGLEJS", {
        section : "Communications",
        name : "Enable Bangle.js Emulator",
        description : "The size of font used in the Terminal and Code Editor windows",
        type : "boolean",
        defaultValue : true
      });
    },
    "getStatus": function(ignoreSettings) {
      if (!Espruino.Config.EMULATOR_BANGLEJS && !ignoreSettings)
        return {warning:"Disabled in Communications Settings"};
      return true;
    },
    "getPorts": function(callback) {
      var emulatorRequested = window.location.search.substr(1).split("&").includes("emulator");
      if (emulatorRequested)
        Espruino.Config.set("EMULATOR_BANGLEJS", true);
      if (!Espruino.Config.EMULATOR_BANGLEJS)
        return callback([]);
      var port = {
        path:'Emulator',
        description:'Bangle.js Emulator',
        type:"emulator"};
      if (emulatorRequested)
        port.autoconnect = true;
      callback([port], true/*instantPorts*/);
    },
    "open": function(path, openCallback, receiveCallback, disconnectCallback) {
      callbacks.open = openCallback;
      callbacks.receive = receiveCallback;
      callbacks.disconnected = function() {
        emu = undefined;
        callbacks = {};
        disconnectCallback();
      }
      var url = window.location.pathname;
      if (url.includes("/"))
        url = url.substr(0,url.lastIndexOf("/"));
      url = window.location.origin + url + "/emu_banglejs.html";
      emu = window.open(url, "banglewindow", "innerWidth=270,innerHeight=248,location=0");
      setTimeout(function() {
        post({type:"init"});
      }, 500);

    },
    "write": function(d, callback) {
      post({type:"rx",data:d});
      setTimeout(callback,10);
    },
    "close": function() {
      emu.close();
      //callbacks.disconnected(); // called by emu.onunload
      emu = undefined;
    },
  };
  Espruino.Core.Serial.devices.push(device);
})();
<commit_msg>bump window size for some platforms<commit_after>/*
Gordon Williams (gw@pur3.co.uk)

If we're running in an iframe, this gets enabled and allows the IDE
to work by passing messages using window.postMessage.

Use embed.js on the client side to link this in.
*/

  var emu;

(function() {
  var callbacks = {};

  function post(msg) {
    if (!emu) return;
    msg.for="emu";
    emu.postMessage(msg,"*");
  }

  window.addEventListener('message', function(e) {
    if (!emu) return;
    var event = e.data;
    //if (typeof event!="object" || event.from!="emu") return;
    console.log("EMU HOST MESSAGE", event);
    switch (event.type) {
      case "init":
        console.log("EMU frame initialised");
        emu.addEventListener('unload', function(e) {
          console.log("EMU frame closed");
          callbacks.disconnected();
          callbacks = {};
        });
        callbacks.open("success");
        break;
      case "tx": {
        var d = event.data;
        var a = new Uint8Array(d.length);
        for (var i=0;i<d.length;i++) a[i]=d.charCodeAt(i);
        callbacks.receive(a.buffer);
        break;
      }
    }
  });

  var device = {
    "name" : "Emulator",
    "init" : function() {
      Espruino.Core.Config.add("EMULATOR_BANGLEJS", {
        section : "Communications",
        name : "Enable Bangle.js Emulator",
        description : "The size of font used in the Terminal and Code Editor windows",
        type : "boolean",
        defaultValue : true
      });
    },
    "getStatus": function(ignoreSettings) {
      if (!Espruino.Config.EMULATOR_BANGLEJS && !ignoreSettings)
        return {warning:"Disabled in Communications Settings"};
      return true;
    },
    "getPorts": function(callback) {
      var emulatorRequested = window.location.search.substr(1).split("&").includes("emulator");
      if (emulatorRequested)
        Espruino.Config.set("EMULATOR_BANGLEJS", true);
      if (!Espruino.Config.EMULATOR_BANGLEJS)
        return callback([]);
      var port = {
        path:'Emulator',
        description:'Bangle.js Emulator',
        type:"emulator"};
      if (emulatorRequested)
        port.autoconnect = true;
      callback([port], true/*instantPorts*/);
    },
    "open": function(path, openCallback, receiveCallback, disconnectCallback) {
      callbacks.open = openCallback;
      callbacks.receive = receiveCallback;
      callbacks.disconnected = function() {
        emu = undefined;
        callbacks = {};
        disconnectCallback();
      }
      var url = window.location.pathname;
      if (url.includes("/"))
        url = url.substr(0,url.lastIndexOf("/"));
      url = window.location.origin + url + "/emu_banglejs.html";
      emu = window.open(url, "banglewindow", "innerWidth=280,innerHeight=258,location=0");
      setTimeout(function() {
        post({type:"init"});
      }, 500);

    },
    "write": function(d, callback) {
      post({type:"rx",data:d});
      setTimeout(callback,10);
    },
    "close": function() {
      emu.close();
      //callbacks.disconnected(); // called by emu.onunload
      emu = undefined;
    },
  };
  Espruino.Core.Serial.devices.push(device);
})();
<|endoftext|>


<commit_before>/**
 * The user's current locale
 */
Saiku.i18n = {
    locale: (navigator.language || navigator.browserLanguage ||
        navigator.systemLanguage || navigator.userLanguage).substring(0,2).toLowerCase(),
    po_file: {},
    translate: function() {
        $('.i18n').i18n(Saiku.i18n.po_file);
    },
    automatic_i18n: function() {
        // Load language file if it isn't English
        if (Saiku.i18n.locale != "en") {
            $.ajax({
                url: "js/saiku/plugins/I18n/po/" + Saiku.i18n.locale + ".json",
                type: 'GET',
                dataType: 'json',
                success: function(data) {
                    Saiku.i18n.po_file = data;
                    Saiku.i18n.translate();
                }
            });
        }

        return true;
    },
    elements: [],
    improve_translation: function() {
        Saiku.tabs.add(new TranslationTab());
        return false;
    }
};

/**
 * jQuery plugin for i18n
 */
(function( $ ){
	/**
	 * Internationalize selected elements with the provided PO file
	 */
	$.fn.i18n = function(po_file) {
		// If no PO file is provided, then don't translate anything
		if (! po_file)
			return this;

		// If key is not found, return original language
		var translate = function(key, po_file) {
			if (typeof po_file[key] == "undefined") {
				return "";
			} else {
				return po_file[key];
			}
		};

		// Iterate over UI elements that need to be translated
		return $.each(this, function() {
			element = $(this);

			// Translate text
			if (element.html()) {
				translated_text = translate( element.html(), po_file );
                if (Saiku.i18n.elements.indexOf &&
                    Saiku.i18n.elements.indexOf(element.html()) === -1) {
                    Saiku.i18n.elements.push(element.html());
                }
				if (translated_text) {
					element.data('original', element.html());
					element.html(translated_text);
					element.removeClass('i18n');
				}
			}

			// Translate title
			if (element.attr('title')) {
				translated_title = translate( element.attr('title'), po_file );
                if (Saiku.i18n.elements.indexOf &&
                    Saiku.i18n.elements.indexOf(element.attr('title')) === -1) {
                    Saiku.i18n.elements.push(element.attr('title'));
                }
				if (translated_title) {
					element.data('original', element.attr('title'));
					element.attr({ 'title': translated_title });
					element.removeClass('i18n');
				}
			}

			// Remove class so this element isn't repeatedly translated
			if (element.hasClass('i18n')) {
			    element.addClass('i18n_failed');
			}
			element.addClass('i18n_translated');
		});
	};

	$.fn.un_i18n = function() {
		// Iterate over UI elements to replace the original text
		return $.each(this, function() {
			element = $(this);

			if (element.text())
				element.text(element.data('original'));

			if (element.attr('title'))
				element.attr({ 'title': element.data('original') });

			element.addClass('i18n');
			element.removeClass('i18n_translated')
			    .removeClass('i18n_failed');
		});
	};
})( jQuery );

/**
 * Backbone view which allows users to submit translations
 */
var TranslationTab = Backbone.View.extend({
    className: 'workspace_area',
    caption: function() {
        return Saiku.i18n.po_file["Improve this translation"] ?
            Saiku.i18n.po_file["Improve this translation"] :
            "Improve this translation";
    },
    events: {
        'submit form': 'submit',
        'change input': 'mark'
    },
    initialize: function() {
        $(window).resize(this.adjust);
        $(this.el).focus(this.adjust);
        this.adjust();
    },
    render: function() {
        var translation_table = {};
        for (var i = 0; i < Saiku.i18n.elements.length; i++) {
            translation_table[Saiku.i18n.elements[i]] = {
                value: Saiku.i18n.po_file[Saiku.i18n.elements[i]],
                name: encodeURI(Saiku.i18n.elements[i])
            };
        }
        var table = _.template("<form class='workspace_results'>" +
        	"Your name: <input type='text' name='translator_name' />" +
            "
Please fill in the appropriate translation in the blanks provided:
" +
        	"<% _.each(translation_table, function(val, key) { %>" +
            "
<%= key %>
" +
            "<input type='text' value='<%= val.value %>' name='<%= val.name %>' />" +
            "
" +
            "<% }); %>" +
            "
<input class='submit-translation' type='submit' value='Submit translation' />
" +
            "</form>")({
                translation_table: translation_table
            });
        $(this.el).html(table).find('div').css({
            float: 'left',
            padding: '20px'
        }).find('input').css({
            width: '300px'
        });
        $(this.el).find('.submit-translation').css({
            padding: '20px'
        });
    },

    mark: function(event) {
        $(event.target).addClass('changed');
    },

    submit: function() {
        var translation = { locale: Saiku.i18n.locale };
        $(this.el).find('.changed').each(function(element) {
            translation[decodeURI($(this).attr('name'))] = encodeURI($(this).val());
        });
        Translate.log(translation);
        Saiku.ui.block('Thank you for improving our translation!');
        this.tab.remove();
        _.delay(function() {
            Saiku.ui.unblock();
        }, 1000);
        return false;
    },

    adjust: function() {
        $(this.el).height($("body").height() - 87);
    }
});

/**
 * Automatically internationalize the UI based on the user's locale
 */
Saiku.i18n.automatic_i18n();

/**
 * Add translate button
 */
Saiku.events.bind('toolbar:render', function(args) {
    if (Saiku.i18n.locale != "en") {
        var $link = $("").text(Saiku.i18n.locale)
            .attr({
                href: "#translate",
                title: "Improve this translation"
            })
            .click(Saiku.i18n.improve_translation)
            .addClass('sprite translate i18n');
        var $li = $("
").append($link);
        $(args.toolbar.el).find('ul').append($li);
    }
});

/**
 * Bind to new workspace
 */
Saiku.events.bind('session:new', function() {
    // Translate elements already rendered
    Saiku.i18n.translate();

    // Translate new workspaces
    Saiku.session.bind('tab:add', Saiku.i18n.translate);
});

/**
 * Initialize Loggly input for user-provided translations
 */
window.Translate = new logger({
    url: Settings.TELEMETRY_SERVER + '/input/translations'
});<commit_msg>Handle missing logger<commit_after>/**
 * The user's current locale
 */
Saiku.i18n = {
    locale: (navigator.language || navigator.browserLanguage ||
        navigator.systemLanguage || navigator.userLanguage).substring(0,2).toLowerCase(),
    po_file: {},
    translate: function() {
        $('.i18n').i18n(Saiku.i18n.po_file);
    },
    automatic_i18n: function() {
        // Load language file if it isn't English
        if (Saiku.i18n.locale != "en") {
            $.ajax({
                url: "js/saiku/plugins/I18n/po/" + Saiku.i18n.locale + ".json",
                type: 'GET',
                dataType: 'json',
                success: function(data) {
                    Saiku.i18n.po_file = data;
                    Saiku.i18n.translate();
                }
            });
        }

        return true;
    },
    elements: [],
    improve_translation: function() {
        Saiku.tabs.add(new TranslationTab());
        return false;
    }
};

/**
 * jQuery plugin for i18n
 */
(function( $ ){
	/**
	 * Internationalize selected elements with the provided PO file
	 */
	$.fn.i18n = function(po_file) {
		// If no PO file is provided, then don't translate anything
		if (! po_file)
			return this;

		// If key is not found, return original language
		var translate = function(key, po_file) {
			if (typeof po_file[key] == "undefined") {
				return "";
			} else {
				return po_file[key];
			}
		};

		// Iterate over UI elements that need to be translated
		return $.each(this, function() {
			element = $(this);

			// Translate text
			if (element.html()) {
				translated_text = translate( element.html(), po_file );
                if (Saiku.i18n.elements.indexOf &&
                    Saiku.i18n.elements.indexOf(element.html()) === -1) {
                    Saiku.i18n.elements.push(element.html());
                }
				if (translated_text) {
					element.data('original', element.html());
					element.html(translated_text);
					element.removeClass('i18n');
				}
			}

			// Translate title
			if (element.attr('title')) {
				translated_title = translate( element.attr('title'), po_file );
                if (Saiku.i18n.elements.indexOf &&
                    Saiku.i18n.elements.indexOf(element.attr('title')) === -1) {
                    Saiku.i18n.elements.push(element.attr('title'));
                }
				if (translated_title) {
					element.data('original', element.attr('title'));
					element.attr({ 'title': translated_title });
					element.removeClass('i18n');
				}
			}

			// Remove class so this element isn't repeatedly translated
			if (element.hasClass('i18n')) {
			    element.addClass('i18n_failed');
			}
			element.addClass('i18n_translated');
		});
	};

	$.fn.un_i18n = function() {
		// Iterate over UI elements to replace the original text
		return $.each(this, function() {
			element = $(this);

			if (element.text())
				element.text(element.data('original'));

			if (element.attr('title'))
				element.attr({ 'title': element.data('original') });

			element.addClass('i18n');
			element.removeClass('i18n_translated')
			    .removeClass('i18n_failed');
		});
	};
})( jQuery );

/**
 * Backbone view which allows users to submit translations
 */
var TranslationTab = Backbone.View.extend({
    className: 'workspace_area',
    caption: function() {
        return Saiku.i18n.po_file["Improve this translation"] ?
            Saiku.i18n.po_file["Improve this translation"] :
            "Improve this translation";
    },
    events: {
        'submit form': 'submit',
        'change input': 'mark'
    },
    initialize: function() {
        $(window).resize(this.adjust);
        $(this.el).focus(this.adjust);
        this.adjust();
    },
    render: function() {
        var translation_table = {};
        for (var i = 0; i < Saiku.i18n.elements.length; i++) {
            translation_table[Saiku.i18n.elements[i]] = {
                value: Saiku.i18n.po_file[Saiku.i18n.elements[i]],
                name: encodeURI(Saiku.i18n.elements[i])
            };
        }
        var table = _.template("<form class='workspace_results'>" +
        	"Your name: <input type='text' name='translator_name' />" +
            "
Please fill in the appropriate translation in the blanks provided:
" +
        	"<% _.each(translation_table, function(val, key) { %>" +
            "
<%= key %>
" +
            "<input type='text' value='<%= val.value %>' name='<%= val.name %>' />" +
            "
" +
            "<% }); %>" +
            "
<input class='submit-translation' type='submit' value='Submit translation' />
" +
            "</form>")({
                translation_table: translation_table
            });
        $(this.el).html(table).find('div').css({
            float: 'left',
            padding: '20px'
        }).find('input').css({
            width: '300px'
        });
        $(this.el).find('.submit-translation').css({
            padding: '20px'
        });
    },

    mark: function(event) {
        $(event.target).addClass('changed');
    },

    submit: function() {
        var translation = { locale: Saiku.i18n.locale };
        $(this.el).find('.changed').each(function(element) {
            translation[decodeURI($(this).attr('name'))] = encodeURI($(this).val());
        });
        Translate.log(translation);
        Saiku.ui.block('Thank you for improving our translation!');
        this.tab.remove();
        _.delay(function() {
            Saiku.ui.unblock();
        }, 1000);
        return false;
    },

    adjust: function() {
        $(this.el).height($("body").height() - 87);
    }
});

/**
 * Automatically internationalize the UI based on the user's locale
 */
Saiku.i18n.automatic_i18n();

/**
 * Add translate button
 */
Saiku.events.bind('toolbar:render', function(args) {
    if (Saiku.i18n.locale != "en") {
        var $link = $("").text(Saiku.i18n.locale)
            .attr({
                href: "#translate",
                title: "Improve this translation"
            })
            .click(Saiku.i18n.improve_translation)
            .addClass('sprite translate i18n');
        var $li = $("
").append($link);
        $(args.toolbar.el).find('ul').append($li);
    }
});

/**
 * Bind to new workspace
 */
Saiku.events.bind('session:new', function() {
    // Translate elements already rendered
    Saiku.i18n.translate();

    // Translate new workspaces
    Saiku.session.bind('tab:add', Saiku.i18n.translate);
});

/**
 * Initialize Loggly input for user-provided translations
 */
if (logger) {
    window.Translate = new logger({
        url: Settings.TELEMETRY_SERVER + '/input/translations'
    });
}<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
/// @brief setup for import tests
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2010-2012 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

(function () {
  var db = require("org/arangodb").db;

  db._drop("UnitTestsImportJson1");
  db._drop("UnitTestsImportJson2");
  db._drop("UnitTestsImportJson3");
  db._drop("UnitTestsImportJson4");
  db._drop("UnitTestsImportCsv1");
  db._drop("UnitTestsImportCsv2");
  db._drop("UnitTestsImportCsv3");
  db._drop("UnitTestsImportCsv4");
  db._drop("UnitTestsImportTsv1");
  db._drop("UnitTestsImportTsv2");
  db._drop("UnitTestsImportVertex");
  db._drop("UnitTestsImportEdge");

  db._create("UnitTestsImportJson1");
  db._create("UnitTestsImportJson2");
  db._create("UnitTestsImportJson3");
  db._create("UnitTestsImportJson4");
  db._create("UnitTestsImportTsv1");
  db._create("UnitTestsImportTsv2");
  db._create("UnitTestsImportVertex");
  db._createEdgeCollection("UnitTestsImportEdge");
})();

return true;

// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
// End:
<commit_msg>fixed tests<commit_after>////////////////////////////////////////////////////////////////////////////////
/// @brief setup for import tests
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2010-2012 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

(function () {
  var db = require("org/arangodb").db;

  db._drop("UnitTestsImportJson1");
  db._drop("UnitTestsImportJson2");
  db._drop("UnitTestsImportJson3");
  db._drop("UnitTestsImportJson4");
  db._drop("UnitTestsImportCsv1");
  db._drop("UnitTestsImportCsv2");
  db._drop("UnitTestsImportCsv3");
  db._drop("UnitTestsImportCsv4");
  db._drop("UnitTestsImportTsv1");
  db._drop("UnitTestsImportTsv2");
  db._drop("UnitTestsImportVertex");
  db._drop("UnitTestsImportEdge");

  db._create("UnitTestsImportJson1");
  db._create("UnitTestsImportJson2");
  db._create("UnitTestsImportJson3");
  db._create("UnitTestsImportJson4");
  db._create("UnitTestsImportTsv1");
  db._create("UnitTestsImportTsv2");
  db._create("UnitTestsImportVertex");
  db._createEdgeCollection("UnitTestsImportEdge");
})();

return {
  status: true
};

// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
// End:
<|endoftext|>


<commit_before>/**
 * The `avatar` helper displays a user's avatar.
 *
 * @param {User} user
 * @param {Object} attrs Attributes to apply to the avatar element
 * @return {Object}
 */
export default function avatar(user, attrs = {}) {
  attrs.className = 'Avatar ' + (attrs.className || '');
  let content = '';

  // If the `title` attribute is set to null or false, we don't want to give the
  // avatar a title. On the other hand, if it hasn't been given at all, we can
  // safely default it to the user's username.
  const hasTitle = attrs.title === 'undefined' || attrs.title;
  if (!hasTitle) delete attrs.title;

  // If a user has been passed, then we will set up an avatar using their
  // uploaded image, or the first letter of their username if they haven't
  // uploaded one.
  if (user) {
    const username = user.displayName() || '?';
    const avatarUrl = user.avatarUrl();

    if (hasTitle) attrs.title = attrs.title || username;

    if (avatarUrl) {
      return ;
    }

    content = username.charAt(0).toUpperCase();
    attrs.style = { background: user.color() };
  }

  return {content};
}
<commit_msg>Added an empty alt attribute to avatar's img tag (#2269)<commit_after>/**
 * The `avatar` helper displays a user's avatar.
 *
 * @param {User} user
 * @param {Object} attrs Attributes to apply to the avatar element
 * @return {Object}
 */
export default function avatar(user, attrs = {}) {
  attrs.className = 'Avatar ' + (attrs.className || '');
  let content = '';

  // If the `title` attribute is set to null or false, we don't want to give the
  // avatar a title. On the other hand, if it hasn't been given at all, we can
  // safely default it to the user's username.
  const hasTitle = attrs.title === 'undefined' || attrs.title;
  if (!hasTitle) delete attrs.title;

  // If a user has been passed, then we will set up an avatar using their
  // uploaded image, or the first letter of their username if they haven't
  // uploaded one.
  if (user) {
    const username = user.displayName() || '?';
    const avatarUrl = user.avatarUrl();

    if (hasTitle) attrs.title = attrs.title || username;

    if (avatarUrl) {
      return ;
    }

    content = username.charAt(0).toUpperCase();
    attrs.style = { background: user.color() };
  }

  return {content};
}
<|endoftext|>


<commit_before>/*
 * testcollation_es.js - test the Collator object in Spanish
 *
 * Copyright © 2014, JEDLSoft
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

function testJSCollatorTraditionalQuatCH_es() {
	var col = new ilib.Collator({
		locale: "es-ES",
		useNative: false,
		sensitivity: "quaternary",
		style: "traditional"
	});

	assertNotUndefined(col);

	// a b c ch d
	assertTrue("a < b",  col.compare("a", "b") < 0);
	assertTrue("b < c",  col.compare("b", "c") < 0);
	assertTrue("ca < ch", col.compare("ca", "ch") < 0);
	assertTrue("cz < ch",  col.compare("cz", "ch") < 0);
	assertTrue("ch < d",  col.compare("ch", "d") < 0);
}

function testJSCollatorTraditionalQuatLL_es() {
	var col = new ilib.Collator({
		locale: "es-ES",
		useNative: false,
		sensitivity: "quaternary",
		style: "traditional"
	});

	assertNotUndefined(col);

	// k l ll m
	assertTrue("k < l",  col.compare("k", "l") < 0);
	assertTrue("l < ll",  col.compare("l", "ll") < 0);
	assertTrue("la < ll", col.compare("la", "ll") < 0);
	assertTrue("lz < ll",  col.compare("lz", "ll") < 0);
	assertTrue("ll < m",  col.compare("ll", "m") < 0);
}

function testJSCollatorTraditionalQuatEnye_es() {
	var col = new ilib.Collator({
		locale: "es-ES",
		useNative: false,
		sensitivity: "quaternary",
		style: "traditional"
	});

	assertNotUndefined(col);

	// m n ñ o
	assertTrue("m < n",  col.compare("m", "n") < 0);
	assertTrue("n < ñ",  col.compare("n", "ñ") < 0);
	assertTrue("ñ < ñ (combining)",  col.compare("ñ", "ñ") < 0);
	assertTrue("ñ (combining) < o", col.compare("ñ", "o") < 0);
	assertTrue("ñ < o", col.compare("ñ", "o") < 0);
}

// differences in ch rr ll and ñ should be primary differences

function testJSCollatorTraditionalPrimaryCH_es() {
	var col = new ilib.Collator({
		locale: "es-ES",
		useNative: false,
		sensitivity: "primary",
		style: "traditional"
	});

	assertNotUndefined(col);

	// a b c ch d
	assertTrue("a < b",  col.compare("a", "b") < 0);
	assertTrue("b < c",  col.compare("b", "c") < 0);
	assertTrue("ca < ch", col.compare("ca", "ch") < 0);
	assertTrue("cz < ch",  col.compare("cz", "ch") < 0);
	assertTrue("ch < d",  col.compare("ch", "d") < 0);
}

function testJSCollatorTraditionalPrimaryLL_es() {
	var col = new ilib.Collator({
		locale: "es-ES",
		useNative: false,
		sensitivity: "primary",
		style: "traditional",
		usage: "search"
	});

	assertNotUndefined(col);

	// k l ll m
	assertTrue("k < l",  col.compare("k", "l") < 0);
	assertTrue("l < ll",  col.compare("l", "ll") < 0);
	assertTrue("la < ll", col.compare("la", "ll") < 0);
	assertTrue("lz < ll",  col.compare("lz", "ll") < 0);
	assertTrue("ll < m",  col.compare("ll", "m") < 0);
}

function testJSCollatorTraditionalPrimaryEnye_es() {
	var col = new ilib.Collator({
		locale: "es-ES",
		useNative: false,
		sensitivity: "primary",
		style: "traditional",
		usage: "search"
	});

	assertNotUndefined(col);

	// m n ñ o
	assertTrue("m < n",  col.compare("m", "n") < 0);
	assertTrue("n < ñ",  col.compare("n", "ñ") < 0);
	assertTrue("ñ = ñ (combining)",  col.compare("ñ", "ñ") === 0);
	assertTrue("ñ < o", col.compare("ñ", "o") < 0);
}

function testCollatorSortWithCase_es() {
	var col = new ilib.Collator({
		locale: "es-ES",
		sensitivity: "tertiary",
		usage: "sort",
		style: "traditional",
		useNative: false
	});
    assertNotUndefined(col);

    var input = [
        "nortenado",
        "chicharones",
        "lave",
        "lugar",
        "calle",
        "mestizo",
        "calabria",
        "czeke",
        "baila",
        "Navidad",
        "norteña",
        "Mestizo",
        "nortenudo",
        "color",
        "llave",
        "calsificasion"
	];

    input.sort(col.getComparator());

    var expected = [
		"baila",
		"calabria",
		"calsificasion",
		"calle",
		"color",
		"czeke",
		"chicharones",
		"lave",
		"lugar",
		"llave",
		"Mestizo",
		"mestizo",
		"Navidad",
		"nortenado",
		"nortenudo",
		"norteña"
	];

    assertArrayEquals(expected, input);
}

function testCollatorWithVariants_es() {
	var col = new ilib.Collator({
		locale: "es-ES",
		sensitivity: "tertiary",
		usage: "sort",
		style: "traditional",
		useNative: false
	});
    assertNotUndefined(col);

    var input = [
        "nortenado",
        "chicharones",
        "lugar",
        "calle",
        "mestizo",
        "calabria",
        "czeke",
        "baila",
        "Navidad",
        "norteña",
        "Mestizo",
        "nortenudo",
        "color",
        "llave",
        "norteña",
        "lave",
        "calsificasion"
	];

    input.sort(col.getComparator());

    console.log("after sorting, input is now " + JSON.stringify(input));
    var expected = [
		"baila",
		"calabria",
		"calsificasion",
		"calle",
		"color",
		"czeke",
		"chicharones",
		"lave",
		"lugar",
		"llave",
		"Mestizo",
		"mestizo",
		"Navidad",
		"nortenado",
		"nortenudo",
		"norteña",
        "norteña"
	];

    assertArrayEquals(expected, input);
}<commit_msg>Removed console.log that should not have been checked in.<commit_after>/*
 * testcollation_es.js - test the Collator object in Spanish
 *
 * Copyright © 2014, JEDLSoft
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

function testJSCollatorTraditionalQuatCH_es() {
	var col = new ilib.Collator({
		locale: "es-ES",
		useNative: false,
		sensitivity: "quaternary",
		style: "traditional"
	});

	assertNotUndefined(col);

	// a b c ch d
	assertTrue("a < b",  col.compare("a", "b") < 0);
	assertTrue("b < c",  col.compare("b", "c") < 0);
	assertTrue("ca < ch", col.compare("ca", "ch") < 0);
	assertTrue("cz < ch",  col.compare("cz", "ch") < 0);
	assertTrue("ch < d",  col.compare("ch", "d") < 0);
}

function testJSCollatorTraditionalQuatLL_es() {
	var col = new ilib.Collator({
		locale: "es-ES",
		useNative: false,
		sensitivity: "quaternary",
		style: "traditional"
	});

	assertNotUndefined(col);

	// k l ll m
	assertTrue("k < l",  col.compare("k", "l") < 0);
	assertTrue("l < ll",  col.compare("l", "ll") < 0);
	assertTrue("la < ll", col.compare("la", "ll") < 0);
	assertTrue("lz < ll",  col.compare("lz", "ll") < 0);
	assertTrue("ll < m",  col.compare("ll", "m") < 0);
}

function testJSCollatorTraditionalQuatEnye_es() {
	var col = new ilib.Collator({
		locale: "es-ES",
		useNative: false,
		sensitivity: "quaternary",
		style: "traditional"
	});

	assertNotUndefined(col);

	// m n ñ o
	assertTrue("m < n",  col.compare("m", "n") < 0);
	assertTrue("n < ñ",  col.compare("n", "ñ") < 0);
	assertTrue("ñ < ñ (combining)",  col.compare("ñ", "ñ") < 0);
	assertTrue("ñ (combining) < o", col.compare("ñ", "o") < 0);
	assertTrue("ñ < o", col.compare("ñ", "o") < 0);
}

// differences in ch rr ll and ñ should be primary differences

function testJSCollatorTraditionalPrimaryCH_es() {
	var col = new ilib.Collator({
		locale: "es-ES",
		useNative: false,
		sensitivity: "primary",
		style: "traditional"
	});

	assertNotUndefined(col);

	// a b c ch d
	assertTrue("a < b",  col.compare("a", "b") < 0);
	assertTrue("b < c",  col.compare("b", "c") < 0);
	assertTrue("ca < ch", col.compare("ca", "ch") < 0);
	assertTrue("cz < ch",  col.compare("cz", "ch") < 0);
	assertTrue("ch < d",  col.compare("ch", "d") < 0);
}

function testJSCollatorTraditionalPrimaryLL_es() {
	var col = new ilib.Collator({
		locale: "es-ES",
		useNative: false,
		sensitivity: "primary",
		style: "traditional",
		usage: "search"
	});

	assertNotUndefined(col);

	// k l ll m
	assertTrue("k < l",  col.compare("k", "l") < 0);
	assertTrue("l < ll",  col.compare("l", "ll") < 0);
	assertTrue("la < ll", col.compare("la", "ll") < 0);
	assertTrue("lz < ll",  col.compare("lz", "ll") < 0);
	assertTrue("ll < m",  col.compare("ll", "m") < 0);
}

function testJSCollatorTraditionalPrimaryEnye_es() {
	var col = new ilib.Collator({
		locale: "es-ES",
		useNative: false,
		sensitivity: "primary",
		style: "traditional",
		usage: "search"
	});

	assertNotUndefined(col);

	// m n ñ o
	assertTrue("m < n",  col.compare("m", "n") < 0);
	assertTrue("n < ñ",  col.compare("n", "ñ") < 0);
	assertTrue("ñ = ñ (combining)",  col.compare("ñ", "ñ") === 0);
	assertTrue("ñ < o", col.compare("ñ", "o") < 0);
}

function testCollatorSortWithCase_es() {
	var col = new ilib.Collator({
		locale: "es-ES",
		sensitivity: "tertiary",
		usage: "sort",
		style: "traditional",
		useNative: false
	});
    assertNotUndefined(col);

    var input = [
        "nortenado",
        "chicharones",
        "lave",
        "lugar",
        "calle",
        "mestizo",
        "calabria",
        "czeke",
        "baila",
        "Navidad",
        "norteña",
        "Mestizo",
        "nortenudo",
        "color",
        "llave",
        "calsificasion"
	];

    input.sort(col.getComparator());

    var expected = [
		"baila",
		"calabria",
		"calsificasion",
		"calle",
		"color",
		"czeke",
		"chicharones",
		"lave",
		"lugar",
		"llave",
		"Mestizo",
		"mestizo",
		"Navidad",
		"nortenado",
		"nortenudo",
		"norteña"
	];

    assertArrayEquals(expected, input);
}

function testCollatorWithVariants_es() {
	var col = new ilib.Collator({
		locale: "es-ES",
		sensitivity: "tertiary",
		usage: "sort",
		style: "traditional",
		useNative: false
	});
    assertNotUndefined(col);

    var input = [
        "nortenado",
        "chicharones",
        "lugar",
        "calle",
        "mestizo",
        "calabria",
        "czeke",
        "baila",
        "Navidad",
        "norteña",
        "Mestizo",
        "nortenudo",
        "color",
        "llave",
        "norteña",
        "lave",
        "calsificasion"
	];

    input.sort(col.getComparator());

    var expected = [
		"baila",
		"calabria",
		"calsificasion",
		"calle",
		"color",
		"czeke",
		"chicharones",
		"lave",
		"lugar",
		"llave",
		"Mestizo",
		"mestizo",
		"Navidad",
		"nortenado",
		"nortenudo",
		"norteña",
        "norteña"
	];

    assertArrayEquals(expected, input);
}<|endoftext|>


<commit_before>'use strict'

// ----------------------------------------------------------------------------

const log       = require ('ololog')
    , ansi      = require ('ansicolor').nice
    , chai      = require ('chai')
    , expect    = chai.expect
    , assert    = chai.assert

/*  ------------------------------------------------------------------------ */

module.exports = (exchange, market, method) => {

    const format = {
        'id':      'btcusd',   // string literal for referencing within an exchange
        'symbol':  'BTC/USD',  // uppercase string literal of a pair of currencies
        'base':    'BTC',      // unified uppercase string, base currency, 3 or more letters
        'quote':   'USD',      // unified uppercase string, quote currency, 3 or more letters
        'taker':   0.0011,     // taker fee, for example, 0.0011 = 0.11%
        'maker':   0.0009,     // maker fee, for example, 0.0009 = 0.09%
        //---------------------------------------------------------------------
        // commented temporarily to bring markets to consistency first
        // 'baseId':  'btc',      // exchange-specific base currency id
        // 'quoteId': 'usd',      // exchange-specific quote currency id
        // 'active': true,       // boolean, market status
        // 'precision': {        // number of decimal digits "after the dot"
        //     'price': 8,       // integer
        //     'amount': 8,      // integer
        //     'cost': 8,        // integer
        // },
        // 'limits': {           // value limits when placing orders on this market
        //     'amount': {
        //         'min': 0.01,  // order amount should be > min
        //         'max': 1000,  // order amount should be < max
        //     },
        //     'price': {
        //         'min': 0.01,  // order price should be > min
        //         'max': 1000,  // order price should be < max
        //     },
        //     'cost':  { // order cost = price * amount
        //         'min': 0.01,  // order cost should be > min
        //         'max': 1000,  // order cost should be < max
        //     },
        // },
        // 'info': {}, // the original unparsed market info from the exchange
        //---------------------------------------------------------------------
    }

    expect (market).to.deep.include.all.keys (format)
    expect (market).to.not.have.key ('lot')

    // assert ((market['baseId'] === undefined) || (typeof market['baseId'] === 'string'))
    // assert ((market['quoteId'] === undefined) || (typeof market['quoteId'] === 'string'))

    assert ((market['taker'] === undefined) || (typeof market['taker'] === 'number'))
    assert ((market['maker'] === undefined) || (typeof market['maker'] === 'number'))

    // expect (market['precision']['amount']).to.not.be.undefined
    // expect (market['precision']['price']).to.not.be.undefined

    // expect (market['limits']['amount']['min']).to.not.be.undefined
    // expect (market['limits']['price']['min']).to.not.be.undefined
    // expect (market['limits']['cost']['min']).to.not.be.undefined

    // log (market)
}
<commit_msg>test.market cleanup<commit_after>'use strict'

// ----------------------------------------------------------------------------

const log       = require ('ololog')
    , ansi      = require ('ansicolor').nice
    , chai      = require ('chai')
    , expect    = chai.expect
    , assert    = chai.assert

/*  ------------------------------------------------------------------------ */

module.exports = (exchange, market, method) => {

    const format = {
        'id':      'btcusd',   // string literal for referencing within an exchange
        'symbol':  'BTC/USD',  // uppercase string literal of a pair of currencies
        'base':    'BTC',      // unified uppercase string, base currency, 3 or more letters
        'quote':   'USD',      // unified uppercase string, quote currency, 3 or more letters
        'taker':   0.0011,     // taker fee, for example, 0.0011 = 0.11%
        'maker':   0.0009,     // maker fee, for example, 0.0009 = 0.09%
        //---------------------------------------------------------------------
        // commented temporarily to bring markets to consistency first
        // 'baseId':  'btc',      // exchange-specific base currency id
        // 'quoteId': 'usd',      // exchange-specific quote currency id
        // 'active': true,       // boolean, market status
        // 'precision': {        // number of decimal digits "after the dot"
        //     'price': 8,       // integer
        //     'amount': 8,      // integer
        //     'cost': 8,        // integer
        // },
        // 'limits': {           // value limits when placing orders on this market
        //     'amount': {
        //         'min': 0.01,  // order amount should be > min
        //         'max': 1000,  // order amount should be < max
        //     },
        //     'price': {
        //         'min': 0.01,  // order price should be > min
        //         'max': 1000,  // order price should be < max
        //     },
        //     'cost':  { // order cost = price * amount
        //         'min': 0.01,  // order cost should be > min
        //         'max': 1000,  // order cost should be < max
        //     },
        // },
        // 'info': {}, // the original unparsed market info from the exchange
        //---------------------------------------------------------------------
    }

    expect (market).to.deep.include.all.keys (format)

    // assert ((market['baseId'] === undefined) || (typeof market['baseId'] === 'string'))
    // assert ((market['quoteId'] === undefined) || (typeof market['quoteId'] === 'string'))

    assert ((market['taker'] === undefined) || (typeof market['taker'] === 'number'))
    assert ((market['maker'] === undefined) || (typeof market['maker'] === 'number'))

    // expect (market['precision']['amount']).to.not.be.undefined
    // expect (market['precision']['price']).to.not.be.undefined

    // expect (market['limits']['amount']['min']).to.not.be.undefined
    // expect (market['limits']['price']['min']).to.not.be.undefined
    // expect (market['limits']['cost']['min']).to.not.be.undefined

    // log (market)
}
<|endoftext|>


<commit_before>'use strict'

// ----------------------------------------------------------------------------

const log       = require ('ololog')
    , ansi      = require ('ansicolor').nice
    , chai      = require ('chai')
    , expect    = chai.expect
    , assert    = chai.assert

/*  ------------------------------------------------------------------------ */

module.exports = (exchange, ticker, method, symbol) => {

    const format = {
        'symbol':       'ETH/BTC',
        'info':          {},
        'timestamp':     1234567890,
        'datetime':     '2017-09-01T00:00:00',
        'high':          1.234, // highest price
        'low':           1.234, // lowest price
        'bid':           1.234, // current best bid (buy) price
        'bidVolume':     1.234, // current best bid (buy) amount (may be missing or undefined)
        'ask':           1.234, // current best ask (sell) price
        'askVolume':     1.234, // current best ask (sell) amount (may be missing or undefined)
        'vwap':          1.234, // volume weighed average price
        'open':          1.234, // opening price
        'close':         1.234, // price of last trade (closing price for current period)
        'last':          1.234, // same as `close`, duplicated for convenience
        'previousClose': 1.234, // closing price for the previous period
        'change':        1.234, // absolute change, `last - open`
        'percentage':    1.234, // relative change, `(change/open) * 100`
        'average':       1.234, // average price, `(last + open) / 2`
        'baseVolume':    1.234, // volume of base currency
        'quoteVolume':   1.234, // volume of quote currency
    }

    expect (ticker).to.have.all.keys (format)

    const keys = [ 'datetime', 'timestamp', 'high', 'low', 'bid', 'ask', 'baseVolume', 'quoteVolume', 'vwap' ]

    assert (!('first' in ticker), '`first` field leftover in ' + exchange.id)
    assert (ticker['last'] === ticker['close'], '`last` != `close` in ' + exchange.id)

    // log (ticker)

    keys.forEach (key => assert (key in ticker))

    const { high, low, vwap, baseVolume, quoteVolume } = ticker

    // this assert breaks QuadrigaCX sometimes... still investigating
    // if (vwap)
    //     assert (vwap >= low && vwap <= high)

    /*
    if (baseVolume && quoteVolume && high && low) {
        assert (quoteVolume >= baseVolume * low) // this assertion breaks therock
        assert (quoteVolume <= baseVolume * high)
    }
    */

    if (baseVolume && vwap)
        assert (quoteVolume)

    if (quoteVolume && vwap)
        assert (baseVolume)

    // log (symbol.green, 'ticker',
    //     ticker['datetime'],
    //     ... (keys.map (key =>
    //         key + ': ' + ticker[key])))

    if (![

        'coinmarketcap',
        'xbtce',
        'coss',

    ].includes (exchange.id)) {

        if (ticker['bid'] && ticker['ask'])
            assert (ticker['bid'] <= ticker['ask'])

    }

    return ticker
}
<commit_msg>exclude okex from bid <= ask ticker assertion<commit_after>'use strict'

// ----------------------------------------------------------------------------

const log       = require ('ololog')
    , ansi      = require ('ansicolor').nice
    , chai      = require ('chai')
    , expect    = chai.expect
    , assert    = chai.assert

/*  ------------------------------------------------------------------------ */

module.exports = (exchange, ticker, method, symbol) => {

    const format = {
        'symbol':       'ETH/BTC',
        'info':          {},
        'timestamp':     1234567890,
        'datetime':     '2017-09-01T00:00:00',
        'high':          1.234, // highest price
        'low':           1.234, // lowest price
        'bid':           1.234, // current best bid (buy) price
        'bidVolume':     1.234, // current best bid (buy) amount (may be missing or undefined)
        'ask':           1.234, // current best ask (sell) price
        'askVolume':     1.234, // current best ask (sell) amount (may be missing or undefined)
        'vwap':          1.234, // volume weighed average price
        'open':          1.234, // opening price
        'close':         1.234, // price of last trade (closing price for current period)
        'last':          1.234, // same as `close`, duplicated for convenience
        'previousClose': 1.234, // closing price for the previous period
        'change':        1.234, // absolute change, `last - open`
        'percentage':    1.234, // relative change, `(change/open) * 100`
        'average':       1.234, // average price, `(last + open) / 2`
        'baseVolume':    1.234, // volume of base currency
        'quoteVolume':   1.234, // volume of quote currency
    }

    expect (ticker).to.have.all.keys (format)

    const keys = [ 'datetime', 'timestamp', 'high', 'low', 'bid', 'ask', 'baseVolume', 'quoteVolume', 'vwap' ]

    assert (!('first' in ticker), '`first` field leftover in ' + exchange.id)
    assert (ticker['last'] === ticker['close'], '`last` != `close` in ' + exchange.id)

    // log (ticker)

    keys.forEach (key => assert (key in ticker))

    const { high, low, vwap, baseVolume, quoteVolume } = ticker

    // this assert breaks QuadrigaCX sometimes... still investigating
    // if (vwap)
    //     assert (vwap >= low && vwap <= high)

    /*
    if (baseVolume && quoteVolume && high && low) {
        assert (quoteVolume >= baseVolume * low) // this assertion breaks therock
        assert (quoteVolume <= baseVolume * high)
    }
    */

    if (baseVolume && vwap)
        assert (quoteVolume)

    if (quoteVolume && vwap)
        assert (baseVolume)

    // log (symbol.green, 'ticker',
    //     ticker['datetime'],
    //     ... (keys.map (key =>
    //         key + ': ' + ticker[key])))

    if (![

        'coinmarketcap',
        'xbtce',
        'coss',
        'okex',

    ].includes (exchange.id)) {

        if (ticker['bid'] && ticker['ask'])
            assert (ticker['bid'] <= ticker['ask'])

    }

    return ticker
}
<|endoftext|>


<commit_before>// set up SVG for D3
var width = $('#graphics').width(),
    height = $('#graphics').height(),
    colors = d3.scaleOrdinal(d3.schemeCategory10);;

var svg = d3.select('#graphics')
  .append('svg')
  .attr('oncontextmenu', 'return false;')
  .attr('width', width)
  .attr('height', height);

// set up initial nodes and links
//  - nodes are known by 'id', not by index in array.
//  - reflexive edges are indicated on the node (as a bold black circle).
//  - links are always source < target; edge directions are set by 'left' and 'right'.
var nodes = [
    { id: 0, reflexive: false },
    { id: 1, reflexive: true },
    { id: 2, reflexive: false }
],
  lastNodeId = 2,
  links = [
    { source: nodes[0], target: nodes[1], left: false, right: true },
    { source: nodes[1], target: nodes[2], left: false, right: true }
  ];

var force = d3.forceSimulation()
    .nodes(nodes)
    .force('link', d3.forceLink(links).id(function (d) { return d.id; }).distance(150).strength(1))
    .force('charge', d3.forceManyBody().strength(-500))
    .force('center', d3.forceCenter(width / 2, height / 2))
    .force('collide', d3.forceCollide(function (d) { return d.r; }).iterations(16))
    .force("y", d3.forceY(0))
    .force("x", d3.forceX(0))
    .on('tick', tick);

// define arrow markers for graph links
svg.append('svg:defs').append('svg:marker')
    .attr('id', 'end-arrow')
    .attr('viewBox', '0 -5 10 10')
    .attr('refX', 6)
    .attr('markerWidth', 3)
    .attr('markerHeight', 3)
    .attr('orient', 'auto')
  .append('svg:path')
    .attr('d', 'M0,-5L10,0L0,5')
    .attr('fill', '#000');

svg.append('svg:defs').append('svg:marker')
    .attr('id', 'start-arrow')
    .attr('viewBox', '0 -5 10 10')
    .attr('refX', 4)
    .attr('markerWidth', 3)
    .attr('markerHeight', 3)
    .attr('orient', 'auto')
  .append('svg:path')
    .attr('d', 'M10,-5L0,0L10,5')
    .attr('fill', '#000');

// line displayed when dragging new nodes
var drag_line = svg.append('svg:path')
  .attr('class', 'link dragline hidden')
  .attr('d', 'M0,0L0,0');

// handles to link and node element groups
var path = svg.append('svg:g').selectAll('path'),
    circle = svg.append('svg:g').selectAll('g');

// mouse event vars
var selected_node = null,
    selected_link = null,
    mousedown_link = null,
    mousedown_node = null,
    mouseup_node = null;

function resetMouseVars() {
    mousedown_node = null;
    mouseup_node = null;
    mousedown_link = null;
}

// update force layout (called automatically each iteration)
function tick() {
    // draw directed edges with proper padding from node centers
    path.attr('d', function (d) {
        var deltaX = d.target.x - d.source.x,
            deltaY = d.target.y - d.source.y,
            dist = Math.sqrt(deltaX * deltaX + deltaY * deltaY),
            normX = deltaX / dist,
            normY = deltaY / dist,
            sourcePadding = d.left ? 17 : 12,
            targetPadding = d.right ? 17 : 12,
            sourceX = d.source.x + (sourcePadding * normX),
            sourceY = d.source.y + (sourcePadding * normY),
            targetX = d.target.x - (targetPadding * normX),
            targetY = d.target.y - (targetPadding * normY);
        return 'M ' + sourceX + ',' + sourceY + 'L ' + targetX + ',' + targetY;
    });

    circle.attr('transform', function (d) {
        return 'translate(' + d.x + ',' + d.y + ')';
    });
}

// update graph (called when needed)
function restart() {
    // path (link) group
    console.log("Links restart:");
    console.log(links);
    console.log(links);
    path = path.data(links);

    // update existing links
    path.classed('selected', function (d) { return d === selected_link; })
      .style('marker-start', function (d) { return d.left ? 'url(#start-arrow)' : ''; })
      .style('marker-end', function (d) { return d.right ? 'url(#end-arrow)' : ''; });

    // add new links
    var p = path.enter().append('svg:path')
      .attr('class', 'link')
      .classed('selected', function (d) { return d === selected_link; })
      .style('marker-start', function (d) { return d.left ? 'url(#start-arrow)' : ''; })
      .style('marker-end', function (d) { return d.right ? 'url(#end-arrow)' : ''; })
      .on('mousedown', function (d) {
          if (d3.event.ctrlKey) return;

          // select link
          mousedown_link = d;
          if (mousedown_link === selected_link) selected_link = null;
          else selected_link = mousedown_link;
          selected_node = null;
          restart();
      });

    path = path.merge(p);

    // remove old links
    path.exit().remove();

    // circle (node) group
    // NB: the function arg is crucial here! nodes are known by id, not by index!
    circle = circle.data(nodes, function (d) { return d.id; });

    // update existing nodes (reflexive & selected visual states)
    circle.selectAll('circle')
      .style('fill', function (d) { return (d === selected_node) ? d3.rgb(colors(d.id)).brighter().toString() : colors(d.id); })
      .classed('reflexive', function (d) { return d.reflexive; });

    // add new nodes
    var g = circle.enter().append('svg:g');

    g.append('svg:circle')
      .attr('class', 'node')
      .attr('r', 12)
      .style('fill', function (d) { return (d === selected_node) ? d3.rgb(colors(d.id)).brighter().toString() : colors(d.id); })
      .style('stroke', function (d) { return d3.rgb(colors(d.id)).darker().toString(); })
      .classed('reflexive', function (d) { return d.reflexive; })
      .on('mouseover', function (d) {
          if (!mousedown_node || d === mousedown_node) return;
          // enlarge target node
          d3.select(this).attr('transform', 'scale(1.1)');
      })
      .on('mouseout', function (d) {
          if (!mousedown_node || d === mousedown_node) return;
          // unenlarge target node
          d3.select(this).attr('transform', '');
      })
      .on('mousedown', function (d) {
          if (d3.event.ctrlKey) return;

          // select node
          mousedown_node = d;
          if (mousedown_node === selected_node) selected_node = null;
          else selected_node = mousedown_node;
          selected_link = null;

          // reposition drag line
          drag_line
            .style('marker-end', 'url(#end-arrow)')
            .classed('hidden', false)
            .attr('d', 'M' + mousedown_node.x + ',' + mousedown_node.y + 'L' + mousedown_node.x + ',' + mousedown_node.y);

          restart();
      })
      .on('mouseup', function (d) {
          if (!mousedown_node) return;

          // needed by FF
          drag_line
            .classed('hidden', true)
            .style('marker-end', '');

          // check for drag-to-self
          mouseup_node = d;
          if (mouseup_node === mousedown_node) { resetMouseVars(); return; }

          // unenlarge target node
          d3.select(this).attr('transform', '');

          // add link to graph (update if exists)
          // NB: links are strictly source < target; arrows separately specified by booleans
          var source, target, direction;
          if (mousedown_node.id < mouseup_node.id) {
              source = mousedown_node;
              target = mouseup_node;
              direction = 'right';
          } else {
              source = mouseup_node;
              target = mousedown_node;
              direction = 'left';
          }

          var link;
          link = links.filter(function (l) {
              return (l.source === source && l.target === target);
          })[0];

          if (link) {
              link[direction] = true;
          } else {
              link = { source: source, target: target, left: false, right: false };
              link[direction] = true;
              links.push(link);
          }

          // select new link
          selected_link = link;
          selected_node = null;

          restart();
      });

    // show node IDs
    g.append('svg:text')
        .attr('x', 0)
        .attr('y', 4)
        .attr('class', 'id')
        .text(function (d) { return d.id; });

    circle = circle.merge(g);

    // remove old nodes
    circle.exit().remove();

    force.nodes(nodes);
    force.force('link').links(links);

    // set the graph in motion
    force.alpha(0.05).restart();
}

function mousedown() {
    // prevent I-bar on drag
    //d3.event.preventDefault();

    // because :active only works in WebKit?
    svg.classed('active', true);

    if (d3.event.ctrlKey || mousedown_node || mousedown_link) return;

    // insert new node at point
    var point = d3.mouse(this),
        node = { id: ++lastNodeId, reflexive: false };
    node.x = point[0];
    node.y = point[1];
    nodes.push(node);

    restart();
}

function mousemove() {
    if (!mousedown_node) return;

    // update drag line
    drag_line.attr('d', 'M ' + mousedown_node.x + ',' + mousedown_node.y + 'L ' + d3.mouse(this)[0] + ',' + d3.mouse(this)[1]);

    restart();
}

function mouseup() {
    if (mousedown_node) {
        // hide drag line
        drag_line
          .classed('hidden', true)
          .style('marker-end', '');
    }

    // because :active only works in WebKit?
    svg.classed('active', false);

    // clear mouse event vars
    resetMouseVars();
}

function spliceLinksForNode(node) {
    var toSplice = links.filter(function (l) {
        return (l.source === node || l.target === node);
    });
    toSplice.map(function (l) {
        links.splice(links.indexOf(l), 1);
    });
    console.log("Links:");
    console.log(links);
    console.log("Nodes:");
    console.log(nodes);
}

// only respond once per keydown
var lastKeyDown = -1;

function keydown() {
    d3.event.preventDefault();
    if (lastKeyDown !== -1) return;
    lastKeyDown = d3.event.keyCode;

    // ctrl
    if (d3.event.keyCode === 17) {
        circle.call(d3.drag()
            .container(d3.select('svg')._groups[0][0])
            .on('start', dragstarted)
            .on('drag', dragged)
            .on('end', dragended));
        svg.classed('ctrl', true);
    }

    if (!selected_node && !selected_link) return;
    switch (d3.event.keyCode) {
        case 8: // backspace
        case 46: // delete
            if (selected_node) {
                nodes.splice(nodes.indexOf(selected_node), 1);
                spliceLinksForNode(selected_node);
            } else if (selected_link) {
                links.splice(links.indexOf(selected_link), 1);
            }
            selected_link = null;
            selected_node = null;
            restart();
            break;
        case 66: // B
            if (selected_link) {
                // set link direction to both left and right
                selected_link.left = true;
                selected_link.right = true;
            }
            restart();
            break;
        case 76: // L
            if (selected_link) {
                // set link direction to left only
                selected_link.left = true;
                selected_link.right = false;
            }
            restart();
            break;
        case 82: // R
            if (selected_node) {
                // toggle node reflexivity
                selected_node.reflexive = !selected_node.reflexive;
            } else if (selected_link) {
                // set link direction to right only
                selected_link.left = false;
                selected_link.right = true;
            }
            restart();
            break;
    }
}

function dragstarted() {
    if (!d3.event.active) force.alphaTarget(0.3).restart();
    d3.event.subject.fx = d3.event.subject.x;
    d3.event.subject.fy = d3.event.subject.y;
}

function dragged() {
    d3.event.subject.fx = d3.event.x;
    d3.event.subject.fy = d3.event.y;
}

function dragended() {
    if (!d3.event.active) force.alphaTarget(0);
    d3.event.subject.fx = null;
    d3.event.subject.fy = null;
    resetMouseVars();
}

function keyup() {
    lastKeyDown = -1;

    // ctrl
    if (d3.event.keyCode === 17) {
        circle.on(".drag", null);
        svg.classed('ctrl', false);
    }
}

// app starts here
svg.on('mousedown', mousedown)
  .on('mousemove', mousemove)
  .on('mouseup', mouseup);
d3.select(window)
  .on('keydown', keydown)
  .on('keyup', keyup);

restart();<commit_msg>Fixed bug with removal of nodes<commit_after>// set up SVG for D3
var width = $('#graphics').width(),
    height = $('#graphics').height(),
    colors = d3.scaleOrdinal(d3.schemeCategory10);;

var svg = d3.select('#graphics')
  .append('svg')
  .attr('oncontextmenu', 'return false;')
  .attr('width', width)
  .attr('height', height);

// set up initial nodes and links
//  - nodes are known by 'id', not by index in array.
//  - reflexive edges are indicated on the node (as a bold black circle).
//  - links are always source < target; edge directions are set by 'left' and 'right'.
var nodes = [
    { id: 0, reflexive: false },
    { id: 1, reflexive: true },
    { id: 2, reflexive: false }
],
  lastNodeId = 2,
  links = [
    { source: nodes[0], target: nodes[1], left: false, right: true },
    { source: nodes[1], target: nodes[2], left: false, right: true }
  ];

var force = d3.forceSimulation()
    .nodes(nodes)
    .force('link', d3.forceLink(links).id(function (d) { return d.id; }).distance(150).strength(1))
    .force('charge', d3.forceManyBody().strength(-500))
    .force('center', d3.forceCenter(width / 2, height / 2))
    .force('collide', d3.forceCollide(function (d) { return d.r; }).iterations(16))
    .force("y", d3.forceY(0))
    .force("x", d3.forceX(0))
    .on('tick', tick);

// define arrow markers for graph links
svg.append('svg:defs').append('svg:marker')
    .attr('id', 'end-arrow')
    .attr('viewBox', '0 -5 10 10')
    .attr('refX', 6)
    .attr('markerWidth', 3)
    .attr('markerHeight', 3)
    .attr('orient', 'auto')
  .append('svg:path')
    .attr('d', 'M0,-5L10,0L0,5')
    .attr('fill', '#000');

svg.append('svg:defs').append('svg:marker')
    .attr('id', 'start-arrow')
    .attr('viewBox', '0 -5 10 10')
    .attr('refX', 4)
    .attr('markerWidth', 3)
    .attr('markerHeight', 3)
    .attr('orient', 'auto')
  .append('svg:path')
    .attr('d', 'M10,-5L0,0L10,5')
    .attr('fill', '#000');

// line displayed when dragging new nodes
var drag_line = svg.append('svg:path')
  .attr('class', 'link dragline hidden')
  .attr('d', 'M0,0L0,0');

// handles to link and node element groups
var path = svg.append('svg:g').selectAll('path'),
    circle = svg.append('svg:g').selectAll('g');

// mouse event vars
var selected_node = null,
    selected_link = null,
    mousedown_link = null,
    mousedown_node = null,
    mouseup_node = null;

function resetMouseVars() {
    mousedown_node = null;
    mouseup_node = null;
    mousedown_link = null;
}

// update force layout (called automatically each iteration)
function tick() {
    // draw directed edges with proper padding from node centers
    path.attr('d', function (d) {
        var deltaX = d.target.x - d.source.x,
            deltaY = d.target.y - d.source.y,
            dist = Math.sqrt(deltaX * deltaX + deltaY * deltaY),
            normX = deltaX / dist,
            normY = deltaY / dist,
            sourcePadding = d.left ? 17 : 12,
            targetPadding = d.right ? 17 : 12,
            sourceX = d.source.x + (sourcePadding * normX),
            sourceY = d.source.y + (sourcePadding * normY),
            targetX = d.target.x - (targetPadding * normX),
            targetY = d.target.y - (targetPadding * normY);
        return 'M ' + sourceX + ',' + sourceY + 'L ' + targetX + ',' + targetY;
    });

    circle.attr('transform', function (d) {
        return 'translate(' + d.x + ',' + d.y + ')';
    });
}

// update graph (called when needed)
function restart() {
    // path (link) group
    path = path.data(links);

    // update existing links
    path.classed('selected', function (d) { return d === selected_link; })
      .style('marker-start', function (d) { return d.left ? 'url(#start-arrow)' : ''; })
      .style('marker-end', function (d) { return d.right ? 'url(#end-arrow)' : ''; });

    // add new links
    var p = path.enter().append('svg:path')
      .attr('class', 'link')
      .classed('selected', function (d) { return d === selected_link; })
      .style('marker-start', function (d) { return d.left ? 'url(#start-arrow)' : ''; })
      .style('marker-end', function (d) { return d.right ? 'url(#end-arrow)' : ''; })
      .on('mousedown', function (d) {
          if (d3.event.ctrlKey) return;

          // select link
          mousedown_link = d;
          if (mousedown_link === selected_link) selected_link = null;
          else selected_link = mousedown_link;
          selected_node = null;
          restart();
      });

    // remove old links
    path.exit().remove();

    path = path.merge(p);
    // circle (node) group
    // NB: the function arg is crucial here! nodes are known by id, not by index!
    circle = circle.data(nodes, function (d) { return d.id; });

    // update existing nodes (reflexive & selected visual states)
    circle.selectAll('circle')
      .style('fill', function (d) { return (d === selected_node) ? d3.rgb(colors(d.id)).brighter().toString() : colors(d.id); })
      .classed('reflexive', function (d) { return d.reflexive; });

    // add new nodes
    var g = circle.enter().append('svg:g');

    g.append('svg:circle')
      .attr('class', 'node')
      .attr('r', 12)
      .style('fill', function (d) { return (d === selected_node) ? d3.rgb(colors(d.id)).brighter().toString() : colors(d.id); })
      .style('stroke', function (d) { return d3.rgb(colors(d.id)).darker().toString(); })
      .classed('reflexive', function (d) { return d.reflexive; })
      .on('mouseover', function (d) {
          if (!mousedown_node || d === mousedown_node) return;
          // enlarge target node
          d3.select(this).attr('transform', 'scale(1.1)');
      })
      .on('mouseout', function (d) {
          if (!mousedown_node || d === mousedown_node) return;
          // unenlarge target node
          d3.select(this).attr('transform', '');
      })
      .on('mousedown', function (d) {
          if (d3.event.ctrlKey) return;

          // select node
          mousedown_node = d;
          if (mousedown_node === selected_node) selected_node = null;
          else selected_node = mousedown_node;
          selected_link = null;

          // reposition drag line
          drag_line
            .style('marker-end', 'url(#end-arrow)')
            .classed('hidden', false)
            .attr('d', 'M' + mousedown_node.x + ',' + mousedown_node.y + 'L' + mousedown_node.x + ',' + mousedown_node.y);

          restart();
      })
      .on('mouseup', function (d) {
          if (!mousedown_node) return;

          // needed by FF
          drag_line
            .classed('hidden', true)
            .style('marker-end', '');

          // check for drag-to-self
          mouseup_node = d;
          if (mouseup_node === mousedown_node) { resetMouseVars(); return; }

          // unenlarge target node
          d3.select(this).attr('transform', '');

          // add link to graph (update if exists)
          // NB: links are strictly source < target; arrows separately specified by booleans
          var source, target, direction;
          if (mousedown_node.id < mouseup_node.id) {
              source = mousedown_node;
              target = mouseup_node;
              direction = 'right';
          } else {
              source = mouseup_node;
              target = mousedown_node;
              direction = 'left';
          }

          var link;
          link = links.filter(function (l) {
              return (l.source === source && l.target === target);
          })[0];

          if (link) {
              link[direction] = true;
          } else {
              link = { source: source, target: target, left: false, right: false };
              link[direction] = true;
              links.push(link);
          }

          // select new link
          selected_link = link;
          selected_node = null;

          restart();
      });

    // show node IDs
    g.append('svg:text')
        .attr('x', 0)
        .attr('y', 4)
        .attr('class', 'id')
        .text(function (d) { return d.id; });

    // remove old nodes
    circle.exit().remove();
    circle = circle.merge(g);

    force.nodes(nodes);
    force.force('link').links(links);

    // set the graph in motion
    force.alpha(0.05).restart();
}

function mousedown() {
    // prevent I-bar on drag
    //d3.event.preventDefault();

    // because :active only works in WebKit?
    svg.classed('active', true);

    if (d3.event.ctrlKey || mousedown_node || mousedown_link) return;

    // insert new node at point
    var point = d3.mouse(this),
        node = { id: ++lastNodeId, reflexive: false };
    node.x = point[0];
    node.y = point[1];
    nodes.push(node);

    restart();
}

function mousemove() {
    if (!mousedown_node) return;

    // update drag line
    drag_line.attr('d', 'M ' + mousedown_node.x + ',' + mousedown_node.y + 'L ' + d3.mouse(this)[0] + ',' + d3.mouse(this)[1]);

    restart();
}

function mouseup() {
    if (mousedown_node) {
        // hide drag line
        drag_line
          .classed('hidden', true)
          .style('marker-end', '');
    }

    // because :active only works in WebKit?
    svg.classed('active', false);

    // clear mouse event vars
    resetMouseVars();
}

function spliceLinksForNode(node) {
    var toSplice = links.filter(function (l) {
        return (l.source === node || l.target === node);
    });
    toSplice.map(function (l) {
        links.splice(links.indexOf(l), 1);
    });
}

// only respond once per keydown
var lastKeyDown = -1;

function keydown() {
    d3.event.preventDefault();
    if (lastKeyDown !== -1) return;
    lastKeyDown = d3.event.keyCode;

    // ctrl
    if (d3.event.keyCode === 17) {
        circle.call(d3.drag()
            .container(d3.select('svg')._groups[0][0])
            .on('start', dragstarted)
            .on('drag', dragged)
            .on('end', dragended));
        svg.classed('ctrl', true);
    }

    if (!selected_node && !selected_link) return;
    switch (d3.event.keyCode) {
        case 8: // backspace
        case 46: // delete
            if (selected_node) {
                nodes.splice(nodes.indexOf(selected_node), 1);
                spliceLinksForNode(selected_node);
            } else if (selected_link) {
                links.splice(links.indexOf(selected_link), 1);
            }
            selected_link = null;
            selected_node = null;
            restart();
            break;
        case 66: // B
            if (selected_link) {
                // set link direction to both left and right
                selected_link.left = true;
                selected_link.right = true;
            }
            restart();
            break;
        case 76: // L
            if (selected_link) {
                // set link direction to left only
                selected_link.left = true;
                selected_link.right = false;
            }
            restart();
            break;
        case 82: // R
            if (selected_node) {
                // toggle node reflexivity
                selected_node.reflexive = !selected_node.reflexive;
            } else if (selected_link) {
                // set link direction to right only
                selected_link.left = false;
                selected_link.right = true;
            }
            restart();
            break;
    }
}

function dragstarted() {
    if (!d3.event.active) force.alphaTarget(0.3).restart();
    d3.event.subject.fx = d3.event.subject.x;
    d3.event.subject.fy = d3.event.subject.y;
}

function dragged() {
    d3.event.subject.fx = d3.event.x;
    d3.event.subject.fy = d3.event.y;
}

function dragended() {
    if (!d3.event.active) force.alphaTarget(0);
    d3.event.subject.fx = null;
    d3.event.subject.fy = null;
    resetMouseVars();
}

function keyup() {
    lastKeyDown = -1;

    // ctrl
    if (d3.event.keyCode === 17) {
        circle.on(".drag", null);
        svg.classed('ctrl', false);
    }
}

// app starts here
svg.on('mousedown', mousedown)
  .on('mousemove', mousemove)
  .on('mouseup', mouseup);
d3.select(window)
  .on('keydown', keydown)
  .on('keyup', keyup);

restart();<|endoftext|>