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

/**
 * Tests if the expected properties are the actual results from meters.
 */

const { mocha, expect, testDB } = require('../common');
const Meter = require('../../models/Meter');
const Point = require('../../models/Point');
const moment = require('moment');
const Unit = require('../../models/Unit');
const gps = new Point(90, 45);

/**
 * Checks if the expected meter properties have the actual properties.
 * @param expected expected meter properties
 * @param actual actual meter properties
 */
function expectMetersToBeEquivalent(expected, actual) {
	expect(actual).to.have.property('id', expected.id);
	expect(actual).to.have.property('name', expected.name);
	expect(actual).to.have.property('enabled', expected.enabled);
	expect(actual).to.have.property('type', expected.type);
	expect(actual).to.have.property('gps');
	expect(actual.gps).to.have.property('latitude', expected.gps.latitude);
	expect(actual.gps).to.have.property('longitude', expected.gps.longitude);
	expect(actual).to.have.property('meterTimezone', expected.meterTimezone);
	expect(actual).to.have.property('identifier', expected.identifier);
	expect(actual).to.have.property('note', expected.note);
	expect(actual).to.have.property('area', expected.area);
	expect(actual).to.have.property('cumulative', expected.cumulative);
	expect(actual).to.have.property('cumulativeReset', expected.cumulativeReset);
	expect(actual).to.have.property('cumulativeResetStart', expected.cumulativeResetStart);
	expect(actual).to.have.property('cumulativeResetEnd', expected.cumulativeResetEnd);
	expect(actual).to.have.property('readingGap', expected.readingGap);
	expect(actual).to.have.property('readingVariation', expected.readingVariation);
	expect(actual).to.have.property('readingDuplication', expected.readingDuplication);
	expect(actual).to.have.property('timeSort', expected.timeSort);
	expect(actual).to.have.property('endOnlyTime', expected.endOnlyTime);
	expect(actual).to.have.property('reading', expected.reading);
	expect(actual.startTimestamp.isSame(moment(expected.startTimestamp))).to.equal(true);
	expect(actual.endTimestamp.isSame(moment(expected.endTimestamp))).to.equal(true);
	expect(actual).to.have.property('unitId', expected.unitId);
	expect(actual).to.have.property('defaultGraphicUnit', expected.defaultGraphicUnit);
}

async function setUpUnits(conn) {

}

mocha.describe('Meters', () => {
	mocha.beforeEach(async () => {
		const unitA = new Unit(undefined, 'Unit A', 'Unit A Id', Unit.unitRepresentType.UNUSED, 1000,
							Unit.unitType.UNIT, 1, 'Unit A Suffix', Unit.displayableType.ALL, true, 'Unit A Note');
		const unitB = new Unit(undefined, 'Unit B', 'Unit B Id', Unit.unitRepresentType.UNUSED, 2000,
							Unit.unitType.UNIT, 2, 'Unit B Suffix', Unit.displayableType.ALL, true, 'Unit B Note');
		const unitC = new Unit(undefined, 'Unit C', 'Unit C Id', Unit.unitRepresentType.UNUSED, 3000,
							Unit.unitType.UNIT, 3, 'Unit C Suffix', Unit.displayableType.ALL, true, 'Unit C Note');
		await Promise.all([unitA, unitB, unitC].map(unit => unit.insert(conn)));
	});

	mocha.it('can be saved and retrieved', async () => {
		const conn = testDB.getConnection();
		const meterPreInsert = new Meter(undefined, 'Meter', null, false, true, Meter.type.MAMAC, 'UTC',
		gps,'Identified', 'notes', 33.5, true, true, '05:05:09', '09:00:01', 0, 0, 1, 'increasing', false,
		25.5, '0001-01-01 23:59:59', '2020-07-02 01:00:10', 1, 2);
		await meterPreInsert.insert(conn);
		const meterPostInsertByName = await Meter.getByName(meterPreInsert.name, conn);
		expectMetersToBeEquivalent(meterPreInsert, meterPostInsertByName);
		const meterPostInsertByID = await Meter.getByID(meterPreInsert.id, conn);
		expectMetersToBeEquivalent(meterPreInsert, meterPostInsertByID);
	});

	mocha.it('can be saved, edited, and retrieved', async () => {
		const conn = testDB.getConnection();
		const meterPreInsert = new Meter(undefined, 'Meter', null, false, true, Meter.type.MAMAC, 'UTC', gps,
			'Identified' ,'notes', 35.0, true, true, '01:01:25' , '00:00:00', 5, 0, 1, 'increasing', false,
			1.5, '0001-01-01 23:59:59', '2020-07-02 01:00:10', 1, 2);
		await meterPreInsert.insert(conn);
		const meterPostInsertByID = await Meter.getByID(meterPreInsert.id, conn);
		expectMetersToBeEquivalent(meterPreInsert, meterPostInsertByID);

		meterPreInsert.name = 'Something Else';
		meterPreInsert.enabled = true;
		meterPreInsert.meterTimezone = 'GMT';
		meterPreInsert.unitId = 3;
		await meterPreInsert.update(conn);
		const meterPostUpdate = await Meter.getByID(meterPreInsert.id, conn);
		expectMetersToBeEquivalent(meterPreInsert, meterPostUpdate);
	});

	mocha.it('can get only enabled meters', async () => {
		const conn = testDB.getConnection();
		const enabledMeter = new Meter(undefined, 'EnabledMeter', null, true, true, Meter.type.MAMAC, null, gps,
		'Identified', 'notes', 35.0, true, true, '01:01:25' , '00:00:00', 7, 11, 1, 'increasing', false,
		1.5, '0001-01-01 23:59:59', '2020-07-02 01:00:10', 1, 2);
		const disabledMeter = new Meter(undefined, 'DisabledMeter', null, false, true, Meter.type.MAMAC, null, gps,
		'Identified 1' ,'Notes 1', 35.0, true, true, '01:01:25' , '00:00:00', 5, 0, 1, 'increasing', false,
		1.5, '0002-01-01 23:59:59', '2020-07-02 01:00:10', 3, 3);
		await enabledMeter.insert(conn);
		await disabledMeter.insert(conn);

		const enabledMeters = await Meter.getEnabled(conn);
		expect(enabledMeters).to.have.lengthOf(1);
		expectMetersToBeEquivalent(enabledMeter, enabledMeters[0]);
	});

	mocha.it('can get only visible meters', async () => {
		const conn = testDB.getConnection();
		const visibleMeter = new Meter(undefined, 'VisibleMeter', null, true, true, Meter.type.MAMAC, null, gps,
		'Identified 1' ,'notes 1', 35.0, true, true, '01:01:25' , '00:00:00', 5, 0, 1, 'increasing', false,
		1.5, '0001-01-01 23:59:59', '2020-07-02 01:00:10', 1, 2);
		const invisibleMeter = new Meter(undefined, 'InvisibleMeter', null, true, false, Meter.type.MAMAC, null, gps,
		'Identified 2' ,'Notes 2', 35.0, true, true, '01:01:25' , '00:00:00', 5, 0, 1, 'increasing', false,
		1.5, '0002-01-01 23:59:59', '2020-07-02 01:00:10', 2, 3);

		await visibleMeter.insert(conn);
		await invisibleMeter.insert(conn);

		const visibleMeters = await Meter.getDisplayable(conn);
		expect(visibleMeters).to.have.lengthOf(1);
		expectMetersToBeEquivalent(visibleMeter, visibleMeters[0]);
	});

	mocha.it('can get unit index', async () => {
		const conn = testDB.getConnection();
		const visibleMeter = new Meter(undefined, 'VisibleMeter', null, true, true, Meter.type.MAMAC, null, gps,
		'Identified 1' ,'notes 1', 35.0, true, true, '01:01:25' , '00:00:00', 5, 0, 1, 'increasing', false,
		1.5, '0001-01-01 23:59:59', '2020-07-02 01:00:10', 2, 3);
		await visibleMeter.insert(conn);

		const actualUnitIndex = await Meter.getUnitIndex(1, conn);
		const expectedUnitIndex = (await Unit.getById(2, conn)).unitIndex;
		expect(actualUnitIndex).to.be.equal(expectedUnitIndex);
	});

	mocha.it('can save and retrieve meters with null unitId', async () => {
		const conn = testDB.getConnection();
		const missingUnitIdMeter = new Meter(undefined, 'MissingUnitId', null, true, true, Meter.type.MAMAC, null, gps,
		'MissingUnitId' ,'notes 1', 35.0, true, true, '01:01:25' , '00:00:00', 5, 0, 1, 'increasing', false,
		1.5, '0001-01-01 23:59:59', '2020-07-02 01:00:10', -99, 1);
		await missingUnitIdMeter.insert(conn);

		const actualMissingUnitIdMeter = await Meter.getByName('MissingUnitId', conn);
		// displayable is expected to switch to false.
		expect(actualMissingUnitIdMeter).to.have.property('displayable', false);
		// defaultGraphicUnit is expected to switch to -99.
		expect(actualMissingUnitIdMeter).to.have.property('defaultGraphicUnit', -99);
		// unitId shouldn't change.
		expect(actualMissingUnitIdMeter).to.have.property('unitId', -99);
	});

	mocha.it('can save and retrieve meters with null defaultGraphicUnit', async () => {
		const missingGraphicUnitMeter = new Meter(undefined, 'MissingGraphicUnit', null, true, true, Meter.type.MAMAC, null, gps,
		'MissingGraphicUnit' ,'notes 2', 35.0, true, true, '01:01:25' , '00:00:00', 5, 0, 1, 'increasing', false,
		1.5, '0001-01-01 23:59:59', '2020-07-02 01:00:10', 1, -99);
		await missingGraphicUnitMeter.insert(conn);

		const actualMissingGraphicUnitMeter = await Meter.getByName('MissingGraphicUnit', conn);
		// defaultGraphicUnit is expected to automatically set to unitId.
		expect(actualMissingGraphicUnitMeter).to.have.property('defaultGraphicUnit', 1);
	});

	mocha.it('can get all meter where unitId is not null', async () => {
		const meterA = new Meter(undefined, 'MeterA', null, true, true, Meter.type.MAMAC, null, gps,
		'MeterA' ,'notes 1', 35.0, true, true, '01:01:25' , '00:00:00', 5, 0, 1, 'increasing', false,
		1.5, '0001-01-01 23:59:59', '2020-07-02 01:00:10', 1, 1);
		const meterB = new Meter(undefined, 'MeterB', null, true, true, Meter.type.MAMAC, null, gps,
		'MeterB' ,'notes 2', 35.0, true, true, '01:01:25' , '00:00:00', 5, 0, 1, 'increasing', false,
		1.5, '0001-01-01 23:59:59', '2020-07-02 01:00:10', 2, 2);
		const meterC = new Meter(undefined, 'Meter C', null, true, true, Meter.type.MAMAC, null);

		await Promise.all([meterA, meterB, meterC].map(meter => meter.insert(conn)));
		const expectedMeters = [meterA, meterB];
		const actualMeters = await Meter.getUnitNotNull(conn);
		actualMeters.sort((a, b) => a.id - b.id);
		expectedMeters.sort((a, b) => a.id - b.id);

		expect(expectedMeters.length).to.be.equal(actualMeters.length);
		for (let i = 0; i < expectedMeters.length; ++i) {
			expectMetersToBeEquivalent(expectedMeters[i], actualMeters[i]);
		}
	});
});
<commit_msg>Remove redundant code<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * Tests if the expected properties are the actual results from meters.
 */

