<commit_before>require('source-map-support').install();

const fs = require('fs');
const path = require('path');
const glob = require('glob');
const rimraf = require('rimraf').sync;
const assert = require('assert');
const child_process = require('child_process');

const degit = require('../index.js');
const degitPath = path.resolve('bin.js');

const timeout = 30000;

function exec(cmd) {
	return new Promise((fulfil, reject) => {
		child_process.exec(cmd, (err, stdout, stderr) => {
			if (err) return reject(err);
			console.log(stdout);
			console.error(stderr);
			fulfil();
		});
	});
}

describe('degit', function() {
	this.timeout(timeout);

	function compare(dir, files) {
		const expected = glob.sync('**', { cwd: dir });
		assert.deepEqual(Object.keys(files).sort(), expected.sort());

		expected.forEach(file => {
			if (!fs.lstatSync(`${dir}/${file}`).isDirectory()) {
				assert.equal(files[file].trim(), read(`${dir}/${file}`).trim());
			}
		});
	}

	beforeEach(async () => await rimraf('.tmp'));
	afterEach(async () => await rimraf('.tmp'));

	describe('github', () => {
		[
			'mhkeller/degit-test-repo-compose',
			'Rich-Harris/degit-test-repo',
			'github:Rich-Harris/degit-test-repo',
			'git@github.com:Rich-Harris/degit-test-repo',
			'https://github.com/Rich-Harris/degit-test-repo.git'
		].forEach(src => {
			it(src, async () => {
				await exec(`node ${degitPath} ${src} .tmp/test-repo -v`);
				compare(`.tmp/test-repo`, {
					'file.txt': 'hello from github!',
					subdir: null,
					'subdir/file.txt': 'hello from a subdirectory!'
				});
			});
		});
	});

	describe('gitlab', () => {
		[
			'gitlab:Rich-Harris/degit-test-repo',
			'git@gitlab.com:Rich-Harris/degit-test-repo',
			'https://gitlab.com/Rich-Harris/degit-test-repo.git'
		].forEach(src => {
			it(src, async () => {
				await exec(`node ${degitPath} ${src} .tmp/test-repo -v`);
				compare(`.tmp/test-repo`, {
					'file.txt': 'hello from gitlab!'
				});
			});
		});
	});

	describe('bitbucket', () => {
		[
			'bitbucket:Rich_Harris/degit-test-repo',
			'git@bitbucket.org:Rich_Harris/degit-test-repo',
			'https://bitbucket.org/Rich_Harris/degit-test-repo.git'
		].forEach(src => {
			it(src, async () => {
				await exec(`node ${degitPath} ${src} .tmp/test-repo -v`);
				compare(`.tmp/test-repo`, {
					'file.txt': 'hello from bitbucket'
				});
			});
		});
	});

	describe('Sourcehut', () => {
		[
			'git.sr.ht/~satotake/degit-test-repo',
			'https://git.sr.ht/~satotake/degit-test-repo',
			'git@git.sr.ht:~satotake/degit-test-repo'
		].forEach(src => {
			it(src, async () => {
				await exec(`node ${degitPath} ${src} .tmp/test-repo -v`);
				compare(`.tmp/test-repo`, {
					'file.txt': 'hello from sourcehut!'
				});
			});
		});
	});

	describe('non-empty directories', () => {
		it('fails without --force', async () => {
			let succeeded;

			try {
				await exec(`mkdir -p .tmp/test-repo`);
				await exec(`echo "not empty" > .tmp/test-repo/file.txt`);
				await exec(
					`node ${degitPath} Rich-Harris/degit-test-repo .tmp/test-repo -v`
				);
				succeeded = true;
			} catch (err) {
				assert.ok(/destination directory is not empty/.test(err.message));
			}

			assert.ok(!succeeded);
		});

		it('succeeds with --force', async () => {
			await exec(
				`node ${degitPath} Rich-Harris/degit-test-repo .tmp/test-repo -fv`
			);
		});
	});

	describe('command line arguments', () => {
		it('allows flags wherever', async () => {
			await exec(
				`node ${degitPath} -v Rich-Harris/degit-test-repo .tmp/test-repo`
			);
			compare(`.tmp/test-repo`, {
				'file.txt': 'hello from github!',
				subdir: null,
				'subdir/file.txt': 'hello from a subdirectory!'
			});
		});
	});

	describe('api', () => {
		it('is usable from node scripts', async () => {
			await degit('Rich-Harris/degit-test-repo', { force: true }).clone(
				'.tmp/test-repo'
			);

			compare(`.tmp/test-repo`, {
				'file.txt': 'hello from github!',
				subdir: null,
				'subdir/file.txt': 'hello from a subdirectory!'
			});
		});
	});

	describe('actions', () => {
		it('removes specified file', async () => {
			await exec(
				`node ${degitPath} -v mhkeller/degit-test-repo-remove-only .tmp/test-repo`
			);
			compare(`.tmp/test-repo`, {});
		});

		it('clones repo and removes specified file', async () => {
			await exec(
				`node ${degitPath} -v mhkeller/degit-test-repo-remove .tmp/test-repo`
			);
			compare(`.tmp/test-repo`, {
				'other.txt': 'hello from github!',
				subdir: null,
				'subdir/file.txt': 'hello from a subdirectory!'
			});
		});

		it('removes and adds nested files', async () => {
			await rimraf('.tmp');

			await exec(
				`node ${degitPath} -v mhkeller/degit-test-repo-nested-actions .tmp/test-repo`
			);
			compare(`.tmp/test-repo`, {
				dir: null,
				folder: null,
				subdir: null,
				'folder/file.txt': 'hello from clobber file!',
				'folder/other.txt': 'hello from other file!',
				'subdir/file.txt': 'hello from a subdirectory!'
			});
		});
	});
});

function read(file) {
	return fs.readFileSync(file, 'utf-8');
}
<commit_msg>add a test<commit_after>require('source-map-support').install();

const fs = require('fs');
const path = require('path');
const glob = require('glob');
const rimraf = require('rimraf').sync;
const assert = require('assert');
const child_process = require('child_process');

const degit = require('../index.js');
const degitPath = path.resolve('bin.js');

const timeout = 30000;

function exec(cmd) {
	return new Promise((fulfil, reject) => {
		child_process.exec(cmd, (err, stdout, stderr) => {
			if (err) return reject(err);
			console.log(stdout);
			console.error(stderr);
			fulfil();
		});
	});
}

describe('degit', function() {
	this.timeout(timeout);

	function compare(dir, files) {
		const expected = glob.sync('**', { cwd: dir });
		assert.deepEqual(Object.keys(files).sort(), expected.sort());

		expected.forEach(file => {
			if (!fs.lstatSync(`${dir}/${file}`).isDirectory()) {
				assert.equal(files[file].trim(), read(`${dir}/${file}`).trim());
			}
		});
	}

	beforeEach(async () => await rimraf('.tmp'));
	afterEach(async () => await rimraf('.tmp'));

	describe('github', () => {
		[
			'mhkeller/degit-test-repo-compose',
			'Rich-Harris/degit-test-repo',
			'github:Rich-Harris/degit-test-repo',
			'git@github.com:Rich-Harris/degit-test-repo',
			'https://github.com/Rich-Harris/degit-test-repo.git'
		].forEach(src => {
			it(src, async () => {
				await exec(`node ${degitPath} ${src} .tmp/test-repo -v`);
				compare(`.tmp/test-repo`, {
					'file.txt': 'hello from github!',
					subdir: null,
					'subdir/file.txt': 'hello from a subdirectory!'
				});
			});
		});
	});

	describe('gitlab', () => {
		[
			'gitlab:Rich-Harris/degit-test-repo',
			'git@gitlab.com:Rich-Harris/degit-test-repo',
			'https://gitlab.com/Rich-Harris/degit-test-repo.git'
		].forEach(src => {
			it(src, async () => {
				await exec(`node ${degitPath} ${src} .tmp/test-repo -v`);
				compare(`.tmp/test-repo`, {
					'file.txt': 'hello from gitlab!'
				});
			});
		});
	});

	describe('bitbucket', () => {
		[
			'bitbucket:Rich_Harris/degit-test-repo',
			'git@bitbucket.org:Rich_Harris/degit-test-repo',
			'https://bitbucket.org/Rich_Harris/degit-test-repo.git'
		].forEach(src => {
			it(src, async () => {
				await exec(`node ${degitPath} ${src} .tmp/test-repo -v`);
				compare(`.tmp/test-repo`, {
					'file.txt': 'hello from bitbucket'
				});
			});
		});
	});

	describe('Sourcehut', () => {
		[
			'git.sr.ht/~satotake/degit-test-repo',
			'https://git.sr.ht/~satotake/degit-test-repo',
			'git@git.sr.ht:~satotake/degit-test-repo'
		].forEach(src => {
			it(src, async () => {
				await exec(`node ${degitPath} ${src} .tmp/test-repo -v`);
				compare(`.tmp/test-repo`, {
					'file.txt': 'hello from sourcehut!'
				});
			});
		});
	});

	describe('Subdirectories', () => {
		[
			'Rich-Harris/degit-test-repo/subdir',
			'github:Rich-Harris/degit-test-repo/subdir',
			'git@github.com:Rich-Harris/degit-test-repo/subdir',
			'https://github.com/Rich-Harris/degit-test-repo.git/subdir'
		].forEach(src => {
			it(src, async () => {
				await exec(`node ${degitPath} ${src} .tmp/test-repo -v`);
				compare(`.tmp/test-repo`, {
					'file.txt': 'hello from a subdirectory!'
				});
			});
		});
	});

	describe('non-empty directories', () => {
		it('fails without --force', async () => {
			let succeeded;

			try {
				await exec(`mkdir -p .tmp/test-repo`);
				await exec(`echo "not empty" > .tmp/test-repo/file.txt`);
				await exec(
					`node ${degitPath} Rich-Harris/degit-test-repo .tmp/test-repo -v`
				);
				succeeded = true;
			} catch (err) {
				assert.ok(/destination directory is not empty/.test(err.message));
			}

			assert.ok(!succeeded);
		});

		it('succeeds with --force', async () => {
			await exec(
				`node ${degitPath} Rich-Harris/degit-test-repo .tmp/test-repo -fv`
			);
		});
	});

	describe('command line arguments', () => {
		it('allows flags wherever', async () => {
			await exec(
				`node ${degitPath} -v Rich-Harris/degit-test-repo .tmp/test-repo`
			);
			compare(`.tmp/test-repo`, {
				'file.txt': 'hello from github!',
				subdir: null,
				'subdir/file.txt': 'hello from a subdirectory!'
			});
		});
	});

	describe('api', () => {
		it('is usable from node scripts', async () => {
			await degit('Rich-Harris/degit-test-repo', { force: true }).clone(
				'.tmp/test-repo'
			);

			compare(`.tmp/test-repo`, {
				'file.txt': 'hello from github!',
				subdir: null,
				'subdir/file.txt': 'hello from a subdirectory!'
			});
		});
	});

	describe('actions', () => {
		it('removes specified file', async () => {
			await exec(
				`node ${degitPath} -v mhkeller/degit-test-repo-remove-only .tmp/test-repo`
			);
			compare(`.tmp/test-repo`, {});
		});

		it('clones repo and removes specified file', async () => {
			await exec(
				`node ${degitPath} -v mhkeller/degit-test-repo-remove .tmp/test-repo`
			);
			compare(`.tmp/test-repo`, {
				'other.txt': 'hello from github!',
				subdir: null,
				'subdir/file.txt': 'hello from a subdirectory!'
			});
		});

		it('removes and adds nested files', async () => {
			await rimraf('.tmp');

			await exec(
				`node ${degitPath} -v mhkeller/degit-test-repo-nested-actions .tmp/test-repo`
			);
			compare(`.tmp/test-repo`, {
				dir: null,
				folder: null,
				subdir: null,
				'folder/file.txt': 'hello from clobber file!',
				'folder/other.txt': 'hello from other file!',
				'subdir/file.txt': 'hello from a subdirectory!'
			});
		});
	});
});

function read(file) {
	return fs.readFileSync(file, 'utf-8');
}
<|endoftext|>


<commit_before>// Core tests
require('can-component/test/tests');
require('can-define/test/test');
// require('can-route/test/test'); in dev-only
// require('can-route-pushstate/can-route-pushstate_test'); in dev-only
// require('can-stache/test/stache-test'); in dev-only
require('can-stache-bindings/test/tests');
require('can-set/test/test');
require('can-value/test/test');
// can-connect
// can-stache-route-helpers

