Completed JsonYatta. Added IwcConnector
This commit is contained in:
94
lib/Connectors/IwcConnector.coffee
Normal file
94
lib/Connectors/IwcConnector.coffee
Normal file
@@ -0,0 +1,94 @@
|
||||
|
||||
createIwcConnector = (callback)->
|
||||
iwcHandler = {}
|
||||
duiClient = new DUIClient()
|
||||
#@duiClient = new iwc.Client()
|
||||
duiClient.connect (intent)=>
|
||||
console.log "intent received iwc: #{JSON.stringify(intent)}"
|
||||
console.log "#{JSON.stringify(@iwcHandler)}"
|
||||
iwcHandler[intent.action]?.map (f)->
|
||||
setTimeout ()->
|
||||
f intent
|
||||
, 0
|
||||
|
||||
duiClient.initOK()
|
||||
|
||||
root_element = null
|
||||
received_HB = null
|
||||
|
||||
|
||||
class IwcConnector
|
||||
constructor: (@engine, @HB, @execution_listener, @yatta)->
|
||||
@duiClient = duiClient
|
||||
@iwcHandler = iwcHandler
|
||||
|
||||
send_ = (o)=>
|
||||
@send o
|
||||
@execution_listener.push send_
|
||||
|
||||
receive_ = (intent)=>
|
||||
o = intent.extras
|
||||
@receive o
|
||||
@iwcHandler["Yatta_new_operation"] = [receive_]
|
||||
|
||||
if root_element?
|
||||
@engine.applyOps received_HB
|
||||
|
||||
sendRootElement = ()=>
|
||||
json = {
|
||||
root_element : @yatta.getRootElement()
|
||||
HB : @yatta.getHistoryBuffer().toJson()
|
||||
}
|
||||
@sendIwcIntent "Yatta_push_root_element", json
|
||||
@iwcHandler["Yatta_get_root_element"] = [sendRootElement]
|
||||
|
||||
getRootElement: ()->
|
||||
root_element
|
||||
|
||||
send: (o)->
|
||||
if o.uid.creator is @HB.getUserId() and (typeof o.uid.op_number isnt "string")
|
||||
@sendIwcIntent "Yatta_new_operation", o
|
||||
|
||||
receive: (o)->
|
||||
if o.uid.creator isnt @HB.getUserId()
|
||||
@engine.applyOp o
|
||||
|
||||
sendIwcIntent: (action_name, content)->
|
||||
intent =
|
||||
action: action_name
|
||||
component: ""
|
||||
data: ""
|
||||
dataType: ""
|
||||
extras: content
|
||||
|
||||
@duiClient.publishToUser(intent)
|
||||
|
||||
sync: ()->
|
||||
throw new Error "Can't use this a.t.m."
|
||||
|
||||
get_root_intent =
|
||||
action: "Yatta_get_root_element"
|
||||
component: ""
|
||||
data: ""
|
||||
dataType: ""
|
||||
extras: {}
|
||||
|
||||
init = ()->
|
||||
duiClient.publishToUser(get_root_intent)
|
||||
|
||||
is_initialized = false
|
||||
receiveRootElement = (json)->
|
||||
root_element = json?.extras.root_element
|
||||
received_HB = json?.extras.HB
|
||||
if not is_initialized
|
||||
is_initialized = true
|
||||
callback IwcConnector
|
||||
iwcHandler["Yatta_push_root_element"] = [receiveRootElement]
|
||||
setTimeout receiveRootElement, 3000
|
||||
|
||||
setTimeout init, 10
|
||||
|
||||
undefined
|
||||
module.exports = createIwcConnector
|
||||
window?.createIwcConnector = createIwcConnector
|
||||
|
||||
@@ -8,27 +8,26 @@ module.exports = (user_list)->
|
||||
@send o
|
||||
@execution_listener.push send_
|
||||
|
||||
@applied_operations = []
|
||||
appliedOperationsListener = (o)=>
|
||||
@applied_operations.push o
|
||||
@execution_listener.push appliedOperationsListener
|
||||
if not (user_list?.length is 0)
|
||||
@engine.applyOps user_list[0].getHistoryBuffer().toJson()
|
||||
|
||||
@unexecuted = {}
|
||||
@applied_operations = []
|
||||
|
||||
appliedOperationsListener = (o)=>
|
||||
@applied_operations.push o
|
||||
@execution_listener.push appliedOperationsListener
|
||||
|
||||
getOpsInExecutionOrder: ()->
|
||||
@applied_operations
|
||||
|
||||
getRootElement: ()->
|
||||
if user_list.length > 0
|
||||
user_list[0].getRootElement()
|
||||
user_list[0].getRootElement().getUid()
|
||||
|
||||
send: (o)->
|
||||
if o.creator is @HB.getUserId()
|
||||
if (o.uid.creator is @HB.getUserId()) and (typeof o.uid.op_number isnt "string")
|
||||
for user in user_list
|
||||
if not user.getUserId() is @HB.getUserId()
|
||||
if user.getUserId() isnt @HB.getUserId()
|
||||
user.getConnector().receive(o)
|
||||
|
||||
receive: (o)->
|
||||
@@ -43,9 +42,8 @@ module.exports = (user_list)->
|
||||
@flushOne (_.random 0, (user_list.length-1))
|
||||
|
||||
flushAll: ()->
|
||||
for ops of @unexecuted
|
||||
for n,ops of @unexecuted
|
||||
@engine.applyOps ops
|
||||
@unexecuted = {}
|
||||
|
||||
sync: ()->
|
||||
throw new Error "Can't use this a.t.m."
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
_ = require "underscore"
|
||||
|
||||
class Engine
|
||||
constructor: (@HB, @parser)->
|
||||
@@ -12,21 +11,37 @@ class Engine
|
||||
throw new Error "You forgot to specify a parser for type #{json.type}. The message is #{JSON.stringify json}."
|
||||
|
||||
# TODO:
|
||||
applyOps: (ops)->
|
||||
applyOps: (ops_json)->
|
||||
ops = []
|
||||
for o in ops_json
|
||||
ops.push @parseOperation o
|
||||
for o in ops
|
||||
@applyOp o
|
||||
@HB.addOperation o
|
||||
for o in ops
|
||||
if not o.execute()
|
||||
@unprocessed_ops.push o
|
||||
@cleanUp()
|
||||
|
||||
cleanUp: ()->
|
||||
while true
|
||||
old_length = @unprocessed_ops.length
|
||||
unprocessed = []
|
||||
for op in @unprocessed_ops
|
||||
if not op.execute()
|
||||
unprocessed.push op
|
||||
@unprocessed_ops = unprocessed
|
||||
if @unprocessed_ops.length is old_length
|
||||
break
|
||||
|
||||
applyOp: (op_json)->
|
||||
# $parse_and_execute will return false if $o_json was parsed and executed, otherwise the parsed operadion
|
||||
o = @parseOperation o_json
|
||||
o = @parseOperation op_json
|
||||
@HB.addOperation o
|
||||
if not o.execute()
|
||||
@unprocessed_ops.push o
|
||||
unprocessed = []
|
||||
for op in @unprocessed_ops
|
||||
if not op.execute()
|
||||
unprocessed.push op
|
||||
@unprocessed_ops = unprocessed
|
||||
@cleanUp()
|
||||
|
||||
|
||||
|
||||
|
||||
module.exports = Engine
|
||||
|
||||
40
lib/Frameworks/JsonYatta.coffee
Normal file
40
lib/Frameworks/JsonYatta.coffee
Normal file
@@ -0,0 +1,40 @@
|
||||
|
||||
json_types_uninitialized = require "../Types/JsonTypes.coffee"
|
||||
HistoryBuffer = require "../HistoryBuffer.coffee"
|
||||
Engine = require "../Engine.coffee"
|
||||
|
||||
class JsonYatta
|
||||
constructor: (user_id, Connector)->
|
||||
@HB = new HistoryBuffer user_id
|
||||
json_types = json_types_uninitialized @HB
|
||||
@engine = new Engine @HB, json_types.parser
|
||||
@connector = new Connector @engine, @HB, json_types.execution_listener, @
|
||||
root_elem = @connector.getRootElement()
|
||||
if not root_elem?
|
||||
first_word = new json_types.types.JsonType @HB.getNextOperationIdentifier()
|
||||
@HB.addOperation(first_word)
|
||||
first_word.execute()
|
||||
@root_element = first_word
|
||||
else
|
||||
@root_element = @HB.getOperation(root_elem)
|
||||
|
||||
getRootElement: ()->
|
||||
@root_element
|
||||
|
||||
getEngine: ()->
|
||||
@engine
|
||||
|
||||
getConnector: ()->
|
||||
@connector
|
||||
|
||||
getHistoryBuffer: ()->
|
||||
@HB
|
||||
|
||||
getUserId: ()->
|
||||
@HB.getUserId()
|
||||
|
||||
val: (name, content)->
|
||||
@root_element.val(name, content)
|
||||
|
||||
window?.JsonYatta = JsonYatta
|
||||
module.exports = JsonYatta
|
||||
@@ -9,12 +9,14 @@ class TextYatta
|
||||
text_types = text_types_uninitialized @HB
|
||||
@engine = new Engine @HB, text_types.parser
|
||||
@connector = new Connector @engine, @HB, text_types.execution_listener
|
||||
@root_element = @connector.getRootElement()
|
||||
if not @root_element?
|
||||
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)
|
||||
|
||||
getRootElement: ()->
|
||||
@root_element
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
_ = require "underscore"
|
||||
|
||||
#
|
||||
# An object that holds all applied operations.
|
||||
@@ -22,13 +21,17 @@ class HistoryBuffer
|
||||
@user_id
|
||||
|
||||
getOperationCounter: ()->
|
||||
_.clone @operation_counter
|
||||
res = {}
|
||||
for user,ctn of @operation_counter
|
||||
res[user] = ctn
|
||||
res
|
||||
|
||||
toJson: ()->
|
||||
json = []
|
||||
for user in @buffer
|
||||
for o of user
|
||||
json.push o.toJson()
|
||||
for u_name,user of @buffer
|
||||
for o_number,o of user
|
||||
if not isNaN(parseInt(o_number))
|
||||
json.push o.toJson()
|
||||
json
|
||||
|
||||
# Get the number of operations that were created by a user.
|
||||
@@ -51,6 +54,7 @@ class HistoryBuffer
|
||||
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!"
|
||||
|
||||
@@ -70,6 +74,4 @@ class HistoryBuffer
|
||||
@operation_counter[o.creator]++
|
||||
o
|
||||
|
||||
|
||||
|
||||
module.exports = HistoryBuffer
|
||||
|
||||
@@ -16,6 +16,7 @@ module.exports = (HB)->
|
||||
{ 'creator': @creator, 'op_number': @op_number }
|
||||
|
||||
execute: ()->
|
||||
@is_executed = true
|
||||
for l in execution_listener
|
||||
l @toJson()
|
||||
@
|
||||
@@ -62,7 +63,7 @@ module.exports = (HB)->
|
||||
if op
|
||||
@[name] = op
|
||||
else
|
||||
uninstantiated[name] = op
|
||||
uninstantiated[name] = op_uid
|
||||
success = false
|
||||
delete @unchecked
|
||||
if not success
|
||||
@@ -102,7 +103,7 @@ module.exports = (HB)->
|
||||
# Define how to parse $Delete operations.
|
||||
#
|
||||
parser['Delete'] = ({'uid' : uid, 'deletes': deletes_uid})->
|
||||
new D uid, deletes_uid
|
||||
new Delete uid, deletes_uid
|
||||
|
||||
#
|
||||
# A simple insert-type operation.
|
||||
@@ -114,7 +115,6 @@ module.exports = (HB)->
|
||||
# - The complete-list (abbrev. cl) maintains all operations
|
||||
#
|
||||
class Insert extends Operation
|
||||
# @param {Value} content The value of the insert operation. E.g. for strings content is a char.
|
||||
# @param {Object} creator A unique user identifier
|
||||
# @param {Integer} op_number This Number was assigned via getNextOperationIdentifier().
|
||||
# @param {Operation} prev_cl The predecessor of this operation in the complete-list (cl)
|
||||
@@ -150,6 +150,9 @@ module.exports = (HB)->
|
||||
if @origin is o
|
||||
break
|
||||
d++
|
||||
#TODO: delete this
|
||||
if @ is @prev_cl
|
||||
throw new Error "this should not happen ;) "
|
||||
o = o.prev_cl
|
||||
d
|
||||
|
||||
@@ -175,10 +178,12 @@ module.exports = (HB)->
|
||||
# Include this operation in the associative lists.
|
||||
#
|
||||
execute: ()->
|
||||
if @is_executed?
|
||||
return @
|
||||
if not @validateSavedOperations()
|
||||
return false
|
||||
else
|
||||
if @prev_cl? and @next_cl?
|
||||
if @prev_cl?.validateSavedOperations() and @next_cl?.validateSavedOperations() and @prev_cl.next_cl isnt @
|
||||
distance_to_origin = 0
|
||||
o = @prev_cl.next_cl
|
||||
i = 0
|
||||
@@ -247,17 +252,19 @@ module.exports = (HB)->
|
||||
0
|
||||
|
||||
execute: ()->
|
||||
a = @validateSavedOperations()
|
||||
for l in execution_listener
|
||||
l @toJson()
|
||||
a
|
||||
if @validateSavedOperations()
|
||||
for l in execution_listener
|
||||
l @toJson()
|
||||
@
|
||||
else
|
||||
false
|
||||
|
||||
toJson: ()->
|
||||
{
|
||||
'type' : "Delimiter"
|
||||
'uid' : @getUid()
|
||||
'prev' : @prev_cl.getUid()
|
||||
'next' : @next_cl.getUid()
|
||||
'prev' : @prev_cl?.getUid()
|
||||
'next' : @next_cl?.getUid()
|
||||
}
|
||||
|
||||
parser['Delimiter'] = (json)->
|
||||
|
||||
49
lib/Types/JsonTypes.coffee
Normal file
49
lib/Types/JsonTypes.coffee
Normal file
@@ -0,0 +1,49 @@
|
||||
text_types_uninitialized = require "./TextTypes.coffee"
|
||||
|
||||
module.exports = (HB)->
|
||||
text_types = text_types_uninitialized HB
|
||||
types = text_types.types
|
||||
parser = text_types.parser
|
||||
|
||||
class JsonType extends types.MapManager
|
||||
constructor: (uid, initial_value)->
|
||||
super uid
|
||||
if initial_value?
|
||||
if typeof initial_value isnt "object"
|
||||
throw new Error "The initial value of JsonTypes must be of type Object! (current type: #{typeof initial_value})"
|
||||
for name,o of initial_value
|
||||
@val name, o
|
||||
|
||||
val: (name, content)->
|
||||
if name? and content?
|
||||
if typeof content is 'string'
|
||||
word = HB.addOperation(new types.Word HB.getNextOperationIdentifier(), content).execute()
|
||||
super name, word
|
||||
content
|
||||
else if typeof content is 'object'
|
||||
json = HB.addOperation(JsonType HB.getNextOperationIdentifier(), content).execute()
|
||||
super name, json
|
||||
content
|
||||
else
|
||||
throw new Error "You must not set #{typeof content}-types in collaborative Json-objects!"
|
||||
else
|
||||
super name, content
|
||||
|
||||
toJson: ()->
|
||||
{
|
||||
'type' : "JsonType"
|
||||
'uid' : @getUid()
|
||||
}
|
||||
|
||||
parser['JsonType'] = (json)->
|
||||
{
|
||||
'uid' : uid
|
||||
} = json
|
||||
new JsonType uid
|
||||
|
||||
|
||||
types['JsonType'] = JsonType
|
||||
|
||||
text_types
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
_ = require "underscore"
|
||||
basic_types_uninitialized = require "./BasicTypes.coffee"
|
||||
|
||||
module.exports = (HB)->
|
||||
@@ -6,24 +5,75 @@ module.exports = (HB)->
|
||||
types = basic_types.types
|
||||
parser = basic_types.parser
|
||||
|
||||
class MapManager
|
||||
constructor: ()->
|
||||
class MapManager extends types.Operation
|
||||
constructor: (uid)->
|
||||
@map = {}
|
||||
super uid
|
||||
|
||||
set: (name, content)->
|
||||
if not @map[name]?
|
||||
@map[name] = new Replaceable HB,
|
||||
val: (name, content)->
|
||||
if content?
|
||||
if not @map[name]?
|
||||
HB.addOperation(new AddName HB.getNextOperationIdentifier(), @, name).execute()
|
||||
@map[name].replace content
|
||||
else if name?
|
||||
@map[name]?.val()
|
||||
else
|
||||
result = {}
|
||||
for name,o of @map
|
||||
result[name] = o.val()
|
||||
result
|
||||
|
||||
class AddName extends types.Operation
|
||||
constructor: (uid, map_manager, @name)->
|
||||
@saveOperation 'map_manager', map_manager
|
||||
super uid
|
||||
|
||||
execute: ()->
|
||||
if not @validateSavedOperations()
|
||||
return false
|
||||
else
|
||||
uid_r = @map_manager.getUid()
|
||||
uid_r.op_number = "_#{uid_r.op_number}_RM_#{@name}"
|
||||
if not HB.getOperation(uid_r)?
|
||||
uid_beg = @map_manager.getUid()
|
||||
uid_beg.op_number = "_#{uid_beg.op_number}_RM_#{@name}_beginning"
|
||||
uid_end = @map_manager.getUid()
|
||||
uid_end.op_number = "_#{uid_end.op_number}_RM_#{@name}_end"
|
||||
beg = HB.addOperation(new types.Delimiter uid_beg, undefined, uid_end)
|
||||
end = HB.addOperation(new types.Delimiter uid_end, beg, undefined).execute()
|
||||
beg.execute()
|
||||
@map_manager.map[@name] = HB.addOperation(new ReplaceManager undefined, uid_r, beg, end).execute()
|
||||
super
|
||||
|
||||
toJson: ()->
|
||||
{
|
||||
'type' : "AddName"
|
||||
'uid' : @getUid()
|
||||
'map_manager' : @map_manager.getUid()
|
||||
'name' : @name
|
||||
}
|
||||
|
||||
parser['AddName'] = (json)->
|
||||
{
|
||||
'map_manager' : map_manager
|
||||
'uid' : uid
|
||||
'name' : name
|
||||
} = json
|
||||
new AddName uid, map_manager, name
|
||||
|
||||
|
||||
class ListManager extends types.Insert
|
||||
constructor: (uid, beginning, end, prev, next, origin)->
|
||||
if beginning? and end?
|
||||
saveOperation "beginning", beginning
|
||||
saveOperation "end", end
|
||||
@saveOperation 'beginning', beginning
|
||||
@saveOperation 'end', end
|
||||
else
|
||||
@beginning = HB.addOperation new types.Delimiter HB.getNextOperationIdentifier(), undefined, undefined
|
||||
@end = HB.addOperation new types.Delimiter HB.getNextOperationIdentifier(), @beginning, undefined
|
||||
@beginning.next_cl = @end
|
||||
@beginning.execute()
|
||||
@end.execute()
|
||||
|
||||
super uid, prev, next, origin
|
||||
|
||||
# Get the element previous to the delemiter at the end
|
||||
@@ -83,8 +133,8 @@ module.exports = (HB)->
|
||||
{
|
||||
'type': "ReplaceManager"
|
||||
'uid' : @getUid()
|
||||
'beginning' : @beginning
|
||||
'end' : @end
|
||||
'beginning' : @beginning.getUid()
|
||||
'end' : @end.getUid()
|
||||
}
|
||||
if @prev_cl? and @next_cl?
|
||||
json['prev'] = @prev_cl.getUid()
|
||||
@@ -113,8 +163,8 @@ module.exports = (HB)->
|
||||
constructor: (content, parent, uid, prev, next, origin)->
|
||||
@saveOperation 'content', content
|
||||
@saveOperation 'parent', parent
|
||||
if not (prev? and next?)
|
||||
throw new Error "You must define prev, and next for Replaceable-types!"
|
||||
if not (prev? and next? and content?)
|
||||
throw new Error "You must define content, prev, and next for Replaceable-types!"
|
||||
super uid, prev, next, origin
|
||||
|
||||
#
|
||||
@@ -126,9 +176,12 @@ module.exports = (HB)->
|
||||
@parent.replace content
|
||||
|
||||
execute: ()->
|
||||
super
|
||||
@content.setReplaceManager?(@parent)
|
||||
@
|
||||
if not @validateSavedOperations()
|
||||
return false
|
||||
else
|
||||
@content.setReplaceManager?(@parent)
|
||||
super
|
||||
@
|
||||
|
||||
#
|
||||
# Convert all relevant information of this operation to the json-format.
|
||||
@@ -139,7 +192,7 @@ module.exports = (HB)->
|
||||
{
|
||||
'type': "Replaceable"
|
||||
'content': @content.getUid()
|
||||
'ReplaceManager' : @parent
|
||||
'ReplaceManager' : @parent.getUid()
|
||||
'prev': @prev_cl.getUid()
|
||||
'next': @next_cl.getUid()
|
||||
'uid' : @getUid()
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
_ = require "underscore"
|
||||
structured_types_uninitialized = require "./StructuredTypes.coffee"
|
||||
|
||||
module.exports = (HB)->
|
||||
@@ -69,8 +68,10 @@ module.exports = (HB)->
|
||||
new TextInsert content, uid, prev, next, origin
|
||||
|
||||
class Word extends types.ListManager
|
||||
constructor: (uid, prev, next, origin)->
|
||||
super uid, prev, next, origin
|
||||
constructor: (uid, initial_content, beginning, end, prev, next, origin)->
|
||||
super uid, beginning, end, prev, next, origin
|
||||
if initial_content?
|
||||
@insertText 0, initial_content
|
||||
|
||||
# inserts a
|
||||
insertText: (position, content)->
|
||||
@@ -114,9 +115,10 @@ module.exports = (HB)->
|
||||
|
||||
toJson: ()->
|
||||
json = {
|
||||
'type': "TextInsert"
|
||||
'content': @content
|
||||
'type': "Word"
|
||||
'uid' : @getUid()
|
||||
'beginning' : @beginning.getUid()
|
||||
'end' : @end.getUid()
|
||||
}
|
||||
if @prev_cl?
|
||||
json['prev'] = @prev_cl.getUid()
|
||||
@@ -128,13 +130,14 @@ module.exports = (HB)->
|
||||
|
||||
parser['Word'] = (json)->
|
||||
{
|
||||
'content' : content
|
||||
'uid' : uid
|
||||
'beginning' : beginning
|
||||
'end' : end
|
||||
'prev': prev
|
||||
'next': next
|
||||
'origin' : origin
|
||||
} = json
|
||||
new Word uid, prev, next, origin
|
||||
new Word uid, undefined, beginning, end, prev, next, origin
|
||||
|
||||
types['TextInsert'] = TextInsert
|
||||
types['TextDelete'] = TextDelete
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
_ = require "underscore"
|
||||
|
||||
Reference in New Issue
Block a user