const { mocha, expect, testDB } = require('../common');
const Meter = require('../../models/Meter');
const Point = require('../../models/Point');
const moment = require('moment');
const Unit = require('../../models/Unit');
const gps = new Point(90, 45);

/**
 * Checks if the expected meter properties have the actual properties.
 * @param expected expected meter properties
 * @param actual actual meter properties
 */
function expectMetersToBeEquivalent(expected, actual) {
	expect(actual).to.have.property('id', expected.id);
	expect(actual).to.have.property('name', expected.name);
	expect(actual).to.have.property('enabled', expected.enabled);
	expect(actual).to.have.property('type', expected.type);
	expect(actual).to.have.property('gps');
	expect(actual.gps).to.have.property('latitude', expected.gps.latitude);
	expect(actual.gps).to.have.property('longitude', expected.gps.longitude);
	expect(actual).to.have.property('meterTimezone', expected.meterTimezone);
	expect(actual).to.have.property('identifier', expected.identifier);
	expect(actual).to.have.property('note', expected.note);
	expect(actual).to.have.property('area', expected.area);
	expect(actual).to.have.property('cumulative', expected.cumulative);
	expect(actual).to.have.property('cumulativeReset', expected.cumulativeReset);
	expect(actual).to.have.property('cumulativeResetStart', expected.cumulativeResetStart);
	expect(actual).to.have.property('cumulativeResetEnd', expected.cumulativeResetEnd);
	expect(actual).to.have.property('readingGap', expected.readingGap);
	expect(actual).to.have.property('readingVariation', expected.readingVariation);
	expect(actual).to.have.property('readingDuplication', expected.readingDuplication);
	expect(actual).to.have.property('timeSort', expected.timeSort);
	expect(actual).to.have.property('endOnlyTime', expected.endOnlyTime);
	expect(actual).to.have.property('reading', expected.reading);
	expect(actual.startTimestamp.isSame(moment(expected.startTimestamp))).to.equal(true);
	expect(actual.endTimestamp.isSame(moment(expected.endTimestamp))).to.equal(true);
	expect(actual).to.have.property('unitId', expected.unitId);
	expect(actual).to.have.property('defaultGraphicUnit', expected.defaultGraphicUnit);
}

mocha.describe('Meters', () => {
	mocha.beforeEach(async () => {
		const unitA = new Unit(undefined, 'Unit A', 'Unit A Id', Unit.unitRepresentType.UNUSED, 1000,
							Unit.unitType.UNIT, 1, 'Unit A Suffix', Unit.displayableType.ALL, true, 'Unit A Note');
		const unitB = new Unit(undefined, 'Unit B', 'Unit B Id', Unit.unitRepresentType.UNUSED, 2000,
							Unit.unitType.UNIT, 2, 'Unit B Suffix', Unit.displayableType.ALL, true, 'Unit B Note');
		const unitC = new Unit(undefined, 'Unit C', 'Unit C Id', Unit.unitRepresentType.UNUSED, 3000,
							Unit.unitType.UNIT, 3, 'Unit C Suffix', Unit.displayableType.ALL, true, 'Unit C Note');
		await Promise.all([unitA, unitB, unitC].map(unit => unit.insert(conn)));
	});

	mocha.it('can be saved and retrieved', async () => {
		const conn = testDB.getConnection();
		const meterPreInsert = new Meter(undefined, 'Meter', null, false, true, Meter.type.MAMAC, 'UTC',
		gps,'Identified', 'notes', 33.5, true, true, '05:05:09', '09:00:01', 0, 0, 1, 'increasing', false,
		25.5, '0001-01-01 23:59:59', '2020-07-02 01:00:10', 1, 2);
		await meterPreInsert.insert(conn);
		const meterPostInsertByName = await Meter.getByName(meterPreInsert.name, conn);
		expectMetersToBeEquivalent(meterPreInsert, meterPostInsertByName);
		const meterPostInsertByID = await Meter.getByID(meterPreInsert.id, conn);
		expectMetersToBeEquivalent(meterPreInsert, meterPostInsertByID);
	});

	mocha.it('can be saved, edited, and retrieved', async () => {
		const conn = testDB.getConnection();
		const meterPreInsert = new Meter(undefined, 'Meter', null, false, true, Meter.type.MAMAC, 'UTC', gps,
			'Identified' ,'notes', 35.0, true, true, '01:01:25' , '00:00:00', 5, 0, 1, 'increasing', false,
			1.5, '0001-01-01 23:59:59', '2020-07-02 01:00:10', 1, 2);
		await meterPreInsert.insert(conn);
		const meterPostInsertByID = await Meter.getByID(meterPreInsert.id, conn);
		expectMetersToBeEquivalent(meterPreInsert, meterPostInsertByID);

		meterPreInsert.name = 'Something Else';
		meterPreInsert.enabled = true;
		meterPreInsert.meterTimezone = 'GMT';
		meterPreInsert.unitId = 3;
		await meterPreInsert.update(conn);
		const meterPostUpdate = await Meter.getByID(meterPreInsert.id, conn);
		expectMetersToBeEquivalent(meterPreInsert, meterPostUpdate);
	});

	mocha.it('can get only enabled meters', async () => {
		const conn = testDB.getConnection();
		const enabledMeter = new Meter(undefined, 'EnabledMeter', null, true, true, Meter.type.MAMAC, null, gps,
		'Identified', 'notes', 35.0, true, true, '01:01:25' , '00:00:00', 7, 11, 1, 'increasing', false,
		1.5, '0001-01-01 23:59:59', '2020-07-02 01:00:10', 1, 2);
		const disabledMeter = new Meter(undefined, 'DisabledMeter', null, false, true, Meter.type.MAMAC, null, gps,
		'Identified 1' ,'Notes 1', 35.0, true, true, '01:01:25' , '00:00:00', 5, 0, 1, 'increasing', false,
		1.5, '0002-01-01 23:59:59', '2020-07-02 01:00:10', 3, 3);
		await enabledMeter.insert(conn);
		await disabledMeter.insert(conn);

		const enabledMeters = await Meter.getEnabled(conn);
		expect(enabledMeters).to.have.lengthOf(1);
		expectMetersToBeEquivalent(enabledMeter, enabledMeters[0]);
	});

	mocha.it('can get only visible meters', async () => {
		const conn = testDB.getConnection();
		const visibleMeter = new Meter(undefined, 'VisibleMeter', null, true, true, Meter.type.MAMAC, null, gps,
		'Identified 1' ,'notes 1', 35.0, true, true, '01:01:25' , '00:00:00', 5, 0, 1, 'increasing', false,
		1.5, '0001-01-01 23:59:59', '2020-07-02 01:00:10', 1, 2);
		const invisibleMeter = new Meter(undefined, 'InvisibleMeter', null, true, false, Meter.type.MAMAC, null, gps,
		'Identified 2' ,'Notes 2', 35.0, true, true, '01:01:25' , '00:00:00', 5, 0, 1, 'increasing', false,
		1.5, '0002-01-01 23:59:59', '2020-07-02 01:00:10', 2, 3);

		await visibleMeter.insert(conn);
		await invisibleMeter.insert(conn);

		const visibleMeters = await Meter.getDisplayable(conn);
		expect(visibleMeters).to.have.lengthOf(1);
		expectMetersToBeEquivalent(visibleMeter, visibleMeters[0]);
	});

	mocha.it('can get unit index', async () => {
		const conn = testDB.getConnection();
		const visibleMeter = new Meter(undefined, 'VisibleMeter', null, true, true, Meter.type.MAMAC, null, gps,
		'Identified 1' ,'notes 1', 35.0, true, true, '01:01:25' , '00:00:00', 5, 0, 1, 'increasing', false,
		1.5, '0001-01-01 23:59:59', '2020-07-02 01:00:10', 2, 3);
		await visibleMeter.insert(conn);

		const actualUnitIndex = await Meter.getUnitIndex(1, conn);
		const expectedUnitIndex = (await Unit.getById(2, conn)).unitIndex;
		expect(actualUnitIndex).to.be.equal(expectedUnitIndex);
	});

	mocha.it('can save and retrieve meters with null unitId', async () => {
		const conn = testDB.getConnection();
		const missingUnitIdMeter = new Meter(undefined, 'MissingUnitId', null, true, true, Meter.type.MAMAC, null, gps,
		'MissingUnitId' ,'notes 1', 35.0, true, true, '01:01:25' , '00:00:00', 5, 0, 1, 'increasing', false,
		1.5, '0001-01-01 23:59:59', '2020-07-02 01:00:10', -99, 1);
		await missingUnitIdMeter.insert(conn);

		const actualMissingUnitIdMeter = await Meter.getByName('MissingUnitId', conn);
		// displayable is expected to switch to false.
		expect(actualMissingUnitIdMeter).to.have.property('displayable', false);
		// defaultGraphicUnit is expected to switch to -99.
		expect(actualMissingUnitIdMeter).to.have.property('defaultGraphicUnit', -99);
		// unitId shouldn't change.
		expect(actualMissingUnitIdMeter).to.have.property('unitId', -99);
	});

	mocha.it('can save and retrieve meters with null defaultGraphicUnit', async () => {
		const missingGraphicUnitMeter = new Meter(undefined, 'MissingGraphicUnit', null, true, true, Meter.type.MAMAC, null, gps,
		'MissingGraphicUnit' ,'notes 2', 35.0, true, true, '01:01:25' , '00:00:00', 5, 0, 1, 'increasing', false,
		1.5, '0001-01-01 23:59:59', '2020-07-02 01:00:10', 1, -99);
		await missingGraphicUnitMeter.insert(conn);

		const actualMissingGraphicUnitMeter = await Meter.getByName('MissingGraphicUnit', conn);
		// defaultGraphicUnit is expected to automatically set to unitId.
		expect(actualMissingGraphicUnitMeter).to.have.property('defaultGraphicUnit', 1);
	});

	mocha.it('can get all meter where unitId is not null', async () => {
		const meterA = new Meter(undefined, 'MeterA', null, true, true, Meter.type.MAMAC, null, gps,
		'MeterA' ,'notes 1', 35.0, true, true, '01:01:25' , '00:00:00', 5, 0, 1, 'increasing', false,
		1.5, '0001-01-01 23:59:59', '2020-07-02 01:00:10', 1, 1);
		const meterB = new Meter(undefined, 'MeterB', null, true, true, Meter.type.MAMAC, null, gps,
		'MeterB' ,'notes 2', 35.0, true, true, '01:01:25' , '00:00:00', 5, 0, 1, 'increasing', false,
		1.5, '0001-01-01 23:59:59', '2020-07-02 01:00:10', 2, 2);
		const meterC = new Meter(undefined, 'Meter C', null, true, true, Meter.type.MAMAC, null);

		await Promise.all([meterA, meterB, meterC].map(meter => meter.insert(conn)));
		const expectedMeters = [meterA, meterB];
		const actualMeters = await Meter.getUnitNotNull(conn);
		actualMeters.sort((a, b) => a.id - b.id);
		expectedMeters.sort((a, b) => a.id - b.id);

		expect(expectedMeters.length).to.be.equal(actualMeters.length);
		for (let i = 0; i < expectedMeters.length; ++i) {
			expectMetersToBeEquivalent(expectedMeters[i], actualMeters[i]);
		}
	});
});
<|endoftext|>