// Infrastructure tests
require('can-ajax/can-ajax-test');
require('can-assign/can-assign-test');
require('can-bind/test/test');
require('can-construct/can-construct_test');
require('can-construct-super/test/can-construct-super_test');
require('can-control/can-control_test');
require('can-define-lazy-value/define-lazy-value-test');
require('can-deparam/can-deparam-test');
require('can-dom-events/can-dom-events-test');
require('can-event-dom-enter/can-event-dom-enter-test');
require('can-event-dom-radiochange/can-event-dom-radiochange-test');
require('can-event-queue/can-event-queue-test');
require('can-globals/can-globals-test');
require('can-key/can-key-test');
require('can-key-tree/can-key-tree-test');
// require('can-observation/can-observation_test'); in dev-only
require('can-param/can-param-test');
require('can-parse-uri/can-parse-uri-test');
require('can-queues/can-queues-test');
require('can-reflect/can-reflect-test');
require('can-reflect-dependencies/test');
require('can-reflect-promise/test/can-reflect-promise_test');
require('can-simple-dom/test/test');
// require('can-simple-map/can-simple-map_test'); in dev-only
require('can-simple-observable/can-simple-observable-test');
require('can-stache-key/can-stache-key-test');
require('can-symbol/can-symbol-test');
// require('can-util/dom/tests'); // importing would cause two versions of can-dom-data-state
require('can-util/js/tests');
require('can-validate-interface/test');
// require('can-view-callbacks/test/callbacks-test'); in dev-only
require('can-view-live/test/test');
require('can-view-model/test/test');
require('can-view-nodelist/test/can-view-nodelist-test');
require('can-view-parser/test/can-view-parser-test');
require('can-view-scope/test/scope-test');
require('can-view-target/test/test');
require('can-stache-converters/test/test');
require('can-types/test/test');

// Legacy tests
require('can-compute/can-compute_test');
require('can-list/can-list_test');
require('can-map/can-map_test');
require('can-map-define/can-map-define_test');
//require('can-view-href/test/test');
//require('can-map-backup/can-map-backup_test');
//require('can-validate-legacy/can-validate-test');

// Ecosystem tests
require('can-fixture/test/fixture_test');
require('can-fixture-socket/test/test');
//require('can-connect-signalr/test');
//require('can-connect-cloneable/test/test');
//require('can-connect-feathers/test/test'); depends on babel-polyfill
require('can-kefir/can-kefir-test');
require('can-stream/can-stream_test');
require('can-stream-kefir/can-stream-kefir_test');
require('can-ndjson-stream/can-ndjson-stream-test');
// require('can-vdom/test/test'); uses mocha
//require('can-connect-ndjson/test/can-connect-ndjson-test');
//if(typeof Proxy === "function"){
//	require('can-observe/test/test');
//}
require('can-define-backup/can-define-backup_test');
require('can-define-stream/can-define-stream_test');
require('can-define-stream-kefir/can-define-stream-kefir_test');
require('can-validate/test');
require('can-validate-validatejs/test');
require('can-define-validate-validatejs/test');
require('react-view-model/test/test#?can/test/browser-supports-react');
require('can-view-import/test/test');
//require('can-react-component/test/test#?can/test/browser-supports-react');
// require('can-jquery/test/test');
// require('can-vdom/test/test');
require('can-view-autorender/test/test');
// require('can-zone/test/test'); needs to publish this
// require('can-zone-storage/test/can-zone-storage_zone-test');

// Integration tests
require('../docs/can-guides/experiment/todomvc/test');
require('./integration/all/test');
<commit_msg>Remove can-types tests<commit_after>// Core tests
require('can-component/test/tests');
require('can-define/test/test');
// require('can-route/test/test'); in dev-only
// require('can-route-pushstate/can-route-pushstate_test'); in dev-only
// require('can-stache/test/stache-test'); in dev-only
require('can-stache-bindings/test/tests');
require('can-set/test/test');
require('can-value/test/test');
// can-connect
// can-stache-route-helpers

// Infrastructure tests
require('can-ajax/can-ajax-test');
require('can-assign/can-assign-test');
require('can-bind/test/test');
require('can-construct/can-construct_test');
require('can-construct-super/test/can-construct-super_test');
require('can-control/can-control_test');
require('can-define-lazy-value/define-lazy-value-test');
require('can-deparam/can-deparam-test');
require('can-dom-events/can-dom-events-test');
require('can-event-dom-enter/can-event-dom-enter-test');
require('can-event-dom-radiochange/can-event-dom-radiochange-test');
require('can-event-queue/can-event-queue-test');
require('can-globals/can-globals-test');
require('can-key/can-key-test');
require('can-key-tree/can-key-tree-test');
// require('can-observation/can-observation_test'); in dev-only
require('can-param/can-param-test');
require('can-parse-uri/can-parse-uri-test');
require('can-queues/can-queues-test');
require('can-reflect/can-reflect-test');
require('can-reflect-dependencies/test');
require('can-reflect-promise/test/can-reflect-promise_test');
require('can-simple-dom/test/test');
// require('can-simple-map/can-simple-map_test'); in dev-only
require('can-simple-observable/can-simple-observable-test');
require('can-stache-key/can-stache-key-test');
require('can-symbol/can-symbol-test');
// require('can-util/dom/tests'); // importing would cause two versions of can-dom-data-state
require('can-util/js/tests');
require('can-validate-interface/test');
// require('can-view-callbacks/test/callbacks-test'); in dev-only
require('can-view-live/test/test');
require('can-view-model/test/test');
require('can-view-nodelist/test/can-view-nodelist-test');
require('can-view-parser/test/can-view-parser-test');
require('can-view-scope/test/scope-test');
require('can-view-target/test/test');
require('can-stache-converters/test/test');

// Legacy tests
require('can-compute/can-compute_test');
require('can-list/can-list_test');
require('can-map/can-map_test');
require('can-map-define/can-map-define_test');
//require('can-view-href/test/test');
//require('can-map-backup/can-map-backup_test');
//require('can-validate-legacy/can-validate-test');

// Ecosystem tests
require('can-fixture/test/fixture_test');
require('can-fixture-socket/test/test');
//require('can-connect-signalr/test');
//require('can-connect-cloneable/test/test');
//require('can-connect-feathers/test/test'); depends on babel-polyfill
require('can-kefir/can-kefir-test');
require('can-stream/can-stream_test');
require('can-stream-kefir/can-stream-kefir_test');
require('can-ndjson-stream/can-ndjson-stream-test');
// require('can-vdom/test/test'); uses mocha
//require('can-connect-ndjson/test/can-connect-ndjson-test');
//if(typeof Proxy === "function"){
//	require('can-observe/test/test');
//}
require('can-define-backup/can-define-backup_test');
require('can-define-stream/can-define-stream_test');
require('can-define-stream-kefir/can-define-stream-kefir_test');
require('can-validate/test');
require('can-validate-validatejs/test');
require('can-define-validate-validatejs/test');
require('react-view-model/test/test#?can/test/browser-supports-react');
require('can-view-import/test/test');
//require('can-react-component/test/test#?can/test/browser-supports-react');
// require('can-jquery/test/test');
// require('can-vdom/test/test');
require('can-view-autorender/test/test');
// require('can-zone/test/test'); needs to publish this
// require('can-zone-storage/test/can-zone-storage_zone-test');

// Integration tests
require('../docs/can-guides/experiment/todomvc/test');
require('./integration/all/test');
<|endoftext|>


<commit_before>// INITIALISE THE GODDAMN BELLS

var bells = [
// Monday
{
	"hours":   [ 8,  8,  9, 10, 11, 11, 13, 13, 13, 14],
	"minutes": [25, 35, 55, 10, 30, 45,  0, 20, 35, 50],
	"desc": ["House Group", 1, "Recess 1", 2, "Recess 2", 3, "Lunch 1", "Lunch 2", 4, "School Ends"]
}];

bells.push(bells[0]);  // Tuesday

// Wednesday
bells.push({
	"hours":   [ 8,  8,  9, 10, 10, 10, 12, 12, 12, 14],
	"minutes": [25, 35, 50, 25, 40, 55,  5, 25, 45, 30],
	"desc": ["House Group", 1, 2, "Recess", "Assembly", 3, "Lunch 1", "Lunch 2", "Sport", "School Ends"]
});

bells.push(bells[0]);  // Thursday

// Friday
bells.push({
	"hours":   [ 8,  8,  9, 10, 11, 11, 13, 13, 13, 14],
	"minutes": [25, 35, 55, 15, 35, 50, 10, 25, 40, 50],
	"desc": ["House Group", 1, "Recess 1", 2, "Recess 2", 3, "Lunch 1", "Lunch 2", 4, "School Ends"]
});

// HALP WHAT AM I DOING

function getEvent(day, eventNo) {
	return {
		"hour": bells[day].hours[eventNo],
		"minute": bells[day].minutes[eventNo],
		"desc": bells[day].desc[eventNo]
	};
}

function getSecondsUntilEvent(ev) {
	var evDate = new Date();
	evDate.setHours(ev.hour);
	evDate.setMinutes(ev.minute);
	return ((evDate.getTime() - new Date().getTime())/1000)|0;
}

function secsToHMS(secs) {
	return [(secs/3600)|0, (secs%3600/60)|0, secs % 60];
}

function displayTimeUntilEvent(ev) {
	var hms = secsToHMS(getSecondsUntilEvent(ev));
	var h = hms[0], m = hms[1], s = hms[2];
	var clock = h + "h " + m + "min " + s + "s";

	document.getElementById("bell-countdown").textContent = clock;
}

function displayEventDesc(ev) {
	var desc = ev.desc;

	if (typeof desc === "number") {
		desc = "Period " + desc;
	}

	document.getElementById("bell-descript").textContent = desc;
}

function getNextEvent() {
	var day = new Date().getDay();
	if (day === 0 || day === 6) {
		// weekend, wrap around to Monday
		day = 0;
	} else {
		// weekday
		// my day index starts with Monday, silly me
		day -= 1;
		// TODO check for end of day
	}

	var dayEvents = bells[day];
	// TODO
	return dayEvents[0];
}

function displayEvent(ev) {
	displayTimeUntilEvent(ev);
	displayEventDesc(ev);
}

function theFinalCountdown() {
	var ev = getNextEvent();
	displayEvent(ev);
}

var tick = setInterval(theFinalCountdown, 1000);
<commit_msg>countdown.js: I can't maths, obviously.<commit_after>// INITIALISE THE GODDAMN BELLS

var bells = [
// Monday
{
	"hours":   [ 8,  8,  9, 10, 11, 11, 13, 13, 13, 14],
	"minutes": [25, 35, 55, 10, 30, 45,  0, 20, 35, 50],
	"desc": ["House Group", 1, "Recess 1", 2, "Recess 2", 3, "Lunch 1", "Lunch 2", 4, "School Ends"]
}];

bells.push(bells[0]);  // Tuesday

// Wednesday
bells.push({
	"hours":   [ 8,  8,  9, 10, 10, 10, 12, 12, 12, 14],
	"minutes": [25, 35, 50, 25, 40, 55,  5, 25, 45, 30],
	"desc": ["House Group", 1, 2, "Recess", "Assembly", 3, "Lunch 1", "Lunch 2", "Sport", "School Ends"]
});

bells.push(bells[0]);  // Thursday

// Friday
bells.push({
	"hours":   [ 8,  8,  9, 10, 11, 11, 13, 13, 13, 14],
	"minutes": [25, 35, 55, 15, 35, 50, 10, 25, 40, 50],
	"desc": ["House Group", 1, "Recess 1", 2, "Recess 2", 3, "Lunch 1", "Lunch 2", 4, "School Ends"]
});

// HALP WHAT AM I DOING

function getEvent(day, eventNo) {
	return {
		"hour": bells[day].hours[eventNo],
		"minute": bells[day].minutes[eventNo],
		"desc": bells[day].desc[eventNo]
	};
}

function getSecondsUntilEvent(ev) {
	var evDate = new Date();
	evDate.setHours(ev.hour);
	evDate.setMinutes(ev.minute);
	return ((new Date() - evDate)/1000)|0;
}

function secsToHMS(secs) {
	return [(secs/3600)|0, (secs%3600/60)|0, secs % 60];
}

function displayTimeUntilEvent(ev) {
	var hms = secsToHMS(getSecondsUntilEvent(ev));
	var h = hms[0], m = hms[1], s = hms[2];
	var clock = h + "h " + m + "min " + s + "s";

	document.getElementById("bell-countdown").textContent = clock;
}

function displayEventDesc(ev) {
	var desc = ev.desc;

	if (typeof desc === "number") {
		desc = "Period " + desc;
	}

	document.getElementById("bell-descript").textContent = desc;
}

