whole_func_string: 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)	
  	}
func_code_string: 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)	
  	}
func_documentation_string: /*
  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)
  }