<commit_before>var $ = require("jquery");
var rowFor = require("./row-for");
var columnFor = require("./column-for");

$(document).ready(function(){
  setUpGame();
});

function setUpGame() {
  var board = new Board();
  console.log(board);
  document.addEventListener('keydown', board.handleKey.bind(board));
}
//TODO:add values at beginning of game
// board.addValueToAvailableCell();
// board.addValueToAvailableCell();

function Board() {
  this.row = 4;
  this.column = 4;
  this.cells = [];
  this.buildGrid(arguments);
}

function Cell(row, column, value) {
  this.row = row;
  this.column = column;
  this.value = value;
}

Cell.prototype.update = function(direction, slice, positionInSlice) {
  for (var i = positionInSlice; direction ? ++i < 4 : --i >= 0;) {
    if (this.value === slice[i].value && this.value !== 0) {
      this.value = this.value * 2;
      var score = 0
      score += this.value
      $('.current-score').text(score)
      slice[i].value = 0;
      return;
    } else if (this.value === 0 && slice[i].value > 0){
      this.value = slice[i].value;
      slice[i].value = 0;
    } else if (slice[i].value > 0) {
      return;
    } else {

    }
  }
};

Board.prototype.buildGrid = function(cells) {
  if (this.cells.length) { return this.cells; }
  for (var i = 0; i < 16; i++) {
    var value = cells[i] ? cells[i] : 0;
    this.cells.push(new Cell(rowFor(i), columnFor(i), value));
  }
};

Board.prototype.createNewGrid = function () {
  return this.buildGrid(4, 4);
};

Board.prototype.changeValue = function (index) {
  ++this.cells[index].value;
};

Board.prototype.checkAvailalbeCells = function() {
   var available = this.cells.filter(function(item, index, array) {
    return item.value === 0;
  });
  return available;
};

Board.prototype.addValueToAvailableCell = function() {
  // TODO: what to do if available is null??
  // TODO: give it a value of 4 sometimes
  var available = this.checkAvailalbeCells();
  var index = Math.floor(Math.random() * (available.length));
  return available[index].value = 2 || 4;
};

Board.prototype.checkAndChangeValue = function(direction) {
  var slice;
  var directionBoolean;

  if (direction === 1 || direction === 3) {
    slice = [
      [this.cells[0], this.cells[1], this.cells[2], this.cells[3]],
      [this.cells[4], this.cells[5], this.cells[6], this.cells[7]],
      [this.cells[8], this.cells[9], this.cells[10], this.cells[11]],
      [this.cells[12], this.cells[13], this.cells[14], this.cells[15]]
    ];
  } else {
    slice = [
      [this.cells[0], this.cells[4], this.cells[8], this.cells[12]],
      [this.cells[1], this.cells[5], this.cells[9], this.cells[13]],
      [this.cells[2], this.cells[6], this.cells[10], this.cells[14]],
      [this.cells[3], this.cells[7], this.cells[11], this.cells[15]]
    ];
  }

  if (direction === 1) {
    directionBoolean = true;
  } else if (direction === 2){
    directionBoolean = true;
  } else if (direction === 3) {
    directionBoolean = false;
  } else {
    directionBoolean = false;
  }

  for (var i = 0; i < 4; i++) {
    for (var j = directionBoolean ? 0 : 3; directionBoolean ? j < 4 : j >= 0; directionBoolean ? j++ : j-- ) {
      slice[i][j].update(directionBoolean, slice[i], j);
    }
  }
  this.printBoard();
  console.log(this.cells[0].value);
};

Board.prototype.printBoard = function() {
  console.log("printing board");
  for (var i = 0; i < 16; i++) {
    console.log(this.cells[i].value);
  }
};

Board.prototype.clearBoard = function() {
  this.cells = [];
};

Board.prototype.render = function() {

  for (var i = 0; i < this.cells.length; i++) {
    var tile = document.getElementById(i);
    tile.innerHTML = this.cells[i].value;
    tile.className = (`tile${this.cells[i].value}`);
  }
};

Board.prototype.handleKey = function(event) {

  var moved = false;
  // var board = new Board(); // don't think this is right... creating a new board.

//TODO: through line 160 may not be necessary

 switch (event.keyCode) {
  case 37: // Left
  case 38: // Up
  case 39: // Right
  case 40: // Down
      event.preventDefault();
      break;
  }

  switch (event.keyCode) {
    case 37: // Left

        console.log("left clicked");
        console.log(moved);
        // debugger;
        console.log(this.checkAndChangeValue(1))
        moved = this.checkAndChangeValue(1);
        console.log(moved);
        break;
    case 38: // Up
        console.log("up clicked");
        moved = this.checkAndChangeValue(2);
        break;
    case 39: // Right
        console.log("right clicked");
        moved = this.checkAndChangeValue(3);
        break;
    case 40: // Down
        console.log("down clicked");
        moved = this.checkAndChangeValue(4);
        break;
  }

   if (true) {
    console.log("log in if statement");
    // Board.clearBoard();
    this.addValueToAvailableCell();
    this.render();

    // if (!canMove()) {
    //     lose();
    // }
  }
};

module.exports = [Board, Cell];
<commit_msg>score function working<commit_after>var $ = require("jquery");
var rowFor = require("./row-for");
var columnFor = require("./column-for");
var score = 0

$(document).ready(function(){
  setUpGame();
});

function setUpGame() {
  var board = new Board();
  console.log(board);
  document.addEventListener('keydown', board.handleKey.bind(board));
}
//TODO:add values at beginning of game
// board.addValueToAvailableCell();
// board.addValueToAvailableCell();

function Board() {
  this.row = 4;
  this.column = 4;
  this.cells = [];
  this.buildGrid(arguments);
}

function Cell(row, column, value) {
  this.row = row;
  this.column = column;
  this.value = value;
}

Cell.prototype.update = function(direction, slice, positionInSlice) {
  for (var i = positionInSlice; direction ? ++i < 4 : --i >= 0;) {
    if (this.value === slice[i].value && this.value !== 0) {
      this.value = this.value * 2;
      //pull this out into a score function
      score += this.value
      debugger;
      $('.current-score').text(score)
      ///
      slice[i].value = 0;
      return;
    } else if (this.value === 0 && slice[i].value > 0){
      this.value = slice[i].value;
      slice[i].value = 0;
    } else if (slice[i].value > 0) {
      return;
    } else {

    }
  }
};

Board.prototype.buildGrid = function(cells) {
  if (this.cells.length) { return this.cells; }
  for (var i = 0; i < 16; i++) {
    var value = cells[i] ? cells[i] : 0;
    this.cells.push(new Cell(rowFor(i), columnFor(i), value));
  }
};

Board.prototype.createNewGrid = function () {
  return this.buildGrid(4, 4);
};

Board.prototype.changeValue = function (index) {
  ++this.cells[index].value;
};

Board.prototype.checkAvailalbeCells = function() {
   var available = this.cells.filter(function(item, index, array) {
    return item.value === 0;
  });
  return available;
};

Board.prototype.addValueToAvailableCell = function() {
  // TODO: what to do if available is null??
  // TODO: give it a value of 4 sometimes
  var available = this.checkAvailalbeCells();
  var index = Math.floor(Math.random() * (available.length));
  return available[index].value = 2 || 4;
};

Board.prototype.checkAndChangeValue = function(direction) {
  var slice;
  var directionBoolean;

  if (direction === 1 || direction === 3) {
    slice = [
      [this.cells[0], this.cells[1], this.cells[2], this.cells[3]],
      [this.cells[4], this.cells[5], this.cells[6], this.cells[7]],
      [this.cells[8], this.cells[9], this.cells[10], this.cells[11]],
      [this.cells[12], this.cells[13], this.cells[14], this.cells[15]]
    ];
  } else {
    slice = [
      [this.cells[0], this.cells[4], this.cells[8], this.cells[12]],
      [this.cells[1], this.cells[5], this.cells[9], this.cells[13]],
      [this.cells[2], this.cells[6], this.cells[10], this.cells[14]],
      [this.cells[3], this.cells[7], this.cells[11], this.cells[15]]
    ];
  }

  if (direction === 1) {
    directionBoolean = true;
  } else if (direction === 2){
    directionBoolean = true;
  } else if (direction === 3) {
    directionBoolean = false;
  } else {
    directionBoolean = false;
  }

  for (var i = 0; i < 4; i++) {
    for (var j = directionBoolean ? 0 : 3; directionBoolean ? j < 4 : j >= 0; directionBoolean ? j++ : j-- ) {
      slice[i][j].update(directionBoolean, slice[i], j);
    }
  }
  this.printBoard();
  console.log(this.cells[0].value);
};

Board.prototype.printBoard = function() {
  console.log("printing board");
  for (var i = 0; i < 16; i++) {
    console.log(this.cells[i].value);
  }
};

Board.prototype.clearBoard = function() {
  this.cells = [];
};

Board.prototype.render = function() {

  for (var i = 0; i < this.cells.length; i++) {
    var tile = document.getElementById(i);
    tile.innerHTML = this.cells[i].value;
    tile.className = (`tile${this.cells[i].value}`);
  }
};

Board.prototype.handleKey = function(event) {

  var moved = false;
  // var board = new Board(); // don't think this is right... creating a new board.

//TODO: through line 160 may not be necessary

 switch (event.keyCode) {
  case 37: // Left
  case 38: // Up
  case 39: // Right
  case 40: // Down
      event.preventDefault();
      break;
  }

  switch (event.keyCode) {
    case 37: // Left

        console.log("left clicked");
        console.log(moved);
        // debugger;
        console.log(this.checkAndChangeValue(1))
        moved = this.checkAndChangeValue(1);
        console.log(moved);
        break;
    case 38: // Up
        console.log("up clicked");
        moved = this.checkAndChangeValue(2);
        break;
    case 39: // Right
        console.log("right clicked");
        moved = this.checkAndChangeValue(3);
        break;
    case 40: // Down
        console.log("down clicked");
        moved = this.checkAndChangeValue(4);
        break;
  }

   if (true) {
    console.log("log in if statement");
    // Board.clearBoard();
    this.addValueToAvailableCell();
    this.render();

    // if (!canMove()) {
    //     lose();
    // }
  }
};