function getNextEvent() {
	var day = new Date().getDay();
	if (day === 0 || day === 6) {
		// weekend, wrap around to Monday
		day = 0;
	} else {
		// weekday
		// my day index starts with Monday, silly me
		day -= 1;
		// TODO check for end of day
	}

	var dayEvents = bells[day];
	// TODO
	return dayEvents[0];
}

function displayEvent(ev) {
	displayTimeUntilEvent(ev);
	displayEventDesc(ev);
}

function theFinalCountdown() {
	var ev = getNextEvent();
	displayEvent(ev);
}

var tick = setInterval(theFinalCountdown, 1000);
<|endoftext|>


<commit_before>// INITIALISE THE GODDAMN BELLS

var bells = [
// Monday
{
	"hours":   [ 8,  8,  9, 10, 11, 11, 13, 13, 13, 14],
	"minutes": [25, 35, 55, 10, 30, 45,  0, 20, 35, 50],
	"desc": ["House Group", 1, "Recess 1", 2, "Recess 2", 3, "Lunch 1", "Lunch 2", 4, "School Ends"]
}];

bells.push(bells[0]);  // Tuesday

// Wednesday
bells.push({
	"hours":   [ 8,  8,  9, 10, 10, 10, 12, 12, 12, 14],
	"minutes": [25, 35, 50, 25, 40, 55,  5, 25, 45, 30],
	"desc": ["House Group", 1, 2, "Recess", "Assembly", 3, "Lunch 1", "Lunch 2", "Sport", "School Ends"]
});

bells.push(bells[0]);  // Thursday

// Friday
bells.push({
	"hours":   [ 8,  8,  9, 10, 11, 11, 13, 13, 13, 14],
	"minutes": [25, 35, 55, 15, 35, 50, 10, 25, 40, 50],
	"desc": ["House Group", 1, "Recess 1", 2, "Recess 2", 3, "Lunch 1", "Lunch 2", 4, "School Ends"]
});

// HALP WHAT AM I DOING

function getEvent(day, eventNo) {
	return {
		"hour": bells[day].hours[eventNo],
		"minute": bells[day].minutes[eventNo],
		"desc": bells[day].desc[eventNo]
	};
}

function getSecondsUntilEvent(ev) {
	var evDate = new Date();
	evDate.setHours(ev.hour);
	evDate.setMinutes(ev.minute);
	return ((new Date() - evDate)/1000)|0;
}

function secsToHMS(secs) {
	var s = secs % 60;
	var m = (secs%3600 - s)/60;
	var h = secs / 3600;
	return [h|0, m|0, s];
}

function displayTimeUntilEvent(ev) {
	var hms = secsToHMS(getSecondsUntilEvent(ev));
	var h = hms[0], m = hms[1], s = hms[2];
	var clock = h + "h " + m + "min " + s + "s";

	document.getElementById("bell-countdown").textContent = clock;
}

function displayEventDesc(ev) {
	var desc = ev.desc;

	if (typeof desc === "number") {
		desc = "Period " + desc;
	}

	document.getElementById("bell-descript").textContent = desc;
}

function getNextEvent() {
	var day = new Date().getDay();
	if (day === 0 || day === 6) {
		// weekend, wrap around to Monday
		day = 0;
	} else {
		// weekday
		// my day index starts with Monday, silly me
		day -= 1;
		// TODO check for end of day
	}

	var dayEvents = bells[day], eventNo = 0;
	// TODO
	return getEvent(day, eventNo);
}

function displayEvent(ev) {
	displayTimeUntilEvent(ev);
	displayEventDesc(ev);
}

function theFinalCountdown() {
	var ev = getNextEvent();
	displayEvent(ev);
}

var tick = setInterval(theFinalCountdown, 1000);
<commit_msg>countdown.js: events start at seconds=0<commit_after>// INITIALISE THE GODDAMN BELLS

var bells = [
// Monday
{
	"hours":   [ 8,  8,  9, 10, 11, 11, 13, 13, 13, 14],
	"minutes": [25, 35, 55, 10, 30, 45,  0, 20, 35, 50],
	"desc": ["House Group", 1, "Recess 1", 2, "Recess 2", 3, "Lunch 1", "Lunch 2", 4, "School Ends"]
}];

bells.push(bells[0]);  // Tuesday

// Wednesday
bells.push({
	"hours":   [ 8,  8,  9, 10, 10, 10, 12, 12, 12, 14],
	"minutes": [25, 35, 50, 25, 40, 55,  5, 25, 45, 30],
	"desc": ["House Group", 1, 2, "Recess", "Assembly", 3, "Lunch 1", "Lunch 2", "Sport", "School Ends"]
});

bells.push(bells[0]);  // Thursday

// Friday
bells.push({
	"hours":   [ 8,  8,  9, 10, 11, 11, 13, 13, 13, 14],
	"minutes": [25, 35, 55, 15, 35, 50, 10, 25, 40, 50],
	"desc": ["House Group", 1, "Recess 1", 2, "Recess 2", 3, "Lunch 1", "Lunch 2", 4, "School Ends"]
});

// HALP WHAT AM I DOING

function getEvent(day, eventNo) {
	return {
		"hour": bells[day].hours[eventNo],
		"minute": bells[day].minutes[eventNo],
		"desc": bells[day].desc[eventNo]
	};
}

function getSecondsUntilEvent(ev) {
	var evDate = new Date();
	evDate.setHours(ev.hour);
	evDate.setMinutes(ev.minute);
	evDate.setSeconds(0);
	return ((new Date() - evDate)/1000)|0;
}

function secsToHMS(secs) {
	var s = secs % 60;
	var m = (secs%3600 - s)/60;
	var h = secs / 3600;
	return [h|0, m|0, s];
}

function displayTimeUntilEvent(ev) {
	var hms = secsToHMS(getSecondsUntilEvent(ev));
	var h = hms[0], m = hms[1], s = hms[2];
	var clock = h + "h " + m + "min " + s + "s";

	document.getElementById("bell-countdown").textContent = clock;
}

function displayEventDesc(ev) {
	var desc = ev.desc;

	if (typeof desc === "number") {
		desc = "Period " + desc;
	}

	document.getElementById("bell-descript").textContent = desc;
}

function getNextEvent() {
	var day = new Date().getDay();
	if (day === 0 || day === 6) {
		// weekend, wrap around to Monday
		day = 0;
	} else {
		// weekday
		// my day index starts with Monday, silly me
		day -= 1;
		// TODO check for end of day
	}

	var dayEvents = bells[day], eventNo = 0;
	// TODO
	return getEvent(day, eventNo);
}

function displayEvent(ev) {
	displayTimeUntilEvent(ev);
	displayEventDesc(ev);
}

function theFinalCountdown() {
	var ev = getNextEvent();
	displayEvent(ev);
}

var tick = setInterval(theFinalCountdown, 1000);
<|endoftext|>


<commit_before>window.addEventListener('DOMContentLoaded', function () {

}, false);

var Countdown = (function () {

    //static private

    /**
     * Adding preceding zeros
     * @param val int
     * @param n int ciphers in number
     */
    var fullFormat = function(val, n){

        var len = ('' + val).length,
            zeros = n - len,
            tmp = '';

        //mozna zaczac od drugiej cyfry bo pierwsza zawsze bedzie
        for(var i = 0 ; i < zeros ; i++){
            tmp += '0';
        }

        return tmp + val;
    };
    //zmienne prywatne statyczne

    var Countdown = function (settings) {

        var /* setInterval id */
            timerId,
            /* days left */
            days,
            /* hours left */
            hours,
            /* minutes left */
            minutes,
            /* seconds left */
            seconds,
            /* miliseconds left */
            miliseconds,
            /* start date Object */
            start,
            /* end date Object */
            end,
            /* how the date will be showed on page */
            pattern = '%D days %HH hours %MM minutes %SS seconds %UU miliseconds',
            /* mode of countdown: 'date' or 'timer' */ /*REQUIRED!!*/
            mode,
            /* element where remaining time will be placed */ /*REQUIRED!!*/
            node,
            /* in seconds or miliseconds, depends on settings */
            timeLeft = null,
            /* current state */
            paused = false,
            /* iteration var */
            i = 0,
            /* required settings */
            required,
            /* time limit for timer mode */
            limit = 0,
            /* step of interval */
            precision = 10,
            /* speed ratio*/
            speed = 1,
            /* state */
            state = null,
            /* date before changing tab */
            before;

        /* initialization */
        required = settings.mode === 'date' ? ['mode', 'endDate', 'nodeId'] : ['mode', 'limit', 'nodeId'];

        /* check required fields */
        for (; i < required.length ; i++) {

            if (!(required[i] in settings)) {

                console.error(required[i] + ' is required');
                return false;
            }
        }

        mode = settings.mode;

        speed = settings.speed || 1;

        precision = ((settings.precision && settings.precision < 10) ? 10 : settings.precision) || 100;

        pattern = settings.pattern || pattern;

        limit = settings.limit;

        endDate = settings.endDate;

        node = document.getElementById(settings.nodeId);

        /* methods */
        var getId = function () {
                return timerId;
            },

            getLeftTime = function () {

                return {
                    days : days,
                    hours : hours,
                    minutes : minutes,
                    seconds : seconds,
                    miliseconds : miliseconds
                };

            },

            setSpeed = function(val){
                speed = val;
            },

            tick = function (timeStep) {

                var elapsedTime = new Date() - before,
                    tStep = typeof timeStep !== 'undefined' ? timeStep : precision;

                //fixing inactive tab time
                if(elapsedTime > tStep){
                    timeLeft -= elapsedTime * speed;
                } else {
                    timeLeft -= tStep * speed;
                }

                if(timeLeft <= 0){
                    end();
                    return;
                }

                count();
                show();

                settings.onTick && settings.onTick(ret);

                before = new Date();
            },

            /* start counting */
            start = function () {

                if (mode === 'date') {

                    startDate = settings.startDate || new Date();

                    timeLeft = endDate - startDate;

                } else if (mode === 'timer') {

                    if (timeLeft === null) {
                        timeLeft = limit;
                    }
                }
before = new Date();
                if(state === null){
                    tick(0);
                    state = 'started';
                }

                timerId = setInterval(function(){
                    tick(precision);
                }, precision);

                settings.onStart && settings.onStart(ret);

                if(timeLeft <= 0){
                    end();
                }
            },
            count = function(){

                miliseconds = (timeLeft%1000)/10 | 0;
                seconds = ((timeLeft/1000) | 0) % 60;
                minutes = ((timeLeft/60000) | 0) % 60;
                hours = ((timeLeft/3600000) | 0) % 24;
                days = (timeLeft/86400000) | 0;

            },
            show = function(){

                var content = pattern;

                content = content.replace(/%HH/g, fullFormat(hours, 2));
                content = content.replace(/%MM/g, fullFormat(minutes, 2));
                content = content.replace(/%SS/g, fullFormat(seconds, 2));
                content = content.replace(/%UU/g, fullFormat(miliseconds, 2));

                content = content.replace(/%D/g, days);
                content = content.replace(/%H/g, hours);
                content = content.replace(/%M/g, minutes);
                content = content.replace(/%S/g, seconds);
                content = content.replace(/%U/g, miliseconds);

                node.innerHTML = content;
            },

            /* stop counting */
            stop = function () {
                clearInterval(timerId);
                settings.onStop && settings.onStop(ret);
            },

            /* end */
            end = function(){
                days = hours = minutes = seconds = miliseconds = 0;
                show();
                clearInterval(timerId);
                settings.onEnd && settings.onEnd(ret);
            },
            /* reset exists only for timer mode, reset to original time */
            reset = function () {
                timeLeft = limit;
                count();
                show();
                settings.onReset && settings.onReset(ret);
            };

        /* public methods & properties */
        var ret = {
            getId : getId,
            getLeftTime : getLeftTime,
            start : start,
            stop : stop,
            setSpeed : setSpeed
        };

        if(mode === 'timer') {
            ret.reset = reset;
        }

        return ret;
    };

    return function (settings) {
        return new Countdown(settings);
    };

})();

/*

speed ?

There is no type checking

var settings = {
    mode : 'date',
    endDate : , //required
    pattern : , //default "{%DD days %HH hours %MM minutes %SS seconds}"
                //%DD %HH %MM %SS preceded by zero
    nodeId : , //html id node required
    onStop : , //user events
    onReset : ,
    onStart : ,
    onTick : ,
    onEnd :,
    step : //default 1s
};

do eventow mozna dodac jako pierwszy argument parametr this moze komus sie przyda

//zrobic druga wersje tego samego na prototype i nie dawac return tylko wszystkie pola musza byc publiczne lub wiekszosc i poprzedzic
//je _ zeby mozna bylo latwo odroznic je od siebie

//na odrebnej galezi

var settings = {
    mode : 'timer',
    limit : ,// ms
    pattern : ,//
    nodeId : //
    onStop : , //user events
    onReset : ,
    onStart : ,
    onTick : ,
    onEnd :
};

*/

