<commit_before>'use strict';

const EventEmitter = require('events');
const events = require('./events');

const SharedMap = require('./shared-map');
const SharedList = require('./shared-list');
const SharedString = require('./shared-string');

const CompoundOperation = require('../operations/compound-operation');
const combined = require('../operations/combined');

class Model {
	constructor(editor) {
		this.editor = editor;
		this.type = editor.type;

		this._lastObjectId = 0;

		this.factories = {};

		this.editors = {};
		this.values = {};
		this.objects = {};

		this.events = new EventEmitter();

		editor.addEventListener('change', change => {
			if(! change.local) {
				change.operation.apply(this._changeHandler);
			}

			this.events.emit('change', change);
		});

		this._changeHandler = {
			update: (id, type, change) => {
				if(typeof this.values[id] !== 'undefined') {
					const current = this.values[id];
					const composed = this.type.types[type].compose(current, change);

					this.values[id] = composed;

					const editor = this.editors[id];
					this.remote = true;
					editor.apply({
						operation: change,
						local: false,
						remote: true
					});
				} else {
					this.values[id] = change;

					const object = this._createObject(id, type);
					this.objects[id] = object;
				}
			}
		};

		this.registerType('map', e => new SharedMap(e));
		this.registerType('list', e => new SharedList(e));
		this.registerType('string', e => new SharedString(e));

		this.root = this._getObject('root', 'map');
		this.root.on('valueChanged', data => this.events.emit('valueChanged', data));
		this.root.on('valueRemoved', data => this.events.emit('valueRemove', data));
	}

	registerType(type, factory) {
		this.factories[type] = factory;
		return this;
	}

	newMap() {
		return this.newObject('map');
	}

	newList() {
		return this.newObject('list');
	}

	newString() {
		return this.newObject('string');
	}

	newObject(type) {
		const objectId = this.editor.id + '-' + (this._lastObjectId++);
		return this._getObject(objectId, type);
	}

	open() {
		return this.editor.connect()
			.then(initial => initial.apply(this._changeHandler));
	}

	close() {
		this.editor.close();
	}

	performEdit(callback) {
		this.editor.performEdit(callback);
	}

	_apply(id, type, op) {
		// Compose together with the current value for the object
		if(typeof this.values[id] !== 'undefined') {
			const current = this.values[id];
			const composed = this.type.types[type].compose(current, op);

			this.values[id] = composed;
		} else {
			this.values[id] = op;
		}

		// Ask the editor to apply the operation and sync it with other editors
		this.remote = false;
		this.editor.apply(combined.delta()
			.update(id, type, op)
			.done()
		);

		// Ask the object to apply the operation as a local one
		const editor = this.editors[id];
		if(editor) {
			editor.apply({
				operation: op,
				local: true,
				remote: false
			});

			editor.queueEvent('change', op);
		}
	}

	getObject(id) {
		let object = this.objects[id];
		return object || null;<commit_msg>Model now applies change locally before sending to editor<commit_after>'use strict';

const EventEmitter = require('events');
const events = require('./events');

const SharedMap = require('./shared-map');
const SharedList = require('./shared-list');
const SharedString = require('./shared-string');

const CompoundOperation = require('../operations/compound-operation');
const combined = require('../operations/combined');

class Model {
	constructor(editor) {
		this.editor = editor;
		this.type = editor.type;

		this._lastObjectId = 0;

		this.factories = {};

		this.editors = {};
		this.values = {};
		this.objects = {};

		this.events = new EventEmitter();

		editor.addEventListener('change', change => {
			if(! change.local) {
				change.operation.apply(this._changeHandler);
			}

			this.events.emit('change', change);
		});

		this._changeHandler = {
			update: (id, type, change) => {
				if(typeof this.values[id] !== 'undefined') {
					const current = this.values[id];
					const composed = this.type.types[type].compose(current, change);

					this.values[id] = composed;

					const editor = this.editors[id];
					this.remote = true;
					editor.apply({
						operation: change,
						local: false,
						remote: true
					});
				} else {
					this.values[id] = change;

					const object = this._createObject(id, type);
					this.objects[id] = object;
				}
			}
		};

		this.registerType('map', e => new SharedMap(e));
		this.registerType('list', e => new SharedList(e));
		this.registerType('string', e => new SharedString(e));

		this.root = this._getObject('root', 'map');
		this.root.on('valueChanged', data => this.events.emit('valueChanged', data));
		this.root.on('valueRemoved', data => this.events.emit('valueRemove', data));
	}

	registerType(type, factory) {
		this.factories[type] = factory;
		return this;
	}

	newMap() {
		return this.newObject('map');
	}

	newList() {
		return this.newObject('list');
	}

	newString() {
		return this.newObject('string');
	}

	newObject(type) {
		const objectId = this.editor.id + '-' + (this._lastObjectId++);
		return this._getObject(objectId, type);
	}

	open() {
		return this.editor.connect()
			.then(initial => initial.apply(this._changeHandler));
	}

	close() {
		this.editor.close();
	}

	performEdit(callback) {
		this.editor.performEdit(callback);
	}

	_apply(id, type, op) {
		// Compose together with the current value for the object
		if(typeof this.values[id] !== 'undefined') {
			const current = this.values[id];
			const composed = this.type.types[type].compose(current, op);

			this.values[id] = composed;
		} else {
			this.values[id] = op;
		}

		// Ask the object to apply the operation as a local one
		const editor = this.editors[id];
		if(editor) {
			this.remote = false;
			editor.apply({
				operation: op,
				local: true,
				remote: false
			});

			editor.queueEvent('change', op);
		}

		// Ask the editor to apply the operation and sync it with other editors
		this.editor.apply(combined.delta()
			.update(id, type, op)
			.done()
		);
	}

	getObject(id) {
		let object = this.objects[id];
		return object || null;
{'fraction_non_alphanumeric': 0.16389740280690435, 'fraction_numerical': 0.00032263268269075657, 'mean_word_length': 3.4412607449856734, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 19, 'https://': 0, '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': '6111666', 'n_tokens_mistral': 2338, 'n_tokens_neox': 1973, 'n_words': 441}