module.exports = [Board, Cell];
<|endoftext|>


<commit_before>require('mocha');

var MetaInspector = require('../index'),
	should = require('should');

require('./fixtures/fixtures');

describe('metainspector', function(){

	describe('multiple clients', function(){
		var firstClient = new MetaInspector('http://www.google.com');
		var secondClient = new MetaInspector('http://www.google.com');

		it('should not keep the same eventEmitter reference among clients', function(done){
			var calledOnce = false;

			firstClient.on('fetch', function(){
				if (!calledOnce) {
					calledOnce = true;
				} else {
					throw new Error('I should not get called twice');
				}
			});

			secondClient.on('fetch', function(){
				should.exists(secondClient.parsedDocument);

				if (calledOnce) done();
			});

			firstClient.fetch();
			secondClient.fetch();
		});
	});

	describe('client', function(){
		var client = null;

		it('should have a url property', function(done){
			client = new MetaInspector("http://www.google.com");

			client.url.should.equal("http://www.google.com/");
			done();
		});

		it('should add http as the default scheme if no scheme is passed', function(done){
			client = new MetaInspector("www.google.com");

			client.url.should.equal("http://www.google.com/");
			done();
		});

		it('should have a scheme property', function(done){
			client = new MetaInspector("http://www.google.com");

			client.scheme.should.equal("http");
			done();
		});

		it('should have a host property', function(done){
			client = new MetaInspector("http://www.google.com");

			client.host.should.equal("www.google.com");
			done();
		});

		it('should have a rootUrl property', function(done){
			client = new MetaInspector("http://www.google.com");

			client.rootUrl.should.equal("http://www.google.com");
			done();
		});

		it('should have a parsedDocument', function(done){
			client = new MetaInspector("http://www.google.com");

			client.once("fetch", function(){
				should.exists(client.parsedDocument);
				done();
			});

			client.fetch();
		});

		it('should have a title', function(done){
			client = new MetaInspector("http://www.google.com", {});

			client.once("fetch", function(){
				client.title.should.equal("Google");
				done();
			});

			client.fetch();
		});

		it('should have keywords', function(done){
			client = new MetaInspector("http://www.simple.com", {});

			client.once("fetch", function(){
				client.keywords.should.be.instanceof(Array).and.be.eql([ 'HTML', 'CSS', 'XML', 'JavaScript' ]).and.have.lengthOf(4);
				done();
			});

			client.fetch();
		});

		it('keywords should be undefined if there is no keywords', function(done){
			client = new MetaInspector("http://www.google.com", {});

			client.once("fetch", function(){
				client.keywords.should.be.instanceof(Array).and.be.eql([]);
				done();
			});

			client.fetch();
		});

		it('author should be undefined if there is no author', function(done){
			client = new MetaInspector("http://www.google.com", {});

			client.once("fetch", function(){
				should.not.exist(client.author);
				done();
			});

			client.fetch();
		});

		it('charset should be undefined if there is no charset', function(done){
			client = new MetaInspector("http://www.google.com", {});

			client.once("fetch", function(){
				should.not.exist(client.charset);
				done();
			});

			client.fetch();
		});

		it('should have author', function(done){
			client = new MetaInspector("http://www.simple.com", {});

			client.once("fetch", function(){
				client.author.should.be.equal('Author Name');
				done();
			});

			client.fetch();
		});

		it('should have charset', function(done){
			client = new MetaInspector("http://www.simple.com", {});

			client.once("fetch", function(){
				client.charset.should.be.equal('UTF-8');
				done();
			});

			client.fetch();
		});

		it('should have links returned as an array', function(done){
			client = new MetaInspector("http://www.google.com", {});

			client.once("fetch", function(){
				client.links.length.should.equal(51);
				done();
			});

			client.fetch();
		});

		it('should have a description', function(done){
			client = new MetaInspector("http://www.google.com", {});

			client.once("fetch", function(){
				client.description.should.equal("Search the world's information, including webpages, images, videos and more. Google has many special features to help you find exactly what you're looking for.");
				done();
			});

			client.fetch();
		});

		it('should have a og:image with relative path and return as absolute', function(done){
			client = new MetaInspector("http://www.fastandfurious7-film.com");

			client.once("fetch", function(){
				client.image.should.equal("http://www.fastandfurious7-film.com/images/fb.jpg");
				done();
			});

			client.fetch();
		});

		it('should return undefined if the meta description is not defined when metaDescription used', function(done){
			client = new MetaInspector("http://www.simple.com", {});

			client.once("fetch", function(){
				should.not.exist(client.metaDescription);
				done();
			});

			client.fetch();
		});

		it('should find a secondary description if there is no description meta element', function(done){
			client = new MetaInspector("http://www.simple.com", {});

			client.once("fetch", function(){
				client.description.should.equal("This is a new paragraph! This paragraph should be very long so we can grab it as the secondary description. What do you think of that?");
				done();
			});

			client.fetch();
		});

		it('should find a the image based on the og:image tag if defined', function(done){
			client = new MetaInspector("http://www.simple.com", {});

			client.once("fetch", function(){
				client.image.should.equal("http://placehold.it/350x150");
				done();
			});

			client.fetch();
		});

		it.skip('should return an array of absolute image paths for all images on the page', function(done){
			client = new MetaInspector("http://www.simple.com", {});

			client.once("fetch", function(){
				client.images.should.be.instanceof(Array).and.be.eql(
					[ 'http://www.simple.com/clouds.jpg',
						'http://www.simple.com/image/relative.gif',
						'http://www.simple.com/image/relative2.gif',
						'http://placehold.it/350x150',
						'https://placehold.it/350x65',
						'//placehold.it/350x65' ]);
				done();
			});

			client.fetch();
		});

		it('should return an array of rss or atom feeds if defined', function(done){
			client = new MetaInspector("http://www.simple.com", {});

			client.once("fetch", function(){
				client.feeds.length.should.equal(2);
				done();
			});

			client.fetch();
		});

		it('should return the open graph title if defined', function(done){
			client = new MetaInspector("http://www.simple.com", {});

			client.once("fetch", function(){
				client.ogTitle.should.equal("I am an Open Graph title");
				done();
			});

			client.fetch();
		});

		it('should emit errors', function(done){
			client = new MetaInspector("http://www.google-404.com/", {});

			client.once("error", function(error){
				should.exists(error);
				done();
			});

			client.fetch();
		});
	});
});
<commit_msg>add test case for og:description<commit_after>require('mocha');

var MetaInspector = require('../index'),
	should = require('should');

require('./fixtures/fixtures');

describe('metainspector', function(){

	describe('multiple clients', function(){
		var firstClient = new MetaInspector('http://www.google.com');
		var secondClient = new MetaInspector('http://www.google.com');

		it('should not keep the same eventEmitter reference among clients', function(done){
			var calledOnce = false;

			firstClient.on('fetch', function(){
				if (!calledOnce) {
					calledOnce = true;
				} else {
					throw new Error('I should not get called twice');
				}
			});

			secondClient.on('fetch', function(){
				should.exists(secondClient.parsedDocument);

				if (calledOnce) done();
			});

			firstClient.fetch();
			secondClient.fetch();
		});
	});

	describe('client', function(){
		var client = null;

		it('should have a url property', function(done){
			client = new MetaInspector("http://www.google.com");

			client.url.should.equal("http://www.google.com/");
			done();
		});

		it('should add http as the default scheme if no scheme is passed', function(done){
			client = new MetaInspector("www.google.com");

			client.url.should.equal("http://www.google.com/");
			done();
		});

		it('should have a scheme property', function(done){
			client = new MetaInspector("http://www.google.com");

			client.scheme.should.equal("http");
			done();
		});

		it('should have a host property', function(done){
			client = new MetaInspector("http://www.google.com");

			client.host.should.equal("www.google.com");
			done();
		});

		it('should have a rootUrl property', function(done){
			client = new MetaInspector("http://www.google.com");

			client.rootUrl.should.equal("http://www.google.com");
			done();
		});

		it('should have a parsedDocument', function(done){
			client = new MetaInspector("http://www.google.com");

			client.once("fetch", function(){
				should.exists(client.parsedDocument);
				done();
			});

			client.fetch();
		});

		it('should have a title', function(done){
			client = new MetaInspector("http://www.google.com", {});

			client.once("fetch", function(){
				client.title.should.equal("Google");
				done();
			});

			client.fetch();
		});

		it('should have keywords', function(done){
			client = new MetaInspector("http://www.simple.com", {});

			client.once("fetch", function(){
				client.keywords.should.be.instanceof(Array).and.be.eql([ 'HTML', 'CSS', 'XML', 'JavaScript' ]).and.have.lengthOf(4);
				done();
			});

			client.fetch();
		});

		it('keywords should be undefined if there is no keywords', function(done){
			client = new MetaInspector("http://www.google.com", {});

			client.once("fetch", function(){
				client.keywords.should.be.instanceof(Array).and.be.eql([]);
				done();
			});

			client.fetch();
		});

		it('author should be undefined if there is no author', function(done){
			client = new MetaInspector("http://www.google.com", {});

			client.once("fetch", function(){
				should.not.exist(client.author);
				done();
			});

			client.fetch();
		});

		it('charset should be undefined if there is no charset', function(done){
			client = new MetaInspector("http://www.google.com", {});

			client.once("fetch", function(){
				should.not.exist(client.charset);
				done();
			});

			client.fetch();
		});

		it('should have author', function(done){
			client = new MetaInspector("http://www.simple.com", {});

			client.once("fetch", function(){
				client.author.should.be.equal('Author Name');
				done();
			});

			client.fetch();
		});

		it('should have charset', function(done){
			client = new MetaInspector("http://www.simple.com", {});

			client.once("fetch", function(){
				client.charset.should.be.equal('UTF-8');
				done();
			});

			client.fetch();
		});

		it('should have links returned as an array', function(done){
			client = new MetaInspector("http://www.google.com", {});

			client.once("fetch", function(){
				client.links.length.should.equal(51);
				done();
			});

			client.fetch();
		});

		it('should have a description', function(done){
			client = new MetaInspector("http://www.google.com", {});

			client.once("fetch", function(){
				client.description.should.equal("Search the world's information, including webpages, images, videos and more. Google has many special features to help you find exactly what you're looking for.");
				done();
			});

			client.fetch();
		});

		it('should have a og:image with relative path and return as absolute', function(done){
			client = new MetaInspector("http://www.fastandfurious7-film.com");

			client.once("fetch", function(){
				client.image.should.equal("http://www.fastandfurious7-film.com/images/fb.jpg");
				done();
			});

			client.fetch();
		});

		it('should have a og:description', function(done){
			client = new MetaInspector("http://www.fastandfurious7-film.com");

			client.once("fetch", function(){
				client.ogDescription.should.equal("Continuing the global exploits in the unstoppable franchise built on speed, Vin Diesel, Paul Walker and Dwayne Johnson lead the returning cast of Fast & Furious 7.");
				done();
			});

			client.fetch();
		});

		it('should return undefined if the meta description is not defined when metaDescription used', function(done){
			client = new MetaInspector("http://www.simple.com", {});

			client.once("fetch", function(){
				should.not.exist(client.metaDescription);
				done();
			});

			client.fetch();
		});

		it('should find a secondary description if there is no description meta element', function(done){
			client = new MetaInspector("http://www.simple.com", {});

			client.once("fetch", function(){
				client.description.should.equal("This is a new paragraph! This paragraph should be very long so we can grab it as the secondary description. What do you think of that?");
				done();
			});

			client.fetch();
		});

		it('should find a the image based on the og:image tag if defined', function(done){
			client = new MetaInspector("http://www.simple.com", {});

			client.once("fetch", function(){
				client.image.should.equal("http://placehold.it/350x150");
				done();
			});

			client.fetch();
		});

		it.skip('should return an array of absolute image paths for all images on the page', function(done){
			client = new MetaInspector("http://www.simple.com", {});

			client.once("fetch", function(){
				client.images.should.be.instanceof(Array).and.be.eql(
					[ 'http://www.simple.com/clouds.jpg',
						'http://www.simple.com/image/relative.gif',
						'http://www.simple.com/image/relative2.gif',
						'http://placehold.it/350x150',
						'https://placehold.it/350x65',
						'//placehold.it/350x65' ]);
				done();
			});

			client.fetch();
		});

		it('should return an array of rss or atom feeds if defined', function(done){
			client = new MetaInspector("http://www.simple.com", {});

			client.once("fetch", function(){
				client.feeds.length.should.equal(2);
				done();
			});

			client.fetch();
		});

		it('should return the open graph title if defined', function(done){
			client = new MetaInspector("http://www.simple.com", {});

			client.once("fetch", function(){
				client.ogTitle.should.equal("I am an Open Graph title");
				done();
			});

			client.fetch();
		});

		it('should emit errors', function(done){
			client = new MetaInspector("http://www.google-404.com/", {});

			client.once("error", function(error){
				should.exists(error);
				done();
			});

			client.fetch();
		});
	});
});
<|endoftext|>