//inactiveTab

//dodac prototype ? zastanowic sie nad funkcjami wspolnymi

/**
 * There are two modes:
 * - date, which counts from now to 'dateTo' date
 * - timer, which counts from 'start' time to 0
 */

//reset, przy statusie zatrzymany powinno ladnie odswiezyc widok<commit_msg>cleaning, new comments, little changes with names<commit_after>
/**
 * @author Mariusz Rymarczyk
 * @license MIT
 * @version 0.1
 */

'use strict';

var Countdown = (function () {

    /**
     * Adding preceding zeros
     * @param val int
     * @param n int ciphers in number
     */
    var fullFormat = function (val, n) {

        var len = ('' + val).length,
            zeros = n - len,
            tmp = '',
            i = 0;

        for ( ; i < zeros ; i++) {
            tmp += '0';
        }

        return tmp + val;
    };

    var Countdown = function (settings) {

        var /* setInterval id */
            timerId,
            /* days left */
            days,
            /* hours left */
            hours,
            /* minutes left */
            minutes,
            /* seconds left */
            seconds,
            /* miliseconds left */
            miliseconds,
            /* start date Object */
            startDate,
            /* end date Object */
            endDate,
            /* how the date will be showed on page */
            pattern = '%D days %HH hours %MM minutes %SS seconds %UU miliseconds',
            /* mode of countdown: 'date' or 'timer' */ /*REQUIRED!!*/
            mode,
            /* element where remaining time will be placed */ /*REQUIRED!!*/
            node,
            /* in seconds or miliseconds, depends on settings */
            timeLeft = null,
            /* iteration var */
            i = 0,
            /* required settings */
            required,
            /* time limit for timer mode */
            limit = 0,
            /* step of interval */
            refreshRate = 10,
            /* speed ratio*/
            speed = 1,
            /* state */
            state = 'init',
            /* inactiveTab */
            inactiveTab,
            /* date before changing tab */
            before;

        required = settings.mode === 'date' ? ['mode', 'endDate', 'nodeId'] : ['mode', 'limit', 'nodeId'];

        /* check required fields */
        for ( ; i < required.length ; i++) {

            if (!(required[i] in settings)) {

                console.error(required[i] + ' is required');
                return false;
            }
        }

        /* initialization */

        mode = settings.mode;

        inactiveTab = settings.inactiveTab || true;

        speed = settings.speed || 1;

        refreshRate = ((settings.refreshRate && settings.refreshRate < 10) ? 10 : settings.refreshRate) || 100;

        pattern = settings.pattern || pattern;

        limit = settings.limit;

        endDate = settings.endDate;

        node = document.getElementById(settings.nodeId);

        /* methods */
        var getTimerId = function () {
                return timerId;
            },

            getTime = function () {

                return {
                    days : days,
                    hours : hours,
                    minutes : minutes,
                    seconds : seconds,
                    miliseconds : miliseconds
                };

            },

            getState = function () {
                return state;
            },

            setSpeed = function (val) {
                speed = val;
            },

            /**
             * Executed by setInterval
             * @param timeStep number not required
             */
            tick = function (timeStep) {

                var elapsedTime = +new Date() - before,
                    tStep = typeof timeStep !== 'undefined' ? timeStep : refreshRate;

                //fixing inactive tab time
                if (inactiveTab && (elapsedTime > tStep + 10) ) {
                    timeLeft -= elapsedTime * speed;
                } else {
                    timeLeft -= tStep * speed;
                }

                if(timeLeft <= 0) {
                    end();
                    return;
                }

                count();
                show();

                settings.onTick && settings.onTick(ret);

                before = new Date();
            },

            /* start counting */
            start = function () {

                if (mode === 'date') {

                    startDate = settings.startDate || new Date();

                    timeLeft = endDate - startDate;

                } else if (mode === 'timer') {

                    if (timeLeft === null) {
                        timeLeft = limit;
                    }
                }

                before = new Date();

                if(state === 'init'){
                    tick(0);
                }

                state = 'started';

                timerId = setInterval(function(){
                    tick(refreshRate);
                }, refreshRate);

                settings.onStart && settings.onStart(ret);

                if(timeLeft <= 0){
                    end();
                }
            },

            count = function () {

                miliseconds = (timeLeft%1000)/10 | 0;
                seconds = ((timeLeft/1000) | 0) % 60;
                minutes = ((timeLeft/60000) | 0) % 60;
                hours = ((timeLeft/3600000) | 0) % 24;
                days = (timeLeft/86400000) | 0;

            },

            show = function () {

                var content = pattern;

                content = content.replace(/%HH/g, fullFormat(hours, 2));
                content = content.replace(/%MM/g, fullFormat(minutes, 2));
                content = content.replace(/%SS/g, fullFormat(seconds, 2));
                content = content.replace(/%UU/g, fullFormat(miliseconds, 2));

                content = content.replace(/%D/g, days);
                content = content.replace(/%H/g, hours);
                content = content.replace(/%M/g, minutes);
                content = content.replace(/%S/g, seconds);
                content = content.replace(/%U/g, miliseconds);

                node.innerHTML = content;
            },

            /* stop counting */
            stop = function () {
                clearInterval(timerId);
                state = 'stopped';
                settings.onStop && settings.onStop(ret);
            },

            /* end */
            end = function () {
                days = hours = minutes = seconds = miliseconds = 0;
                show();
                clearInterval(timerId);
                state = 'ended';
                settings.onEnd && settings.onEnd(ret);
            },

            /* reset exists only for timer mode, reset to original time */
            reset = function () {
                state = null;
                timeLeft = limit;
                count();
                show();
                stop();
                settings.onReset && settings.onReset(ret);
            };

        /* public methods & properties */
        var ret = {
            getTimerId : getTimerId,
            getTime : getTime,
            getState : getState,
            start : start,
            stop : stop,
            setSpeed : setSpeed
        };

        if(mode === 'timer') {
            ret.reset = reset;
        }

        return ret;
    };

    return function (settings) {
        return new Countdown(settings);
    };

})();<|endoftext|>


<commit_before>'use strict'

const got = require('got')
const proj4 = require('proj4')
const epsg = require('epsg')
const kinks = require('turf-kinks')
const range = require('lodash.range')
const _ = require('lodash')
const pad = require('lodash.padstart')
const moment = require('moment')
const through2 = require('through2')
const clone = require('lodash.clonedeep')
const fromPairs = require('lodash.frompairs')
const get = require('lodash.get')
const satlib = require('@sat-utils/api-lib')

const collection = {
  id: 'sentinel-2-l1c',
  title: 'Sentinel 2 L1C',
  description: 'Sentinel-2a and Sentinel-2b imagery',
  keywords: ['sentinel', 'earth observation', 'esa'],
  version: '0.1.0',
  stac_version: satlib.api.stac_version,
  extent: {
    spatial: [-180, -90, 180, 90],
    temporal: ['2013-06-01', null]
  },
  provider: [{
    name: 'ESA', roles: ['producer'], href: 'https://sentinel.esa.int/web/sentinel/home'
  }, {
    name: 'Sinergise', roles: ['processor'], href: 'http://sentinel-pds.s3-website.eu-central-1.amazonaws.com/'
  }, {
    name: 'AWS', roles: ['host'], href: 'https://aws.amazon.com/blogs/publicsector/complete-sentinel-2-archives-freely-available-to-users/'
  }, {
    name: 'Development Seed', roles: ['processor'], url: 'https://github.com/sat-utils/sat-api'
  }],
  license: 'proprietary',
  properties: {
    collection: 'sentinel-2-l1c',
    'eo:gsd': 10,
    'eo:instrument': 'MSI',
    'eo:constellation': 'sentinel-2',
    'eo:off_nadir': 0,
    'eo:bands': [
      {
        id: 'B01',
        common_name: 'coastal',
        gsd: 60.0,
        center_wavelength: 0.4439,
        full_width_half_max: 0.027
      }, {
        id: 'B02',
        common_name: 'blue',
        gsd: 10.0,
        center_wavelength: 0.4966,
        full_width_half_max: 0.098
      }, {
        name: 'B03',
        common_name: 'green',
        gsd: 10.0,
        center_wavelength: 0.56,
        full_width_half_max: 0.045
      }, {
        id: 'B04',
        common_name: 'red',
        gsd: 10.0,
        center_wavelength: 0.6645,
        full_width_half_max: 0.038
      }, {
        id: 'B05',
        gsd: 20.0,
        center_wavelength: 0.7039,
        full_width_half_max: 0.019
      }, {
        id: 'B06',
        gsd: 20.0,
        center_wavelength: 0.7402,
        full_width_half_max: 0.018
      }, {
        id: 'B07',
        gsd: 20.0,
        center_wavelength: 0.7825,
        full_width_half_max: 0.028
      }, {
        id: 'B08',
        common_name: 'nir',
        gsd: 10.0,
        center_wavelength: 0.8351,
        full_width_half_max: 0.145
      }, {
        id: 'B8A',
        gsd: 20.0,
        center_wavelength: 0.8648,
        full_width_half_max: 0.033
      }, {
        id: 'B09',
        gsd: 60.0,
        center_wavelength: 0.945,
        full_width_half_max: 0.026
      }, {
        id: 'B10',
        common_name: 'cirrus',
        gsd: 60.0,
        center_wavelength: 1.3735,
        full_width_half_max: 0.075
      }, {
        id: 'B11',
        common_name: 'swir16',
        gsd: 20.0,
        center_wavelength: 1.6137,
        full_width_half_max: 0.143
      }, {
        id: 'B12',
        common_name: 'swir22',
        gsd: 20.0,
        center_wavelength: 2.22024,
        full_width_half_max: 0.242
      }
    ]
  },
  'assets': {
    'B01': { type: 'image/jp2', 'eo:bands': [0], title: 'Band 1 (coastal)' },
    'B02': { type: 'image/jp2', 'eo:bands': [1], title: 'Band 2 (blue)' },
    'B03': { type: 'image/jp2', 'eo:bands': [2], title: 'Band 3 (green)' },
    'B04': { type: 'image/jp2', 'eo:bands': [3], title: 'Band 4 (red)' },
    'B05': { type: 'image/jp2', 'eo:bands': [4], title: 'Band 5' },
    'B06': { type: 'image/jp2', 'eo:bands': [5], title: 'Band 6' },
    'B07': { type: 'image/jp2', 'eo:bands': [6], title: 'Band 7' },
    'B08': { type: 'image/jp2', 'eo:bands': [7], title: 'Band 8 (nir)' },
    'B8A': { type: 'image/jp2', 'eo:bands': [8], title: 'Band 8A' },
    'B09': { type: 'image/jp2', 'eo:bands': [9], title: 'Band 9' },
    'B10': { type: 'image/jp2', 'eo:bands': [10], title: 'Band 10 (cirrus)' },
    'B11': { type: 'image/jp2', 'eo:bands': [11], title: 'Band 11 (swir16)' },
    'B12': { type: 'image/jp2', 'eo:bands': [12], title: 'Band 12 (swir22)' }
  },
  links: [
    { rel: 'license', href: 'https://sentinel.esa.int/documents/247904/690755/Sentinel_Data_Legal_Notice' }
  ]
}

const awsBaseUrl = 'https://sentinel-s2-l1c.s3.amazonaws.com'

function parseMgrs(mgrs) {
  const vals = {
    utm_zone: parseInt(mgrs),
    latitude_band: mgrs.slice(mgrs.length - 3, mgrs.length - 2),
    grid_square: mgrs.slice(mgrs.length - 2, mgrs.length)
  }
  let alphaVal = vals.latitude_band.toLowerCase().charCodeAt(0)
  alphaVal = (alphaVal - 97) + 1
  if (alphaVal > 13) {
    // northern hemisphere
    vals.epsg = `326${vals.utm_zone}`
  } else {
    vals.epsg = `327${vals.utm_zone}`
  }
  return vals
}

function getTilePath(date, parsedMgrs) {
  return [
    'tiles',
    parsedMgrs.utm_zone,
    parsedMgrs.latitude_band,
    parsedMgrs.grid_square,
    date.format('YYYY'),
    date.format('M'),
    date.format('D'),
    0
  ].join('/')
}

