server/ap_state.js
function saveEdit(typeCode, id, op, e, syncId, timestamp){
		_.assertNumber(timestamp)
		_.assertInt(op)
		//TODO selectTopObject?
		
		//console.log('saving edit: ', id, op, e, syncId)

		/*if(op === editCodes.selectObject){
			if(e.id < 0) e.id = translateTemporary(e.id, syncId)
		}else if(op === editCodes.selectSubObject){
			if(e.id < 0) e.id = translateTemporary(e.id, syncId)
		}else if(op === editCodes.selectObjectKey){
			//console.log('key: ' + e.key)
			if(e.key < 0) e.key = translateTemporary(e.key, syncId)
		}*/

		
		if(currentId !== id && id !== -1){
			ap.selectTopObject({id: id})
			//log('wrote selectTopObject(' + id + ')')
			currentId = id
		}
		
		ap[editNames[op]](e)

		/*var n = */ol.persist(op, e, syncId, timestamp, id)//{top: id, topTypeCode: typeCode})

		//log(n.editId, id, op, e, syncId)

		//TODO stop doing this here? - who cares if the path has been changed?
		//broadcaster.input.objectUpdated(typeCode, id, op, e, syncId, n.editId)	
	}
function saveEdit(typeCode, id, op, e, syncId, timestamp){
		_.assertNumber(timestamp)
		_.assertInt(op)
		//TODO selectTopObject?
		
		//console.log('saving edit: ', id, op, e, syncId)

		/*if(op === editCodes.selectObject){
			if(e.id < 0) e.id = translateTemporary(e.id, syncId)
		}else if(op === editCodes.selectSubObject){
			if(e.id < 0) e.id = translateTemporary(e.id, syncId)
		}else if(op === editCodes.selectObjectKey){
			//console.log('key: ' + e.key)
			if(e.key < 0) e.key = translateTemporary(e.key, syncId)
		}*/

		
		if(currentId !== id && id !== -1){
			ap.selectTopObject({id: id})
			//log('wrote selectTopObject(' + id + ')')
			currentId = id
		}
		
		ap[editNames[op]](e)

		/*var n = */ol.persist(op, e, syncId, timestamp, id)//{top: id, topTypeCode: typeCode})

		//log(n.editId, id, op, e, syncId)

		//TODO stop doing this here? - who cares if the path has been changed?
		//broadcaster.input.objectUpdated(typeCode, id, op, e, syncId, n.editId)	
	}
/*
function translateWithTemporaries(path, syncId){
var ti = temporaryIdsBySync[syncId];

var result = [].concat(path);

for(var i=0;i<path.length;++i){
var temporary = result[i]
if(temporary >= 0) continue
var id = ti[temporary]
_.assertInt(id)
result[i] = id
}

for(var i=0;i<result.length;++i){
if(result[i] < 0){
console.log(JSON.stringify(path) + ' -> ' + JSON.stringify(result));
console.log(JSON.stringify(tp));
_.errout('failed to fully translate path');
}
}

return result;
} /*var temporaryIdsBySync = {};
function translateTemporary(temp, syncId){
console.log('translating ' + temp + ' (' + syncId + ')')
_.assertInt(temp)
_.assertString(syncId)
_.assertLength(syncId, 8)
_.assert(temp < -1)
console.log('translating ' + temp + ' -> ' + real + ' (' + syncId + ')')
var real = temporaryIdsBySync[syncId].temporaryIds[temp];
console.log(JSON.stringify(temporaryIdsBySync[syncId]))
if(!real) console.log('ERROR, cannot translate temporary id: ' + temp)
_.assertInt(real)
return real;
}
function mapTemporary(temp, real, syncId, editId){
_.assertString(syncId)
var syncIdStr = seedrandom.uuidBufferToString(syncId)
_.assert(temp < -1)
var te = temporaryIdsBySync[syncId]
if(te === undefined){
te = temporaryIdsBySync[syncId] = {

temporaryIds: {},
editIdsHistory: [],
temporaryHistory: []
}
}


if(te.temporaryIds[temp] !== undefined){
_.errout('temporary id already mapped ' + temp + ' -> ' + temporaryIds[temp] + ', now being mapped to ' + real);
}
te.temporaryIds[temp] = real;
te.editIdsHistory.push(editId)
te.temporaryHistory.push(temp)
te.mappedIds[real] = true;

console.log('mapped temporary ' + temp + ' -> ' + real + ' ' + syncId + ' ' + editId)
}