<commit_before>
/**
 * Module dependencies.
 */

var base64 = require('base64-js');
var xmlbuilder = require('xmlbuilder');

/**
 * Module exports.
 */

exports.build = build;

function ISODateString(d){
  function pad(n){
    return n < 10 ? '0' + n : n;
  }
  return d.getUTCFullYear()+'-'
    + pad(d.getUTCMonth()+1)+'-'
    + pad(d.getUTCDate())+'T'
    + pad(d.getUTCHours())+':'
    + pad(d.getUTCMinutes())+':'
    + pad(d.getUTCSeconds())+'Z';
}

/**
 * Returns the internal "type" of `obj` via the
 * `Object.prototype.toString()` trick.
 *
 * @param {Mixed} obj - any value
 * @returns {String} the internal "type" name
 * @api private
 */

var toString = Object.prototype.toString;
function type (obj) {
  var m = toString.call(obj).match(/\[object (.*)\]/);
  return m ? m[1] : m;
}

/**
 * generate an XML plist string from the input object
 *
 * @param {Object} obj - the object to convert
 * @returns {String} converted plist
 * @api public
 */

function build (obj) {
  var XMLHDR = { 'version': '1.0','encoding': 'UTF-8' }
    , XMLDTD = { 'ext': 'PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"' }
    , doc = xmlbuilder.create()
    , child = doc.begin('plist', XMLHDR, XMLDTD).att('version', '1.0');

  walk_obj(obj, child);

  return child.end({
    pretty: true
  });
}

/**
 * depth first, recursive traversal of a javascript object. when complete,
 * next_child contains a reference to the build XML object.
 *
 * @api private
 */

function walk_obj(next, next_child) {
  var tag_type, i, prop;
  var name = type(next);

  if (Array.isArray(next)) {
    next_child = next_child.ele('array');
    for (i = 0; i < next.length; i++) {
      walk_obj(next[i], next_child);
    }

  } else if (Buffer.isBuffer(next)) {
    next_child.ele('data').raw(next.toString('base64'));

  } else if ('Object' == name) {
    next_child = next_child.ele('dict');
    for (prop in next) {
      if (next.hasOwnProperty(prop)) {
        next_child.ele('key').txt(prop);
        walk_obj(next[prop], next_child);
      }
    }

  } else if ('Number' == name) {
    // detect if this is an integer or real
    // TODO: add an ability to force one way or another via a "cast"
    tag_type = (next % 1 === 0) ? 'integer' : 'real';
    next_child.ele(tag_type).txt(next.toString());

  } else if ('Date' == name) {
    next_child.ele('date').txt(ISODateString(new Date(next)));

  } else if ('Boolean' == name) {
    val = next ? 'true' : 'false';
    next_child.ele(val);

  } else if ('String' == name) {
    next_child.ele('string').txt(next);

  } else if ('ArrayBuffer' == name) {
    addArrayBuffer(next, next_child);

  } else if (next.buffer && 'ArrayBuffer' == type(next.buffer)) {
    // a typed array
    addArrayBuffer(next.buffer, next_child);

  }
}

/**
 * The `base64.fromByteArray()` function checks for the `length` property,
 * but not the `byteLength` property... :(
 *
 * We have to set the `length` manually here so that the stringification
 * works properly, and then restore the original value afterwards.
 *
 * @api private
 */

function addArrayBuffer (next, next_child) {
  // setup
  var has = 'length' in next;
  var orig = next.length;;
  next.length = next.byteLength;

  // encode to base64
  next_child.ele('data').raw(base64.fromByteArray(next));

  // cleanup
  if (has) next.length = orig;
  else delete next.length;
}
<commit_msg>build: fix encoding a typed array in the browser<commit_after>
/**
 * Module dependencies.
 */

var base64 = require('base64-js');
var xmlbuilder = require('xmlbuilder');

/**
 * Module exports.
 */

exports.build = build;

function ISODateString(d){
  function pad(n){
    return n < 10 ? '0' + n : n;
  }
  return d.getUTCFullYear()+'-'
    + pad(d.getUTCMonth()+1)+'-'
    + pad(d.getUTCDate())+'T'
    + pad(d.getUTCHours())+':'
    + pad(d.getUTCMinutes())+':'
    + pad(d.getUTCSeconds())+'Z';
}

/**
 * Returns the internal "type" of `obj` via the
 * `Object.prototype.toString()` trick.
 *
 * @param {Mixed} obj - any value
 * @returns {String} the internal "type" name
 * @api private
 */

var toString = Object.prototype.toString;
function type (obj) {
  var m = toString.call(obj).match(/\[object (.*)\]/);
  return m ? m[1] : m;
}

/**
 * generate an XML plist string from the input object
 *
 * @param {Object} obj - the object to convert
 * @returns {String} converted plist
 * @api public
 */

function build (obj) {
  var XMLHDR = { 'version': '1.0','encoding': 'UTF-8' }
    , XMLDTD = { 'ext': 'PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"' }
    , doc = xmlbuilder.create()
    , child = doc.begin('plist', XMLHDR, XMLDTD).att('version', '1.0');

  walk_obj(obj, child);

  return child.end({
    pretty: true
  });
}

/**
 * depth first, recursive traversal of a javascript object. when complete,
 * next_child contains a reference to the build XML object.
 *
 * @api private
 */

function walk_obj(next, next_child) {
  var tag_type, i, prop;
  var name = type(next);

  if (Array.isArray(next)) {
    next_child = next_child.ele('array');
    for (i = 0; i < next.length; i++) {
      walk_obj(next[i], next_child);
    }

  } else if (Buffer.isBuffer(next)) {
    next_child.ele('data').raw(next.toString('base64'));

  } else if ('Object' == name) {
    next_child = next_child.ele('dict');
    for (prop in next) {
      if (next.hasOwnProperty(prop)) {
        next_child.ele('key').txt(prop);
        walk_obj(next[prop], next_child);
      }
    }

  } else if ('Number' == name) {
    // detect if this is an integer or real
    // TODO: add an ability to force one way or another via a "cast"
    tag_type = (next % 1 === 0) ? 'integer' : 'real';
    next_child.ele(tag_type).txt(next.toString());

  } else if ('Date' == name) {
    next_child.ele('date').txt(ISODateString(new Date(next)));

  } else if ('Boolean' == name) {
    val = next ? 'true' : 'false';
    next_child.ele(val);

  } else if ('String' == name) {
    next_child.ele('string').txt(next);

  } else if ('ArrayBuffer' == name) {
    next_child.ele('data').raw(base64.fromByteArray(next));

  } else if (next.buffer && 'ArrayBuffer' == type(next.buffer)) {
    // a typed array
    next_child.ele('data').raw(base64.fromByteArray(new Uint8Array(next.buffer), next_child));

  }
}
<|endoftext|>


