116 lines
3.1 KiB
CoffeeScript
116 lines
3.1 KiB
CoffeeScript
|
|
window?.unprocessed_counter = 0 # del this
|
|
window?.unprocessed_exec_counter = 0 # TODO
|
|
window?.unprocessed_types = []
|
|
|
|
#
|
|
# @nodoc
|
|
# The Engine handles how and in which order to execute operations and add operations to the HistoryBuffer.
|
|
#
|
|
class Engine
|
|
|
|
#
|
|
# @param {HistoryBuffer} HB
|
|
# @param {Object} types list of available types
|
|
#
|
|
constructor: (@HB, @types)->
|
|
@unprocessed_ops = []
|
|
|
|
#
|
|
# Parses an operatio from the json format. It uses the specified parser in your OperationType module.
|
|
#
|
|
parseOperation: (json)->
|
|
type = @types[json.type]
|
|
if type?.parse?
|
|
type.parse json
|
|
else
|
|
throw new Error "You forgot to specify a parser for type #{json.type}. The message is #{JSON.stringify json}."
|
|
|
|
|
|
#
|
|
# Apply a set of operations. E.g. the operations you received from another users HB._encode().
|
|
# @note You must not use this method when you already have ops in your HB!
|
|
###
|
|
applyOpsBundle: (ops_json)->
|
|
ops = []
|
|
for o in ops_json
|
|
ops.push @parseOperation o
|
|
for o in ops
|
|
if not o.execute()
|
|
@unprocessed_ops.push o
|
|
@tryUnprocessed()
|
|
###
|
|
|
|
#
|
|
# Same as applyOps but operations that are already in the HB are not applied.
|
|
# @see Engine.applyOps
|
|
#
|
|
applyOpsCheckDouble: (ops_json)->
|
|
for o in ops_json
|
|
if not @HB.getOperation(o.uid)?
|
|
@applyOp o
|
|
|
|
#
|
|
# Apply a set of operations. (Helper for using applyOp on Arrays)
|
|
# @see Engine.applyOp
|
|
applyOps: (ops_json)->
|
|
@applyOp ops_json
|
|
|
|
#
|
|
# Apply an operation that you received from another peer.
|
|
# TODO: make this more efficient!!
|
|
# - operations may only executed in order by creator, order them in object of arrays (key by creator)
|
|
# - you can probably make something like dependencies (creator1 waits for creator2)
|
|
applyOp: (op_json_array, fromHB = false)->
|
|
if op_json_array.constructor isnt Array
|
|
op_json_array = [op_json_array]
|
|
for op_json in op_json_array
|
|
if fromHB
|
|
op_json.fromHB = "true" # execute immediately, if
|
|
# $parse_and_execute will return false if $o_json was parsed and executed, otherwise the parsed operadion
|
|
o = @parseOperation op_json
|
|
o.parsed_from_json = op_json
|
|
if op_json.fromHB?
|
|
o.fromHB = op_json.fromHB
|
|
# @HB.addOperation o
|
|
if @HB.getOperation(o)?
|
|
# nop
|
|
else if ((not @HB.isExpectedOperation(o)) and (not o.fromHB?)) or (not o.execute())
|
|
@unprocessed_ops.push o
|
|
window?.unprocessed_types.push o.type # TODO: delete this
|
|
@tryUnprocessed()
|
|
|
|
#
|
|
# Call this method when you applied a new operation.
|
|
# It checks if operations that were previously not executable are now executable.
|
|
#
|
|
tryUnprocessed: ()->
|
|
while true
|
|
old_length = @unprocessed_ops.length
|
|
unprocessed = []
|
|
for op in @unprocessed_ops
|
|
if @HB.getOperation(op)?
|
|
# nop
|
|
else if (not @HB.isExpectedOperation(op) and (not op.fromHB?)) or (not op.execute())
|
|
unprocessed.push op
|
|
@unprocessed_ops = unprocessed
|
|
if @unprocessed_ops.length is old_length
|
|
break
|
|
if @unprocessed_ops.length isnt 0
|
|
@HB.invokeSync()
|
|
|
|
|
|
module.exports = Engine
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|