<commit_before>import {expect} from "chai";
import Util from "../../js/helpers/Util";

describe("Util", function () {

  describe("extendObject", function () {

    it("returns a new object with merged properties", function () {
      var objA = {hello: "world", foo: "bar"};
      var objB = {hello: "there", baz: "foo"};
      var expectedResult = {hello: "there", foo: "bar", baz: "foo"};
      var result = Util.extendObject(objA, objB);
      expect(result).to.deep.equal(expectedResult);
    });

    it("returns a new object without modifying the source", function () {
      var objA = {hello: "world", foo: "bar"};
      var objB = {hello: "there", baz: "foo"};
      var result = Util.extendObject(objA, objB);
      expect(objA).to.deep.equal({hello: "world", foo: "bar"});
    });

    it("accepts several sources", function () {
      var objA = {hello: "world", foo: "bar"};
      var objB = {hello: "there", baz: "foo"};
      var objC = {id: null, flag: true};
      var expectedResult = {
        hello: "there",
        foo: "bar",
        baz: "foo",
        id: null,
        flag: true
      };

      var result = Util.extendObject(objA, objB, objC);
      expect(result).to.deep.equal(expectedResult);
    });

    it("always returns an object", function () {
      var expectedResult = {"0": "faz", "1": "bar"};
      var result = Util.extendObject(["foo", "bar"], ["faz"]);
      expect(result).to.deep.equal(expectedResult);
    });

    it("treats accessors", function () {
      var counter = 0;

      var obj = {
        get count() {
          return ++counter;
        }
      };

      var newObj = Util.extendObject({}, obj);

      expect(newObj.count).to.equal(1);
      expect(newObj.count).to.equal(2);
    });
  });

  describe("initKeyValue", function () {

    it("initialises a value for a given key", function () {
      let obj = {};
      Util.initKeyValue(obj, "key", []);
      expect(obj).to.have.property("key");
      expect(obj.key).to.eql([]);
    });

  });

  describe("isArray", function () {

    it("array is an array", function () {
      expect(Util.isArray([])).to.be.true;
    });

    it("object is not an array", function () {
      expect(Util.isArray({})).to.be.false;
    });

  });

  describe("isNumber", function () {

    it("detects numbers", function () {
      expect(Util.isNumber(1)).to.be.true;
      expect(Util.isNumber(2.3)).to.be.true;
      expect(Util.isNumber(-5)).to.be.true;
    });

    it("string is not a number", function () {
      expect(Util.isNumber("666")).to.be.false;
    });

  });

  describe("isObject", function () {

    it("object is an object", function () {
      expect(Util.isObject({})).to.be.true;
    });

    it("array is not an object", function () {
      expect(Util.isObject([])).to.be.false;
    });

    it("primitives are not objects", function () {
      expect(Util.isObject(new Number(1))).to.be.false;
      expect(Util.isObject(2)).to.be.false;
      expect(Util.isObject(true)).to.be.false;
      expect(Util.isObject(new String("string"))).to.be.false;
      expect(Util.isObject("")).to.be.false;
      expect(Util.isObject(Symbol("unique"))).to.be.false;
    });

  });

  describe("isString", function () {

    it("detects strings", function () {
      expect(Util.isString("1")).to.be.true;
      expect(Util.isString("abc")).to.be.true;
    });

    it("number is not a string", function () {
      expect(Util.isString(123)).to.be.false;
    });

    it("function is not a string", function () {
      expect(Util.isString(function () {})).to.be.false;
    });

  });

  describe("isStringAndEmpty", function () {

    it("handles bad input", function () {
      expect(Util.isStringAndEmpty({"Object": true})).to.be.false;
      expect(Util.isStringAndEmpty([1, 2, 3])).to.be.false;
      expect(Util.isStringAndEmpty(null)).to.be.false;
    });

    it("detects empty srting", function () {
      expect(Util.isStringAndEmpty("")).to.be.true;
    });

    it("detects a non-empty string", function () {
      expect(Util.isStringAndEmpty(" ")).to.be.false;
      expect(Util.isStringAndEmpty("not empty")).to.be.false;
    });

  });

  describe("detectPathsInObject", function () {
    it("detects all non object paths in an object recursively", function () {
      var obj = {
        obj1: {
          string2: "string2",
          number2: 2,
          obj2: {
            string3: "string3",
            number3: 3,
            obj3: {
              array2: ["a", "b"],
              number3: 3
            }
          }
        },
        string1: "string1",
        number1: 1,
        array1: [1, 2]
      };

      expect(Util.detectObjectPaths(obj)).to.deep.equal([
        "obj1.string2",
        "obj1.number2",
        "obj1.obj2.string3",
        "obj1.obj2.number3",
        "obj1.obj2.obj3.array2",
        "obj1.obj2.obj3.number3",
        "string1",
        "number1",
        "array1"
      ]);
    });

    it("detects all non object paths in an prefixed object", function () {
      var obj = {
        obj1: {
          obj2: {
            string3: "string3",
          },
          string2: "string2"
        },
        string1: "don't see me"
      };

      expect(Util.detectObjectPaths(obj, "obj1")).to.deep.equal([
        "obj1.obj2.string3",
        "obj1.string2"
      ]);
    });

    it("excludes an object for parsing", function () {
      var obj = {
        obj1: {
          obj2: {
            string3: "string3",
          },
          string2: "string2"
        },
        string1: "don't see me"
      };

      expect(Util.detectObjectPaths(obj, "obj1", ["obj1.obj2"]))
          .to.deep.equal([
        "obj1.obj2",
        "obj1.string2"
      ]);
    });
  });

  describe("objectPathSet", function () {
    it("should work on inital null-values", function () {
      var obj = {
        a: null
      };

      Util.objectPathSet(obj, "a.b.c", "true");

      expect(obj).to.deep.equal({
        a: {
          b: {
            c: "true"
          }
        }
      });
    });
  });

  describe("deepFreeze", function () {

    it("shouldn't mutate an nested object-property", function () {
      var obj = {
        a: {
          b: "immutable"
        }
      };

      Util.deepFreeze(obj);

      expect(function () {
        obj.a.b = "modified";
      }).to.throw(TypeError);
    });

    it("shouldn't mutate an nested array-value", function () {
      var obj = {
        a: [
          {},
          "immutable"
        ]
      };

      Util.deepFreeze(obj);

      expect(function () {
        obj.a[1] = "modified";
      }).to.throw(TypeError);
    });

    it("shouldn't mutate first level property", function () {
      var obj = {
        a: 5
      };

      Util.deepFreeze(obj);

      expect(function () {
        obj.a = 8;
      }).to.throw(TypeError);
    });

    it("does not freeze non-object types", function () {
      var number = 5;

      Util.deepFreeze(number);

      expect(function () {
        number = 8;
      }).to.not.throw(TypeError);

      expect(number).to.equal(8);
    });

  });

  describe("filesize", function () {
    beforeEach(function () {
      this.baseSize = 796;
    });

    // Regular tests
    it("should convert to correct unit of B", function () {
      expect(Util.filesize(this.baseSize)).to.equal("796 B");
    });

    it("should convert to correct unit of KiB", function () {
      expect(Util.filesize(this.baseSize * 1024)).to.equal("796 KiB");
    });

    it("should convert to correct unit of MiB", function () {
      var factorize = Math.pow(1024, 2);
      expect(Util.filesize(this.baseSize * factorize)).to.equal("796 MiB");
    });

    it("should convert to correct unit of GiB", function () {
      var factorize = Math.pow(1024, 3);
      expect(Util.filesize(this.baseSize * factorize)).to.equal("796 GiB");
    });

    it("should convert to correct unit of PiB", function () {
      var factorize = Math.pow(1024, 5);
      expect(Util.filesize(this.baseSize * factorize)).to.equal("796 PiB");
    });

    it("should convert to correct unit of large PiB", function () {
      var factorize = Math.pow(1024, 6);
      expect(Util.filesize(this.baseSize * factorize)).to.equal("815104 PiB");
    });

    it("should convert to correct unit of MiB", function () {
      expect(Util.filesize((this.baseSize + 108) * 1024)).to.equal("0.88 MiB");
    });

    it("should convert to correct unit of GiB", function () {
      var factorize = Math.pow(1024, 2);
      expect(Util.filesize((this.baseSize + 128) * factorize))
        .to.equal("0.9 GiB");
    });

    it("should convert to correct unit of TiB", function () {
      var factorize = Math.pow(1024, 3);
      expect(Util.filesize((this.baseSize + 158) * factorize))
        .to.equal("0.93 TiB");
    });

    it("should convert to correct unit of PiB", function () {
      var factorize = Math.pow(1024, 5);
      expect(Util.filesize((this.baseSize + 230) * factorize))
        .to.equal("1026 PiB");
    });

    // Special tests
    it("should return '0 B' for vales of zero", function () {
      expect(Util.filesize(0, 0)).to.equal("0 B");
    });

    it("does not show decimals if set to 0", function () {
      var size = (this.baseSize + 352) * 1024;
      var filesize = Util.filesize(size, 0, 1024);
      expect(filesize).to.equal("1 MiB");
    });

    it("trims trailing zeroes from the mantissa", function () {
      var size = (this.baseSize + 102) * 1024;
      var filesize = Util.filesize(size, 4);
      expect(filesize).to.equal("0.877 MiB");
    });

    it("shows decimals places to the specified accuracy", function () {
      var size = (this.baseSize + 116) * 1024;
      var filesize = Util.filesize(size, 4);
      expect(filesize).to.equal("0.8906 MiB");
    });

    it("has correct custom unit and threshold", function () {
      var size = (this.baseSize + 24) * 1024 * 1024;
      var filesize = Util.filesize(size, 2, 500, 1024, ["byte", "KB", "MB", "GB"]);
      expect(filesize).to.equal("0.8 GB");
    });

    it("has correct amount of 0 digits", function () {
      var size = 1000 * 1024;
      var filesize = Util.filesize(size, 2, 1024);
      expect(filesize).to.equal("1000 KiB");
    });

  });

  describe("compareProperties", function () {
    it("returns true when all properties match", function () {
      expect(Util.compareProperties(
        {a: 1, b: 2, c: 3},
        {a: 1, b: 2, c: 3},
        "a", "b", "c"
      )).to.equal(true);
    });
    it("returns true when only specified properties match", function () {
      expect(Util.compareProperties(
        {a: 1, b: 2, c: 3, d: 4},
        {a: 1, b: 2, c: 3, d: 5},
        "a", "b", "c"
      )).to.equal(true);
    });
    it("returns false when some specified properties do not match",
        function () {
      expect(Util.compareProperties(
        {a: 1, b: 2, c: 3, d: 4},
        {a: 1, b: 2, c: 3, d: 5},
        "a", "b", "d"
      )).to.equal(false);
    });
    it("correctly identifies and compares similar arrays", function () {
      expect(Util.compareProperties(
        {a: ["a", "b", "c"]},
        {a: ["a", "b", "c"]},
        "a"
      )).to.equal(true);
    });
    it("correctly identifies and compares dissimilar arrays", function () {
      expect(Util.compareProperties(
        {a: ["a", "b", "c"]},
        {a: ["a", "banana", "c"]},
        "a"
      )).to.equal(false);
    });
    it("correctly identifies and compares similar objects", function () {
      expect(Util.compareProperties(
        {a: {a: 1}},
        {a: {a: 1}},
        "a"
      )).to.equal(true);
    });
    it("correctly identifies and compares dissimilar objects", function () {
      expect(Util.compareProperties(
        {a: {a: 1}},
        {a: {a: 1, b: 2}},
        "a"
      )).to.equal(false);
    });
  });

  describe("deepCopy", function () {

    it("it returns an actual deep copy", function () {
      var currentDate = new Date();

      var originalObject = {
        obj1: {
          string2: "string2",
          number2: 2,
          func: function () {
            return true;
          },
          obj2: {
            string3: "string3",
            number3: 3,
            date: currentDate,
            obj3: {
              array2: ["a", "b"],
              number3: 3
            }
          }
        },
        string1: "string1",
        number1: 1,
        array1: [1, 2]
      };

      var copiedObject = Util.deepCopy(originalObject);
      expect(copiedObject).to.eql(originalObject);
    });

    it("mutating the copy does not affect the original", function () {
      var currentDate = new Date();

      var originalObject = {
        obj1: {
          obj2: {
            string3: "string3",
            number3: 3,
            date: currentDate,
            obj3: {
              array2: ["a", "b"],
              number3: 3
            }
          }
        },
        string1: "string1",
        number1: 1,
        array1: [1, 2]
      };

      // An exact replica of the originalObject
      var originalObject2 = {
        obj1: {
          obj2: {
            string3: "string3",
            number3: 3,
            date: currentDate,
            obj3: {
              array2: ["a", "b"],
              number3: 3
            }
          }
        },
        string1: "string1",
        number1: 1,
        array1: [1, 2]
      };

      var copiedObject = Util.deepCopy(originalObject);

      copiedObject.obj1.obj2 = null;
      expect(copiedObject).to.not.eql(originalObject);
      expect(originalObject2).to.eql(originalObject);
    });
  });
});
<commit_msg>Add out of bounds tests to deepClone<commit_after>import {expect} from "chai";
import Util from "../../js/helpers/Util";