<commit_before>export class JFrogSubRouter {
    constructor($state, $location, $rootScope, $timeout) {
        this.$masterScope = $rootScope.$new();
        this.$rootScope = $rootScope;
        this.$state = $state;
        this.$timeout = $timeout;
        this.$location = $location;

        this.supportedEvents = ['state.change', 'params.change']
        this._watchStateChanges();
        this._watchLocationChanges();

    }

    createLocalRouter(config) {

        if (this.$config) {
            this._exitState();
        }

        this.$config = _.cloneDeep(config);
        this.$config.parentScope = config.parentScope;

        this.$config.baseStateName = this.$state.current.name;
        this.$config.basePath = _.trim(this._sharedStartOfString([this.$state.current.url, this.$location.path()]),'/');
        if (this.$state.current.url.indexOf('JFrogSubRouterPath') === -1) {
            console.warn('In order for JFrogSubRouter to work properly, you must define the sub path in your ui-router state, as being of type \'JFrogSubRouterPath\'. In your ui-router state definition, set the \'url\' field as \'/YOUR/BASE/PATH/{anySubRouteParamName:JFrogSubRouterPath}\' ')
        }

        this.$config.$listeners = {};

        this._initParams();

        this._mapPathToParams();

        this._createScope();

        this._createApiOnConfig();

        this.$config.parentScope.$on('$destroy', () => {
            console.log('Parent scope is dead, killing route !');
            this._exitState();
        })

        return this.$config.$api;
    }

    getActiveRouter() {
        return this.$config.$api;
    }

    _createApiOnConfig() {
        let THIS = this;

        this.$config.$api = {
            get params() {
                return THIS.$config.$params;
            },
            set params(params) {
                THIS.$config.$params = params;
                THIS._mapParamsToPath();
            },
            get state() {
                return THIS._getCurrentState();
            },
            get config() {
                return THIS.$config;
            },
            updateUrl() {
                if (THIS.$config.hotSyncUrl) return;
                else THIS._mapParamsToPath();
            },
            goto(stateName, params) {
                THIS._gotoState(stateName, params);
            },
            on(event, listener) {
                if (!_.includes(THIS.supportedEvents, event)) {
                    console.error('JFrogSubRouter: Unsupported Event: ' + event);
                    return;
                }
                if (!THIS.$config.$listeners[event]) THIS.$config.$listeners[event] = [];
                THIS.$config.$listeners[event].push(listener);
            },
            off(event, listener) {
                if (!_.includes(THIS.supportedEvents, event)) {
                    console.error('jf-table-view: Unsupported Event: ' + event);
                    return;
                }
                if (THIS.$config.$listeners[event]) {
                    if (listener) {
                        _.remove(THIS.$config.$listeners[event],l=>l===listener);
                    }
                    else {
                        THIS.$config.$listeners[event] = [];
                    }
                }
            },
            fire(event, ...params) {
                if (THIS.$config.$listeners[event]) {
                    THIS.$config.$listeners[event].forEach(listener=>listener(...params))
                }
            }

        }
    }
    _sharedStartOfString(array){
        let A= array.concat().sort(),
            a1= A[0], a2= A[A.length-1], L= a1.length, i= 0;
        while(i<L && a1.charAt(i)=== a2.charAt(i)) i++;
        return a1.substring(0, i);
    }

    _createScope() {
        this.$config.$scope = this.$rootScope.$new();
        this.$config.$scope.config = this.$config;

        this.$config.$scope.unwatchParams = this.$config.$scope.$watch('config.$params', (newVal, oldVal) => {
            if (!this.$config.$params || this.$config.baseStateName !== this.$state.current.name || (_.isEqual(newVal, oldVal) && _.isEqual(this._getPathAsParams(), this.$config.$params))) {
                return;
            }
            if (this.$config.hotSyncUrl) this._mapParamsToPath();
        }, true);
        this.$config.$scope.unwatchState = this.$config.$scope.$watch('config.$api.state', (newVal, oldVal) => {
            if (newVal !== oldVal) {
                if (newVal === null) {
                    if (this.$config.onInvalidState) {
                        this.$config.onInvalidState(oldVal, this.$config.$params);
                    }
                }
                else {
                    if (this.$config.onStateChange) {
                        this.$config.onStateChange(oldVal, newVal);
                        this.$config.$api.fire('state.change', oldVal, newVal)
                    }
                }
            }
        });

    }

    _destroyScope() {
        this.$config.$scope.unwatchParams();
        this.$config.$scope.unwatchState();
        this.$config.$scope.$destroy();
    }

    _exitState() {
        this.$config.$destroyed = true;
        this._destroyScope();
        this.$config = null;
    }

    _getCurrentState() {
        let state = null;
        if (this.$config.states && this.$config.$initialized) {
            for (let i = 0; i < this.$config.states.length; i++) {
                let stateDef = this.$config.states[i];

                let mandatoriesCheck = stateDef.params.mandatory ? stateDef.params.mandatory.reduce((acc, curr) => acc && !!this.$config.$params[curr], true) : true;
                let nullifiedCheck  = stateDef.params.nullify ? stateDef.params.nullify.reduce((acc, curr) => acc && this.$config.$params[curr] === null, true) : true;

                if (mandatoriesCheck && nullifiedCheck) {
                    state = stateDef.name;
                    break;
                }
            }
        }
        return state;
    }

    _gotoState(stateName, params) {
        if (!this.$config.states) {
            console.error(`Trying to go to state ${stateName}, but no states were defined`);
        }
        else {
            let stateDef = _.find(this.$config.states, {name: stateName});
            if (!stateDef) {
                console.error(`Trying to go to state ${stateName}, but no such state were defined`);
            }
            else {

                if (params) _.extend(this.$config.$params, params);

                if (stateDef.params.mandatory) {
                    stateDef.params.mandatory.forEach(mandatoryParam => {
                        if (!this.$config.$params[mandatoryParam]) {
                            console.error(`State change error: Missing mandatory param: ${mandatoryParam}`);
                        }
                    })
                }
                if (stateDef.params.nullify) {
                    stateDef.params.nullify.forEach(nullifiedParam => {
                        if (params && params[nullifiedParam]) {
                            console.error(`State change error: Unsupported param: ${nullifiedParam}`);
                        }
                        this.$config.$params[nullifiedParam] = null
                    });
                }
                if (stateDef.params.defaults) {
                    for (let key in stateDef.params.defaults) {
                        let value = stateDef.params.defaults[key];
                        if (this.$config.$params[key] === null) {
                            this.$config.$params[key] = value;
                        }
                    }
                }

            }
        }
    }

    _watchLocationChanges() {
        if (!this.$masterScope.$location) this.$masterScope.$location = this.$location;
        this.unwatchUrl = this.$masterScope.$watch('$location.absUrl()', () => {
            if (this.$config) {
                let beforeParams = _.cloneDeep(this.$config.$params);
                this._mapPathToParams();
                if (!this.$config.$initialized) {
                    this.$config.$initialized = true;
                    if (this.$config.onInit) {
                        this.$config.onInit();
                        beforeParams = _.cloneDeep(this.$config.$params);
                    }
                }
                if (!_.isEqual(beforeParams, this.$config.$params)) {
                    if (this.$config.onChangeFromUrl) this.$config.onChangeFromUrl(beforeParams, this.$config.$params)
                    this.$config.$api.fire('params.change', beforeParams, this.$config.$params)
                }
            }
        })
    }

    _watchStateChanges() {
        let onStateChange = (e, toState, toParams, fromState, fromParams) => {
            if (this.$config) {
                if (toState.name === this.$config.baseStateName && fromState.name === this.$config.baseStateName) {
                    let search = this.$location.search();
                    this.$state.go(toState.name, toParams, {notify: false, reload: false, location: 'replace', inherit: true});
                    this.unwatchUrl();
                    this.$timeout(() => {
                        this.$location.search(search).replace();
                        this._watchLocationChanges();
                    });
                    e.preventDefault();
                }
            }
        }
        this.$rootScope.$on('$stateChangeStart', onStateChange);
    }

    _getParametersFromConfig() {
        let urlStructure = this.$config.urlStructure;
        let [pathParams, searchParams] = urlStructure.split('?');
        pathParams = _.without(pathParams.split('/'), '');
        searchParams = searchParams.split('&');
        pathParams = _.map(pathParams, param => param.substr(1));
        searchParams = _.map(searchParams, param => param.substr(1));
        return {
            path: pathParams,
            search: searchParams,
            all: pathParams.concat(searchParams)
        }
    }

    _getPathAsParams() {

        let params = {};
        let configParams = this._getParametersFromConfig();

        let path = this.$location.path();
        let search = this.$location.search();

        let basePathParts = this.$config.basePath.split('/');
        let pathParts = _.filter(path.split('/'), part => part !== '' && !_.includes(basePathParts, part));

        configParams.all.forEach(param => {
            if (params[param]) {
                params[param] = null;
            }
        });

        pathParts.forEach((part, index) => {
            let param = configParams.path[index];
            if (param) params[param] = part;
        });

        configParams.search.forEach(searchParam => {
            if (search[searchParam]) {
                params[searchParam] = search[searchParam];
            }
        })

        return params;

    }

    _initParams() {
        this.$config.$params = {};
        let configParams = this._getParametersFromConfig();
        configParams.all.forEach(param => {
            this.$config.$params[param] = null;
        });

    }

    _mapPathToParams() {
        if (!this.$config) return;

        let currentUrlParams = this._getPathAsParams();

        _.extend(this.$config.$params, currentUrlParams);

        let configParams = this._getParametersFromConfig();
        let remove = false;
        configParams.path.forEach(pathParam => {
            if (!currentUrlParams[pathParam]) remove = true;
            if (remove) this.$config.$params[pathParam] = null;
        })
        let search = this.$location.search();
        configParams.search.forEach(queryParam => {
            if (!search[queryParam]) {
                this.$config.$params[queryParam] = null;
            }
        })

    }

    _mapParamsToPath() {
        if (!this.$config) return;

        let configParams = this._getParametersFromConfig();

        let pathParts = [''].concat(this.$config.basePath.split('/'));
        let stop = false;
        configParams.path.forEach(param => {
            if (!stop) {
                let val = this.$config.$params[param];
                if (val === null) {
                    stop = true;
                }
                else {
                    pathParts.push(val);
                }
            }
        })

        let searchParams = {};
        configParams.search.forEach(param => {
            let val = this.$config.$params[param];
            if (val !== null) {
                searchParams[param] = val;
            }
        })

        this.$location.search(searchParams);
        this.$location.path(pathParts.join('/') + '/');
    }

}