function getTileUrl(tilePath) {
  return `${awsBaseUrl}/${tilePath}`
}

function reproject(inputGeojson) {
  let geojson = clone(inputGeojson)
  const crs = geojson.crs.properties.name.replace(
    'urn:ogc:def:crs:', ''
  ).replace('8.8.1:', '')
  const from = epsg[crs]
  //const to = proj4.default.defs('EPSG:4326', '+proj=longlat +datum=WGS84 +no_defs');
  const to = '+proj=longlat +datum=WGS84 +over'

  //console.log(`${geojson.type} ${JSON.stringify(geojson)}`)
  if (geojson.type === 'Polygon') {
    geojson = {
      type: 'Polygon',
      coordinates: [geojson.coordinates[0].map((c) => proj4.default(from, to, c))]
    }
  } else if (geojson.type === 'Point') {
    geojson = {
      type: 'Point',
      coordinates: proj4.default(from, to, geojson.coordinates)
    }
  } else {
    throw Error('cannot process non Point or Polygon geometries')
  }
  if (kinks(geojson).features.length > 0) {
    throw Error('self-intersecting polygon')
  }
  return geojson
}

function _transform(data, encoding, next) {
  const dt = moment(data.SENSING_TIME)
  const mgrs = data.MGRS_TILE
  const parsedMgrs = parseMgrs(mgrs)
  const tilePath = getTilePath(dt, parsedMgrs)
  const tileBaseUrl = getTileUrl(tilePath)
  const rodaBaseUrl = tileBaseUrl.replace('sentinel-s2-l1c.s3.amazonaws.com', 'roda.sentinel-hub.com/sentinel-s2-l1c')
  const bands = range(1, 13).map((i) => pad(i, 3, 'B0'))
  bands.push('B8A')
  const tileInfo = `${rodaBaseUrl}/tileInfo.json`
  got(tileInfo, { json: true }).then((info) => {
    const sat = info.body.productName.slice(0, 3)
    const satname = `Sentinel-2${sat.slice(-1)}`
    let val
    const files = fromPairs(bands.map((b) => {
      val = { href: `${tileBaseUrl}/${b}.jp2` }
      return [b, val]
    }))
    files.thumbnail = { href: `${rodaBaseUrl}/preview.jpg` }
    files.tki = { href: `${tileBaseUrl}/TKI.jp2`, description: 'True Color Image' }
    files.metadata = { href: `${rodaBaseUrl}/metadata.xml` }
    // reproject to EPSG:4326
    const geom = reproject(info.body.tileDataGeometry)
    const lons = geom.coordinates[0].map((pt) => pt[0])
    const lats = geom.coordinates[0].map((pt) => pt[1])
    const record = {
      id: data.GRANULE_ID,
      bbox: [Math.min(...lons), Math.min(...lats), Math.max(...lons), Math.max(...lats)],
      geometry: geom,
      properties: {
        collection: 'sentinel-2-l1c',
        datetime: dt.toISOString(),
        'eo:platform': satname,
        'eo:cloud_cover': parseInt(data.CLOUD_COVER),
        'eo:epsg': parsedMgrs.epsg,
        'sentinel:product_id': data.PRODUCT_ID
        //'sentinel:tile_geometry': reproject(info.body.tileGeometry),
        //'sentinel:tile_origin': reproject(info.body.tileOrigin)
      },
      assets: _.merge({}, files, collection.assets),
      links: []
    }
    this.push(record)
    next()
  }).catch((e) => {
    // don't want to break stream, just log and continue
    console.log(`error processing ${data.GRANULE_ID}: ${e}`)
    next()
  })
}

function handler(event, context, cb) {
  console.log(JSON.stringify(event))
  const bucket = get(event, 'bucket')
  const key = get(event, 'key')
  const currentFileNum = get(event, 'currentFileNum', 0)
  const lastFileNum = get(event, 'lastFileNum', 0)
  const arn = get(event, 'arn', null)
  const retries = get(event, 'retries', 0)
  const transform = through2({ objectMode: true }, _transform)

  // add collection
  satlib.es.saveCollection(collection)
    .then(() => {
      // ensure mapping exists
      satlib.es.prepare('items').then(() => {
        // add items from files
        satlib.ingestcsv.processFiles({
          bucket, key, transform, cb, currentFileNum, lastFileNum, arn, retries
        })
      })
    })
    .catch((e) => console.log(e))
}

module.exports.handler = handler
<commit_msg>change provider to providers in sentinel collection<commit_after>'use strict'

const got = require('got')
const proj4 = require('proj4')
const epsg = require('epsg')
const kinks = require('turf-kinks')
const range = require('lodash.range')
const _ = require('lodash')
const pad = require('lodash.padstart')
const moment = require('moment')
const through2 = require('through2')
const clone = require('lodash.clonedeep')
const fromPairs = require('lodash.frompairs')
const get = require('lodash.get')
const satlib = require('@sat-utils/api-lib')

const collection = {
  id: 'sentinel-2-l1c',
  title: 'Sentinel 2 L1C',
  description: 'Sentinel-2a and Sentinel-2b imagery',
  keywords: ['sentinel', 'earth observation', 'esa'],
  version: '0.1.0',
  stac_version: satlib.api.stac_version,
  extent: {
    spatial: [-180, -90, 180, 90],
    temporal: ['2013-06-01', null]
  },
  providers: [{
    name: 'ESA', roles: ['producer'], href: 'https://sentinel.esa.int/web/sentinel/home'
  }, {
    name: 'Sinergise', roles: ['processor'], href: 'http://sentinel-pds.s3-website.eu-central-1.amazonaws.com/'
  }, {
    name: 'AWS', roles: ['host'], href: 'https://aws.amazon.com/blogs/publicsector/complete-sentinel-2-archives-freely-available-to-users/'
  }, {
    name: 'Development Seed', roles: ['processor'], url: 'https://github.com/sat-utils/sat-api'
  }],
  license: 'proprietary',
  properties: {
    collection: 'sentinel-2-l1c',
    'eo:gsd': 10,
    'eo:instrument': 'MSI',
    'eo:constellation': 'sentinel-2',
    'eo:off_nadir': 0,
    'eo:bands': [
      {
        id: 'B01',
        common_name: 'coastal',
        gsd: 60.0,
        center_wavelength: 0.4439,
        full_width_half_max: 0.027
      }, {
        id: 'B02',
        common_name: 'blue',
        gsd: 10.0,
        center_wavelength: 0.4966,
        full_width_half_max: 0.098
      }, {
        name: 'B03',
        common_name: 'green',
        gsd: 10.0,
        center_wavelength: 0.56,
        full_width_half_max: 0.045
      }, {
        id: 'B04',
        common_name: 'red',
        gsd: 10.0,
        center_wavelength: 0.6645,
        full_width_half_max: 0.038
      }, {
        id: 'B05',
        gsd: 20.0,
        center_wavelength: 0.7039,
        full_width_half_max: 0.019
      }, {
        id: 'B06',
        gsd: 20.0,
        center_wavelength: 0.7402,
        full_width_half_max: 0.018
      }, {
        id: 'B07',
        gsd: 20.0,
        center_wavelength: 0.7825,
        full_width_half_max: 0.028
      }, {
        id: 'B08',
        common_name: 'nir',
        gsd: 10.0,
        center_wavelength: 0.8351,
        full_width_half_max: 0.145
      }, {
        id: 'B8A',
        gsd: 20.0,
        center_wavelength: 0.8648,
        full_width_half_max: 0.033
      }, {
        id: 'B09',
        gsd: 60.0,
        center_wavelength: 0.945,
        full_width_half_max: 0.026
      }, {
        id: 'B10',
        common_name: 'cirrus',
        gsd: 60.0,
        center_wavelength: 1.3735,
        full_width_half_max: 0.075
      }, {
        id: 'B11',
        common_name: 'swir16',
        gsd: 20.0,
        center_wavelength: 1.6137,
        full_width_half_max: 0.143
      }, {
        id: 'B12',
        common_name: 'swir22',
        gsd: 20.0,
        center_wavelength: 2.22024,
        full_width_half_max: 0.242
      }
    ]
  },
  'assets': {
    'B01': { type: 'image/jp2', 'eo:bands': [0], title: 'Band 1 (coastal)' },
    'B02': { type: 'image/jp2', 'eo:bands': [1], title: 'Band 2 (blue)' },
    'B03': { type: 'image/jp2', 'eo:bands': [2], title: 'Band 3 (green)' },
    'B04': { type: 'image/jp2', 'eo:bands': [3], title: 'Band 4 (red)' },
    'B05': { type: 'image/jp2', 'eo:bands': [4], title: 'Band 5' },
    'B06': { type: 'image/jp2', 'eo:bands': [5], title: 'Band 6' },
    'B07': { type: 'image/jp2', 'eo:bands': [6], title: 'Band 7' },
    'B08': { type: 'image/jp2', 'eo:bands': [7], title: 'Band 8 (nir)' },
    'B8A': { type: 'image/jp2', 'eo:bands': [8], title: 'Band 8A' },
    'B09': { type: 'image/jp2', 'eo:bands': [9], title: 'Band 9' },
    'B10': { type: 'image/jp2', 'eo:bands': [10], title: 'Band 10 (cirrus)' },
    'B11': { type: 'image/jp2', 'eo:bands': [11], title: 'Band 11 (swir16)' },
    'B12': { type: 'image/jp2', 'eo:bands': [12], title: 'Band 12 (swir22)' }
  },
  links: [
    { rel: 'license', href: 'https://sentinel.esa.int/documents/247904/690755/Sentinel_Data_Legal_Notice' }
  ]
}

const awsBaseUrl = 'https://sentinel-s2-l1c.s3.amazonaws.com'

function parseMgrs(mgrs) {
  const vals = {
    utm_zone: parseInt(mgrs),
    latitude_band: mgrs.slice(mgrs.length - 3, mgrs.length - 2),
    grid_square: mgrs.slice(mgrs.length - 2, mgrs.length)
  }
  let alphaVal = vals.latitude_band.toLowerCase().charCodeAt(0)
  alphaVal = (alphaVal - 97) + 1
  if (alphaVal > 13) {
    // northern hemisphere
    vals.epsg = `326${vals.utm_zone}`
  } else {
    vals.epsg = `327${vals.utm_zone}`
  }
  return vals
}

function getTilePath(date, parsedMgrs) {
  return [
    'tiles',
    parsedMgrs.utm_zone,
    parsedMgrs.latitude_band,
    parsedMgrs.grid_square,
    date.format('YYYY'),
    date.format('M'),
    date.format('D'),
    0
  ].join('/')
}

function getTileUrl(tilePath) {
  return `${awsBaseUrl}/${tilePath}`
}

function reproject(inputGeojson) {
  let geojson = clone(inputGeojson)
  const crs = geojson.crs.properties.name.replace(
    'urn:ogc:def:crs:', ''
  ).replace('8.8.1:', '')
  const from = epsg[crs]
  //const to = proj4.default.defs('EPSG:4326', '+proj=longlat +datum=WGS84 +no_defs');
  const to = '+proj=longlat +datum=WGS84 +over'

  //console.log(`${geojson.type} ${JSON.stringify(geojson)}`)
  if (geojson.type === 'Polygon') {
    geojson = {
      type: 'Polygon',
      coordinates: [geojson.coordinates[0].map((c) => proj4.default(from, to, c))]
    }
  } else if (geojson.type === 'Point') {
    geojson = {
      type: 'Point',
      coordinates: proj4.default(from, to, geojson.coordinates)
    }
  } else {
    throw Error('cannot process non Point or Polygon geometries')
  }
  if (kinks(geojson).features.length > 0) {
    throw Error('self-intersecting polygon')
  }
  return geojson
}