describe("Util", function () {

  describe("extendObject", function () {

    it("returns a new object with merged properties", function () {
      var objA = {hello: "world", foo: "bar"};
      var objB = {hello: "there", baz: "foo"};
      var expectedResult = {hello: "there", foo: "bar", baz: "foo"};
      var result = Util.extendObject(objA, objB);
      expect(result).to.deep.equal(expectedResult);
    });

    it("returns a new object without modifying the source", function () {
      var objA = {hello: "world", foo: "bar"};
      var objB = {hello: "there", baz: "foo"};
      var result = Util.extendObject(objA, objB);
      expect(objA).to.deep.equal({hello: "world", foo: "bar"});
    });

    it("accepts several sources", function () {
      var objA = {hello: "world", foo: "bar"};
      var objB = {hello: "there", baz: "foo"};
      var objC = {id: null, flag: true};
      var expectedResult = {
        hello: "there",
        foo: "bar",
        baz: "foo",
        id: null,
        flag: true
      };

      var result = Util.extendObject(objA, objB, objC);
      expect(result).to.deep.equal(expectedResult);
    });

    it("always returns an object", function () {
      var expectedResult = {"0": "faz", "1": "bar"};
      var result = Util.extendObject(["foo", "bar"], ["faz"]);
      expect(result).to.deep.equal(expectedResult);
    });

    it("treats accessors", function () {
      var counter = 0;

      var obj = {
        get count() {
          return ++counter;
        }
      };

      var newObj = Util.extendObject({}, obj);

      expect(newObj.count).to.equal(1);
      expect(newObj.count).to.equal(2);
    });
  });

  describe("initKeyValue", function () {

    it("initialises a value for a given key", function () {
      let obj = {};
      Util.initKeyValue(obj, "key", []);
      expect(obj).to.have.property("key");
      expect(obj.key).to.eql([]);
    });

  });

  describe("isArray", function () {

    it("array is an array", function () {
      expect(Util.isArray([])).to.be.true;
    });

    it("object is not an array", function () {
      expect(Util.isArray({})).to.be.false;
    });

  });

  describe("isNumber", function () {

    it("detects numbers", function () {
      expect(Util.isNumber(1)).to.be.true;
      expect(Util.isNumber(2.3)).to.be.true;
      expect(Util.isNumber(-5)).to.be.true;
    });

    it("string is not a number", function () {
      expect(Util.isNumber("666")).to.be.false;
    });

  });

  describe("isObject", function () {

    it("object is an object", function () {
      expect(Util.isObject({})).to.be.true;
    });

    it("array is not an object", function () {
      expect(Util.isObject([])).to.be.false;
    });

    it("primitives are not objects", function () {
      expect(Util.isObject(new Number(1))).to.be.false;
      expect(Util.isObject(2)).to.be.false;
      expect(Util.isObject(true)).to.be.false;
      expect(Util.isObject(new String("string"))).to.be.false;
      expect(Util.isObject("")).to.be.false;
      expect(Util.isObject(Symbol("unique"))).to.be.false;
    });

  });

  describe("isString", function () {

    it("detects strings", function () {
      expect(Util.isString("1")).to.be.true;
      expect(Util.isString("abc")).to.be.true;
    });

    it("number is not a string", function () {
      expect(Util.isString(123)).to.be.false;
    });

    it("function is not a string", function () {
      expect(Util.isString(function () {})).to.be.false;
    });

  });

  describe("isStringAndEmpty", function () {

    it("handles bad input", function () {
      expect(Util.isStringAndEmpty({"Object": true})).to.be.false;
      expect(Util.isStringAndEmpty([1, 2, 3])).to.be.false;
      expect(Util.isStringAndEmpty(null)).to.be.false;
    });

    it("detects empty srting", function () {
      expect(Util.isStringAndEmpty("")).to.be.true;
    });

    it("detects a non-empty string", function () {
      expect(Util.isStringAndEmpty(" ")).to.be.false;
      expect(Util.isStringAndEmpty("not empty")).to.be.false;
    });

  });

  describe("detectPathsInObject", function () {
    it("detects all non object paths in an object recursively", function () {
      var obj = {
        obj1: {
          string2: "string2",
          number2: 2,
          obj2: {
            string3: "string3",
            number3: 3,
            obj3: {
              array2: ["a", "b"],
              number3: 3
            }
          }
        },
        string1: "string1",
        number1: 1,
        array1: [1, 2]
      };

      expect(Util.detectObjectPaths(obj)).to.deep.equal([
        "obj1.string2",
        "obj1.number2",
        "obj1.obj2.string3",
        "obj1.obj2.number3",
        "obj1.obj2.obj3.array2",
        "obj1.obj2.obj3.number3",
        "string1",
        "number1",
        "array1"
      ]);
    });

    it("detects all non object paths in an prefixed object", function () {
      var obj = {
        obj1: {
          obj2: {
            string3: "string3",
          },
          string2: "string2"
        },
        string1: "don't see me"
      };

      expect(Util.detectObjectPaths(obj, "obj1")).to.deep.equal([
        "obj1.obj2.string3",
        "obj1.string2"
      ]);
    });

    it("excludes an object for parsing", function () {
      var obj = {
        obj1: {
          obj2: {
            string3: "string3",
          },
          string2: "string2"
        },
        string1: "don't see me"
      };

      expect(Util.detectObjectPaths(obj, "obj1", ["obj1.obj2"]))
        .to.deep.equal([
          "obj1.obj2",
          "obj1.string2"
        ]);
    });
  });

  describe("objectPathSet", function () {
    it("should work on inital null-values", function () {
      var obj = {
        a: null
      };

      Util.objectPathSet(obj, "a.b.c", "true");

      expect(obj).to.deep.equal({
        a: {
          b: {
            c: "true"
          }
        }
      });
    });
  });

  describe("deepFreeze", function () {

    it("shouldn't mutate an nested object-property", function () {
      var obj = {
        a: {
          b: "immutable"
        }
      };

      Util.deepFreeze(obj);

      expect(function () {
        obj.a.b = "modified";
      }).to.throw(TypeError);
    });

    it("shouldn't mutate an nested array-value", function () {
      var obj = {
        a: [
          {},
          "immutable"
        ]
      };

      Util.deepFreeze(obj);

      expect(function () {
        obj.a[1] = "modified";
      }).to.throw(TypeError);
    });

    it("shouldn't mutate first level property", function () {
      var obj = {
        a: 5
      };

      Util.deepFreeze(obj);

      expect(function () {
        obj.a = 8;
      }).to.throw(TypeError);
    });

    it("does not freeze non-object types", function () {
      var number = 5;

      Util.deepFreeze(number);

      expect(function () {
        number = 8;
      }).to.not.throw(TypeError);

      expect(number).to.equal(8);
    });

  });

  describe("filesize", function () {
    beforeEach(function () {
      this.baseSize = 796;
    });

    // Regular tests
    it("should convert to correct unit of B", function () {
      expect(Util.filesize(this.baseSize)).to.equal("796 B");
    });

    it("should convert to correct unit of KiB", function () {
      expect(Util.filesize(this.baseSize * 1024)).to.equal("796 KiB");
    });

    it("should convert to correct unit of MiB", function () {
      var factorize = Math.pow(1024, 2);
      expect(Util.filesize(this.baseSize * factorize)).to.equal("796 MiB");
    });

    it("should convert to correct unit of GiB", function () {
      var factorize = Math.pow(1024, 3);
      expect(Util.filesize(this.baseSize * factorize)).to.equal("796 GiB");
    });

    it("should convert to correct unit of PiB", function () {
      var factorize = Math.pow(1024, 5);
      expect(Util.filesize(this.baseSize * factorize)).to.equal("796 PiB");
    });

    it("should convert to correct unit of large PiB", function () {
      var factorize = Math.pow(1024, 6);
      expect(Util.filesize(this.baseSize * factorize)).to.equal("815104 PiB");
    });

    it("should convert to correct unit of MiB", function () {
      expect(Util.filesize((this.baseSize + 108) * 1024)).to.equal("0.88 MiB");
    });

    it("should convert to correct unit of GiB", function () {
      var factorize = Math.pow(1024, 2);
      expect(Util.filesize((this.baseSize + 128) * factorize))
        .to.equal("0.9 GiB");
    });

    it("should convert to correct unit of TiB", function () {
      var factorize = Math.pow(1024, 3);
      expect(Util.filesize((this.baseSize + 158) * factorize))
        .to.equal("0.93 TiB");
    });

    it("should convert to correct unit of PiB", function () {
      var factorize = Math.pow(1024, 5);
      expect(Util.filesize((this.baseSize + 230) * factorize))
        .to.equal("1026 PiB");
    });

    // Special tests
    it("should return '0 B' for vales of zero", function () {
      expect(Util.filesize(0, 0)).to.equal("0 B");
    });

    it("does not show decimals if set to 0", function () {
      var size = (this.baseSize + 352) * 1024;
      var filesize = Util.filesize(size, 0, 1024);
      expect(filesize).to.equal("1 MiB");
    });

    it("trims trailing zeroes from the mantissa", function () {
      var size = (this.baseSize + 102) * 1024;
      var filesize = Util.filesize(size, 4);
      expect(filesize).to.equal("0.877 MiB");
    });

    it("shows decimals places to the specified accuracy", function () {
      var size = (this.baseSize + 116) * 1024;
      var filesize = Util.filesize(size, 4);
      expect(filesize).to.equal("0.8906 MiB");
    });

    it("has correct custom unit and threshold", function () {
      var size = (this.baseSize + 24) * 1024 * 1024;
      var filesize = Util.filesize(size, 2, 500, 1024, ["byte", "KB", "MB", "GB"]);
      expect(filesize).to.equal("0.8 GB");
    });

    it("has correct amount of 0 digits", function () {
      var size = 1000 * 1024;
      var filesize = Util.filesize(size, 2, 1024);
      expect(filesize).to.equal("1000 KiB");
    });

  });

  describe("compareProperties", function () {
    it("returns true when all properties match", function () {
      expect(Util.compareProperties(
        {a: 1, b: 2, c: 3},
        {a: 1, b: 2, c: 3},
        "a", "b", "c"
      )).to.equal(true);
    });
    it("returns true when only specified properties match", function () {
      expect(Util.compareProperties(
        {a: 1, b: 2, c: 3, d: 4},
        {a: 1, b: 2, c: 3, d: 5},
        "a", "b", "c"
      )).to.equal(true);
    });
    it("returns false when some specified properties do not match",
        function () {
      expect(Util.compareProperties(
        {a: 1, b: 2, c: 3, d: 4},
        {a: 1, b: 2, c: 3, d: 5},
        "a", "b", "d"
      )).to.equal(false);
    });
    it("correctly identifies and compares similar arrays", function () {
      expect(Util.compareProperties(
        {a: ["a", "b", "c"]},
        {a: ["a", "b", "c"]},
        "a"
      )).to.equal(true);
    });
    it("correctly identifies and compares dissimilar arrays", function () {
      expect(Util.compareProperties(
        {a: ["a", "b", "c"]},
        {a: ["a", "banana", "c"]},
        "a"
      )).to.equal(false);
    });
    it("correctly identifies and compares similar objects", function () {
      expect(Util.compareProperties(
        {a: {a: 1}},
        {a: {a: 1}},
        "a"
      )).to.equal(true);
    });
    it("correctly identifies and compares dissimilar objects", function () {
      expect(Util.compareProperties(
        {a: {a: 1}},
        {a: {a: 1, b: 2}},
        "a"
      )).to.equal(false);
    });
  });

  describe("deepCopy", function () {

    it("it returns an actual deep copy", function () {
      var currentDate = new Date();

      var originalObject = {
        obj1: {
          string2: "string2",
          number2: 2,
          func: function () {
            return true;
          },
          obj2: {
            string3: "string3",
            number3: 3,
            date: currentDate,
            obj3: {
              array2: ["a", "b"],
              number3: 3
            }
          }
        },
        string1: "string1",
        number1: 1,
        array1: [1, 2]
      };

      var copiedObject = Util.deepCopy(originalObject);
      expect(copiedObject).to.eql(originalObject);
    });

    it("mutating the copy does not affect the original", function () {
      var currentDate = new Date();

      var originalObject = {
        obj1: {
          obj2: {
            string3: "string3",
            number3: 3,
            date: currentDate,
            obj3: {
              array2: ["a", "b"],
              number3: 3
            }
          }
        },
        string1: "string1",
        number1: 1,
        array1: [1, 2]
      };

      // An exact replica of the originalObject
      var originalObject2 = {
        obj1: {
          obj2: {
            string3: "string3",
            number3: 3,
            date: currentDate,
            obj3: {
              array2: ["a", "b"],
              number3: 3
            }
          }
        },
        string1: "string1",
        number1: 1,
        array1: [1, 2]
      };

      var copiedObject = Util.deepCopy(originalObject);

      copiedObject.obj1.obj2 = null;
      expect(copiedObject).to.not.eql(originalObject);
      expect(originalObject2).to.eql(originalObject);
    });

    it("does not clone out of bounds arrays", function () {
      var originalObject = {
        obj1: {
          array1: [1, 2]
        }
      };

      var number = 83864234234;
      originalObject.obj1.array1[number] = 3;

      var copiedObject = Util.deepCopy(originalObject);
      expect(copiedObject).to.not.eql(originalObject);
    });
  });
});
<|endoftext|>