<commit_msg>JFrogSubRouter - More improvements<commit_after>export class JFrogSubRouter {
    constructor($state, $location, $rootScope, $timeout) {
        this.$masterScope = $rootScope.$new();
        this.$rootScope = $rootScope;
        this.$state = $state;
        this.$timeout = $timeout;
        this.$location = $location;
        this.$listeners = {};

        this.supportedEvents = ['state.change', 'params.change']
        this._watchStateChanges();
        this._watchLocationChanges();

    }

    createLocalRouter(config) {

        if (this.$config) {
            this._exitState();
        }

        this.$config = _.cloneDeep(config);
        this.$config.parentScope = config.parentScope;

        this.$config.baseStateName = this.$state.current.name;
        this.$config.basePath = _.trim(this._sharedStartOfString([this.$state.current.url, this.$location.path()]),'/');
        if (this.$state.current.url.indexOf('JFrogSubRouterPath') === -1) {
            console.warn('In order for JFrogSubRouter to work properly, you must define the sub path in your ui-router state, as being of type \'JFrogSubRouterPath\'. In your ui-router state definition, set the \'url\' field as \'/YOUR/BASE/PATH/{anySubRouteParamName:JFrogSubRouterPath}\' ')
        }

        this._initParams();

        this._mapPathToParams();

        this._createScope();

        this._createApiOnConfig();

        this.$config.parentScope.$on('$destroy', () => {
            this._exitState();
        })

        return this.$config.$api;
    }

    getActiveRouter() {
        return this.$config.$api;
    }

    _createApiOnConfig() {
        let THIS = this;

        this.$config.$api = {
            get params() {
                return THIS.$config.$params;
            },
            set params(params) {
                THIS.$config.$params = params;
                THIS._mapParamsToPath();
            },
            get state() {
                return THIS._getCurrentState();
            },
            get config() {
                return THIS.$config;
            },
            updateUrl() {
                if (THIS.$config.hotSyncUrl) return;
                else THIS._mapParamsToPath();
            },
            goto(stateName, params) {
                THIS._gotoState(stateName, params);
            },
            on(event, listener, scope) {
                if (!_.includes(THIS.supportedEvents, event)) {
                    console.error('JFrogSubRouter: Unsupported Event: ' + event);
                    return;
                }
                if (!THIS.$listeners[event]) THIS.$listeners[event] = [];
                THIS.$listeners[event].push(listener);
                if (scope) {
                    scope.$on('$destroy', () => {
                        this.off(event, listener);
                    })
                }
            },
            off(event, listener) {
                if (!_.includes(THIS.supportedEvents, event)) {
                    console.error('jf-table-view: Unsupported Event: ' + event);
                    return;
                }
                if (THIS.$listeners[event]) {
                    if (listener) {
                        _.remove(THIS.$listeners[event],l=>l===listener);
                    }
                    else {
                        THIS.$listeners[event] = [];
                    }
                }
            },
            fire(event, ...params) {
                if (THIS.$listeners[event]) {
                    THIS.$listeners[event].forEach(listener=>listener(...params))
                }
            }

        }
    }
    _sharedStartOfString(array){
        let A= array.concat().sort(),
            a1= A[0], a2= A[A.length-1], L= a1.length, i= 0;
        while(i<L && a1.charAt(i)=== a2.charAt(i)) i++;
        return a1.substring(0, i);
    }

    _createScope() {
        this.$config.$scope = this.$rootScope.$new();
        this.$config.$scope.config = this.$config;

        this.$config.$scope.unwatchParams = this.$config.$scope.$watch('config.$params', (newVal, oldVal) => {
            if (!this.$config.$params || this.$config.baseStateName !== this.$state.current.name || (_.isEqual(newVal, oldVal) && _.isEqual(this._getPathAsParams(), this.$config.$params))) {
                return;
            }
            if (this.$config.hotSyncUrl) this._mapParamsToPath();
        }, true);
        this.$config.$scope.unwatchState = this.$config.$scope.$watch('config.$api.state', (newVal, oldVal) => {
            if (newVal !== oldVal) {
                if (newVal === null) {
                    if (this.$config.onInvalidState) {
                        this.$config.onInvalidState(oldVal, this.$config.$params);
                    }
                }
                else {
                    if (this.$config.onStateChange) {
                        this.$config.onStateChange(oldVal, newVal);
                        this.$config.$api.fire('state.change', oldVal, newVal)
                    }
                }
            }
        });

    }

    _destroyScope() {
        this.$config.$scope.unwatchParams();
        this.$config.$scope.unwatchState();
        this.$config.$scope.$destroy();
    }

    _exitState() {
        this.$config.$destroyed = true;
        this._destroyScope();
        this.$config = null;
    }

    _getCurrentState() {
        let state = null;
        if (this.$config.states && this.$config.$initialized) {
            for (let i = 0; i < this.$config.states.length; i++) {
                let stateDef = this.$config.states[i];

                let mandatoriesCheck = stateDef.params.mandatory ? stateDef.params.mandatory.reduce((acc, curr) => acc && !!this.$config.$params[curr], true) : true;
                let nullifiedCheck  = stateDef.params.nullify ? stateDef.params.nullify.reduce((acc, curr) => acc && this.$config.$params[curr] === null, true) : true;

                if (mandatoriesCheck && nullifiedCheck) {
                    state = stateDef.name;
                    break;
                }
            }
        }
        return state;
    }

    _gotoState(stateName, params) {
        if (!this.$config.states) {
            console.error(`Trying to go to state ${stateName}, but no states were defined`);
        }
        else {
            let stateDef = _.find(this.$config.states, {name: stateName});
            if (!stateDef) {
                console.error(`Trying to go to state ${stateName}, but no such state were defined`);
            }
            else {

                if (params) _.extend(this.$config.$params, params);

                if (stateDef.params.mandatory) {
                    stateDef.params.mandatory.forEach(mandatoryParam => {
                        if (!this.$config.$params[mandatoryParam]) {
                            console.error(`State change error: Missing mandatory param: ${mandatoryParam}`);
                        }
                    })
                }
                if (stateDef.params.nullify) {
                    stateDef.params.nullify.forEach(nullifiedParam => {
                        if (params && params[nullifiedParam]) {
                            console.error(`State change error: Unsupported param: ${nullifiedParam}`);
                        }
                        this.$config.$params[nullifiedParam] = null
                    });
                }
                if (stateDef.params.defaults) {
                    for (let key in stateDef.params.defaults) {
                        let value = stateDef.params.defaults[key];
                        if (this.$config.$params[key] === null) {
                            this.$config.$params[key] = value;
                        }
                    }
                }

            }
        }
    }

    _watchLocationChanges() {
        if (!this.$masterScope.$location) this.$masterScope.$location = this.$location;
        this.unwatchUrl = this.$masterScope.$watch('$location.absUrl()', () => {
            if (this.$config) {
                let beforeParams = _.cloneDeep(this.$config.$params);
                this._mapPathToParams();
                if (!this.$config.$initialized) {
                    this.$config.$initialized = true;
                    if (this.$config.onInit) {
                        this.$config.onInit();
                        beforeParams = _.cloneDeep(this.$config.$params);
                    }
                }
                if (!_.isEqual(beforeParams, this.$config.$params)) {
                    if (this.$config.onChangeFromUrl) this.$config.onChangeFromUrl(beforeParams, this.$config.$params)
                    this.$config.$api.fire('params.change', beforeParams, this.$config.$params)
                }
            }
        })
    }

    _watchStateChanges() {
        let onStateChange = (e, toState, toParams, fromState, fromParams) => {
            if (this.$config) {
                if (toState.name === this.$config.baseStateName && fromState.name === this.$config.baseStateName) {
                    let search = this.$location.search();
                    this.$state.go(toState.name, toParams, {notify: false, reload: false, location: 'replace', inherit: true});
                    this.unwatchUrl();
                    this.$timeout(() => {
                        this.$location.search(search).replace();
                        this._watchLocationChanges();
                    });
                    e.preventDefault();
                }
            }
        }
        this.$rootScope.$on('$stateChangeStart', onStateChange);
    }

    _getParametersFromConfig() {
        let urlStructure = this.$config.urlStructure;
        let [pathParams, searchParams] = urlStructure.split('?');
        pathParams = _.without(pathParams.split('/'), '');
        searchParams = searchParams.split('&');
        pathParams = _.map(pathParams, param => param.substr(1));
        searchParams = _.map(searchParams, param => param.substr(1));
        return {
            path: pathParams,
            search: searchParams,
            all: pathParams.concat(searchParams)
        }
    }

    _getPathAsParams() {

        let params = {};
        let configParams = this._getParametersFromConfig();

        let path = this.$location.path();
        let search = this.$location.search();

        let basePathParts = this.$config.basePath.split('/');
        let pathParts = _.filter(path.split('/'), part => part !== '' && !_.includes(basePathParts, part));

        configParams.all.forEach(param => {
            if (params[param]) {
                params[param] = null;
            }
        });

        pathParts.forEach((part, index) => {
            let param = configParams.path[index];
            if (param) params[param] = part;
        });

        configParams.search.forEach(searchParam => {
            if (search[searchParam]) {
                params[searchParam] = search[searchParam];
            }
        })

        return params;

    }

    _initParams() {
        this.$config.$params = {};
        let configParams = this._getParametersFromConfig();
        configParams.all.forEach(param => {
            this.$config.$params[param] = null;
        });

    }

    _mapPathToParams() {
        if (!this.$config) return;

        let currentUrlParams = this._getPathAsParams();

        _.extend(this.$config.$params, currentUrlParams);

        let configParams = this._getParametersFromConfig();
        let remove = false;
        configParams.path.forEach(pathParam => {
            if (!currentUrlParams[pathParam]) remove = true;
            if (remove) this.$config.$params[pathParam] = null;
        })
        let search = this.$location.search();
        configParams.search.forEach(queryParam => {
            if (!search[queryParam]) {
                this.$config.$params[queryParam] = null;
            }
        })

    }

    _mapParamsToPath() {
        if (!this.$config) return;

        let configParams = this._getParametersFromConfig();

        let pathParts = [''].concat(this.$config.basePath.split('/'));
        let stop = false;
        configParams.path.forEach(param => {
            if (!stop) {
                let val = this.$config.$params[param];
                if (val === null) {
                    stop = true;
                }
                else {
                    pathParts.push(val);
                }
            }
        })

        let searchParams = {};
        configParams.search.forEach(param => {
            let val = this.$config.$params[param];
            if (val !== null) {
                searchParams[param] = val;
            }
        })

        this.$location.search(searchParams);
        this.$location.path(pathParts.join('/') + '/');
    }

}

<|endoftext|>


<commit_before>'use strict';

var hashStream = require('./hash-stream'),
    concat = require('concat-stream'),
    constants = require('../constants'),
    url = require('url'),
    util = require('util'),
    defaultTimeout = constants.capabilityTimeoutInSeconds;

module.exports = function isRequestValid(context, req, cb) {
  var timeout = context.capabilityTimeoutInSeconds || defaultTimeout;
  var uri = url.parse(req.url, true);
  if(uri.query.messageHash && uri.query.dt) {
      var headers = req.headers,
      queryString = uri.query,
      body = JSON.stringify(req.body),
      requestDate = new Date(queryString.dt),
      currentDate = new Date(),
      diff = (currentDate - requestDate) / 1000;

      if(Object.keys(req.body).length == 0) {
        return cb(new Error(util.format("Unauthorized access from %s -- Missing request body", headers.host)));
      }

      req.pipe(hashStream(context.sharedSecret, queryString.dt, body)).pipe(concat(function (hash) {
        if (hash !== queryString.messageHash || diff > timeout) {
          return cb(new Error(util.format("Unauthorized access from %s, %s, %s Computed: %s", headers.host, queryString.messageHash, queryString.dt, hash)));
        } else {
          return cb(null);
        }
      }));
  } else {
    var headers = req.headers,
        body = JSON.stringify(req.body),
        requestDate = new Date(headers.date),
        currentDate = new Date(),
        diff = (currentDate - requestDate) / 1000;

    if(!headers.date || !headers[constants.headerPrefix + constants.headers['SHA256']]) {
        return cb(new Error(util.format("Unauthorized access from %s -- Missing one or more headers", headers.host)));
    }
    if(Object.keys(req.body).length == 0) {
        return cb(new Error(util.format("Unauthorized access from %s -- Missing request body", headers.host)));
    }
    req.pipe(hashStream(context.sharedSecret, headers.date, body)).pipe(concat(function (hash) {
        if (hash !== headers[constants.headerPrefix + constants.headers['SHA256']] || diff > timeout) {
        return cb(new Error(util.format("Unauthorized access from %s, %s, %s Computed: %s", headers.host, headers[constants.headerPrefix + constants.headers['SHA256']], headers.date, hash)));
        } else {
        return cb(null);
        }
    }));
  }
};<commit_msg>Added src file<commit_after>'use strict';

var hashStream = require('./hash-stream'),
    concat = require('concat-stream'),
    constants = require('../constants'),
    url = require('url'),
    util = require('util'),
    defaultTimeout = constants.capabilityTimeoutInSeconds;