function _transform(data, encoding, next) {
  const dt = moment(data.SENSING_TIME)
  const mgrs = data.MGRS_TILE
  const parsedMgrs = parseMgrs(mgrs)
  const tilePath = getTilePath(dt, parsedMgrs)
  const tileBaseUrl = getTileUrl(tilePath)
  const rodaBaseUrl = tileBaseUrl.replace('sentinel-s2-l1c.s3.amazonaws.com', 'roda.sentinel-hub.com/sentinel-s2-l1c')
  const bands = range(1, 13).map((i) => pad(i, 3, 'B0'))
  bands.push('B8A')
  const tileInfo = `${rodaBaseUrl}/tileInfo.json`
  got(tileInfo, { json: true }).then((info) => {
    const sat = info.body.productName.slice(0, 3)
    const satname = `Sentinel-2${sat.slice(-1)}`
    let val
    const files = fromPairs(bands.map((b) => {
      val = { href: `${tileBaseUrl}/${b}.jp2` }
      return [b, val]
    }))
    files.thumbnail = { href: `${rodaBaseUrl}/preview.jpg` }
    files.tki = { href: `${tileBaseUrl}/TKI.jp2`, description: 'True Color Image' }
    files.metadata = { href: `${rodaBaseUrl}/metadata.xml` }
    // reproject to EPSG:4326
    const geom = reproject(info.body.tileDataGeometry)
    const lons = geom.coordinates[0].map((pt) => pt[0])
    const lats = geom.coordinates[0].map((pt) => pt[1])
    const record = {
      id: data.GRANULE_ID,
      bbox: [Math.min(...lons), Math.min(...lats), Math.max(...lons), Math.max(...lats)],
      geometry: geom,
      properties: {
        collection: 'sentinel-2-l1c',
        datetime: dt.toISOString(),
        'eo:platform': satname,
        'eo:cloud_cover': parseInt(data.CLOUD_COVER),
        'eo:epsg': parsedMgrs.epsg,
        'sentinel:product_id': data.PRODUCT_ID
        //'sentinel:tile_geometry': reproject(info.body.tileGeometry),
        //'sentinel:tile_origin': reproject(info.body.tileOrigin)
      },
      assets: _.merge({}, files, collection.assets),
      links: []
    }
    this.push(record)
    next()
  }).catch((e) => {
    // don't want to break stream, just log and continue
    console.log(`error processing ${data.GRANULE_ID}: ${e}`)
    next()
  })
}

function handler(event, context, cb) {
  console.log(JSON.stringify(event))
  const bucket = get(event, 'bucket')
  const key = get(event, 'key')
  const currentFileNum = get(event, 'currentFileNum', 0)
  const lastFileNum = get(event, 'lastFileNum', 0)
  const arn = get(event, 'arn', null)
  const retries = get(event, 'retries', 0)
  const transform = through2({ objectMode: true }, _transform)

  // add collection
  satlib.es.saveCollection(collection)
    .then(() => {
      // ensure mapping exists
      satlib.es.prepare('items').then(() => {
        // add items from files
        satlib.ingestcsv.processFiles({
          bucket, key, transform, cb, currentFileNum, lastFileNum, arn, retries
        })
      })
    })
    .catch((e) => console.log(e))
}

module.exports.handler = handler
<|endoftext|>


<commit_before>/*
  Indexer deals with indexing documents. It's public API consists of:

    update: indexings changes from a git repo

    search: get documents back out of the index

*/

const {
  Repository,
  Reference,
  Branch,
  Commit
} = require('nodegit');

const { safeEntryByName } = require('./mutable-tree');

const logger = require('heimdalljs-logger');
const makeClient = require('./elastic-client');
const { isEqual } = require('lodash');
const BulkOps = require('./bulk-ops');