<commit_before>/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow
 */

import type {Fiber} from 'react-reconciler/src/ReactFiber';
import type {TouchedViewDataAtPoint, InspectorData} from './ReactNativeTypes';

import {
  findCurrentHostFiber,
  findCurrentFiberUsingSlowPath,
} from 'react-reconciler/src/ReactFiberTreeReflection';
import getComponentName from 'shared/getComponentName';
import {HostComponent} from 'react-reconciler/src/ReactWorkTags';
import invariant from 'shared/invariant';
// Module provided by RN:
import {UIManager} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';

import {getClosestInstanceFromNode} from './ReactNativeComponentTree';

const emptyObject = {};
if (__DEV__) {
  Object.freeze(emptyObject);
}

let getInspectorDataForViewTag;
let getInspectorDataForViewAtPoint;

if (__DEV__) {
  const traverseOwnerTreeUp = function(hierarchy, instance: any) {
    if (instance) {
      hierarchy.unshift(instance);
      traverseOwnerTreeUp(hierarchy, instance._debugOwner);
    }
  };

  const getOwnerHierarchy = function(instance: any) {
    const hierarchy = [];
    traverseOwnerTreeUp(hierarchy, instance);
    return hierarchy;
  };

  const lastNonHostInstance = function(hierarchy) {
    for (let i = hierarchy.length - 1; i > 1; i--) {
      const instance = hierarchy[i];

      if (instance.tag !== HostComponent) {
        return instance;
      }
    }
    return hierarchy[0];
  };

  const getHostProps = function(fiber) {
    const host = findCurrentHostFiber(fiber);
    if (host) {
      return host.memoizedProps || emptyObject;
    }
    return emptyObject;
  };

  const getHostNode = function(fiber: Fiber | null, findNodeHandle) {
    let hostNode;
    // look for children first for the hostNode
    // as composite fibers do not have a hostNode
    while (fiber) {
      if (fiber.stateNode !== null && fiber.tag === HostComponent) {
        hostNode = findNodeHandle(fiber.stateNode);
      }
      if (hostNode) {
        return hostNode;
      }
      fiber = fiber.child;
    }
    return null;
  };

  const createHierarchy = function(fiberHierarchy) {
    return fiberHierarchy.map(fiber => ({
      name: getComponentName(fiber.type),
      getInspectorData: findNodeHandle => {
        return {
          props: getHostProps(fiber),
          source: fiber._debugSource,
          measure: callback => {
            // If this is Fabric, we'll find a ShadowNode and use that to measure.
            const hostFiber = findCurrentHostFiber(fiber);
            const shadowNode =
              hostFiber != null &&
              hostFiber.stateNode !== null &&
              hostFiber.stateNode.node;

            if (shadowNode) {
              nativeFabricUIManager.measure(shadowNode, callback);
            } else {
              return UIManager.measure(
                getHostNode(fiber, findNodeHandle),
                callback,
              );
            }
          },
        };
      },
    }));
  };

  const getInspectorDataForInstance = function(closestInstance): InspectorData {
    // Handle case where user clicks outside of ReactNative
    if (!closestInstance) {
      return {
        hierarchy: [],
        props: emptyObject,
        selectedIndex: null,
        source: null,
      };
    }

    const fiber = findCurrentFiberUsingSlowPath(closestInstance);
    const fiberHierarchy = getOwnerHierarchy(fiber);
    const instance = lastNonHostInstance(fiberHierarchy);
    const hierarchy = createHierarchy(fiberHierarchy);
    const props = getHostProps(instance);
    const source = instance._debugSource;
    const selectedIndex = fiberHierarchy.indexOf(instance);

    return {
      hierarchy,
      props,
      selectedIndex,
      source,
    };
  };

  getInspectorDataForViewTag = function(viewTag: number): Object {
    const closestInstance = getClosestInstanceFromNode(viewTag);

    // Handle case where user clicks outside of ReactNative
    if (!closestInstance) {
      return {
        hierarchy: [],
        props: emptyObject,
        selectedIndex: null,
        source: null,
      };
    }

    const fiber = findCurrentFiberUsingSlowPath(closestInstance);
    const fiberHierarchy = getOwnerHierarchy(fiber);
    const instance = lastNonHostInstance(fiberHierarchy);
    const hierarchy = createHierarchy(fiberHierarchy);
    const props = getHostProps(instance);
    const source = instance._debugSource;
    const selectedIndex = fiberHierarchy.indexOf(instance);

    return {
      hierarchy,
      props,
      selectedIndex,
      source,
    };
  };

  getInspectorDataForViewAtPoint = function(
    findNodeHandle: (componentOrHandle: any) => ?number,
    inspectedView: Object,
    locationX: number,
    locationY: number,
    callback: (viewData: TouchedViewDataAtPoint) => mixed,
  ): void {
    let closestInstance = null;

    if (inspectedView._internalInstanceHandle != null) {
      // For Fabric we can look up the instance handle directly and measure it.
      nativeFabricUIManager.findNodeAtPoint(
        inspectedView._internalInstanceHandle.stateNode.node,
        locationX,
        locationY,
        internalInstanceHandle => {
          if (internalInstanceHandle == null) {
            callback({
              pointerY: locationY,
              frame: {left: 0, top: 0, width: 0, height: 0},
              ...getInspectorDataForInstance(closestInstance),
            });
          }

          closestInstance =
            internalInstanceHandle.stateNode.canonical._internalInstanceHandle;
          nativeFabricUIManager.measure(
            internalInstanceHandle.stateNode.node,
            (x, y, width, height, pageX, pageY) => {
              callback({
                pointerY: locationY,
                frame: {left: pageX, top: pageY, width, height},
                ...getInspectorDataForInstance(closestInstance),
              });
            },
          );
        },
      );
    } else if (inspectedView._internalFiberInstanceHandleDEV != null) {
      // For Paper we fall back to the old strategy using the React tag.
      UIManager.findSubviewIn(
        findNodeHandle(inspectedView),
        [locationX, locationY],
        (nativeViewTag, left, top, width, height) => {
          const inspectorData = getInspectorDataForInstance(
            getClosestInstanceFromNode(nativeViewTag),
          );
          callback({
            ...inspectorData,
            pointerY: locationY,
            frame: {left, top, width, height},
            touchedViewTag: nativeViewTag,
          });
        },
      );
    } else {
      console.error(
        'getInspectorDataForViewAtPoint expects to receieve a host component',
      );

      return;
    }
  };
} else {
  getInspectorDataForViewTag = () => {
    invariant(
      false,
      'getInspectorDataForViewTag() is not available in production',
    );
  };

  getInspectorDataForViewAtPoint = (
    findNodeHandle: (componentOrHandle: any) => ?number,
    inspectedView: Object,
    locationX: number,
    locationY: number,
    callback: (viewData: TouchedViewDataAtPoint) => mixed,
  ): void => {
    invariant(
      false,
      'getInspectorDataForViewAtPoint() is not available in production.',
    );
  };
}

