100% documentation
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
|
||||
#
|
||||
# @param {Function} callback The callback is called when the connector is initialized.
|
||||
#
|
||||
createIwcConnector = (callback)->
|
||||
iwcHandler = {}
|
||||
duiClient = new DUIClient()
|
||||
@@ -21,6 +24,13 @@ createIwcConnector = (callback)->
|
||||
# @see http://dbis.rwth-aachen.de/cms/projects/ROLE
|
||||
#
|
||||
class IwcConnector
|
||||
|
||||
#
|
||||
# @param {Engine} engine The transformation engine
|
||||
# @param {HistoryBuffer} HB
|
||||
# @param {Array<Function>} execution_listener You must ensure that whenever an operation is executed, every function in this Array is called.
|
||||
# @param {Yatta} yatta The Yatta framework.
|
||||
#
|
||||
constructor: (@engine, @HB, @execution_listener, @yatta)->
|
||||
@duiClient = duiClient
|
||||
@iwcHandler = iwcHandler
|
||||
@@ -44,14 +54,27 @@ createIwcConnector = (callback)->
|
||||
@sendIwcIntent "Yatta_push_HB_element", json
|
||||
@iwcHandler["Yatta_get_HB_element"] = [sendHistoryBuffer]
|
||||
|
||||
#
|
||||
# This function is called whenever an operation was executed.
|
||||
# @param {Operation} o The operation that was executed.
|
||||
#
|
||||
send: (o)->
|
||||
if o.uid.creator is @HB.getUserId() and (typeof o.uid.op_number isnt "string")
|
||||
@sendIwcIntent "Yatta_new_operation", o
|
||||
|
||||
#
|
||||
# This function is called whenever an operation was received from another peer.
|
||||
# @param {Operation} o The operation that was received.
|
||||
#
|
||||
receive: (o)->
|
||||
if o.uid.creator isnt @HB.getUserId()
|
||||
@engine.applyOp o
|
||||
|
||||
#
|
||||
# Helper for sending iwc intents.
|
||||
# @param {String} action_name The name of the action that is going to be send.
|
||||
# @param {String} content The content that is atteched to the intent.
|
||||
#
|
||||
sendIwcIntent: (action_name, content)->
|
||||
intent =
|
||||
action: action_name
|
||||
@@ -62,9 +85,6 @@ createIwcConnector = (callback)->
|
||||
|
||||
@duiClient.sendIntent(intent)
|
||||
|
||||
sync: ()->
|
||||
throw new Error "Can't use this a.t.m."
|
||||
|
||||
get_HB_intent =
|
||||
action: "Yatta_get_HB_element"
|
||||
component: ""
|
||||
|
||||
@@ -7,6 +7,13 @@ module.exports = (user_list)->
|
||||
# A trivial Connector that simulates network delay.
|
||||
#
|
||||
class TestConnector
|
||||
|
||||
#
|
||||
# @param {Engine} engine The transformation engine
|
||||
# @param {HistoryBuffer} HB
|
||||
# @param {Array<Function>} execution_listener You must ensure that whenever an operation is executed, every function in this Array is called.
|
||||
# @param {Yatta} yatta The Yatta framework.
|
||||
#
|
||||
constructor: (@engine, @HB, @execution_listener)->
|
||||
send_ = (o)=>
|
||||
@send o
|
||||
@@ -21,29 +28,49 @@ module.exports = (user_list)->
|
||||
|
||||
@unexecuted = {}
|
||||
|
||||
#
|
||||
# This engine applied operations in a specific order.
|
||||
# Get the ops in the right order.
|
||||
#
|
||||
getOpsInExecutionOrder: ()->
|
||||
@applied_operations
|
||||
|
||||
#
|
||||
# This function is called whenever an operation was executed.
|
||||
# @param {Operation} o The operation that was executed.
|
||||
#
|
||||
send: (o)->
|
||||
if (o.uid.creator is @HB.getUserId()) and (typeof o.uid.op_number isnt "string")
|
||||
for user in user_list
|
||||
if user.getUserId() isnt @HB.getUserId()
|
||||
user.getConnector().receive(o)
|
||||
|
||||
#
|
||||
# This function is called whenever an operation was received from another peer.
|
||||
# @param {Operation} o The operation that was received.
|
||||
#
|
||||
receive: (o)->
|
||||
@unexecuted[o.uid.creator] ?= []
|
||||
@unexecuted[o.uid.creator].push o
|
||||
|
||||
#
|
||||
# Flush one operation from the line of a specific user.
|
||||
#
|
||||
flushOne: (user)->
|
||||
if @unexecuted[user]?.length > 0
|
||||
@engine.applyOp @unexecuted[user].shift()
|
||||
|
||||
#
|
||||
# Flush one operation on a random line.
|
||||
#
|
||||
flushOneRandom: ()->
|
||||
@flushOne (_.random 0, (user_list.length-1))
|
||||
|
||||
#
|
||||
# Flush all operations on every line.
|
||||
#
|
||||
flushAll: ()->
|
||||
for n,ops of @unexecuted
|
||||
@engine.applyOps ops
|
||||
@unexecuted = {}
|
||||
sync: ()->
|
||||
throw new Error "Can't use this a.t.m."
|
||||
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
# The Engine handles how and in which order to execute operations and add operations to the HistoryBuffer.
|
||||
#
|
||||
class Engine
|
||||
|
||||
#
|
||||
# @param {HistoryBuffer} HB
|
||||
# @param {Array} parser Defines how to parse encoded messages.
|
||||
#
|
||||
constructor: (@HB, @parser)->
|
||||
@unprocessed_ops = []
|
||||
|
||||
@@ -31,6 +36,10 @@ class Engine
|
||||
@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 @HB.getOperation(o.uid)?
|
||||
@@ -49,6 +58,7 @@ class Engine
|
||||
applyOp: (op_json)->
|
||||
# $parse_and_execute will return false if $o_json was parsed and executed, otherwise the parsed operadion
|
||||
o = @parseOperation op_json
|
||||
@HB.addToCounter o
|
||||
# @HB.addOperation o
|
||||
if not o.execute()
|
||||
@unprocessed_ops.push o
|
||||
|
||||
@@ -11,6 +11,11 @@ Engine = require "../Engine.coffee"
|
||||
# * Array
|
||||
#
|
||||
class JsonYatta
|
||||
|
||||
#
|
||||
# @param {String} user_id Unique id of the peer.
|
||||
# @param {Connector} Connector the connector class.
|
||||
#
|
||||
constructor: (user_id, Connector)->
|
||||
@HB = new HistoryBuffer user_id
|
||||
json_types = json_types_uninitialized @HB
|
||||
@@ -21,7 +26,6 @@ class JsonYatta
|
||||
@HB.addOperation(first_word).execute()
|
||||
@root_element = first_word
|
||||
|
||||
|
||||
#
|
||||
# @result JsonType
|
||||
#
|
||||
|
||||
@@ -7,46 +7,76 @@ Engine = require "../Engine.coffee"
|
||||
# Framework for Text Datastructures.
|
||||
#
|
||||
class TextYatta
|
||||
|
||||
#
|
||||
# @param {String} user_id Uniqe user id that defines this peer.
|
||||
# @param {Connector} Connector The connector defines how you connect to the other peers.
|
||||
#
|
||||
constructor: (user_id, Connector)->
|
||||
@HB = new HistoryBuffer user_id
|
||||
text_types = text_types_uninitialized @HB
|
||||
@engine = new Engine @HB, text_types.parser
|
||||
@connector = new Connector @engine, @HB, text_types.execution_listener
|
||||
root_elem = @connector.getRootElement()
|
||||
if not root_elem?
|
||||
first_word = new text_types.types.Word @HB.getNextOperationIdentifier()
|
||||
@HB.addOperation(first_word)
|
||||
first_word.execute()
|
||||
@root_element = @HB.addOperation(new text_types.types.ReplaceManager first_word, @HB.getNextOperationIdentifier()).execute()
|
||||
else
|
||||
@root_element = @HB.getOperation(root_elem)
|
||||
|
||||
first_word = new text_types.types.Word undefined
|
||||
@HB.addOperation(first_word).execute()
|
||||
@root_element = first_word
|
||||
|
||||
#
|
||||
# @result Word
|
||||
#
|
||||
getRootElement: ()->
|
||||
@root_element
|
||||
|
||||
#
|
||||
# @see Engine
|
||||
#
|
||||
getEngine: ()->
|
||||
@engine
|
||||
|
||||
#
|
||||
# Get the initialized connector.
|
||||
#
|
||||
getConnector: ()->
|
||||
@connector
|
||||
|
||||
#
|
||||
# @see HistoryBuffer
|
||||
#
|
||||
getHistoryBuffer: ()->
|
||||
@HB
|
||||
|
||||
#
|
||||
# Get the UserId from the HistoryBuffer object.
|
||||
# In most cases this will be the same as the user_id value with which
|
||||
# JsonYatta was initialized (Depending on the HistoryBuffer implementation).
|
||||
#
|
||||
getUserId: ()->
|
||||
@HB.getUserId()
|
||||
|
||||
#
|
||||
# @see JsonType.val
|
||||
#
|
||||
val: ()->
|
||||
@root_element.val().val()
|
||||
@root_element.val()
|
||||
|
||||
#
|
||||
# @see Word.insertText
|
||||
#
|
||||
insertText: (pos, content)->
|
||||
@root_element.val().insertText pos, content
|
||||
@root_element.insertText pos, content
|
||||
|
||||
#
|
||||
# @see Word.deleteText
|
||||
#
|
||||
deleteText: (pos, length)->
|
||||
@root_element.val().deleteText pos, length
|
||||
@root_element.deleteText pos, length
|
||||
|
||||
#
|
||||
# @see Word.replaceText
|
||||
#
|
||||
replaceText: (text)->
|
||||
@root_element.val().replaceText text
|
||||
@root_element.replaceText text
|
||||
|
||||
|
||||
module.exports = TextYatta
|
||||
|
||||
@@ -42,6 +42,9 @@ class HistoryBuffer
|
||||
res[user] = ctn
|
||||
res
|
||||
|
||||
#
|
||||
# Encode this operation in such a way that it can be parsed by remote peers.
|
||||
#
|
||||
_encode: (state_vector={})->
|
||||
json = []
|
||||
unknown = (user, o_number)->
|
||||
@@ -84,28 +87,38 @@ class HistoryBuffer
|
||||
@operation_counter[user_id]++
|
||||
uid
|
||||
|
||||
#
|
||||
# Retrieve an operation from a unique id.
|
||||
#
|
||||
getOperation: (uid)->
|
||||
if uid instanceof Object
|
||||
@buffer[uid.creator]?[uid.op_number]
|
||||
else if not uid?
|
||||
else
|
||||
throw new Error "This type of uid is not defined!"
|
||||
|
||||
#
|
||||
# Add an operation to the HB. Note that this will not link it against
|
||||
# other operations (it wont executed)
|
||||
#
|
||||
addOperation: (o)->
|
||||
if not @buffer[o.creator]?
|
||||
@buffer[o.creator] = {}
|
||||
if not @operation_counter[o.creator]?
|
||||
@operation_counter[o.creator] = 0
|
||||
#if @operation_counter[o.creator] isnt o.op_number and typeof o.op_number is 'number'
|
||||
# throw new Error "You don't receive operations in the proper order. Try counting like this 0,1,2,3,4,.. ;)"
|
||||
if @buffer[o.creator][o.op_number]?
|
||||
throw new Error "You must not overwrite operations!"
|
||||
@buffer[o.creator][o.op_number] = o
|
||||
if typeof o.op_number is 'number' and o.creator isnt @getUserId()
|
||||
@operation_counter[o.creator]++
|
||||
o
|
||||
|
||||
#
|
||||
# Increment the operation_counter that defines the current state of the Engine.
|
||||
#
|
||||
addToCounter: (o)->
|
||||
if not @operation_counter[o.creator]?
|
||||
@operation_counter[o.creator] = 0
|
||||
if typeof o.op_number is 'number' and o.creator isnt @getUserId()
|
||||
@operation_counter[o.creator]++
|
||||
#if @operation_counter[o.creator] isnt (o.op_number + 1)
|
||||
#console.log (@operation_counter[o.creator] - (o.op_number + 1))
|
||||
#console.log o
|
||||
#throw new Error "You don't receive operations in the proper order. Try counting like this 0,1,2,3,4,.. ;)"
|
||||
|
||||
module.exports = HistoryBuffer
|
||||
|
||||
@@ -303,6 +303,9 @@ module.exports = (HB)->
|
||||
val : ()->
|
||||
@content
|
||||
|
||||
#
|
||||
# Encode this operation in such a way that it can be parsed by remote peers.
|
||||
#
|
||||
_encode: ()->
|
||||
json = {
|
||||
'type': "ImmutableObject"
|
||||
|
||||
@@ -64,6 +64,10 @@ module.exports = (HB)->
|
||||
# console.log(w.newProperty == "Awesome") # true!
|
||||
#
|
||||
class JsonWrapper
|
||||
|
||||
#
|
||||
# @param {JsonType} jsonType Instance of the JsonType that this class wrappes.
|
||||
#
|
||||
constructor: (jsonType)->
|
||||
for name, obj of jsonType.map
|
||||
do (name, obj)->
|
||||
@@ -105,9 +109,15 @@ module.exports = (HB)->
|
||||
for name,o of initial_value
|
||||
@val name, o, mutable
|
||||
|
||||
#
|
||||
# Whether the default is 'mutable' (true) or 'immutable' (false)
|
||||
#
|
||||
mutable_default:
|
||||
true
|
||||
|
||||
#
|
||||
# Set if the default is 'mutable' or 'immutable'
|
||||
# @param {String|Boolean} mutable Set either 'mutable' / true or 'immutable' / false
|
||||
setMutableDefault: (mutable)->
|
||||
if mutable is true or mutable is 'mutable'
|
||||
JsonType.prototype.mutable_default = true
|
||||
|
||||
@@ -17,6 +17,9 @@ module.exports = (HB)->
|
||||
@map = {}
|
||||
super uid
|
||||
|
||||
#
|
||||
# @see JsonTypes.val
|
||||
#
|
||||
val: (name, content)->
|
||||
if content?
|
||||
if not @map[name]?
|
||||
@@ -55,6 +58,12 @@ module.exports = (HB)->
|
||||
@saveOperation 'map_manager', map_manager
|
||||
super uid
|
||||
|
||||
#
|
||||
# If map_manager doesn't have the property name, then add it.
|
||||
# The ReplaceManager that is being written on the property is unique
|
||||
# in such a way that if AddName is executed (from another peer) it will
|
||||
# always have the same result (ReplaceManager, and its beginning and end are the same)
|
||||
#
|
||||
execute: ()->
|
||||
if not @validateSavedOperations()
|
||||
return false
|
||||
@@ -72,6 +81,9 @@ module.exports = (HB)->
|
||||
@map_manager.map[@name] = HB.addOperation(new ReplaceManager undefined, uid_r, beg, end).execute()
|
||||
super
|
||||
|
||||
#
|
||||
# Encode this operation in such a way that it can be parsed by remote peers.
|
||||
#
|
||||
_encode: ()->
|
||||
{
|
||||
'type' : "AddName"
|
||||
@@ -163,17 +175,27 @@ module.exports = (HB)->
|
||||
if initial_content?
|
||||
@replace initial_content
|
||||
|
||||
#
|
||||
# Replace the existing word with a new word.
|
||||
#
|
||||
replace: (content)->
|
||||
o = @getLastOperation()
|
||||
op = new Replaceable content, @, undefined, o, o.next_cl
|
||||
HB.addOperation(op).execute()
|
||||
|
||||
#
|
||||
# Get the value of this Word
|
||||
# @return {String}
|
||||
#
|
||||
val: ()->
|
||||
o = @getLastOperation()
|
||||
if o instanceof types.Delimiter
|
||||
throw new Error "dtrn"
|
||||
o.val()
|
||||
|
||||
#
|
||||
# Encode this operation in such a way that it can be parsed by remote peers.
|
||||
#
|
||||
_encode: ()->
|
||||
json =
|
||||
{
|
||||
@@ -220,12 +242,22 @@ module.exports = (HB)->
|
||||
throw new Error "You must define content, prev, and next for Replaceable-types!"
|
||||
super uid, prev, next, origin
|
||||
|
||||
#
|
||||
# Return the content that this operation holds.
|
||||
#
|
||||
val: ()->
|
||||
@content
|
||||
|
||||
#
|
||||
# Replace the content of this replaceable with new content.
|
||||
#
|
||||
replace: (content)->
|
||||
@parent.replace content
|
||||
|
||||
#
|
||||
# If possible set the replace manager in the content.
|
||||
# @see Word.setReplaceManager
|
||||
#
|
||||
execute: ()->
|
||||
if not @validateSavedOperations()
|
||||
return false
|
||||
@@ -235,8 +267,7 @@ module.exports = (HB)->
|
||||
@
|
||||
|
||||
#
|
||||
# Convert all relevant information of this operation to the json-format.
|
||||
# This result can be send to other clients.
|
||||
# Encode this operation in such a way that it can be parsed by remote peers.
|
||||
#
|
||||
_encode: ()->
|
||||
json =
|
||||
|
||||
@@ -140,6 +140,9 @@ module.exports = (HB)->
|
||||
@saveOperation 'replace_manager', op
|
||||
@validateSavedOperations
|
||||
|
||||
#
|
||||
# Encode this operation in such a way that it can be parsed by remote peers.
|
||||
#
|
||||
_encode: ()->
|
||||
json = {
|
||||
'type': "Word"
|
||||
|
||||
Reference in New Issue
Block a user