module.exports = class Indexer {
  constructor({ elasticsearch, repoPath }) {
    this.es = makeClient(elasticsearch);
    this.repoPath = repoPath;
    this.repo = null;
    this.log = logger('indexer');
  }

  /*
    This indexes the content of the repo, based on the following general rules:
    - each `.json` file in git is a document to be indexed.
    - the id is the name of the file without the extension
    - the type is the name of the directory in which the file appears
      (just the final part of the path)
    - a special mappings.json file at the top of the repo can make
      declarations about the schema.

    `realTime` determines whether this operation will block until
    searches will reflect our changes. It's more expensive, but very
    helpful particularly in automated test scenarios.

  */
  async update(realTime=false) {
    await this._ensureRepo();
    let branches = await this._branches();
    await Promise.all(branches.map(branch => this._updateBranch(branch, realTime)));
  }

  async _ensureRepo() {
    if (!this.repo) {
      this.repo = await Repository.open(this.repoPath);
    }
  }

  async _esMapping(branch) {
    let mapping = await this.es.indices.getMapping({ index: branch, ignore: [404] });
    if (mapping.status === 404) {
      return null;
    } else {
      let index = Object.keys(mapping)[0];
      return mapping[index].mappings;
    }
  }

  async _commitAtBranch(branchName) {
    let branch = await Branch.lookup(this.repo, branchName, Branch.BRANCH.LOCAL);
    return Commit.lookup(this.repo, branch.target());
  }

  async _gitMapping(tree) {
    let entry = await safeEntryByName(tree, 'mapping.json');
    if (entry && entry.isBlob()) {
      let buffer = (await entry.getBlob()).content();
      let mapping = JSON.parse(buffer);
      return mapping;
    } else {
      return {};
    }
  }

  _tempIndexName(branch) {
    return `${branch}_${Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)}`;
  }

  _stableMapping(have, want) {
    if (!have) { return false; }
    // We only check types in "want". Extraneous types in "have" are OK.
    return Object.keys(want).every(
      type => isEqual(have[type], want[type])
    );
  }

  async _updateBranch(branch, realTime) {
    let commit = await this._commitAtBranch(branch);
    let tree = await commit.getTree();
    let haveMapping = await this._esMapping(branch);
    let wantMapping = await this._gitMapping(tree);
    if (this._stableMapping(haveMapping, wantMapping)) {
      this.log.info('%s: mapping already OK', branch);
    } else {
      this.log.info('%s: mapping needs update', branch);
      this.log.debug('%j', { haveMapping, wantMapping });
      let tmpIndex = this._tempIndexName(branch);
      await this.es.indices.create({
        index: tmpIndex,
        body: {
          mappings: wantMapping
        }
      });
      await this._reindex(tmpIndex, branch);
    }
    await this._updateState(branch, tree, commit, realTime);
  }

  async _updateState(branch, tree, commit, realTime) {
    let originalTree;
    let meta = await this.es.getSource({ index: branch, type: 'meta', id: 'indexer', ignore: [404] });
    if (meta) {
      let oldCommit = await Commit.lookup(this.repo, meta.commit);
      originalTree = await oldCommit.getTree();
    }

    let bulkOps = new BulkOps(this.es, { realTime });

    await this._indexTree(branch, originalTree, tree, bulkOps);

    await bulkOps.add({
      index: {
        _index: branch,
        _type: 'meta',
        _id: 'indexer',
      }
    }, {
      commit: commit.id().tostrS()
    });

    await bulkOps.flush();
  }

  async _indexTree(branch, oldTree, newTree, bulkOps) {
    let seen = new Map();
    if (newTree) {
      for (let newEntry of newTree.entries()) {
        let name = newEntry.name();
        let oldEntry;
        seen.set(name, true);
        if (oldTree) {
          oldEntry = safeEntryByName(oldTree, name);
          if (oldEntry && oldEntry.id().equal(newEntry.id())) {
            // We can prune whole subtrees when we find an identical
            // entry. Which is kinda the point of Git's data
            // structure in the first place.
            continue;
          }
        }
        if (newEntry.isTree()) {
          await this._indexTree(
            branch,
            oldEntry && oldEntry.isTree() ? (await oldEntry.getTree()) : null,
            await newEntry.getTree(),
            bulkOps
          );
        } else {
          let { type, id } = identify(newEntry);
          await bulkOps.add({
            index: {
              _index: branch,
              _type: type,
              _id: id,
            }
          }, (await newEntry.getBlob()).content().toString('utf8'));
        }
      }
    }
    if (oldTree) {
      for (let oldEntry of oldTree.entries()) {
        let name = oldEntry.name();
        if (!seen.get(name)) {
          if (oldEntry.isTree()) {
            await this._indexTree(branch, await oldEntry.getTree(), null, bulkOps);
          } else {
            let { type, id } = identify(oldEntry);
            await bulkOps.add({
              delete: {
                _index: branch,
                _type: type,
                _id: id
              }
            });
          }
        }
      }
    }
  }

  // 1. Index the branch into newIndex.
  // 2. Update the canonical elasticsearch alias for the branch to point at newIndex
  // 3. Delete any old index that we just replaced.
  async _reindex(newIndex, branch) {
    let alias = await this.es.indices.getAlias({ name: branch, ignore: [404] });
    if (alias.status === 404) {
      this.log.info('%s is new, nothing to reindex', branch);
    } else {
      this.log.info('reindexing %s', branch);
      await this.es.reindex({
        body: {
          source: { index: branch },
          dest: { index: newIndex }
        }
      });

    }
    await this.es.indices.updateAliases({
      body: {
        actions: [
          { remove: { index: '_all', alias: branch } },
          { add: { index: newIndex, alias: branch } }
        ]
      }
    });

  }

  async _branches() {
    // nodegit docs show a Branch.iteratorNew method that would be
    // more appropriate than this, but as far as I can tell it is not
    // fully implemented
    return (await Reference.list(this.repo)).map(entry => entry.replace(/^refs\/heads\//, ''));
  }

};

function identify(entry) {
  let parts = entry.path().split('/');
  let type = parts[parts.length - 2] || 'tops';
  let filename = parts[parts.length - 1];
  let id = filename.replace(/\.json$/, '');
  return { type, id };
}
<commit_msg>refactor indexer for readability<commit_after>/*
  Indexer deals with indexing documents. It's public API consists of:

    update: indexings changes from a git repo

    search: get documents back out of the index

*/

const {
  Repository,
  Reference,
  Branch,
  Commit
} = require('nodegit');

const { safeEntryByName } = require('./mutable-tree');

const logger = require('heimdalljs-logger');
const makeClient = require('./elastic-client');
const { isEqual } = require('lodash');
const BulkOps = require('./bulk-ops');

module.exports = class Indexer {
  constructor({ elasticsearch, repoPath }) {
    this.es = makeClient(elasticsearch);
    this.repoPath = repoPath;
    this.repo = null;
    this.log = logger('indexer');
  }

  /*
    This indexes the content of the repo, based on the following general rules:
    - each `.json` file in git is a document to be indexed.
    - the id is the name of the file without the extension
    - the type is the name of the directory in which the file appears
      (just the final part of the path)
    - a special mappings.json file at the top of the repo can make
      declarations about the schema.

    `realTime` determines whether this operation will block until
    searches will reflect our changes. It's more expensive, but very
    helpful particularly in automated test scenarios.

  */
  async update(realTime=false) {
    await this._ensureRepo();
    let branches = await this._branches();
    await Promise.all(branches.map(branch => this._updateBranch(branch, realTime)));
  }

  async _ensureRepo() {
    if (!this.repo) {
      this.repo = await Repository.open(this.repoPath);
    }
  }

  async _esMapping(branch) {
    let mapping = await this.es.indices.getMapping({ index: branch, ignore: [404] });
    if (mapping.status === 404) {
      return null;
    } else {
      let index = Object.keys(mapping)[0];
      return mapping[index].mappings;
    }
  }

  async _commitAtBranch(branchName) {
    let branch = await Branch.lookup(this.repo, branchName, Branch.BRANCH.LOCAL);
    return Commit.lookup(this.repo, branch.target());
  }

  async _gitMapping(tree) {
    let entry = await safeEntryByName(tree, 'mapping.json');
    if (entry && entry.isBlob()) {
      let buffer = (await entry.getBlob()).content();
      let mapping = JSON.parse(buffer);
      return mapping;
    } else {
      return {};
    }
  }

  _tempIndexName(branch) {
    return `${branch}_${Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)}`;
  }

  _stableMapping(have, want) {
    if (!have) { return false; }
    // We only check types in "want". Extraneous types in "have" are OK.
    return Object.keys(want).every(
      type => isEqual(have[type], want[type])
    );
  }

  async _updateBranch(branch, realTime) {
    let commit = await this._commitAtBranch(branch);
    let tree = await commit.getTree();
    let haveMapping = await this._esMapping(branch);
    let wantMapping = await this._gitMapping(tree);
    if (this._stableMapping(haveMapping, wantMapping)) {
      this.log.info('%s: mapping already OK', branch);
    } else {
      this.log.info('%s: mapping needs update', branch);
      this.log.debug('%j', { haveMapping, wantMapping });
      let tmpIndex = this._tempIndexName(branch);
      await this.es.indices.create({
        index: tmpIndex,
        body: {
          mappings: wantMapping
        }
      });
      await this._reindex(tmpIndex, branch);
    }
    await this._updateState(branch, tree, commit, realTime);
  }

  async _updateState(branch, tree, commit, realTime) {
    let originalTree;
    let meta = await this.es.getSource({ index: branch, type: 'meta', id: 'indexer', ignore: [404] });
    if (meta) {
      let oldCommit = await Commit.lookup(this.repo, meta.commit);
      originalTree = await oldCommit.getTree();
    }

    let bulkOps = new BulkOps(this.es, { realTime });

    await this._indexTree(branch, originalTree, tree, bulkOps);

    await bulkOps.add({
      index: {
        _index: branch,
        _type: 'meta',
        _id: 'indexer',
      }
    }, {
      commit: commit.id().tostrS()
    });

    await bulkOps.flush();
  }

  async _indexTree(branch, oldTree, newTree, bulkOps) {
    let seen = new Map();
    if (newTree) {
      for (let newEntry of newTree.entries()) {
        let name = newEntry.name();
        seen.set(name, true);
        await this._indexEntry(branch, name, oldTree, newEntry, bulkOps);
      }
    }
    if (oldTree) {
      for (let oldEntry of oldTree.entries()) {
        let name = oldEntry.name();
        if (!seen.get(name)) {
          await this._deleteEntry(branch, oldEntry, bulkOps);
        }
      }
    }
  }

  async _indexEntry(branch, name, oldTree, newEntry, bulkOps) {
    let oldEntry;
    if (oldTree) {
      oldEntry = safeEntryByName(oldTree, name);
      if (oldEntry && oldEntry.id().equal(newEntry.id())) {
        // We can prune whole subtrees when we find an identical
        // entry. Which is kinda the point of Git's data
        // structure in the first place.
        return;
      }
    }
    if (newEntry.isTree()) {
      await this._indexTree(
        branch,
        oldEntry && oldEntry.isTree() ? (await oldEntry.getTree()) : null,
        await newEntry.getTree(),
        bulkOps
      );
    } else {
      let { type, id } = identify(newEntry);
      await bulkOps.add({
        index: {
          _index: branch,
          _type: type,
          _id: id,
        }
      }, (await newEntry.getBlob()).content().toString('utf8'));
    }
  }

  async _deleteEntry(branch, oldEntry, bulkOps) {
    if (oldEntry.isTree()) {
      await this._indexTree(branch, await oldEntry.getTree(), null, bulkOps);
    } else {
      let { type, id } = identify(oldEntry);
      await bulkOps.add({
        delete: {
          _index: branch,
          _type: type,
          _id: id
        }
      });
    }
  }

  // 1. Index the branch into newIndex.
  // 2. Update the canonical elasticsearch alias for the branch to point at newIndex
  // 3. Delete any old index that we just replaced.
  async _reindex(newIndex, branch) {
    let alias = await this.es.indices.getAlias({ name: branch, ignore: [404] });
    if (alias.status === 404) {
      this.log.info('%s is new, nothing to reindex', branch);
    } else {
      this.log.info('reindexing %s', branch);
      await this.es.reindex({
        body: {
          source: { index: branch },
          dest: { index: newIndex }
        }
      });

    }
    await this.es.indices.updateAliases({
      body: {
        actions: [
          { remove: { index: '_all', alias: branch } },
          { add: { index: newIndex, alias: branch } }
        ]
      }
    });

  }

  async _branches() {
    // nodegit docs show a Branch.iteratorNew method that would be
    // more appropriate than this, but as far as I can tell it is not
    // fully implemented
    return (await Reference.list(this.repo)).map(entry => entry.replace(/^refs\/heads\//, ''));
  }

};

function identify(entry) {
  let parts = entry.path().split('/');
  let type = parts[parts.length - 2] || 'tops';
  let filename = parts[parts.length - 1];
  let id = filename.replace(/\.json$/, '');
  return { type, id };
}
<|endoftext|>


<commit_before>/*
  The MIT License (MIT)
  Copyright (c) 2016 Slither Sessions.  All rights reserved.

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights to
  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  of the Software, and to permit persons to whom the Software is furnished to do
  so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  SOFTWARE.
*/

/** This is an example of how to notify a remote server with IP information. This
    requires server side scripting to fully implement.
  */

// ==UserScript==
// @name         SS Host Updater
// @namespace    mods.slithersessions.com
// @version      1.0.0
// @description  Slither Sessions Mod Pack
// @author       Slither Sessions
// @updateURL    http://mods.slithersessions.com/js/ss-host-updater.user.js
// @downloadURL  http://mods.slithersessions.com/js/ss-host-updater.user.js
// @require      http://code.jquery.com/jquery-latest.js
// @match        http://slither.io
// @grant        none
// @run-at       document-end
// ==/UserScript==

var ssHostUpdateUrl = "http://example.com/update_slither_host.php";

function ssHostUpdaterInit () {
    if (typeof window.ss != 'undefined')
    {
        var ss = window.ss;
        ss.onHostChanged = function() {
            $.get (ssHostUpdateUrl, { host: ss.currentIp() },
              function (data) {
               ss.log ("[SS] remote host updated: " + data);
              })
            ;
        };

        return;
    }

    setTimeout (ssHostUpdaterInit, 300);
}

(function () {
    'use strict';
    ssHostUpdaterInit ();
})();
<commit_msg>Housekeeping: updated code documentation<commit_after>/*
  The MIT License (MIT)
  Copyright (c) 2016 Slither Sessions.  All rights reserved.

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights to
  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  of the Software, and to permit persons to whom the Software is furnished to do
  so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  SOFTWARE.
*/

/** This is an example of how to notify a remote server with IP information using
    jQuery. This requires server side scripting to fully implement.
  */

// ==UserScript==
// @name         SS Host Updater
// @namespace    mods.slithersessions.com
// @version      1.0.0
// @description  Slither Sessions Mod Pack
// @author       Slither Sessions
// @updateURL    http://mods.slithersessions.com/js/ss-host-updater.user.js
// @downloadURL  http://mods.slithersessions.com/js/ss-host-updater.user.js
// @require      http://code.jquery.com/jquery-latest.js
// @match        http://slither.io
// @grant        none
// @run-at       document-end
// ==/UserScript==

var ssHostUpdateUrl = "http://example.com/update_slither_host.php";

function ssHostUpdaterInit () {
    if (typeof window.ss != 'undefined')
    {
        var ss = window.ss;
        ss.onHostChanged = function() {
            $.get (ssHostUpdateUrl, { host: ss.currentIp() },
              function (data) {
               ss.log ("[SS] remote host updated: " + data);
              })
            ;
        };

        return;
    }

    setTimeout (ssHostUpdaterInit, 300);
}

(function () {
    'use strict';
    ssHostUpdaterInit ();
})();
<|endoftext|>


<commit_before>/*
 * test.js: cross platform vows test driver.
 *
 * (C) Microsoft Open Technologies, Inc.
 *
 */

// spawns the following vows command line in a cross platform manner
// NOCK=on vows test/*/*/*-test.js --spec --isolate"

// Note:
// "echo y  | ssh-keygen -f test/fixtures/testkey -q -t rsa -N '' 2>&1 > /dev/null &&
// is no longer needed in the test command line.
// testkeys for testing are now located in tests/fixtures/testkey and testkey.pub

var path = require('path'),
  exec = require('child_process').exec,
  fs = require('fs'),
  errs = require('errs'),
  util = require('util'),
  vowsCmd,
  options,
  env = process.env || {},
  vows;

// turn on NOCK
env.NOCK = 'on';

options = {
  cwd : process.cwd(),
  env: env
};

// Since our tests use require('vows') we must use the version of vows installed in the node_modules.
// The globally installed version may be the wrong version and therefore different from the version
// we required in our tests.
vowsCmd = (process.platform === 'win32') ? path.resolve('./node_modules/.bin/vows.cmd')
  : path.resolve('./node_modules/.bin/vows');

// check if vows exists and is properly installed in ./node_modules
if(!fs.existsSync(vowsCmd)) {
  throw(errs.create("vows is not installed in ./node_modules. Please run npm install -d to install testing modules."));
};

// add vows args. Needed to add --color to get typical vows output text colors
vowsCmd += ' -i --spec --color ./test/*/*/*-test.js';

// run vows
vows = exec(vowsCmd, options);

// test results
vows.stdout.on('data', function (data) {
  util.print(data);
});

// test errors
vows.stderr.on('data', function (data) {
  util.error(data);
});

vows.on('exit', function (code) {
  console.log('vows exited with code ' + code);
});<commit_msg>Propagating exit code for npm test<commit_after>/*
 * test.js: cross platform vows test driver.
 *
 * (C) Microsoft Open Technologies, Inc.
 *
 */

// spawns the following vows command line in a cross platform manner
// NOCK=on vows test/*/*/*-test.js --spec --isolate"

// Note:
// "echo y  | ssh-keygen -f test/fixtures/testkey -q -t rsa -N '' 2>&1 > /dev/null &&
// is no longer needed in the test command line.
// testkeys for testing are now located in tests/fixtures/testkey and testkey.pub

var path = require('path'),
  exec = require('child_process').exec,
  fs = require('fs'),
  errs = require('errs'),
  util = require('util'),
  vowsCmd,
  options,
  env = process.env || {},
  vows;

// turn on NOCK
env.NOCK = 'on';

options = {
  cwd : process.cwd(),
  env: env
};

// Since our tests use require('vows') we must use the version of vows installed in the node_modules.
// The globally installed version may be the wrong version and therefore different from the version
// we required in our tests.
vowsCmd = (process.platform === 'win32') ? path.resolve('./node_modules/.bin/vows.cmd')
  : path.resolve('./node_modules/.bin/vows');

// check if vows exists and is properly installed in ./node_modules
if(!fs.existsSync(vowsCmd)) {
  throw(errs.create("vows is not installed in ./node_modules. Please run npm install -d to install testing modules."));
};

// add vows args. Needed to add --color to get typical vows output text colors
vowsCmd += ' -i --spec --color ./test/*/*/*-test.js';

// run vows
vows = exec(vowsCmd, options);

// test results
vows.stdout.on('data', function (data) {
  util.print(data);
});

// test errors
vows.stderr.on('data', function (data) {
  util.error(data);
});

vows.on('exit', function (code) {
  console.log('vows exited with code ' + code);

  // propagate our exit code through for travis-ci
  process.exit(code);
});<|endoftext|>


<commit_before>goog.provide('og.PlanetSegmentHelper');

goog.require('og.quadTree');

og.PlanetSegmentHelper.initIndexesTables = function (powerOfTwo) {
    if (powerOfTwo != og.PlanetSegmentHelper.powerOfTwo) {
        og.PlanetSegmentHelper.powerOfTwo = powerOfTwo;
        og.PlanetSegmentHelper.centerIndexesTable = og.PlanetSegmentHelper.initIndexBodiesTable(powerOfTwo);
        og.PlanetSegmentHelper.textureCoordsTable = og.PlanetSegmentHelper.initTextureCoordsTable(powerOfTwo);
        og.PlanetSegmentHelper.skirtsIndexesTable = og.PlanetSegmentHelper.initIndexesBodySkirts(powerOfTwo);
    }
    og.PlanetSegmentHelper.centerIndexesTable[0] = [];
    og.PlanetSegmentHelper.textureCoordsTable[0] = [];
};

og.PlanetSegmentHelper.createSegmentIndexes = function (size, sidesSizes) {
    if (size != 1) {
        var c = og.PlanetSegmentHelper.centerIndexesTable[size],
            w = og.PlanetSegmentHelper.skirtsIndexesTable[og.quadTree.W][size][sidesSizes[og.quadTree.W]],
            n = og.PlanetSegmentHelper.skirtsIndexesTable[og.quadTree.N][size][sidesSizes[og.quadTree.N]],
            e = og.PlanetSegmentHelper.skirtsIndexesTable[og.quadTree.E][size][sidesSizes[og.quadTree.E]],
            s = og.PlanetSegmentHelper.skirtsIndexesTable[og.quadTree.S][size][sidesSizes[og.quadTree.S]];
        var indexes = new Uint16Array(c.length + w.length + n.length + e.length + s.length);
        var k = 0, i = 0;
        for (k = 0; k < c.length; k++) {
            indexes[i++] = c[k];
        }
        for (k = 0; k < w.length; k++) {
            indexes[i++] = w[k];
        }
        for (k = 0; k < n.length; k++) {
            indexes[i++] = n[k];
        }
        for (k = 0; k < e.length; k++) {
            indexes[i++] = e[k];
        }
        for (k = 0; k < s.length; k++) {
            indexes[i++] = s[k];
        }
        return indexes;
    } else {
        return new Uint16Array([0, 2, 1, 3]);
    }
};

og.PlanetSegmentHelper.createCenterBodyIndexes = function (size, indexes) {

    var i0 = 1,
        j0 = 1;

    var i1 = 1,
        j1 = 1;

    var ind1, ind2, nr;
    for (var i = i0; i < size - 1 - i1; i++) {
        for (var j = j0; j < size - j1; j++) {
            ind1 = i * size + j;
            nr = (i + 1) * size;
            ind2 = nr + j;
            indexes.push(ind1, ind2);
        }
        indexes.push(ind2, nr + j0);
    }
    indexes.push(indexes[indexes.length - 1], size * size - size);
};

og.PlanetSegmentHelper.createWestNeighborSkirt = function (size, deltaGr, indexes) {
    var grCount = (size - 1) / deltaGr;
    var b = size * size - size;
    var k = 0;
    for (var i = 0; i < size - 2; i++) {
        if (i % grCount === 0) {
            k = i;
        }
        var rind = b - size * i - size + 1,
            lind = b - size * k;
        indexes.push(lind, rind);
    }

    if (deltaGr === (size - 1)) {
        indexes.push(size);
        indexes.push(0);
    }
};

og.PlanetSegmentHelper.createNorthNeighborSkirt = function (size, deltaGr, indexes) {
    var grCount = (size - 1) / deltaGr;
    var k = 0;
    for (var i = 0; i < size - 2; i++) {
        if (i % grCount === 0) {
            k = i;
        }
        var rind = size + i + 1,
            lind = k;
        indexes.push(lind, rind);
    }

    if (deltaGr === (size - 1)) {
        indexes.push(size - 2);
        indexes.push(size - 1);
    }
};

og.PlanetSegmentHelper.createEastNeighborSkirt = function (size, deltaGr, indexes) {
    var grCount = (size - 1) / deltaGr;
    var k = 0;
    for (var i = 0; i < size - 2; i++) {
        if (i % grCount === 0) {
            k = i;
        }
        var rind = size * (i + 1) + size - 2,
            lind = size + size * k - 1;
        indexes.push(lind, rind);
    }

    if (deltaGr === (size - 1)) {
        indexes.push(size * (size - 1) - 1);
        indexes.push(size * size - 1);
    }
};

og.PlanetSegmentHelper.createSouthNeighborSkirt = function (size, deltaGr, indexes) {
    var grCount = (size - 1) / deltaGr;
    var k = 0;
    var rb = size * (size - 1) - 2;
    var lb = size * size - 1;
    for (var i = 0; i < size - 2; i++) {
        if (i % grCount === 0) {
            k = i;
        }
        var rind = rb - i,
            lind = lb - k;
        indexes.push(lind, rind);
    }

    if (deltaGr === (size - 1)) {
        indexes.push(size * size - size + 1);
    }
    indexes.push(size * size - size);
};

og.PlanetSegmentHelper.initIndexesBodySkirts = function (pow) {
    var table = [];
    table[og.quadTree.N] = [];
    table[og.quadTree.W] = [];
    table[og.quadTree.S] = [];
    table[og.quadTree.E] = [];

    table[og.quadTree.N][0] = [];
    table[og.quadTree.W][0] = [];
    table[og.quadTree.S][0] = [];
    table[og.quadTree.E][0] = [];

    for (var i = 0; i <= pow; i++) {
        var d = Math.pow(2, i);
        table[og.quadTree.N][d] = [];
        table[og.quadTree.W][d] = [];
        table[og.quadTree.S][d] = [];
        table[og.quadTree.E][d] = [];

        table[og.quadTree.N][d][0] = [];
        table[og.quadTree.W][d][0] = [];
        table[og.quadTree.S][d][0] = [];
        table[og.quadTree.E][d][0] = [];

        for (var j = 0; j <= pow; j++) {
            var dd = Math.pow(2, j);
            var nt = table[og.quadTree.N][d][dd] = [];
            var wt = table[og.quadTree.W][d][dd] = [];
            var st = table[og.quadTree.S][d][dd] = [];
            var et = table[og.quadTree.E][d][dd] = [];
            og.PlanetSegmentHelper.createWestNeighborSkirt(d + 1, dd, wt);
            og.PlanetSegmentHelper.createNorthNeighborSkirt(d + 1, dd, nt);
            og.PlanetSegmentHelper.createEastNeighborSkirt(d + 1, dd, et);
            og.PlanetSegmentHelper.createSouthNeighborSkirt(d + 1, dd, st);
        }
    }
    return table;
};

og.PlanetSegmentHelper.initTextureCoordsTable = function (pow) {
    var table = [];
    for (var i = 0; i <= pow; i++) {
        var d = Math.pow(2, i);
        table[d] = og.PlanetSegmentHelper.createTextureCoords(d);
    }
    return table;
};

og.PlanetSegmentHelper.initIndexBodiesTable = function (pow) {
    var table = [];
    for (var i = 0; i <= pow; i++) {
        var d = Math.pow(2, i);
        var t = table[d] = [];
        og.PlanetSegmentHelper.createCenterBodyIndexes(d + 1, t);
    }
    return table;
};

og.PlanetSegmentHelper.createTextureCoords = function (size) {
    var texCoords = [];
    for (var i = 0; i <= size; i++) {
        for (var j = 0; j <= size; j++) {
            texCoords.push(j / size, i / size);
        }
    }
    return new Float32Array(texCoords);
};<commit_msg>Segment helper little improvement.<commit_after>goog.provide('og.PlanetSegmentHelper');

goog.require('og.quadTree');

og.PlanetSegmentHelper.CURRENT_POWER = 0;

og.PlanetSegmentHelper.initIndexesTables = function (powerOfTwo) {
    if (powerOfTwo > og.PlanetSegmentHelper.CURRENT_POWER) {
        og.PlanetSegmentHelper.CURRENT_POWER = powerOfTwo;
        og.PlanetSegmentHelper.centerIndexesTable = og.PlanetSegmentHelper.initIndexBodiesTable(powerOfTwo);
        og.PlanetSegmentHelper.textureCoordsTable = og.PlanetSegmentHelper.initTextureCoordsTable(powerOfTwo);
        og.PlanetSegmentHelper.skirtsIndexesTable = og.PlanetSegmentHelper.initIndexesBodySkirts(powerOfTwo);
    }
    og.PlanetSegmentHelper.centerIndexesTable[0] = [];
    og.PlanetSegmentHelper.textureCoordsTable[0] = [];
};

og.PlanetSegmentHelper.createSegmentIndexes = function (size, sidesSizes) {
    if (size != 1) {
        var c = og.PlanetSegmentHelper.centerIndexesTable[size],
            w = og.PlanetSegmentHelper.skirtsIndexesTable[og.quadTree.W][size][sidesSizes[og.quadTree.W]],
            n = og.PlanetSegmentHelper.skirtsIndexesTable[og.quadTree.N][size][sidesSizes[og.quadTree.N]],
            e = og.PlanetSegmentHelper.skirtsIndexesTable[og.quadTree.E][size][sidesSizes[og.quadTree.E]],
            s = og.PlanetSegmentHelper.skirtsIndexesTable[og.quadTree.S][size][sidesSizes[og.quadTree.S]];
        var indexes = new Uint16Array(c.length + w.length + n.length + e.length + s.length);
        var k = 0, i = 0;
        for (k = 0; k < c.length; k++) {
            indexes[i++] = c[k];
        }
        for (k = 0; k < w.length; k++) {
            indexes[i++] = w[k];
        }
        for (k = 0; k < n.length; k++) {
            indexes[i++] = n[k];
        }
        for (k = 0; k < e.length; k++) {
            indexes[i++] = e[k];
        }
        for (k = 0; k < s.length; k++) {
            indexes[i++] = s[k];
        }
        return indexes;
    } else {
        return new Uint16Array([0, 2, 1, 3]);
    }
};

og.PlanetSegmentHelper.createCenterBodyIndexes = function (size, indexes) {

    var i0 = 1,
        j0 = 1;

    var i1 = 1,
        j1 = 1;

    var ind1, ind2, nr;
    for (var i = i0; i < size - 1 - i1; i++) {
        for (var j = j0; j < size - j1; j++) {
            ind1 = i * size + j;
            nr = (i + 1) * size;
            ind2 = nr + j;
            indexes.push(ind1, ind2);
        }
        indexes.push(ind2, nr + j0);
    }
    indexes.push(indexes[indexes.length - 1], size * size - size);
};

og.PlanetSegmentHelper.createWestNeighborSkirt = function (size, deltaGr, indexes) {
    var grCount = (size - 1) / deltaGr;
    var b = size * size - size;
    var k = 0;
    for (var i = 0; i < size - 2; i++) {
        if (i % grCount === 0) {
            k = i;
        }
        var rind = b - size * i - size + 1,
            lind = b - size * k;
        indexes.push(lind, rind);
    }

    if (deltaGr === (size - 1)) {
        indexes.push(size);
        indexes.push(0);
    }
};

og.PlanetSegmentHelper.createNorthNeighborSkirt = function (size, deltaGr, indexes) {
    var grCount = (size - 1) / deltaGr;
    var k = 0;
    for (var i = 0; i < size - 2; i++) {
        if (i % grCount === 0) {
            k = i;
        }
        var rind = size + i + 1,
            lind = k;
        indexes.push(lind, rind);
    }

    if (deltaGr === (size - 1)) {
        indexes.push(size - 2);
        indexes.push(size - 1);
    }
};

og.PlanetSegmentHelper.createEastNeighborSkirt = function (size, deltaGr, indexes) {
    var grCount = (size - 1) / deltaGr;
    var k = 0;
    for (var i = 0; i < size - 2; i++) {
        if (i % grCount === 0) {
            k = i;
        }
        var rind = size * (i + 1) + size - 2,
            lind = size + size * k - 1;
        indexes.push(lind, rind);
    }

    if (deltaGr === (size - 1)) {
        indexes.push(size * (size - 1) - 1);
        indexes.push(size * size - 1);
    }
};

og.PlanetSegmentHelper.createSouthNeighborSkirt = function (size, deltaGr, indexes) {
    var grCount = (size - 1) / deltaGr;
    var k = 0;
    var rb = size * (size - 1) - 2;
    var lb = size * size - 1;
    for (var i = 0; i < size - 2; i++) {
        if (i % grCount === 0) {
            k = i;
        }
        var rind = rb - i,
            lind = lb - k;
        indexes.push(lind, rind);
    }

    if (deltaGr === (size - 1)) {
        indexes.push(size * size - size + 1);
    }
    indexes.push(size * size - size);
};

og.PlanetSegmentHelper.initIndexesBodySkirts = function (pow) {
    var table = [];
    table[og.quadTree.N] = [];
    table[og.quadTree.W] = [];
    table[og.quadTree.S] = [];
    table[og.quadTree.E] = [];

    table[og.quadTree.N][0] = [];
    table[og.quadTree.W][0] = [];
    table[og.quadTree.S][0] = [];
    table[og.quadTree.E][0] = [];

    for (var i = 0; i <= pow; i++) {
        var d = Math.pow(2, i);
        table[og.quadTree.N][d] = [];
        table[og.quadTree.W][d] = [];
        table[og.quadTree.S][d] = [];
        table[og.quadTree.E][d] = [];

        table[og.quadTree.N][d][0] = [];
        table[og.quadTree.W][d][0] = [];
        table[og.quadTree.S][d][0] = [];
        table[og.quadTree.E][d][0] = [];

        for (var j = 0; j <= pow; j++) {
            var dd = Math.pow(2, j);
            var nt = table[og.quadTree.N][d][dd] = [];
            var wt = table[og.quadTree.W][d][dd] = [];
            var st = table[og.quadTree.S][d][dd] = [];
            var et = table[og.quadTree.E][d][dd] = [];
            og.PlanetSegmentHelper.createWestNeighborSkirt(d + 1, dd, wt);
            og.PlanetSegmentHelper.createNorthNeighborSkirt(d + 1, dd, nt);
            og.PlanetSegmentHelper.createEastNeighborSkirt(d + 1, dd, et);
            og.PlanetSegmentHelper.createSouthNeighborSkirt(d + 1, dd, st);
        }
    }
    return table;
};

og.PlanetSegmentHelper.initTextureCoordsTable = function (pow) {
    var table = [];
    for (var i = 0; i <= pow; i++) {
        var d = Math.pow(2, i);
        table[d] = og.PlanetSegmentHelper.createTextureCoords(d);
    }
    return table;
};

og.PlanetSegmentHelper.initIndexBodiesTable = function (pow) {
    var table = [];
    for (var i = 0; i <= pow; i++) {
        var d = Math.pow(2, i);
        var t = table[d] = [];
        og.PlanetSegmentHelper.createCenterBodyIndexes(d + 1, t);
    }
    return table;
};

og.PlanetSegmentHelper.createTextureCoords = function (size) {
    var texCoords = [];
    for (var i = 0; i <= size; i++) {
        for (var j = 0; j <= size; j++) {
            texCoords.push(j / size, i / size);
        }
    }
    return new Float32Array(texCoords);
};<|endoftext|>