export {getInspectorDataForViewAtPoint, getInspectorDataForViewTag};
<commit_msg>Fix a warning typo (#18443)<commit_after>/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow
 */

import type {Fiber} from 'react-reconciler/src/ReactFiber';
import type {TouchedViewDataAtPoint, InspectorData} from './ReactNativeTypes';

import {
  findCurrentHostFiber,
  findCurrentFiberUsingSlowPath,
} from 'react-reconciler/src/ReactFiberTreeReflection';
import getComponentName from 'shared/getComponentName';
import {HostComponent} from 'react-reconciler/src/ReactWorkTags';
import invariant from 'shared/invariant';
// Module provided by RN:
import {UIManager} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';

import {getClosestInstanceFromNode} from './ReactNativeComponentTree';

const emptyObject = {};
if (__DEV__) {
  Object.freeze(emptyObject);
}

let getInspectorDataForViewTag;
let getInspectorDataForViewAtPoint;

if (__DEV__) {
  const traverseOwnerTreeUp = function(hierarchy, instance: any) {
    if (instance) {
      hierarchy.unshift(instance);
      traverseOwnerTreeUp(hierarchy, instance._debugOwner);
    }
  };

  const getOwnerHierarchy = function(instance: any) {
    const hierarchy = [];
    traverseOwnerTreeUp(hierarchy, instance);
    return hierarchy;
  };

  const lastNonHostInstance = function(hierarchy) {
    for (let i = hierarchy.length - 1; i > 1; i--) {
      const instance = hierarchy[i];

      if (instance.tag !== HostComponent) {
        return instance;
      }
    }
    return hierarchy[0];
  };

  const getHostProps = function(fiber) {
    const host = findCurrentHostFiber(fiber);
    if (host) {
      return host.memoizedProps || emptyObject;
    }
    return emptyObject;
  };

  const getHostNode = function(fiber: Fiber | null, findNodeHandle) {
    let hostNode;
    // look for children first for the hostNode
    // as composite fibers do not have a hostNode
    while (fiber) {
      if (fiber.stateNode !== null && fiber.tag === HostComponent) {
        hostNode = findNodeHandle(fiber.stateNode);
      }
      if (hostNode) {
        return hostNode;
      }
      fiber = fiber.child;
    }
    return null;
  };

  const createHierarchy = function(fiberHierarchy) {
    return fiberHierarchy.map(fiber => ({
      name: getComponentName(fiber.type),
      getInspectorData: findNodeHandle => {
        return {
          props: getHostProps(fiber),
          source: fiber._debugSource,
          measure: callback => {
            // If this is Fabric, we'll find a ShadowNode and use that to measure.
            const hostFiber = findCurrentHostFiber(fiber);
            const shadowNode =
              hostFiber != null &&
              hostFiber.stateNode !== null &&
              hostFiber.stateNode.node;

            if (shadowNode) {
              nativeFabricUIManager.measure(shadowNode, callback);
            } else {
              return UIManager.measure(
                getHostNode(fiber, findNodeHandle),
                callback,
              );
            }
          },
        };
      },
    }));
  };

  const getInspectorDataForInstance = function(closestInstance): InspectorData {
    // Handle case where user clicks outside of ReactNative
    if (!closestInstance) {
      return {
        hierarchy: [],
        props: emptyObject,
        selectedIndex: null,
        source: null,
      };
    }

    const fiber = findCurrentFiberUsingSlowPath(closestInstance);
    const fiberHierarchy = getOwnerHierarchy(fiber);
    const instance = lastNonHostInstance(fiberHierarchy);
    const hierarchy = createHierarchy(fiberHierarchy);
    const props = getHostProps(instance);
    const source = instance._debugSource;
    const selectedIndex = fiberHierarchy.indexOf(instance);

    return {
      hierarchy,
      props,
      selectedIndex,
      source,
    };
  };

  getInspectorDataForViewTag = function(viewTag: number): Object {
    const closestInstance = getClosestInstanceFromNode(viewTag);

    // Handle case where user clicks outside of ReactNative
    if (!closestInstance) {
      return {
        hierarchy: [],
        props: emptyObject,
        selectedIndex: null,
        source: null,
      };
    }

    const fiber = findCurrentFiberUsingSlowPath(closestInstance);
    const fiberHierarchy = getOwnerHierarchy(fiber);
    const instance = lastNonHostInstance(fiberHierarchy);
    const hierarchy = createHierarchy(fiberHierarchy);
    const props = getHostProps(instance);
    const source = instance._debugSource;
    const selectedIndex = fiberHierarchy.indexOf(instance);

    return {
      hierarchy,
      props,
      selectedIndex,
      source,
    };
  };

  getInspectorDataForViewAtPoint = function(
    findNodeHandle: (componentOrHandle: any) => ?number,
    inspectedView: Object,
    locationX: number,
    locationY: number,
    callback: (viewData: TouchedViewDataAtPoint) => mixed,
  ): void {
    let closestInstance = null;

    if (inspectedView._internalInstanceHandle != null) {
      // For Fabric we can look up the instance handle directly and measure it.
      nativeFabricUIManager.findNodeAtPoint(
        inspectedView._internalInstanceHandle.stateNode.node,
        locationX,
        locationY,
        internalInstanceHandle => {
          if (internalInstanceHandle == null) {
            callback({
              pointerY: locationY,
              frame: {left: 0, top: 0, width: 0, height: 0},
              ...getInspectorDataForInstance(closestInstance),
            });
          }

          closestInstance =
            internalInstanceHandle.stateNode.canonical._internalInstanceHandle;
          nativeFabricUIManager.measure(
            internalInstanceHandle.stateNode.node,
            (x, y, width, height, pageX, pageY) => {
              callback({
                pointerY: locationY,
                frame: {left: pageX, top: pageY, width, height},
                ...getInspectorDataForInstance(closestInstance),
              });
            },
          );
        },
      );
    } else if (inspectedView._internalFiberInstanceHandleDEV != null) {
      // For Paper we fall back to the old strategy using the React tag.
      UIManager.findSubviewIn(
        findNodeHandle(inspectedView),
        [locationX, locationY],
        (nativeViewTag, left, top, width, height) => {
          const inspectorData = getInspectorDataForInstance(
            getClosestInstanceFromNode(nativeViewTag),
          );
          callback({
            ...inspectorData,
            pointerY: locationY,
            frame: {left, top, width, height},
            touchedViewTag: nativeViewTag,
          });
        },
      );
    } else {
      console.error(
        'getInspectorDataForViewAtPoint expects to receive a host component',
      );

      return;
    }
  };
} else {
  getInspectorDataForViewTag = () => {
    invariant(
      false,
      'getInspectorDataForViewTag() is not available in production',
    );
  };

  getInspectorDataForViewAtPoint = (
    findNodeHandle: (componentOrHandle: any) => ?number,
    inspectedView: Object,
    locationX: number,
    locationY: number,
    callback: (viewData: TouchedViewDataAtPoint) => mixed,
  ): void => {
    invariant(
      false,
      'getInspectorDataForViewAtPoint() is not available in production.',
    );
  };
}

export {getInspectorDataForViewAtPoint, getInspectorDataForViewTag};
<|endoftext|>


<commit_before>import {builders, types} from '../../utils/build-types'
import {TAG_CSS_PROPERTY} from '../../constants'
import cssEscape from 'cssesc'
import getPreprocessorTypeByAttribute from '../../utils/get-preprocessor-type-by-attribute'
import preprocess from '../../utils/preprocess-node'

/**
 * Matches valid, multiline JavaScript comments in almost all its forms.
 * @const {RegExp}
 * @static
 */
const R_MLCOMMS = /\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//g

/**
 * Source for creating regexes matching valid quoted, single-line JavaScript strings.
 * It recognizes escape characters, including nested quotes and line continuation.
 * @const {string}
 */
const S_LINESTR = /"[^"\n\\]*(?:\\[\S\s][^"\n\\]*)*"|'[^'\n\\]*(?:\\[\S\s][^'\n\\]*)*'/.source

/**
 * Matches CSS selectors, excluding those beginning with '@' and quoted strings.
 * @const {RegExp}
 */

const CSS_SELECTOR = RegExp(`([{}]|^)[; ]*((?:[^@ ;{}][^{}]*)?[^@ ;{}:] ?)(?={)|${S_LINESTR}`, 'g')

/**
 * Parses styles enclosed in a "scoped" tag
 * The "css" string is received without comments or surrounding spaces.
 *
 * @param   {string} tag - Tag name of the root element
 * @param   {string} css - The CSS code
 * @returns {string} CSS with the styles scoped to the root element
 */
function scopedCSS(tag, css) {
  const host = ':host'
  const selectorsBlacklist = ['from', 'to']

  return css.replace(CSS_SELECTOR, function(m, p1, p2) {
    // skip quoted strings
    if (!p2) return m

    // we have a selector list, parse each individually
    p2 = p2.replace(/[^,]+/g, function(sel) {
      const s = sel.trim()

      // skip selectors already using the tag name
      if (s.indexOf(tag) === 0) {
        return sel
      }

      // skips the keywords and percents of css animations
      if (!s || selectorsBlacklist.indexOf(s) > -1 || s.slice(-1) === '%') {
        return sel
      }

      // replace the `:host` pseudo-selector, where it is, with the root tag name;
      // if `:host` was not included, add the tag name as prefix, and mirror all
      // `[data-is]`
      if (s.indexOf(host) < 0) {
        return `${tag} ${s},[is="${tag}"] ${s}`
      } else {
        return `${s.replace(host, tag)},${
          s.replace(host, `[is="${tag}"]`)}`
      }
    })

    // add the danling bracket char and return the processed selector list
    return p1 ? `${p1} ${p2}` : p2
  })
}

/**
 * Remove comments, compact and trim whitespace
 * @param {RiotParser.Node} cssNode - css node
 * @returns {RiotParser.Node} css node normalized
 */
function compactCss(cssNode) {
  return {
    ...cssNode,
    text: cssNode.text.replace(R_MLCOMMS, '').replace(/\s+/g, ' ').trim()
  }
}

const escapeIdentifier = identifier => JSON.stringify(cssEscape(identifier, {
  isIdentifier: true
})).replace(/"$|^"/g, '')

/**
 * Generate the component css
 * @param   { Object } sourceNode - node generated by the riot compiler
 * @param   { string } source - original component source code
 * @param   { Object } meta - compilation meta information
 * @param   { AST } ast - current AST output
 * @returns { AST } the AST generated
 */
export default function css(sourceNode, source, meta, ast) {
  const preprocessorName = getPreprocessorTypeByAttribute(sourceNode)
  const { options } = meta
  const cssNode = compactCss(sourceNode.text)
  const preprocessorOutput = preprocess('css', preprocessorName, meta, cssNode)
  const escapedCssIdentifier = escapeIdentifier(meta.tagName)

  const cssCode = (options.scopedCss ?
    scopedCSS(escapedCssIdentifier, cssEscape(preprocessorOutput.code)) :
    cssEscape(preprocessorOutput.code)
  ).trim()

  types.visit(ast, {
    visitProperty(path) {
      if (path.value.key.value === TAG_CSS_PROPERTY) {
        path.value.value = builders.templateLiteral(
          [builders.templateElement({ raw: cssCode, cooked: '' }, false)],
          []
        )

        return false
      }

      this.traverse(path)
    }
  })

  return ast
}<commit_msg>closes #121<commit_after>import {builders, types} from '../../utils/build-types'
import {TAG_CSS_PROPERTY} from '../../constants'
import cssEscape from 'cssesc'
import getPreprocessorTypeByAttribute from '../../utils/get-preprocessor-type-by-attribute'
import preprocess from '../../utils/preprocess-node'

/**
 * Matches valid, multiline JavaScript comments in almost all its forms.
 * @const {RegExp}
 * @static
 */
const R_MLCOMMS = /\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//g

/**
 * Source for creating regexes matching valid quoted, single-line JavaScript strings.
 * It recognizes escape characters, including nested quotes and line continuation.
 * @const {string}
 */
const S_LINESTR = /"[^"\n\\]*(?:\\[\S\s][^"\n\\]*)*"|'[^'\n\\]*(?:\\[\S\s][^'\n\\]*)*'/.source

/**
 * Matches CSS selectors, excluding those beginning with '@' and quoted strings.
 * @const {RegExp}
 */

const CSS_SELECTOR = RegExp(`([{}]|^)[; ]*((?:[^@ ;{}][^{}]*)?[^@ ;{}:] ?)(?={)|${S_LINESTR}`, 'g')

/**
 * Parses styles enclosed in a "scoped" tag
 * The "css" string is received without comments or surrounding spaces.
 *
 * @param   {string} tag - Tag name of the root element
 * @param   {string} css - The CSS code
 * @returns {string} CSS with the styles scoped to the root element
 */
function scopedCSS(tag, css) {
  const host = ':host'
  const selectorsBlacklist = ['from', 'to']

  return css.replace(CSS_SELECTOR, function(m, p1, p2) {
    // skip quoted strings
    if (!p2) return m

    // we have a selector list, parse each individually
    p2 = p2.replace(/[^,]+/g, function(sel) {
      const s = sel.trim()

      // skip selectors already using the tag name
      if (s.indexOf(tag) === 0) {
        return sel
      }

      // skips the keywords and percents of css animations
      if (!s || selectorsBlacklist.indexOf(s) > -1 || s.slice(-1) === '%') {
        return sel
      }

      // replace the `:host` pseudo-selector, where it is, with the root tag name;
      // if `:host` was not included, add the tag name as prefix, and mirror all
      // `[data-is]`
      if (s.indexOf(host) < 0) {
        return `${tag} ${s},[is="${tag}"] ${s}`
      } else {
        return `${s.replace(host, tag)},${
          s.replace(host, `[is="${tag}"]`)}`
      }
    })

    // add the danling bracket char and return the processed selector list
    return p1 ? `${p1} ${p2}` : p2
  })
}

/**
 * Remove comments, compact and trim whitespace
 * @param {RiotParser.Node} cssNode - css node
 * @returns {RiotParser.Node} css node normalized
 */
function compactCss(cssNode) {
  return {
    ...cssNode,
    text: cssNode.text.replace(R_MLCOMMS, '').replace(/\s+/g, ' ').trim()
  }
}

const escapeBackslashes = s => s.replace(/\\/g, '\\\\')
const escapeIdentifier = identifier => escapeBackslashes(cssEscape(identifier, {
  isIdentifier: true
}))

/**
 * Generate the component css
 * @param   { Object } sourceNode - node generated by the riot compiler
 * @param   { string } source - original component source code
 * @param   { Object } meta - compilation meta information
 * @param   { AST } ast - current AST output
 * @returns { AST } the AST generated
 */
export default function css(sourceNode, source, meta, ast) {
  const preprocessorName = getPreprocessorTypeByAttribute(sourceNode)
  const { options } = meta
  const cssNode = compactCss(sourceNode.text)
  const preprocessorOutput = preprocess('css', preprocessorName, meta, cssNode)
  const escapedCssIdentifier = escapeIdentifier(meta.tagName)

  const cssCode = (options.scopedCss ?
    scopedCSS(escapedCssIdentifier, escapeBackslashes(preprocessorOutput.code)) :
    escapeBackslashes(preprocessorOutput.code)
  ).trim()

  types.visit(ast, {
    visitProperty(path) {
      if (path.value.key.value === TAG_CSS_PROPERTY) {
        path.value.value = builders.templateLiteral(
          [builders.templateElement({ raw: cssCode, cooked: '' }, false)],
          []
        )

        return false
      }

      this.traverse(path)
    }
  })

  return ast
}<|endoftext|>


<commit_before>import React from 'react';
import { connect } from 'react-redux';
import { func, bool, string, oneOf } from 'prop-types';

import { modules } from 'veritone-redux-common';
const {
  user: { userIsAuthenticated },
  auth: { requestOAuthGrant, requestOAuthGrantImplicit },
  config: { getConfig }
} = modules;

import widget from '../../shared/widget';

@connect(
  state => ({
    userIsAuthenticated: userIsAuthenticated(state),
    apiRoot: getConfig(state).apiRoot
  }),
  { requestOAuthGrant, requestOAuthGrantImplicit },
  null,
  { withRef: true }
)
class OAuthLoginButton extends React.Component {
  static propTypes = {
    requestOAuthGrant: func.isRequired,
    requestOAuthGrantImplicit: func.isRequired,
    userIsAuthenticated: bool.isRequired,
    mode: oneOf(['implicit', 'authCode']),
    onAuthSuccess: func,
    onAuthFailure: func,
    apiRoot: string.isRequired,
    OAuthURI: string,
    // required params for implicit grant only:
    responseType: string,
    scope: string,
    redirectUri: string,
    clientId: string
  };

  static defaultProps = {
    mode: 'implicit'
  };

  handleLogin = () => {
    /* prettier-ignore */
    const defaultImplicitGrantURI = `${this.props.apiRoot}/v1/admin/oauth/authorize`;

    const OAuthURI =
      this.props.OAuthURI ||
      (this.props.mode === 'implicit' ? defaultImplicitGrantURI : undefined);

    const grantFn = {
      implicit: this.props.requestOAuthGrantImplicit,
      authCode: this.props.requestOAuthGrant
    }[this.props.mode];

    grantFn({
      OAuthURI,
      responseType: this.props.responseType,
      scope: this.props.scope,
      clientId: this.props.clientId,
      redirectUri: this.props.redirectUri,
      onSuccess: this.props.onAuthSuccess,
      onFailure: this.props.onAuthFailure
    });
  };

  render() {
    return (
      !this.props.userIsAuthenticated && (
        <button onClick={this.handleLogin}>Log in with Veritone</button>
      )
    );
  }
}

export default widget(OAuthLoginButton);
<commit_msg>use styled OAuthLoginButton component in OAuthLoginButton widget<commit_after>import React from 'react';
import { connect } from 'react-redux';
import { func, bool, string, oneOf } from 'prop-types';

import { modules } from 'veritone-redux-common';
const {
  user: { userIsAuthenticated },
  auth: { requestOAuthGrant, requestOAuthGrantImplicit },
  config: { getConfig }
} = modules;
import { OAuthLoginButton as OAuthLoginButtonComponent } from 'veritone-react-common';

import widget from '../../shared/widget';

@connect(
  state => ({
    userIsAuthenticated: userIsAuthenticated(state),
    apiRoot: getConfig(state).apiRoot
  }),
  { requestOAuthGrant, requestOAuthGrantImplicit },
  null,
  { withRef: true }
)
class OAuthLoginButton extends React.Component {
  static propTypes = {
    requestOAuthGrant: func.isRequired,
    requestOAuthGrantImplicit: func.isRequired,
    userIsAuthenticated: bool.isRequired,
    mode: oneOf(['implicit', 'authCode']),
    onAuthSuccess: func,
    onAuthFailure: func,
    apiRoot: string.isRequired,
    OAuthURI: string,
    // required params for implicit grant only:
    responseType: string,
    scope: string,
    redirectUri: string,
    clientId: string
  };

  static defaultProps = {
    mode: 'implicit'
  };

  handleLogin = () => {
    /* prettier-ignore */
    const defaultImplicitGrantURI = `${this.props.apiRoot}/v1/admin/oauth/authorize`;

    const OAuthURI =
      this.props.OAuthURI ||
      (this.props.mode === 'implicit' ? defaultImplicitGrantURI : undefined);

    const grantFn = {
      implicit: this.props.requestOAuthGrantImplicit,
      authCode: this.props.requestOAuthGrant
    }[this.props.mode];

    grantFn({
      OAuthURI,
      responseType: this.props.responseType,
      scope: this.props.scope,
      clientId: this.props.clientId,
      redirectUri: this.props.redirectUri,
      onSuccess: this.props.onAuthSuccess,
      onFailure: this.props.onAuthFailure
    });
  };

  render() {
    return (
      !this.props.userIsAuthenticated && (
        <OAuthLoginButtonComponent {...this.props} onClick={this.handleLogin} />
      )
    );
  }
}

export default widget(OAuthLoginButton);
<|endoftext|>


<commit_before>/* eslint-disable no-console */

// newsletter scheduling with MailChimp

import moment from 'moment';
import { getSetting } from 'meteor/vulcan:core';
import Newsletters from '../../modules/collection.js';
import MailChimpNPM from 'mailchimp';

/*

API

*/

const settings = getSetting('mailchimp');

if (settings) {

  const { apiKey, listId, fromName, fromEmail } = settings;
  const mailChimpAPI = new MailChimpNPM.MailChimpAPI(apiKey, { version : '2.0' });

  const callSyncAPI = ( section, method, options, callback ) => {
    const wrapped = Meteor.wrapAsync( mailChimpAPI.call, mailChimpAPI );
    return wrapped( section, method, options );
  };

  /*

  Methods

  */

  Newsletters.mailchimp = {

    // add a user to a MailChimp list.
    // called when a new user is created, or when an existing user fills in their email
    subscribe(email, confirm = false) {
      try {
        const subscribeOptions = {
          id: listId,
          email: {email: email},
          double_optin: confirm
        };
        // subscribe user
        const subscribe = callSyncAPI('lists', 'subscribe', subscribeOptions);
        return {result: 'subscribed', ...subscribe};
      } catch (error) {
        // if the email is already in the Mailchimp list, no need to throw an error
        if (error.message === "214") {
          return {result: 'already-subscribed'};
        }
        throw new Error("subscription-failed", error.message);
      }
    },

    // remove a user to a MailChimp list.
    // called from the user's account
    unsubscribe(email) {
      try {
        const subscribeOptions = {
          id: listId,
          email: {email: email},
          delete_member: true // delete the member from the list to make it possible for him to *resubscribe* via API (mailchimp's spam prevention policy)
        };
        // unsubscribe user
        const subscribe = callSyncAPI('lists', 'unsubscribe', subscribeOptions);
        return {result: 'unsubscribed', ...subscribe};
      } catch (error) {
        throw new Error("unsubscribe-failed", error.message);
      }
    },

    send({ title, subject, text, html, isTest = false }) {

      try {

        var campaignOptions = {
          type: 'regular',
          options: {
            list_id: listId,
            subject: subject,
            from_email: fromEmail,
            from_name: fromName
          },
          content: {
            html: html,
            text: text
          }
        };

        // create campaign
        const mailchimpNewsletter = callSyncAPI('campaigns', 'create', campaignOptions);

        console.log('// Newsletter created');
        // console.log(campaign)

        const scheduledMoment = moment().utcOffset(0).add(1, 'hours');
        const scheduledTime = scheduledMoment.format("YYYY-MM-DD HH:mm:ss");

        const scheduleOptions = {
          cid: mailchimpNewsletter.id,
          schedule_time: scheduledTime
        };

        // schedule campaign
        const schedule = callSyncAPI('campaigns', 'schedule', scheduleOptions); // eslint-disable-line

        console.log('// Newsletter scheduled for '+scheduledTime);

        return mailchimpNewsletter;

      } catch (error) {
        console.log(error);
        return false;
      }
    }

  }

}<commit_msg>use correct code for Mailchimp "already subscribed" state<commit_after>/* eslint-disable no-console */

// newsletter scheduling with MailChimp

import moment from 'moment';
import { getSetting } from 'meteor/vulcan:core';
import Newsletters from '../../modules/collection.js';
import MailChimpNPM from 'mailchimp';

/*

API

*/

const settings = getSetting('mailchimp');

if (settings) {

  const { apiKey, listId, fromName, fromEmail } = settings;
  const mailChimpAPI = new MailChimpNPM.MailChimpAPI(apiKey, { version : '2.0' });

  const callSyncAPI = ( section, method, options, callback ) => {
    const wrapped = Meteor.wrapAsync( mailChimpAPI.call, mailChimpAPI );
    return wrapped( section, method, options );
  };

  /*

  Methods

  */

  Newsletters.mailchimp = {

    // add a user to a MailChimp list.
    // called when a new user is created, or when an existing user fills in their email
    subscribe(email, confirm = false) {
      try {
        const subscribeOptions = {
          id: listId,
          email: {email: email},
          double_optin: confirm
        };
        // subscribe user
        const subscribe = callSyncAPI('lists', 'subscribe', subscribeOptions);
        return {result: 'subscribed', ...subscribe};
      } catch (error) {
        // if the email is already in the Mailchimp list, no need to throw an error
        if (error.code === 214) {
          return {result: 'already-subscribed'};
        }
        throw new Error("subscription-failed", error.message);
      }
    },

    // remove a user to a MailChimp list.
    // called from the user's account
    unsubscribe(email) {
      try {
        const subscribeOptions = {
          id: listId,
          email: {email: email},
          delete_member: true // delete the member from the list to make it possible for him to *resubscribe* via API (mailchimp's spam prevention policy)
        };
        // unsubscribe user
        const subscribe = callSyncAPI('lists', 'unsubscribe', subscribeOptions);
        return {result: 'unsubscribed', ...subscribe};
      } catch (error) {
        throw new Error("unsubscribe-failed", error.message);
      }
    },

    send({ title, subject, text, html, isTest = false }) {

      try {

        var campaignOptions = {
          type: 'regular',
          options: {
            list_id: listId,
            subject: subject,
            from_email: fromEmail,
            from_name: fromName
          },
          content: {
            html: html,
            text: text
          }
        };

        // create campaign
        const mailchimpNewsletter = callSyncAPI('campaigns', 'create', campaignOptions);

        console.log('// Newsletter created');
        // console.log(campaign)

        const scheduledMoment = moment().utcOffset(0).add(1, 'hours');
        const scheduledTime = scheduledMoment.format("YYYY-MM-DD HH:mm:ss");

        const scheduleOptions = {
          cid: mailchimpNewsletter.id,
          schedule_time: scheduledTime
        };

        // schedule campaign
        const schedule = callSyncAPI('campaigns', 'schedule', scheduleOptions); // eslint-disable-line

        console.log('// Newsletter scheduled for '+scheduledTime);

        return mailchimpNewsletter;

      } catch (error) {
        console.log(error);
        return false;
      }
    }

  }

}
<|endoftext|>


<commit_before>import R from 'ramda';
import { openResponseModal } from '../store/actions';

const errorHandler = dispatch => error => R.compose(
    dispatch,
    openResponseModal,
)({
    message: `${error}: ${error.response.data}`
});

export default errorHandler;
<commit_msg>Show error message from server if there is one<commit_after>import R from 'ramda';
import { openResponseModal } from '../store/actions';

const errorHandler = dispatch => error => R.compose(
    dispatch,
    openResponseModal,
)({
    // Show error message from server if there is one
    message: error.response.data.error ?
             error.response.data.error :
             `${error}: ${error.response.data}`,
});

export default errorHandler;
<|endoftext|>


<commit_before>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

import { useRef, useState, useCallback, useMemo } from 'react';

import {
  DataTablePageReducer,
  getPageActions,
  getColumns,
  getPageInfoColumns,
  getPageInitialiser,
} from '../pages/dataTableUtilities';

import { debounce } from '../utilities/index';

/**
 * Wrapper around useState, reimplementing useReducer with thunks.
 *
 * For DataTable pages within the app. Injects the initial state passed
 * with three fields:
 *
 * columns: an array of column objects defining the DataTable for the page.
 * getPageInfoColumns: closure function, returning an array of objects for
 *                     the pages PageInfo component.
 * PageActions: Object of composed ActionCreators for an individual page.
 *
 * Dispatch returned from useReducer is wrapped allowing the use
 * of thunks. Action creators can return either a plain object or a function.
 * If a function is returned, it is called, rather than dispatched,
 * allowing actions to perform side-effects.
 *
 * Returns the current state as well as three dispatchers for actions to the
 * reducer - a thunk dispatch (as above) and two debounced dispatchers - which
 * group sequential calls within a timeout period, calling either the last
 * invocation or the first within the timeout period.
 *
 * @param {String} page                   routeName for the current page.
 * @param {Object} initialState           Initial state of the reducer
 * @param {Func}   initialiser            Function to generate the initial state (optional)
 * @param {Object} pageObject             base PageObject for the page i.e. a Requisition.
 * @param {Number} debounceTimeout        Timeout period for a regular debounce
 * @param {Number} instantDebounceTimeout Timeout period for an instant debounce
 */
export const usePageReducer = (
  initialState,
  debounceTimeout = 250,
  instantDebounceTimeout = 250
) => {
  const { page, pageObject } = initialState;
  const columns = useMemo(() => getColumns(page), []);
  const pageInfoColumns = useMemo(() => getPageInfoColumns(page), []);
  const PageActions = useMemo(() => getPageActions(page), []);
  const initialiser = useMemo(() => getPageInitialiser(page), []);

  const [pageState, setPageState] = useState({
    ...(initialiser ? initialiser(pageObject) : initialState),
    columns,
    getPageInfoColumns: pageInfoColumns,
    PageActions,
  });

  // Reference to the current state object, independent of closures.
  const stateRef = useRef(pageState);
  const getState = () => stateRef.current;

  // Basic dispatch function.
  const dispatch = useCallback(action => {
    const newState = DataTablePageReducer(getState(), action);
    setPageState(newState);
    stateRef.current = newState;
  }, []);

  const thunkDispatcher = useCallback(
    action => (typeof action === 'function' ? action(thunkDispatcher, getState) : dispatch(action)),
    []
  );

  const debouncedDispatch = useCallback(debounce(thunkDispatcher, debounceTimeout), []);
  const instantDebouncedDispatch = useCallback(
    debounce(thunkDispatcher, instantDebounceTimeout, true),
    []
  );

  return [pageState, thunkDispatcher, instantDebouncedDispatch, debouncedDispatch];
};

export default usePageReducer;
<commit_msg>Add optional initialiser parameter to page reducer<commit_after>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

import { useRef, useState, useCallback, useMemo } from 'react';

import {
  DataTablePageReducer,
  getPageActions,
  getColumns,
  getPageInfoColumns,
  getPageInitialiser,
} from '../pages/dataTableUtilities';

import { debounce } from '../utilities/index';

/**
 * Wrapper around useState, reimplementing useReducer with thunks.
 *
 * For DataTable pages within the app. Injects the initial state passed
 * with three fields:
 *
 * columns: an array of column objects defining the DataTable for the page.
 * getPageInfoColumns: closure function, returning an array of objects for
 *                     the pages PageInfo component.
 * PageActions: Object of composed ActionCreators for an individual page.
 *
 * Dispatch returned from useReducer is wrapped allowing the use
 * of thunks. Action creators can return either a plain object or a function.
 * If a function is returned, it is called, rather than dispatched,
 * allowing actions to perform side-effects.
 *
 * Returns the current state as well as three dispatchers for actions to the
 * reducer - a thunk dispatch (as above) and two debounced dispatchers - which
 * group sequential calls within a timeout period, calling either the last
 * invocation or the first within the timeout period.
 *
 * @param {String} page                   routeName for the current page.
 * @param {Object} initialState           Initial state of the reducer
 * @param {Func}   initialiser            Function to generate the initial state (optional)
 * @param {Object} pageObject             base PageObject for the page i.e. a Requisition.
 * @param {Number} debounceTimeout        Timeout period for a regular debounce
 * @param {Number} instantDebounceTimeout Timeout period for an instant debounce
 */
export const usePageReducer = (
  initialState,
  initialiser,
  debounceTimeout = 250,
  instantDebounceTimeout = 250
) => {
  const { page, pageObject } = initialState;
  const columns = useMemo(() => getColumns(page), []);
  const pageInfoColumns = useMemo(() => getPageInfoColumns(page), []);
  const PageActions = useMemo(() => getPageActions(page), []);
  const pageInitialiser = useMemo(() => initialiser || getPageInitialiser(page), []);

  const [pageState, setPageState] = useState({
    ...(pageInitialiser ? pageInitialiser(pageObject) : initialState),
    columns,
    getPageInfoColumns: pageInfoColumns,
    PageActions,
  });

  // Reference to the current state object, independent of closures.
  const stateRef = useRef(pageState);
  const getState = () => stateRef.current;

  // Basic dispatch function.
  const dispatch = useCallback(action => {
    const newState = DataTablePageReducer(getState(), action);
    setPageState(newState);
    stateRef.current = newState;
  }, []);

  const thunkDispatcher = useCallback(
    action => (typeof action === 'function' ? action(thunkDispatcher, getState) : dispatch(action)),
    []
  );

  const debouncedDispatch = useCallback(debounce(thunkDispatcher, debounceTimeout), []);
  const instantDebouncedDispatch = useCallback(
    debounce(thunkDispatcher, instantDebounceTimeout, true),
    []
  );

  return [pageState, thunkDispatcher, instantDebouncedDispatch, debouncedDispatch];
};

export default usePageReducer;
<|endoftext|>


<commit_before>morpheus.LegendWithStops = function () {
  morpheus.AbstractCanvas.call(this, false);
  this.setBounds({
    width: 300,
    height: 40
  });
  var that = this;
  this.hammer = morpheus.Util.hammer(this.canvas, ['pan', 'tap', 'press'])
  .on(
    'panmove',
    this.panmove = function (event) {
      if (that.panStartSelectedIndex !== -1) {
        var position = morpheus.CanvasUtil.getMousePos(
          event.target, event);
        var fraction = that.fractionToStopPix
        .invert(position.x);
        fraction = Math.max(0, fraction);
        fraction = Math.min(1, fraction);
        that.trigger('moved', {
          fraction: fraction
        });
      }
    }).on(
    'panstart',
    this.panstart = function (event) {
      that.panStartSelectedIndex = that
      .findIndexForPosition(morpheus.CanvasUtil
      .getMousePos(event.target, event, true));
    }).on('panend', this.panend = function (event) {
    that.panStartSelectedIndex = -1;
  }).on(
    'tap',
    this.tap = function (event) {
      var position = morpheus.CanvasUtil.getMousePos(
        event.target, event);
      if (event.tapCount > 1) {
        var fraction = that.fractionToStopPix
        .invert(position.x);
        that.trigger('added', {
          fraction: fraction
        });
      } else {
        that.selectedIndex = that
        .findIndexForPosition(position);
        that.trigger('selectedIndex', {
          selectedIndex: that.selectedIndex
        });
      }
    });
  $(this.canvas).on('keydown', function (e) {
    // 8=backspace, 46=delete
    if ((e.which == 8 || e.which == 46) && that.selectedIndex !== -1) {
      that.trigger('delete');
      e.preventDefault();
      e.stopPropagation();
      e.stopImmediatePropagation();
    }
  });
};
morpheus.LegendWithStops.prototype = {
  selectedIndex: -1,
  border: 7,
  stopHalfSize: 5,
  panStartSelectedIndex: -1,
  destroy: function () {
    $(this.canvas).off('keyup');
    this.hammer.off('panstart',
      this.panstart).off('panmove', this.panmove).off('tap', this.tap);
    this.hammer.destroy();
  },
  setSelectedIndex: function (index) {
    this.panStartSelectedIndex = -1;
  },
  findIndexForPosition: function (position) {
    // pix - stopHalfSize to pix + stopHalfSize
    if (position.y >= 22) {
      for (var i = 0, length = this.fractions.length; i < length; i++) {
        var pix = this.fractionToStopPix(this.fractions[i]);
        var start = pix - this.stopHalfSize;
        var end = pix + this.stopHalfSize;
        if (position.x >= start && position.x <= end) {
          return i;
        }
      }
    }
    return -1;
  },
  draw: function (fractions, colors, stepped, fractionToStopPix) {
    this.fractions = fractions;
    this.colors = colors;
    this.stepped = stepped;
    this.fractionToStopPix = fractionToStopPix;
    var context = this.canvas.getContext('2d');
    morpheus.CanvasUtil.resetTransform(context);
    context.clearRect(0, 0, this.getUnscaledWidth(), this
    .getUnscaledHeight());
    context.translate(this.border, 0);
    morpheus.HeatMapColorSchemeLegend.draw(context, fractions, colors, this
      .getUnscaledWidth()
      - 2 * this.border, this.getUnscaledHeight() - 20, stepped);
    context.translate(-this.border, 0);
    context.lineWidth = 1;
    context.strokeStyle = 'Grey';
    context.strokeRect(this.border, 0, this.getUnscaledWidth() - 2
      * this.border, this.getUnscaledHeight() - 20);
    for (var i = 0; i < fractions.length; i++) {
      if (i > 0 && fractions[i] === fractions[i - 1]) {
        continue;
      }
      context.fillStyle = colors[i];
      var pix = fractionToStopPix(fractions[i]);
      context.fillRect(pix - this.stopHalfSize, 22,
        this.stopHalfSize * 2, this.stopHalfSize * 2);
      if (this.selectedIndex === i) {
        context.lineWidth = 2;
        context.strokeStyle = 'black';
      } else {
        context.lineWidth = 1;
        context.strokeStyle = 'Grey';
      }
      context.strokeRect(pix - this.stopHalfSize, 22,
        this.stopHalfSize * 2, this.stopHalfSize * 2);
    }
  }
};
morpheus.Util.extend(morpheus.LegendWithStops, morpheus.AbstractCanvas);
morpheus.Util.extend(morpheus.LegendWithStops, morpheus.Events);
<commit_msg>overlapping text<commit_after>morpheus.LegendWithStops = function () {
  morpheus.AbstractCanvas.call(this, false);
  this.setBounds({
    width: 300,
    height: 40
  });
  var that = this;
  this.hammer = morpheus.Util.hammer(this.canvas, ['pan', 'tap', 'press'])
  .on(
    'panmove',
    this.panmove = function (event) {
      if (that.panStartSelectedIndex !== -1) {
        var position = morpheus.CanvasUtil.getMousePos(
          event.target, event);
        var fraction = that.fractionToStopPix
        .invert(position.x);
        fraction = Math.max(0, fraction);
        fraction = Math.min(1, fraction);
        that.trigger('moved', {
          fraction: fraction
        });
      }
    }).on(
    'panstart',
    this.panstart = function (event) {
      that.panStartSelectedIndex = that
      .findIndexForPosition(morpheus.CanvasUtil
      .getMousePos(event.target, event, true));
    }).on('panend', this.panend = function (event) {
    that.panStartSelectedIndex = -1;
  }).on(
    'tap',
    this.tap = function (event) {
      var position = morpheus.CanvasUtil.getMousePos(
        event.target, event);
      if (event.tapCount > 1) {
        // don't add on double-click
        // var fraction = that.fractionToStopPix
        // .invert(position.x);
        // that.trigger('added', {
        //   fraction: fraction
        // });
      } else {
        that.selectedIndex = that
        .findIndexForPosition(position);
        that.trigger('selectedIndex', {
          selectedIndex: that.selectedIndex
        });
      }
    });
  $(this.canvas).on('keydown', function (e) {
    // 8=backspace, 46=delete
    if ((e.which == 8 || e.which == 46) && that.selectedIndex !== -1) {
      that.trigger('delete');
      e.preventDefault();
      e.stopPropagation();
      e.stopImmediatePropagation();
    }
  });
};
morpheus.LegendWithStops.prototype = {
  selectedIndex: -1,
  border: 7,
  stopHalfSize: 5,
  panStartSelectedIndex: -1,
  destroy: function () {
    $(this.canvas).off('keyup');
    this.hammer.off('panstart',
      this.panstart).off('panmove', this.panmove).off('tap', this.tap);
    this.hammer.destroy();
  },
  setSelectedIndex: function (index) {
    this.panStartSelectedIndex = -1;
  },
  findIndexForPosition: function (position) {
    // pix - stopHalfSize to pix + stopHalfSize
    if (position.y >= 22) {
      for (var i = 0, length = this.fractions.length; i < length; i++) {
        var pix = this.fractionToStopPix(this.fractions[i]);
        var start = pix - this.stopHalfSize;
        var end = pix + this.stopHalfSize;
        if (position.x >= start && position.x <= end) {
          return i;
        }
      }
    }
    return -1;
  },
  draw: function (fractions, colors, stepped, fractionToStopPix) {
    this.fractions = fractions;
    this.colors = colors;
    this.stepped = stepped;
    this.fractionToStopPix = fractionToStopPix;
    var context = this.canvas.getContext('2d');
    morpheus.CanvasUtil.resetTransform(context);
    context.clearRect(0, 0, this.getUnscaledWidth(), this
    .getUnscaledHeight());
    context.translate(this.border, 0);
    morpheus.HeatMapColorSchemeLegend.draw(context, fractions, colors, this
      .getUnscaledWidth()
      - 2 * this.border, this.getUnscaledHeight() - 20, stepped);
    context.translate(-this.border, 0);
    context.lineWidth = 1;
    context.strokeStyle = 'Grey';
    context.strokeRect(this.border, 0, this.getUnscaledWidth() - 2
      * this.border, this.getUnscaledHeight() - 20);
    for (var i = 0; i < fractions.length; i++) {
      if (i > 0 && fractions[i] === fractions[i - 1]) {
        continue;
      }
      context.fillStyle = colors[i];
      var pix = fractionToStopPix(fractions[i]);
      context.fillRect(pix - this.stopHalfSize, 22,
        this.stopHalfSize * 2, this.stopHalfSize * 2);
      if (this.selectedIndex === i) {
        context.lineWidth = 2;
        context.strokeStyle = 'black';
      } else {
        context.lineWidth = 1;
        context.strokeStyle = 'Grey';
      }
      context.strokeRect(pix - this.stopHalfSize, 22,
        this.stopHalfSize * 2, this.stopHalfSize * 2);
    }
  }
};
morpheus.Util.extend(morpheus.LegendWithStops, morpheus.AbstractCanvas);
morpheus.Util.extend(morpheus.LegendWithStops, morpheus.Events);
<|endoftext|>


<commit_before>import { assert } from 'chai'
import TableBase from '../../app/base/TableBase.js'
import { __RewireAPI__ as TB } from '../../app/base/TableBase.js'
import Backbone from 'backbone'
import $ from 'jquery'

describe('base', () => {
  describe('TableBase', () => {
    let handler = new Backbone.Model()

    describe('update', () => {
      it('calls draw_table', (done) => {
        let TableBase_ = TableBase.extend({
          constructor: function () {
            Backbone.Model.apply(this, arguments);
          },

          draw_table: () => done()
        })

        let tb = new TableBase_()

        tb.update('notUndefined')
      })

      it('sets this.data to its argument', () => {
        let acceptData = 'foo'

        let TableBase_ = TableBase.extend({
          constructor: function () {
            Backbone.Model.apply(this, arguments);
          },

          draw_table: () => null
        })

        let tb = new TableBase_()

        tb.update(acceptData)

        assert.equal(tb.data, acceptData)
      })
    })

    describe('get_tabular_data', () => {
      it('throws', () => {
        let TableBase_ = TableBase.extend({
          constructor: function () {
            Backbone.Model.apply(this, arguments);
          }
        })

        let tb = new TableBase_()

        assert.throws(() => {
          tb.get_tabular_data()
        })
      })
    })

    describe('showError', () => {
      let id = 'test_selector'
      let selector = '#' + id

      before(() => {
        $(`<div id="${ id }"></div>`).appendTo($('body'))
      })

      after(() => {
        document.body.innerHTML = ''
      })

      it('prepends an error message to its selector\'s el', () => {

        let TableBase_ = TableBase.extend({
          constructor: function () {
            Backbone.Model.apply(this, arguments);
          }
        })

        let tb = new TableBase_({ selector })

        tb.showError()

        assert.isOk($('.bg-warning').length)
      })
    })

    describe('draw_table', () => {
      let id = 'test_selector'
      let selector = '#' + id

      before(() => {
        $(`<div id="${ id }"></div>`).appendTo($('body'))
      })

      after(() => {
        document.body.innerHTML = ''
      })

      it('attaches a table using its matrix', () => {
        let TableBase_ = TableBase.extend({
          constructor: function () {
            Backbone.Model.apply(this, arguments);
          },
          get_tabular_data: () => [
            [1, 2, 3],
            [4, 5, 6],
            [7, 8, 9]
          ]
        })

        let tb = new TableBase_({ selector })

        tb.draw_table()

        assert.isOk($('table.table.table-striped.table-condensed.dash-tables').length)
        assert.equal(3, $('th').length)
        assert.equal(6, $('td').length)
      })
    })
  })
})
<commit_msg>Adding TableBase tests<commit_after>import { assert } from 'chai'
import TableBase from '../../app/base/TableBase.js'
import { __RewireAPI__ as TB } from '../../app/base/TableBase.js'
import Backbone from 'backbone'
import $ from 'jquery'

describe('base', () => {
  describe('TableBase', () => {
    let handler = new Backbone.Model()

    describe('constructor', () => {
      it('binds this.update to its handler\'s get_data Promise resolution', (done) => {
        let accept = true
        let handler = {
          get_data: () => new Promise((resolve, reject) => resolve(accept))
        }
        let TableBase_ = TableBase.extend({
          update: (data) => {
            assert.equal(data, accept)
            done()
          },

          showError: () => true
        })

        new TableBase_({handler})
      })

      it('binds this.showError to its handler\'s get_data Promise rejection', (done) => {
        let accept = true
        let handler = {
          get_data: () => new Promise((resolve, reject) => reject(accept))
        }
        let TableBase_ = TableBase.extend({
          showError: (error) => {
            assert.equal(error, accept)
            done()
          },

          update: () => true
        })

        new TableBase_({handler})
      })
    })

    describe('update', () => {
      it('calls draw_table', (done) => {
        let TableBase_ = TableBase.extend({
          constructor: function () {
            Backbone.Model.apply(this, arguments);
          },

          draw_table: () => done()
        })

        let tb = new TableBase_()

        tb.update('notUndefined')
      })

      it('sets this.data to its argument', () => {
        let acceptData = 'foo'

        let TableBase_ = TableBase.extend({
          constructor: function () {
            Backbone.Model.apply(this, arguments);
          },

          draw_table: () => null
        })

        let tb = new TableBase_()

        tb.update(acceptData)

        assert.equal(tb.data, acceptData)
      })
    })

    describe('get_tabular_data', () => {
      it('throws', () => {
        let TableBase_ = TableBase.extend({
          constructor: function () {
            Backbone.Model.apply(this, arguments);
          }
        })

        let tb = new TableBase_()

        assert.throws(() => {
          tb.get_tabular_data()
        })
      })
    })

    describe('showError', () => {
      let id = 'test_selector'
      let selector = '#' + id

      before(() => {
        $(`<div id="${ id }"></div>`).appendTo($('body'))
      })

      after(() => {
        document.body.innerHTML = ''
      })

      it('prepends an error message to its selector\'s el', () => {

        let TableBase_ = TableBase.extend({
          constructor: function () {
            Backbone.Model.apply(this, arguments);
          }
        })

        let tb = new TableBase_({ selector })

        tb.showError()

        assert.isOk($('.bg-warning').length)
      })
    })

    describe('draw_table', () => {
      let id = 'test_selector'
      let selector = '#' + id

      before(() => {
        $(`<div id="${ id }"></div>`).appendTo($('body'))
      })

      after(() => {
        document.body.innerHTML = ''
      })

      it('attaches a table using its matrix', () => {
        let TableBase_ = TableBase.extend({
          constructor: function () {
            Backbone.Model.apply(this, arguments);
          },
          get_tabular_data: () => [
            [1, 2, 3],
            [4, 5, 6],
            [7, 8, 9]
          ]
        })

        let tb = new TableBase_({ selector })

        tb.draw_table()

        assert.isOk($('table.table.table-striped.table-condensed.dash-tables').length)
        assert.equal(3, $('th').length)
        assert.equal(6, $('td').length)
      })
    })
  })
})
<|endoftext|>


<commit_before>'use strict';

// Members controller
angular.module('members').controller('MembersController', ['$scope', '$stateParams', '$location', 'Authentication', 'Members', 'Participants',
	function($scope, $stateParams, $location, Authentication, Members, Participants) {
		$scope.authentication = Authentication;
		$scope.errorStatus = {};

		$scope.setErrors = function(errors) {
			$scope.errorStatus = {};
			if (errors.fields) {
				for (var ei in errors.fields) {
					$scope.errorStatus[errors.fields[ei]] = 'has-error';
				}
			}
			$scope.errorMessages = errors.messages;
		};

		// Create new Member
		$scope.create = function() {

			var clearFields = function(Participant) {
				$scope.errorMessages = null;
				$scope.errorStatus = {};
				$scope.member.firstName = '';
				$scope.member.lastName = '';
				$scope.member.phone = '';
				$scope.member.email = '';
				$scope.member.identity = '';
				$scope.member.affiliation = '';
				$scope.member.address = '';
				$scope.member.city = '';
				$scope.member.state = '';
				$scope.member.zipCode = '';
				$scope.member.shirtSize = '';
				$scope.member.shirtReceived = '';
				$scope.member.talent = '';
				$scope.member.placeOfWorship = '';
				$scope.member.recruitment = '';
				$scope.member.communityNetworks = '';
				$scope.member.extraGroups = '';
				$scope.member.otherNetworks = '';
			};

			// Create new Member
			var member = $scope.member;

			if(member && member._id) {
				member.$update(function(response) {
					$location.path('members/' + response._id);
					clearFields(response);
				}, function(errorResponse) {
				    $scope.error = errorResponse.data.message;
				    $scope.errors = $scope.setErrors(errorResponse.data);

				});

			} else {
				// Redirect after save
				member.$save(function(response) {
					$location.path('members/' + response._id);
					clearFields(response);
				}, function(errorResponse) {
					$scope.error = errorResponse.data.message;
				    $scope.errors = $scope.setErrors(errorResponse.data);
				});

			}
		};

		// Remove existing Member
		$scope.remove = function(member) {
			if ( member ) {
				member.$remove();

				for (var i in $scope.members) {
					if ($scope .members [i] === member) {
						$scope.members.splice(i, 1);
					}
				}
			} else {
				$scope.member.$remove(function() {
					$location.path('members');
				});
			}
		};

		// Update existing Member
		$scope.update = function() {
			var member = $scope.member;

			member.$update(function() {
				$location.path('members/' + member._id);
			}, function(errorResponse) {
				$scope.error = errorResponse.data.message;
			    $scope.errors = $scope.setErrors(errorResponse.data);
			});
		};

		// Find a list of Members
		$scope.find = function() {
			$scope.members = Members.query();
		};

		$scope.showOnlyShirtlessMembers = false;

		$scope.shirtFilter = function(member) {
			if($scope.showOnlyShirtlessMembers === true) {
				return member.shirtReceived === false;
			} else {
				return member;
			}
		};

		$scope.giveShirt = function(member) {
			//var member = $scope.member;
			if(member.shirtReceived === false) {
				member.shirtReceived = true;

				member.$update(function() {

				}, function(errorResponse) {
					$scope.error = errorResponse.data.message;
				});
			}
		};

		// Find existing participants
		$scope.findParticipants = function(name) {
			return Participants.query({name: name}).$promise;
		};

		// Find existing Member
		$scope.findOne = function() {
			$scope.member = Members.get({
				memberId: $stateParams.memberId
			});
		};

		// Initialize a new Member
		$scope.newMember = function() {
			$scope.member = new Members({
				communityNetworks: '',
				extraGroups: '',
				otherNetworks: ''
			});
		};

		// A member is selected from the typehead search
		$scope.selectMember = function(participant) {
			$scope.member = new Members(participant);
			$scope.member.communityNetworks = '';
			$scope.member.extraGroups = '';
			$scope.member.otherNetworks = '';
		};

		$scope.exportToExcel = function() {
			var csvData = [[
				'Name',
				'Phone',
				'Email',
				'Identity',
				'Affiliation',
				'Address',
				'City',
				'State',
				'Zip Code',
				'Shirt Size',
				'Shirt Received',
				'Talent',
				'Place of Worship',
				'Recruitment',
				'Community Networks',
				'Extra Groups',
				'Other Networks'
			]];

			for (var i = 0; i < $scope.members.length; i++) {
				var member = $scope.members[i];
				if ($scope.shirtFilter(member)) {
					csvData.push([
						member.displayName,
						member.phone,
						member.email,
						member.identity,
						member.affiliation,
						member.address,
						member.city,
						member.state,
						member.zipCode,
						member.shirtSize,
						member.shirtReceived ? 'Yes' : 'No',
						member.talent,
						member.placeOfWorship,
						member.recruitment,
						member.communityNetworks,
						member.extraGroups,
						member.otherNetworks
					].join(','))
				}
			}
			var el = document.createElement('a');
			el.id = 'downloadFile';
			el.href = 'data:text/csv;charset=utf8,' + encodeURIComponent(csvData.join('\n'));
			el.download = 'members.csv';

			var before = document.getElementById('exportLink');
			before.parentNode.insertBefore(el, before);
			el.click();
			el.remove();
		};
	}
]);<commit_msg>Another missing semicolon<commit_after>'use strict';

// Members controller
angular.module('members').controller('MembersController', ['$scope', '$stateParams', '$location', 'Authentication', 'Members', 'Participants',
	function($scope, $stateParams, $location, Authentication, Members, Participants) {
		$scope.authentication = Authentication;
		$scope.errorStatus = {};

		$scope.setErrors = function(errors) {
			$scope.errorStatus = {};
			if (errors.fields) {
				for (var ei in errors.fields) {
					$scope.errorStatus[errors.fields[ei]] = 'has-error';
				}
			}
			$scope.errorMessages = errors.messages;
		};

		// Create new Member
		$scope.create = function() {

			var clearFields = function(Participant) {
				$scope.errorMessages = null;
				$scope.errorStatus = {};
				$scope.member.firstName = '';
				$scope.member.lastName = '';
				$scope.member.phone = '';
				$scope.member.email = '';
				$scope.member.identity = '';
				$scope.member.affiliation = '';
				$scope.member.address = '';
				$scope.member.city = '';
				$scope.member.state = '';
				$scope.member.zipCode = '';
				$scope.member.shirtSize = '';
				$scope.member.shirtReceived = '';
				$scope.member.talent = '';
				$scope.member.placeOfWorship = '';
				$scope.member.recruitment = '';
				$scope.member.communityNetworks = '';
				$scope.member.extraGroups = '';
				$scope.member.otherNetworks = '';
			};

			// Create new Member
			var member = $scope.member;

			if(member && member._id) {
				member.$update(function(response) {
					$location.path('members/' + response._id);
					clearFields(response);
				}, function(errorResponse) {
				    $scope.error = errorResponse.data.message;
				    $scope.errors = $scope.setErrors(errorResponse.data);

				});

			} else {
				// Redirect after save
				member.$save(function(response) {
					$location.path('members/' + response._id);
					clearFields(response);
				}, function(errorResponse) {
					$scope.error = errorResponse.data.message;
				    $scope.errors = $scope.setErrors(errorResponse.data);
				});

			}
		};

		// Remove existing Member
		$scope.remove = function(member) {
			if ( member ) {
				member.$remove();

				for (var i in $scope.members) {
					if ($scope .members [i] === member) {
						$scope.members.splice(i, 1);
					}
				}
			} else {
				$scope.member.$remove(function() {
					$location.path('members');
				});
			}
		};

		// Update existing Member
		$scope.update = function() {
			var member = $scope.member;

			member.$update(function() {
				$location.path('members/' + member._id);
			}, function(errorResponse) {
				$scope.error = errorResponse.data.message;
			    $scope.errors = $scope.setErrors(errorResponse.data);
			});
		};

		// Find a list of Members
		$scope.find = function() {
			$scope.members = Members.query();
		};

		$scope.showOnlyShirtlessMembers = false;

		$scope.shirtFilter = function(member) {
			if($scope.showOnlyShirtlessMembers === true) {
				return member.shirtReceived === false;
			} else {
				return member;
			}
		};

		$scope.giveShirt = function(member) {
			//var member = $scope.member;
			if(member.shirtReceived === false) {
				member.shirtReceived = true;

				member.$update(function() {

				}, function(errorResponse) {
					$scope.error = errorResponse.data.message;
				});
			}
		};

		// Find existing participants
		$scope.findParticipants = function(name) {
			return Participants.query({name: name}).$promise;
		};

		// Find existing Member
		$scope.findOne = function() {
			$scope.member = Members.get({
				memberId: $stateParams.memberId
			});
		};

		// Initialize a new Member
		$scope.newMember = function() {
			$scope.member = new Members({
				communityNetworks: '',
				extraGroups: '',
				otherNetworks: ''
			});
		};

		// A member is selected from the typehead search
		$scope.selectMember = function(participant) {
			$scope.member = new Members(participant);
			$scope.member.communityNetworks = '';
			$scope.member.extraGroups = '';
			$scope.member.otherNetworks = '';
		};

		$scope.exportToExcel = function() {
			var csvData = [[
				'Name',
				'Phone',
				'Email',
				'Identity',
				'Affiliation',
				'Address',
				'City',
				'State',
				'Zip Code',
				'Shirt Size',
				'Shirt Received',
				'Talent',
				'Place of Worship',
				'Recruitment',
				'Community Networks',
				'Extra Groups',
				'Other Networks'
			]];

			for (var i = 0; i < $scope.members.length; i++) {
				var member = $scope.members[i];
				if ($scope.shirtFilter(member)) {
					csvData.push([
						member.displayName,
						member.phone,
						member.email,
						member.identity,
						member.affiliation,
						member.address,
						member.city,
						member.state,
						member.zipCode,
						member.shirtSize,
						member.shirtReceived ? 'Yes' : 'No',
						member.talent,
						member.placeOfWorship,
						member.recruitment,
						member.communityNetworks,
						member.extraGroups,
						member.otherNetworks
					].join(','));
				}
			}
			var el = document.createElement('a');
			el.id = 'downloadFile';
			el.href = 'data:text/csv;charset=utf8,' + encodeURIComponent(csvData.join('\n'));
			el.download = 'members.csv';

			var before = document.getElementById('exportLink');
			before.parentNode.insertBefore(el, before);
			el.click();
			el.remove();
		};
	}
]);<|endoftext|>


<commit_before>//requires Jquery 2.0

//Warning: HTML below is a temporary hack to test the Javascript. (:

var chimpoBar = '\
' +

				'<form class="chimpoForm" id="chimpoForm" action="http://github.us7.list-manage1.com/subscribe/post" method="POST">' +
				 ' <input type="hidden" name="u" value="fab15eaf19084212d78240d70">' +
				 ' <input type="hidden" name="id" value="6b08a63dfd">' +
				 ' <input class="input" type="email" value="" name="MERGE0" placeholder="Your Email Address" required>' +
				 ' <input type="submit" value="Send" name="submit" id="mc-embedded-subscribe">' +
				 '</form>' +

				'\
';

$(document).ready(function() {
	$('body').append(chimpoBar);

	$('#chimpoForm').submit(function(e) {
	  var $this = $(this);
	  $.ajax({
	      type: "POST", // GET & url for json slightly different
	      url: "http://github.us7.list-manage1.com/subscribe/post-json?c=?",
	      data: $this.serialize(),
	      dataType    : 'json',
	      contentType: "application/json; charset=utf-8",
	      error       : function(err) { alert("Could not connect to the registration server."); },
	      success     : function(data) {
	          if (data.result != "success") {
				  alert('Shit broken');
	          } else {
				  alert('Worked');
	          }
	      }
	  });
	  return false;
	});
	});

	//TODO:
	// fix html
	// add validation * IN PROGRESS
	// add ajax POST call * DONE
	// mailchimp specific data * DONE<commit_msg>works. now to fix issues in todo.<commit_after>//Prototype version. Currently works all right. Going to prettify now. ^_^

//requires Jquery 2.0

//Warning: HTML below is a temporary hack to test the Javascript. (:

var chimpoBar = '
' +

				'<form class="chimpoForm" id="chimpoForm" action="http://github.us7.list-manage1.com/subscribe/post" method="POST">' +
				 ' <input type="hidden" name="u" value="fab15eaf19084212d78240d70">' +
				 ' <input type="hidden" name="id" value="6b08a63dfd">' +
				 ' <input class="input" type="email" value="" name="MERGE0" placeholder="Your Email Address" required>' +
				 ' <input type="submit" value="Send" name="submit" id="mc-embedded-subscribe">' +
				 '</form>' +

				'
';

var chimpoError = '
'+
                  'There was an error processing your newsletter signup. Please reload and try again.' +
                  '
';

var chimpoSuccess = '
Thankyou
';

$(document).ready(function() {
	$('body').append(chimpoBar);

	$('#chimpoForm').submit(function(e) {
	  var $this = $(this);
	  $.ajax({
	      type: "POST", // GET & url for json slightly different
	      url: "http://github.us7.list-manage1.com/subscribe/post-json?c=?",
	      data: $this.serialize(),
	      dataType    : 'json',
	      contentType: "application/json; charset=utf-8",
	      error       : function(err) { alert("There was an error processing your newsletter signup. Please reload and try again."); },
	      success     : function(data) {
	          if (data.result != "success") {
				  $('#chimpoBar').slideUp('slow');
				  $('body').append(chimpoError).show();

			  } else {
				  $('#chimpoBar').slideUp('slow');
				  $('body').append(chimpoSuccess).show();
	          }
	      }
	  });
	  return false;
	});
	});

	//TODO:
	// fix html and css. Make it POP!
	// add validation * Basics done. Need to improve.
	// add ajax POST call * DONE
	// mailchimp specific data * DONE<|endoftext|>


<commit_before>mini.Module(
	"engine/rendering/renderer"
)
.requires(

)
.defines(function() {
	var Renderer = mini.Class.subclass({
		initialize: function() {
			this.canvasId = env.canvas.canvasId;
			this.canvas = env.canvas.domElement;
			this.context = this.canvas.getContext('2d');
		}
	});

	return Renderer;
});
<commit_msg>added draw function to Renderer<commit_after>mini.Module(
	"engine/rendering/renderer"
)
.requires(

)
.defines(function() {
	var Renderer = mini.Class.subclass({
		initialize: function() {
			this.canvasId = env.canvas.canvasId;
			this.canvas = env.canvas.domElement;
			this.context = this.canvas.getContext('2d');
		},

		/*
		 * Drawing
		 */
		draw: function(objectToDraw) {

		}
	});

	return Renderer;
});
<|endoftext|>