module.exports = function isRequestValid(context, req, cb) {
  var timeout = context.capabilityTimeoutInSeconds || defaultTimeout;
  var uri = url.parse(req.url, true);
  if(uri.query.messageHash && uri.query.dt) {
      var headers = req.headers,
      queryString = uri.query,
      requestDate = new Date(queryString.dt),
      currentDate = new Date(),
      diff = (currentDate - requestDate) / 1000,
      messageHash = decodeURI(queryString.messageHash);

      if(Object.keys(req.body).length == 0) {
        return cb(new Error(util.format("Unauthorized access from %s -- Missing request body", headers.host)));
      }

      var bodyKeys = Object.keys(req.body);
      var bodyString = "";

      for(var i = 0; i < bodyKeys.length; i++) {
          bodyString += (bodyKeys[i] + '=' + req.body[bodyKeys[i]]);
          if(i < (bodyKeys.length - 1)) {
              bodyString += '&';
          }
      }

      req.pipe(hashStream(context.sharedSecret, queryString.dt, bodyString)).pipe(concat(function (hash) {
        if (hash !== messageHash || diff > timeout) {
          return cb(new Error(util.format("Unauthorized access from %s, %s, %s Computed: %s", headers.host, messageHash, queryString.dt, hash)));
        } else {
          return cb(null);
        }
      }));
  } else {
    var headers = req.headers,
        body = JSON.stringify(req.body),
        requestDate = new Date(headers.date),
        currentDate = new Date(),
        diff = (currentDate - requestDate) / 1000;

    if(!headers.date || !headers[constants.headerPrefix + constants.headers['SHA256']]) {
        return cb(new Error(util.format("Unauthorized access from %s -- Missing one or more headers", headers.host)));
    }
    if(Object.keys(req.body).length == 0) {
        return cb(new Error(util.format("Unauthorized access from %s -- Missing request body", headers.host)));
    }
    req.pipe(hashStream(context.sharedSecret, headers.date, body)).pipe(concat(function (hash) {
        if (hash !== headers[constants.headerPrefix + constants.headers['SHA256']] || diff > timeout) {
        return cb(new Error(util.format("Unauthorized access from %s, %s, %s Computed: %s", headers.host, headers[constants.headerPrefix + constants.headers['SHA256']], headers.date, hash)));
        } else {
        return cb(null);
        }
    }));
  }
};<|endoftext|>


<commit_before>declareUpdate();

var util = require('./util.sjs');

// console.log(xdmp.getRequestFieldNames());

var files = xdmp.getRequestField('files'); // Array<Node>
var fileNames = xdmp.getRequestFieldFilename('files').toArray(); // Array<string>

var uris = xdmp.getRequestField('uris', 'filename');

var collectionBatch = xdmp.getRequestField('collection-batch');
var collectionsDefault = xdmp.getRequestField('collection-defaults');
var collections = xdmp.getRequestField('collections');
// TODO: Ugly
if('undefined' === typeof collections || null === collections) { collections = []; }
else if('string' === typeof collections) { collections = [collections]; }
if(collectionBatch) {
  collections.push('batch-' + util.uuid());
}
if(collectionsDefault) {
  collections = collections.concat(xdmp.defaultCollections().toArray())
}

/*
Permissions are conveyed from the browser as arrays of capabilities, keyed on
the string, `permission*`, concatenated with a role name,
e.g. `permission*rest-reader: ['read']`.
`*` is used as a delimiter becuase it's not a valid character in a role name.
 */
var permissions = xdmp.getRequestFieldNames()
  .toArray()
  .filter(function(f){
    //console.log(f);
    return f.match(/^permission\*/);
  })
  .map(function(f){
    var perm = {};
    perm[f.split('\*')[1]] = xdmp.getRequestField(f);
    return perm;
  });

/**
 * Map the data structure derived from the browser to proper `xdmp.permission`
 * instances.
 *
 * @param  {object} permissions `{role: [capability, capability]}`
 * @param  {boolean} defaults   Whether to include default permissions from
 *                              the current user
 * @return {Array<xdmp.permission>}
 */
function calculatePermissions(permissions, defaults) {
  var perms = [];
  permissions.forEach(function(role){
    for(r in role) {
      if(!Array.isArray(role[r])) { role[r] = [role[r]]; }
      perms = perms.concat(
        role[r].map(function(cap){
          //console.log(xdmp.permission(r, cap));
          return xdmp.permission(r, cap);
        })
      );
    }
  });
  if(defaults) {
    perms = perms.concat(xdmp.defaultPermissions());
  }
  return perms;
}

// FIXME: This is brittle
// TODO: What about XML namespaces? (Really, what people will want to do is type their own XPath. Sigh.)
function extractID(doc) {
  var id = doc.xpath('(//(id|_id))[1]').next().value;
  if(!id) { // Why doesn't comparison to null work here?
    throw new Error('The document does not contain an ID, either id or _id.\n' + doc);
  }
  if(id instanceof XMLNode) return id.textContent;
  return id;
}

/**
 * Derive a URI for identifying documents based on one of three policies:
 *   * filename: Uses the filename given by the client
 *   * id: Finds the first `id` or `_id` property in the document and uses its
 *     value
 *   * uuid: Generates a UUID
 * For `id` and `uuid` it appends a file extension based on the node kind,
 * one of `json`, `xml`, `text`, or `binary`.
 * All URIs are prepended with a `/` so that you can use things that require
 * directory scoping, like CPF
 * @param  {Node} node     The document
 * @param  {string} policy   One of `filename`, `id`, or `uuid`
 * @param  {string} filename The name of the file from the client file system
 * @return {string}          A fully formed URI based on the policy
 */
function deriveURI(node, policy, filename) {
  var basename = filename;
  // console.log(node);
  var kind = util.documentKind(node);
  switch (policy) {
    case 'filename':
      break;
    case 'id':
      basename = extractID(node) + '.' + kind;
      break;
    case 'uuid':
      basename = util.uuid() + '.' + kind;
      break;
    default:
      throw new TypeError(policy + ' is not a valid URI policy');
  }
  return '/' + basename;
}

var docCount = files.length;
for(var i = 0; i < docCount; i++) {
  var node = xdmp.unquote(files[i]).next().value; // Yikes! unquote always returns a ValueIterator.
  var uri = deriveURI(node, uris, fileNames[i]);
  console.log('Inserting ' + uri);
  xdmp.documentInsert(
    uri,
    node,
    calculatePermissions(permissions, xdmp.getRequestField('permission-defaults')),
    collections
  );
}

xdmp.setResponseCode(200, 'OK');
xdmp.addResponseHeader('Content-Type', 'application/json;charset=utf-8');

var response =
  {
    count: docCount,
    collections: collections
  };
response;
<commit_msg>Nit-picky clean-up<commit_after>declareUpdate();

var util = require('./util.sjs');

// console.log(xdmp.getRequestFieldNames());

var files = xdmp.getRequestField('files'); // Array<Node>
var fileNames = xdmp.getRequestFieldFilename('files').toArray(); // Array<string>

var uris = xdmp.getRequestField('uris', 'filename');

var collectionBatch = xdmp.getRequestField('collection-batch');
var collectionsDefault = xdmp.getRequestField('collection-defaults');
var collections = xdmp.getRequestField('collections');
// TODO: Ugly
if('undefined' === typeof collections || null === collections) { collections = []; }
else if('string' === typeof collections) { collections = [collections]; }
if(collectionBatch) {
  collections.push('batch-' + util.uuid());
}
if(collectionsDefault) {
  collections = collections.concat(xdmp.defaultCollections().toArray())
}

/*
Permissions are conveyed from the browser as arrays of capabilities, keyed on
the string, `permission*`, concatenated with a role name,
e.g. `permission*rest-reader: ['read']`.
`*` is used as a delimiter becuase it's not a valid character in a role name.
 */
var permissions = xdmp.getRequestFieldNames()
  .toArray()
  .filter(function(f){
    //console.log(f);
    return f.match(/^permission\*/);
  })
  .map(function(f){
    var perm = {};
    perm[f.split('\*')[1]] = xdmp.getRequestField(f);
    return perm;
  });

/**
 * Map the data structure derived from the browser to proper `xdmp.permission`
 * instances.
 *
 * @param  {object} permissions `{role: [capability, capability]}`
 * @param  {boolean} defaults   Whether to include default permissions from
 *                              the current user
 * @return {Array<xdmp.permission>}
 */
function calculatePermissions(permissions, defaults) {
  var perms = [];
  permissions.forEach(function(role){
    for(r in role) {
      if(!Array.isArray(role[r])) { role[r] = [role[r]]; }
      perms = perms.concat(
        role[r].map(function(cap){
          //console.log(xdmp.permission(r, cap));
          return xdmp.permission(r, cap);
        })
      );
    }
  });
  if(defaults) {
    return perms.concat(xdmp.defaultPermissions());
  }
  return perms;
}

// FIXME: This is brittle
// TODO: What about XML namespaces? (Really, what people will want to do is type their own XPath. Sigh.)
function extractID(doc) {
  var id = doc.xpath('(//(id|_id))[1]').next().value;
  if(!id) { // Why doesn't comparison to null work here?
    throw new Error('The document does not contain an ID, either id or _id.\n' + doc);
  }
  if(id instanceof XMLNode) return id.textContent;
  return id;
}

/**
 * Derive a URI for identifying documents based on one of three policies:
 *   * filename: Uses the filename given by the client
 *   * id: Finds the first `id` or `_id` property in the document and uses its
 *     value
 *   * uuid: Generates a UUID
 * For `id` and `uuid` it appends a file extension based on the node kind,
 * one of `json`, `xml`, `text`, or `binary`.
 * All URIs are prepended with a `/` so that you can use things that require
 * directory scoping, like CPF
 * @param  {Node} node     The document
 * @param  {string} policy   One of `filename`, `id`, or `uuid`
 * @param  {string} filename The name of the file from the client file system
 * @return {string}          A fully formed URI based on the policy
 */
function deriveURI(node, policy, filename) {
  var basename = filename;
  // console.log(node);
  var kind = util.documentKind(node);
  switch (policy) {
    case 'filename':
      break;
    case 'id':
      basename = extractID(node) + '.' + kind;
      break;
    case 'uuid':
      basename = util.uuid() + '.' + kind;
      break;
    default:
      throw new TypeError(policy + ' is not a valid URI policy');
  }
  return '/' + basename;
}

var docCount = files.length;
for(var i = 0; i < docCount; i++) {
  var node = xdmp.unquote(files[i]).next().value; // Yikes! unquote always returns a ValueIterator.
  var uri = deriveURI(node, uris, fileNames[i]);
  console.log('Inserting ' + uri);
  xdmp.documentInsert(
    uri,
    node,
    calculatePermissions(permissions, xdmp.getRequestField('permission-defaults')),
    collections
  );
}

xdmp.setResponseCode(200, 'OK');
xdmp.addResponseHeader('Content-Type', 'application/json;charset=utf-8');

var response =
  {
    count: docCount,
    collections: collections
  };
response;
<|endoftext|>