<commit_before>(function () {

	// Simple localStorage abstration
	var Cache = function () {
		this.namespace = 'c';
	};
	Cache.prototype.expandKey = function (key) {
		return this.namespace +':'+ key;
	};
	Cache.prototype.set = function (key, val) {
		if (!window.localStorage) {
			return;
		}
		window.localStorage.setItem(this.expandKey(key), JSON.stringify(val));
	};
	Cache.prototype.get = function (key) {
		if (!window.localStorage) {
			return null;
		}
		return JSON.parse(window.localStorage.getItem(this.expandKey(key)));
	};
	Cache.prototype.remove = function (key) {
		if (!window.localStorage) {
			return;
		}
		window.localStorage.removeItem(this.expandKey(key));
	};

	// Simple scoring algorithm
	StringScore = function (str, abbr) {
		str = str.toLowerCase();
		abbr = abbr.toLowerCase();

		if (str === abbr) {
			return 1;
		}

		var index = str.indexOf(abbr);

		if (index === -1) {
			return 0;
		}

		if (index === 0) {
			return 1;
		}

		return abbr.length / str.length;
	};

	var Contacts = {};

	Contacts.allowedOrigin = /^.*$/; // TODO: make this configurable with an array of hostnames

	// listen to postMessage
	Contacts.run = function () {
		window.addEventListener("message", Contacts.receiveMessage, false);
	};

	Contacts.init = function () {
		Contacts.setCredentials.apply(null, arguments);
		Contacts.cache = new Cache();
	};

	Contacts.deinit = function () {
		Contacts.client = null;
		Contacts.cache = null;
	};

	Contacts.receiveMessage = function (event) {
		if (!Contacts.allowedOrigin.test(event.origin)) {
			return; // ignore everything from "un-trusted" hosts
		}

		// each message must be an object
		// with `name`, `args`, and `id` members
		// corresponding to the function to be called,
		// the args to call it with, and the id with
		// which the response should be associated.

		var callback = function (res) {
			event.source.postMessage({
				id: event.data.id,
				res: res
			}, event.origin);
		};

		switch (event.data.name) {
			case "find":
				Contacts.find.apply(null, event.data.args.concat([callback]));
			break;

			case "search":
				Contacts.search.apply(null, event.data.args.concat([callback]));
			break;

			case "init":
				Contacts.init.apply(null, event.data.args || []);
				callback();
			break;

			case "deinit":
				Contacts.deinit.apply(null, event.data.args || []);
				callback();
			break;
		}
	};

	Contacts.setCredentials = function (entity, serverMetaPost, credentials) {
		Contacts.client = new TentClient(entity, {
			serverMetaPost: serverMetaPost,
			credentials: credentials
		});
	};

	// fetch new relationships and cached update names and avatars
	Contacts.sync = function () {
	};

	/*
	 * public API
	 */

	// find contact by entity
	Contacts.find = function (entity, callback) {
	};

	// find contacts with name or entity matching queryString
	Contacts.search = function (queryString, callback) {
	};

	Contacts.run();

})();
<commit_msg>Setup contacts sync<commit_after>(function () {

	// Simple localStorage abstration
	var Cache = function () {
		this.namespace = 'c';
	};
	Cache.prototype.expandKey = function (key) {
		return this.namespace +':'+ key;
	};
	Cache.prototype.set = function (key, val) {
		if (!window.localStorage) {
			return;
		}
		window.localStorage.setItem(this.expandKey(key), JSON.stringify(val));
	};
	Cache.prototype.get = function (key) {
		if (!window.localStorage) {
			return null;
		}
		return JSON.parse(window.localStorage.getItem(this.expandKey(key)));
	};
	Cache.prototype.remove = function (key) {
		if (!window.localStorage) {
			return;
		}
		window.localStorage.removeItem(this.expandKey(key));
	};

	// Simple scoring algorithm
	StringScore = function (str, abbr) {
		str = str.toLowerCase();
		abbr = abbr.toLowerCase();

		if (str === abbr) {
			return 1;
		}

		var index = str.indexOf(abbr);

		if (index === -1) {
			return 0;
		}

		if (index === 0) {
			return 1;
		}

		return abbr.length / str.length;
	};

	var Contacts = {};
	Contacts.displayName = "TentContacts (Daemon)";

	var __syncInterval;

	Contacts.allowedOrigin = /^.*$/; // TODO: make this configurable with an array of hostnames

	// listen to postMessage
	Contacts.run = function () {
		window.addEventListener("message", Contacts.receiveMessage, false);
	};

	Contacts.init = function () {
		Contacts.setCredentials.apply(null, arguments);
		Contacts.cache = new Cache();
		__syncInterval = setInterval(Contacts.sync, 14400000); // sync every 4 hours
		Contacts.sync();
	};

	Contacts.deinit = function () {
		Contacts.client = null;
		Contacts.cache = null;
		clearInterval(__syncInterval);
	};

	Contacts.receiveMessage = function (event) {
		if (!Contacts.allowedOrigin.test(event.origin)) {
			return; // ignore everything from "un-trusted" hosts
		}

		// each message must be an object
		// with `name`, `args`, and `id` members
		// corresponding to the function to be called,
		// the args to call it with, and the id with
		// which the response should be associated.

		var callback = function (res) {
			event.source.postMessage({
				id: event.data.id,
				res: res
			}, event.origin);
		};

		switch (event.data.name) {
			case "find":
				Contacts.find.apply(null, event.data.args.concat([callback]));
			break;

			case "search":
				Contacts.search.apply(null, event.data.args.concat([callback]));
			break;

			case "init":
				Contacts.init.apply(null, event.data.args || []);
				callback();
			break;

			case "deinit":
				Contacts.deinit.apply(null, event.data.args || []);
				callback();
			break;
		}
	};

	Contacts.setCredentials = function (entity, serverMetaPost, credentials) {
		Contacts.client = new TentClient(entity, {
			serverMetaPost: serverMetaPost,
			credentials: credentials
		});
	};

	// fetch new relationships and cached update names and avatars
	Contacts.sync = function () {
		if (!Contacts.client) {
			throw Error(Contacts.displayName +": Can not sync without Tent client!");
		}

		var cursor = Contacts.cache.get('cursor');
		if (cursor && ((Date.now() - cursor.updated_at) > 86400000)) {
			// refresh everything every 24 hours
			cursor = null;
		}
		if (!cursor) {
			cursor = {
				since: 0,
				updated_at: Date.now()
			};
		}

		var handleSuccess = function (res, xhr) {
			if (!res.posts.length) {
				// empty response
				return;
			}

			// update cursor
			var latestPost = res.posts[0];
			cursor = {
				since: latestPost.received_at +" "+ latestPost.version.id,
				updated_at: Date.now()
			};
			Contacts.cache.set('cursor', cursor);

			// fetch the next page
			Contacts.fetch({
				since: cursor.since
			}, handleSuccess);

			// cache returned profiles
			var entity, profile, name, avatarDigest;
			for (entity in res.profiles) {
				profile = res.profiles[entity];
				name = profile.name || entity.replace(/^https?:\/\//, '');
				avatarDigest = profile.avatar_digest;
				Contacts.cacheProfile(entity, name, avatarDigest);
			}
		};

		Contacts.fetch({
			since: cursor.since
		}, handleSuccess);
	};

	Contacts.fetch = function (params, successCallback) {
		params.types = ["https://tent.io/types/relationship/v0#"];
		params.profiles = "mentions";
		Contacts.client.getPostsFeed({
			params: [params],
			callback: {
				success: successCallback,
				failure: function (res, xhr) {
					throw Error(Contacts.displayName +": Failed to fetch relationships - "+ xhr.status +": "+ JSON.stringify(res));
				}
			}
		});
	};

	// returns cached object with entity URIs
	// as keys and profile names as values
	// (used for searching).
	Contacts.getCacheManifest = function () {
		if (!Contacts.cache) {
			return null;
		}
		return Contacts.cache.get('manifest');
	};

	Contacts.cacheProfile = function (entity, name, avatarDigest) {
		Contacts.cache.set(entity, {
			name: name,
			avatarDigest: avatarDigest
		});

		var manifest = Contacts.getCacheManifest() || {};
		manifest[entity] = name;
		Contacts.cache.set('manifest', manifest);
	};

	/*
	 * public API
	 */

	// find contact by entity
	Contacts.find = function (entity, callback) {
	};

	// find contacts with name or entity matching queryString
	Contacts.search = function (queryString, callback) {
	};

	Contacts.run();

})();

{'fraction_non_alphanumeric': 0.14716658524670248, 'fraction_numerical': 0.0039081582804103565, 'mean_word_length': 3.282949790794979, 'pattern_counts': {'":': 11, '<': 3, '<?xml version=': 0, '>': 4, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5089415', 'n_tokens_mistral': 3052, 'n_tokens_neox': 2633, 'n_words': 634}