http/js/api/topobject.js
function changeOnPath(local, op, edit, syncId, editId){
	_.assertInt(op)
	//console.log('path: ' + JSON.stringify(path))
	//_.assertArray(path)
	
	_.assertInt(editId)
	
	/*
	We need:
	- an index of every existing inner object by id
	*/
	
	//console.log('GAR: ' + editNames[op] + ' ' + JSON.stringify(edit))
	
	//console.log('inputObject: ' + local.inputObject)
	//console.log(JSON.stringify(local.edits))
	/*local.edits.forEach(function(e, index){
		console.log(index + ' ' + editNames[e.op] + ' ' + JSON.stringify(e))
	})*/
	//console.log('self: ' + local.id())
	_.assertInt(local.inputProperty)

	//console.log('own: ' + local.objectId + ' ' + local.inputObject + ' ')// + local._forkedObject._internalId())
	
	var ch;
	if(!local.inputObject || local.inputObject === local.objectId || local.inputObject === -1){//TODO handle case where inputObject is the forked object
		ch = local
	}else if(local.lastSelf === local.inputObject){
		ch = local
	}else{
		/*if(editId > 0 && editId !== local.getEditingId() && local.inputObject < 0){
			_.errout('invalid inputObject: ' + local.inputObject + '\t' + editId + ' !== ' + local.getEditingId())
		}*/
		
		if(local.objectApiCache){
			ch = local.objectApiCache[local.inputObject]
		}
		
		/*if(local.inputObject < 0){
			var v = local.parent.temporaryCache[local.inputObject]
			if(v){
				ch = v.n
				//console.log('looked up temporary ' + local.inputObject + ' got ' + Object.keys(ch))
			}
		}*/
		/*if(local.inputObject < 0){
			ch = local.temporary
		}*/
		if(!ch){
			console.log('WARNING: not found ' + local.inputObject + ', only got: ' + JSON.stringify(Object.keys(local.objectApiCache||{})) + ' in ' + local.objectId + ' --- ' + editId)
			console.log(JSON.stringify(local.edits, null, 2))
			throw new Error('here')
			return
		}
		_.assertObject(ch)
	}
	
	var property = ch.typeSchema.propertiesByCode[local.inputProperty]
	if(property === undefined){
		var candidates = ' ' + JSON.stringify(_.map(Object.keys(ch.typeSchema.propertiesByCode),function(p){
			return ch.typeSchema.propertiesByCode[p].name + ':'+ch.typeSchema.propertiesByCode[p].code}))
		if(TopObjectHandle.prototype.errorOnWarn){
			_.errout('WARNING: logic error or removed property, ' + ch.typeSchema.name + ' cannot find property with code: ' + local.inputProperty + candidates)
		}else{
			console.log('WARNING: logic error or removed property, ' + ch.typeSchema.name + ' cannot find property with code: ' + local.inputProperty + candidates)
		}
		return
	}	

	//console.log('modifying property: ' + property.name)
	
	if(op === editCodes.setObject || op === editCodes.setViewObject){
		//_.assertInt(descentCode)
		//var ps = this.typeSchema.propertiesByCode[descentCode];

		//_.assertObject(ps)
		if(op === editCodes.setObject){
			if(property.type.type !== 'object'){
				console.log(JSON.stringify(local.edits, null, 2))
				_.errout('(' + editId + ') setObject called on non object type, type is: ' + JSON.stringify(property.type))
			}
			//_.assertEqual(ps.type.type, 'object')
		}

		var subj
		if(local.inputObject === local.objectId || local.inputObject === local.lastSelf){
			subj = local
		}else{
			subj = local.inputObject!==undefined?local.objectApiCache[local.inputObject]:local
		}

		var pv = subj[property.name]//.cachedProperties[ps.name]
		if(pv && pv.objectId === edit.id){
			//already done
			//console.log('setObject redundant (local view object?), skipping: ' + pv.objectId + ' ' + edit.id)
		}else{
			//this.log('set to: ' + edit.id + ' ' + descentCode + ' ' + this.objectId + ' ' + ps.name)
			var setObj = local.getObjectApi(edit.id)
			if(setObj === undefined){
				local.log.warn('object not found, may have been del\'ed: ' + edit.id)
				local.log(new Error().stack)
				return
			}
			
			if(setObj.isDestroyed()) return

			//subj.obj[local.inputProperty] = setObj;
			setObj.prepare()
			subj[property.name] = setObj
			//console.log('EMITTING SET OBJECT ' + property.name)
			subj.emit(edit, 'set', property.name, setObj)			
		}
	}else if(op === editCodes.clearObject || op === editCodes.clearProperty){

		var subj = local.inputObject!==undefined?local.objectApiCache[local.inputObject]:local

		var ps = subj.typeSchema.propertiesByCode[local.inputProperty];
		
		var chp = ch.property(ps.name)
		
		if(ps.type.type === 'set' || ps.type.type === 'list'){
			chp.obj = []
		}else if(ps.type.type === 'map'){
			chp.obj = {}
		}else{
			chp.obj = undefined
		}
		//subj[ps.name] = undefined
		//subj.obj[local.inputProperty] = undefined
	}else{
		
		//console.log(chp.constructor+'')
		var chp = ch.property(property.name)//[property.name]

		_.assertEqual(ch[property.name], chp)
		
		if(!chp){
			_.errout('property value missing: ' + property.name)
		}
		
		if(!chp.changeListener){
			//console.dir(chp)
			_.errout('cannot determine current edit target(' + property.name+' in ' + ch.typeSchema.name + ' ' + ch.prepared + ' - ' + chp.constructor + ') failed to run edit: ' + JSON.stringify([editNames[op], edit, syncId, editId]))
			return
		}
		//console.log('HERERERERER')
		//console.log('editing ' + editNames[op] + ' ' + (chp.rally = Math.random()))
		
		//console.log(typeof(chp.changeListener))
		
		chp.changeListener(local.inputSubObject, local.inputKey, op, edit, syncId, editId)
	}
}
function changeOnPath(local, op, edit, syncId, editId){
	_.assertInt(op)
	//console.log('path: ' + JSON.stringify(path))
	//_.assertArray(path)
	
	_.assertInt(editId)
	
	/*
	We need:
	- an index of every existing inner object by id
	*/
	
	//console.log('GAR: ' + editNames[op] + ' ' + JSON.stringify(edit))
	
	//console.log('inputObject: ' + local.inputObject)
	//console.log(JSON.stringify(local.edits))
	/*local.edits.forEach(function(e, index){
		console.log(index + ' ' + editNames[e.op] + ' ' + JSON.stringify(e))
	})*/
	//console.log('self: ' + local.id())
	_.assertInt(local.inputProperty)

	//console.log('own: ' + local.objectId + ' ' + local.inputObject + ' ')// + local._forkedObject._internalId())
	
	var ch;
	if(!local.inputObject || local.inputObject === local.objectId || local.inputObject === -1){//TODO handle case where inputObject is the forked object
		ch = local
	}else if(local.lastSelf === local.inputObject){
		ch = local
	}else{
		/*if(editId > 0 && editId !== local.getEditingId() && local.inputObject < 0){
			_.errout('invalid inputObject: ' + local.inputObject + '\t' + editId + ' !== ' + local.getEditingId())
		}*/
		
		if(local.objectApiCache){
			ch = local.objectApiCache[local.inputObject]
		}
		
		/*if(local.inputObject < 0){
			var v = local.parent.temporaryCache[local.inputObject]
			if(v){
				ch = v.n
				//console.log('looked up temporary ' + local.inputObject + ' got ' + Object.keys(ch))
			}
		}*/
		/*if(local.inputObject < 0){
			ch = local.temporary
		}*/
		if(!ch){
			console.log('WARNING: not found ' + local.inputObject + ', only got: ' + JSON.stringify(Object.keys(local.objectApiCache||{})) + ' in ' + local.objectId + ' --- ' + editId)
			console.log(JSON.stringify(local.edits, null, 2))
			throw new Error('here')
			return
		}
		_.assertObject(ch)
	}
	
	var property = ch.typeSchema.propertiesByCode[local.inputProperty]
	if(property === undefined){
		var candidates = ' ' + JSON.stringify(_.map(Object.keys(ch.typeSchema.propertiesByCode),function(p){
			return ch.typeSchema.propertiesByCode[p].name + ':'+ch.typeSchema.propertiesByCode[p].code}))
		if(TopObjectHandle.prototype.errorOnWarn){
			_.errout('WARNING: logic error or removed property, ' + ch.typeSchema.name + ' cannot find property with code: ' + local.inputProperty + candidates)
		}else{
			console.log('WARNING: logic error or removed property, ' + ch.typeSchema.name + ' cannot find property with code: ' + local.inputProperty + candidates)
		}
		return
	}	

	//console.log('modifying property: ' + property.name)
	
	if(op === editCodes.setObject || op === editCodes.setViewObject){
		//_.assertInt(descentCode)
		//var ps = this.typeSchema.propertiesByCode[descentCode];

		//_.assertObject(ps)
		if(op === editCodes.setObject){
			if(property.type.type !== 'object'){
				console.log(JSON.stringify(local.edits, null, 2))
				_.errout('(' + editId + ') setObject called on non object type, type is: ' + JSON.stringify(property.type))
			}
			//_.assertEqual(ps.type.type, 'object')
		}

		var subj
		if(local.inputObject === local.objectId || local.inputObject === local.lastSelf){
			subj = local
		}else{
			subj = local.inputObject!==undefined?local.objectApiCache[local.inputObject]:local
		}

		var pv = subj[property.name]//.cachedProperties[ps.name]
		if(pv && pv.objectId === edit.id){
			//already done
			//console.log('setObject redundant (local view object?), skipping: ' + pv.objectId + ' ' + edit.id)
		}else{
			//this.log('set to: ' + edit.id + ' ' + descentCode + ' ' + this.objectId + ' ' + ps.name)
			var setObj = local.getObjectApi(edit.id)
			if(setObj === undefined){
				local.log.warn('object not found, may have been del\'ed: ' + edit.id)
				local.log(new Error().stack)
				return
			}
			
			if(setObj.isDestroyed()) return

			//subj.obj[local.inputProperty] = setObj;
			setObj.prepare()
			subj[property.name] = setObj
			//console.log('EMITTING SET OBJECT ' + property.name)
			subj.emit(edit, 'set', property.name, setObj)			
		}
	}else if(op === editCodes.clearObject || op === editCodes.clearProperty){

		var subj = local.inputObject!==undefined?local.objectApiCache[local.inputObject]:local

		var ps = subj.typeSchema.propertiesByCode[local.inputProperty];
		
		var chp = ch.property(ps.name)
		
		if(ps.type.type === 'set' || ps.type.type === 'list'){
			chp.obj = []
		}else if(ps.type.type === 'map'){
			chp.obj = {}
		}else{
			chp.obj = undefined
		}
		//subj[ps.name] = undefined
		//subj.obj[local.inputProperty] = undefined
	}else{
		
		//console.log(chp.constructor+'')
		var chp = ch.property(property.name)//[property.name]

		_.assertEqual(ch[property.name], chp)
		
		if(!chp){
			_.errout('property value missing: ' + property.name)
		}
		
		if(!chp.changeListener){
			//console.dir(chp)
			_.errout('cannot determine current edit target(' + property.name+' in ' + ch.typeSchema.name + ' ' + ch.prepared + ' - ' + chp.constructor + ') failed to run edit: ' + JSON.stringify([editNames[op], edit, syncId, editId]))
			return
		}
		//console.log('HERERERERER')
		//console.log('editing ' + editNames[op] + ' ' + (chp.rally = Math.random()))
		
		//console.log(typeof(chp.changeListener))
		
		chp.changeListener(local.inputSubObject, local.inputKey, op, edit, syncId, editId)
	}
}
function changeOnPath ( local , op , edit , syncId , editId ) { _ . assertInt ( op ) //console.log('path: ' + JSON.stringify(path)) //_.assertArray(path) _ . assertInt ( editId ) /*
	We need:
	- an index of every existing inner object by id
	*/ //console.log('GAR: ' + editNames[op] + ' ' + JSON.stringify(edit)) //console.log('inputObject: ' + local.inputObject) //console.log(JSON.stringify(local.edits)) /*local.edits.forEach(function(e, index){
		console.log(index + ' ' + editNames[e.op] + ' ' + JSON.stringify(e))
	})*/ //console.log('self: ' + local.id()) _ . assertInt ( local . inputProperty ) //console.log('own: ' + local.objectId + ' ' + local.inputObject + ' ')// + local._forkedObject._internalId()) var ch ; if ( ! local . inputObject || local . inputObject === local . objectId || local . inputObject === - 1 ) { //TODO handle case where inputObject is the forked object ch = local } else if ( local . lastSelf === local . inputObject ) { ch = local } else { /*if(editId > 0 && editId !== local.getEditingId() && local.inputObject < 0){
			_.errout('invalid inputObject: ' + local.inputObject + '\t' + editId + ' !== ' + local.getEditingId())
		}*/ if ( local . objectApiCache ) { ch = local . objectApiCache [ local . inputObject ] } /*if(local.inputObject < 0){
			var v = local.parent.temporaryCache[local.inputObject]
			if(v){
				ch = v.n
				//console.log('looked up temporary ' + local.inputObject + ' got ' + Object.keys(ch))
			}
		}*/ /*if(local.inputObject < 0){
			ch = local.temporary
		}*/ if ( ! ch ) { console . log ( 'WARNING: not found ' + local . inputObject + ', only got: ' + JSON . stringify ( Object . keys ( local . objectApiCache || { } ) ) + ' in ' + local . objectId + ' --- ' + editId ) console . log ( JSON . stringify ( local . edits , null , 2 ) ) throw new Error ( 'here' ) return } _ . assertObject ( ch ) } var property = ch . typeSchema . propertiesByCode [ local . inputProperty ] if ( property === undefined ) { var candidates = ' ' + JSON . stringify ( _ . map ( Object . keys ( ch . typeSchema . propertiesByCode ) , function ( p ) { return ch . typeSchema . propertiesByCode [ p ] . name + ':' + ch . typeSchema . propertiesByCode [ p ] . code } ) ) if ( TopObjectHandle . prototype . errorOnWarn ) { _ . errout ( 'WARNING: logic error or removed property, ' + ch . typeSchema . name + ' cannot find property with code: ' + local . inputProperty + candidates ) } else { console . log ( 'WARNING: logic error or removed property, ' + ch . typeSchema . name + ' cannot find property with code: ' + local . inputProperty + candidates ) } return } //console.log('modifying property: ' + property.name) if ( op === editCodes . setObject || op === editCodes . setViewObject ) { //_.assertInt(descentCode) //var ps = this.typeSchema.propertiesByCode[descentCode]; //_.assertObject(ps) if ( op === editCodes . setObject ) { if ( property . type . type !== 'object' ) { console . log ( JSON . stringify ( local . edits , null , 2 ) ) _ . errout ( '(' + editId + ') setObject called on non object type, type is: ' + JSON . stringify ( property . type ) ) } //_.assertEqual(ps.type.type, 'object') } var subj if ( local . inputObject === local . objectId || local . inputObject === local . lastSelf ) { subj = local } else { subj = local . inputObject !== undefined ? local . objectApiCache [ local . inputObject ] : local } var pv = subj [ property . name ] //.cachedProperties[ps.name] if ( pv && pv . objectId === edit . id ) { //already done //console.log('setObject redundant (local view object?), skipping: ' + pv.objectId + ' ' + edit.id) } else { //this.log('set to: ' + edit.id + ' ' + descentCode + ' ' + this.objectId + ' ' + ps.name) var setObj = local . getObjectApi ( edit . id ) if ( setObj === undefined ) { local . log . warn ( 'object not found, may have been del\'ed: ' + edit . id ) local . log ( new Error ( ) . stack ) return } if ( setObj . isDestroyed ( ) ) return //subj.obj[local.inputProperty] = setObj; setObj . prepare ( ) subj [ property . name ] = setObj //console.log('EMITTING SET OBJECT ' + property.name) subj . emit ( edit , 'set' , property . name , setObj ) } } else if ( op === editCodes . clearObject || op === editCodes . clearProperty ) { var subj = local . inputObject !== undefined ? local . objectApiCache [ local . inputObject ] : local var ps = subj . typeSchema . propertiesByCode [ local . inputProperty ] ; var chp = ch . property ( ps . name ) if ( ps . type . type === 'set' || ps . type . type === 'list' ) { chp . obj = [ ] } else if ( ps . type . type === 'map' ) { chp . obj = { } } else { chp . obj = undefined } //subj[ps.name] = undefined //subj.obj[local.inputProperty] = undefined } else { //console.log(chp.constructor+'') var chp = ch . property ( property . name ) //[property.name] _ . assertEqual ( ch [ property . name ] , chp ) if ( ! chp ) { _ . errout ( 'property value missing: ' + property . name ) } if ( ! chp . changeListener ) { //console.dir(chp) _ . errout ( 'cannot determine current edit target(' + property . name + ' in ' + ch . typeSchema . name + ' ' + ch . prepared + ' - ' + chp . constructor + ') failed to run edit: ' + JSON . stringify ( [ editNames [ op ] , edit , syncId , editId ] ) ) return } //console.log('HERERERERER') //console.log('editing ' + editNames[op] + ' ' + (chp.rally = Math.random())) //console.log(typeof(chp.changeListener)) chp . changeListener ( local . inputSubObject , local . inputKey , op , edit , syncId , editId ) } }
/*
TopObjectHandle.prototype._acceptForkChange = function(subObj, key, op, edit, syncId, editId){//, path){

console.log('MAINTAINING FORK: ' + op + ' ' + JSON.stringify(edit) + ' on ' + this.objectId + ' ' + syncId + ' ' + this.getEditingId())

var manyToRemove = 0
for(var i=0;i<this.edits.length;++i){
var e = this.edits[i]
if((e.op === editCodes.made || e.op === editCodes.madeFork) && e.edit.sourceId === this._forkedObject._internalId()){
break;
}
++manyToRemove
}
this.edits = this.edits.slice(manyToRemove)
this.edits = [].concat(this._forkedObject._getEdits()).concat(this.edits)

this._rebuild()
}
/ * TopObjectHandle . prototype . _acceptForkChange = function ( subObj key op edit syncId editId ) { // path ) {