Documentation and cleanup

This commit is contained in:
Kevin Jahns
2014-08-24 01:52:15 +02:00
parent 77739deda3
commit 86849ae8b1
496 changed files with 10102 additions and 87342 deletions

View File

@@ -25,6 +25,9 @@ createIwcConnector = (callback, options)->
#
# The Iwc Connector adds support for the Inter-Widget-Communication protocol that is used in the Role-SDK.
#
# You must not use your own IWC client when using this connector!!
#
# @see http://dbis.rwth-aachen.de/cms/projects/the-xmpp-experience#interwidget-communication
# @see http://dbis.rwth-aachen.de/cms/projects/ROLE
#
@@ -34,7 +37,7 @@ createIwcConnector = (callback, options)->
# @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.
# @param {YattaFramework} yatta The Yatta framework.
#
constructor: (@engine, @HB, @execution_listener, @yatta)->
@duiClient = duiClient
@@ -75,20 +78,11 @@ createIwcConnector = (callback, options)->
@iwcHandler["Yatta_get_HB_element"] = [sendHistoryBuffer]
#
# This function is called whenever an operation was executed.
# @param {Operation} o The operation that was executed.
# Set your own IWC handler. It will be called after Yatta consumed the
# data from the received intent.
#
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
setIwcHandler: (f)->
userIwcHandler = f
#
# Helper for sending iwc intents.
@@ -114,8 +108,23 @@ createIwcConnector = (callback, options)->
@duiClient.sendIntent(intent)
setIwcHandler: (f)->
userIwcHandler = f
#
# @private
# 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
#
# @private
# 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
init = ()->

View File

@@ -19,6 +19,8 @@ createPeerJsConnector = ()->
#
# PeerJs is a Framework that enables you to connect to other peers. You just need the
# user-id of the peer (browser/client). And then you can connect to it.
# @see http://peerjs.com
#
class PeerJsConnector
@@ -38,25 +40,46 @@ createPeerJsConnector = ()->
@addConnection conn
send_ = (o)=>
@send o
if o.uid.creator is @HB.getUserId() and (typeof o.uid.op_number isnt "string")
for conn_id,conn of @connections
conn.send
op: o
@execution_listener.push send_
#
# Connect the Framework to another peer. Therefore you have to receive his
# user_id. If the other peer is connected to other peers, the PeerJsConnector
# will automatically connect to them too.
#
# Transmitting the user_id is your job.
# See [TextEditing](../../examples/TextEditing/) for a nice example
# on how to do that with urls.
#
# @param id {String} Connection id
#
connectToPeer: (id)->
if not @connections[id]? and id isnt @yatta.getUserId()
@addConnection peer.connect id
#
# Receive the id of every connected peer.
# @return {Array<String>} A list of Peer-Ids
#
getAllConnectionIds: ()->
for conn_id of @connections
conn_id
#
# What this method does:
# * Send state vector
# * Receive HB -> apply them
# * Send connections
# * Receive Connections -> Connect to unknow connections
# Adds an existing connection to this connector.
# @param conn {PeerJsConnection}
#
addConnection: (conn)->
#
# What this method does:
# * Send state vector
# * Receive HB -> apply them
# * Send connections
# * Receive Connections -> Connect to unknow connections
@connections[conn.peer] = conn
initialized_me = false
initialized_him = false
@@ -91,24 +114,6 @@ createPeerJsConnector = ()->
setTimeout sendStateVector, 100
sendStateVector()
#
# 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 conn_id,conn of @connections
conn.send
op: 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
peer.on 'open', (id)->
callback PeerJsConnector, id

View File

@@ -4,6 +4,7 @@ _ = require "underscore"
module.exports = (user_list)->
#
# @nodoc
# A trivial Connector that simulates network delay.
#
class TestConnector

View File

@@ -1,5 +1,6 @@
#
# @nodoc
# The Engine handles how and in which order to execute operations and add operations to the HistoryBuffer.
#
class Engine

View File

@@ -10,7 +10,7 @@ Engine = require "../Engine"
# * Integer
# * Array
#
class JsonYatta
class JsonFramework
#
# @param {String} user_id Unique id of the peer.
@@ -22,23 +22,16 @@ class JsonYatta
@types = type_manager.types
@engine = new Engine @HB, type_manager.parser
@connector = new Connector @engine, @HB, type_manager.execution_listener, @
first_word = new @types.JsonType @HB.getReservedUniqueIdentifier()
@HB.addOperation(first_word).execute()
@root_element = first_word
#
# @result JsonType
# @return JsonType
#
getSharedObject: ()->
@root_element
#
# @see Engine
#
getEngine: ()->
@engine
#
# Get the initialized connector.
#
@@ -60,7 +53,7 @@ class JsonYatta
#
# 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).
# JsonFramework was initialized (Depending on the HistoryBuffer implementation).
#
getUserId: ()->
@HB.getUserId()
@@ -77,16 +70,22 @@ class JsonYatta
val : (name, content, mutable)->
@root_element.val(name, content, mutable)
#
# @see Operation.on
#
on: ()->
@root_element.on arguments...
#
# @see Operation.deleteListener
#
deleteListener: ()->
@root_element.deleteListener arguments...
#
# @see JsonType.value
#
Object.defineProperty JsonYatta.prototype, 'value',
Object.defineProperty JsonFramework.prototype, 'value',
get : -> @root_element.value
set : (o)->
if o.constructor is {}.constructor
@@ -95,8 +94,8 @@ class JsonYatta
else
throw new Error "You must only set Object values!"
module.exports = JsonYatta
module.exports = JsonFramework
if window?
if not window.Y?
window.Y = {}
window.Y.JsonYatta = JsonYatta
window.Y.JsonFramework = JsonFramework

View File

@@ -6,7 +6,7 @@ Engine = require "../Engine"
#
# Framework for Text Datastructures.
#
class TextYatta
class TextFramework
#
# @param {String} user_id Uniqe user id that defines this peer.
@@ -24,7 +24,7 @@ class TextYatta
beginning.next_cl = end
beginning.execute()
end.execute()
first_word = new @types.Word {creator: '_', op_number: '_'}, beginning, end
first_word = new @types.WordType {creator: '_', op_number: '_'}, beginning, end
@HB.addOperation(first_word).execute()
uid_r = { creator: '_', op_number: "RM" }
@@ -37,17 +37,11 @@ class TextYatta
#
# @result Word
# @return WordType
#
getSharedObject: ()->
@root_element.val()
#
# @see Engine
#
getEngine: ()->
@engine
#
# Get the initialized connector.
#
@@ -63,7 +57,7 @@ class TextYatta
#
# 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).
# JsonFramework was initialized (Depending on the HistoryBuffer implementation).
#
getUserId: ()->
@HB.getUserId()
@@ -75,35 +69,38 @@ class TextYatta
@getSharedObject().val()
#
# @see Word.insertText
# @see WordType.insertText
#
insertText: (pos, content)->
@getSharedObject().insertText pos, content
#
# @see Word.deleteText
# @see WordType.deleteText
#
deleteText: (pos, length)->
@getSharedObject().deleteText pos, length
#
# @see Word.bind
# @see WordType.bind
#
bind: (textarea)->
@getSharedObject().bind textarea
#
# @see Word.replaceText
# @see WordType.replaceText
#
replaceText: (text)->
@getSharedObject().replaceText text
#
# @see Operation.on
#
on: ()->
@root_element.on arguments...
module.exports = TextYatta
module.exports = TextFramework
if window?
if not window.Y?
window.Y = {}
window.Y.TextYatta = TextYatta
window.Y.TextFramework = TextFramework

View File

@@ -1,5 +1,6 @@
#
# @nodoc
# An object that holds all applied operations.
#
# @note The HistoryBuffer is commonly abbreviated to HB.

View File

@@ -4,14 +4,17 @@ module.exports = (HB)->
execution_listener = []
#
# @private
# @abstract
# @nodoc
# A generic interface to operations.
#
# An operation has the following methods:
# _encode: encodes an operation (needed only if instance of this operation is sent).
# execute: execute the effects of this operations. Good examples are Insert-type and AddName-type
# val: in the case that the operation holds a value
# * _encode: encodes an operation (needed only if instance of this operation is sent).
# * execute: execute the effects of this operations. Good examples are Insert-type and AddName-type
# * val: in the case that the operation holds a value
#
# Furthermore an encodable operation has a parser.
# Furthermore an encodable operation has a parser. We extend the parser object in order to parse encoded operations.
#
class Operation
@@ -40,6 +43,16 @@ module.exports = (HB)->
@event_listeners[e] ?= []
@event_listeners[e].push f
#
# Deletes a function from an event / list of events.
# @see Operation.on
#
# @overload deleteListener(event, f)
# @param event {String} An event name
# @param f {Function} The function that you want to delete from these events
# @overload deleteListener(events, f)
# @param events {Array<String>} A list of event names
# @param f {Function} The function that you want to delete from these events.
deleteListener: (events, f)->
if events.constructor isnt [].constructor
events = [events]
@@ -148,6 +161,7 @@ module.exports = (HB)->
#
# @nodoc
# A simple Delete-type operation that deletes an Insert-type operation.
#
class Delete extends Operation
@@ -194,6 +208,7 @@ module.exports = (HB)->
new Delete uid, deletes_uid
#
# @nodoc
# A simple insert-type operation.
#
# An insert operation is always positioned between two other insert operations.
@@ -350,7 +365,9 @@ module.exports = (HB)->
position++
prev = prev.prev_cl
position
#
# @nodoc
# Defines an object that is cannot be changed. You can use this to set an immutable string, or a number.
#
class ImmutableObject extends Insert
@@ -396,6 +413,7 @@ module.exports = (HB)->
new ImmutableObject uid, content, prev, next, origin
#
# @nodoc
# A delimiter is placed at the end and at the beginning of the associative lists.
# This is necessary in order to have a beginning and an end even if the content
# of the Engine is empty.

View File

@@ -5,15 +5,17 @@ module.exports = (HB)->
types = text_types.types
parser = text_types.parser
createJsonWrapper = (_jsonType)->
createJsonTypeWrapper = (_jsonType)->
#
# A JsonWrapper was intended to be a convenient wrapper for the JsonType.
# @note EXPERIMENTAL
#
# A JsonTypeWrapper was intended to be a convenient wrapper for the JsonType.
# But it can make things more difficult than they are.
# @see JsonType
#
# @example create a JsonWrapper
# # You get a JsonWrapper from a JsonType by calling
# @example create a JsonTypeWrapper
# # You get a JsonTypeWrapper from a JsonType by calling
# w = yatta.value
#
# It creates Javascripts -getter and -setter methods for each property that JsonType maintains.
@@ -34,10 +36,10 @@ module.exports = (HB)->
# yatta.val('x', "text")
#
# In order to set a new property you have to overwrite an existing property.
# Therefore the JsonWrapper supports a special feature that should make things more convenient
# Therefore the JsonTypeWrapper supports a special feature that should make things more convenient
# (we can argue about that, use the JsonType if you don't like it ;).
# If you overwrite an object property of the JsonWrapper with a new object, it will result in a merged version of the objects.
# Let w.p the property that is to be overwritten and o the new value. E.g. w.p = o
# If you overwrite an object property of the JsonTypeWrapper with a new object, it will result in a merged version of the objects.
# Let `yatta.value.p` the property that is to be overwritten and o the new value. E.g. `yatta.value.p = o`
# * The result has all properties of o
# * The result has all properties of w.p if they don't occur under the same property-name in o.
#
@@ -63,7 +65,7 @@ module.exports = (HB)->
# yatta.value = {newProperty : "Awesome"}
# console.log(w.newProperty == "Awesome") # true!
#
class JsonWrapper
class JsonTypeWrapper
#
# @param {JsonType} jsonType Instance of the JsonType that this class wrappes.
@@ -71,11 +73,11 @@ module.exports = (HB)->
constructor: (jsonType)->
for name, obj of jsonType.map
do (name, obj)->
Object.defineProperty JsonWrapper.prototype, name,
Object.defineProperty JsonTypeWrapper.prototype, name,
get : ->
x = obj.val()
if x instanceof JsonType
createJsonWrapper x
createJsonTypeWrapper x
else if x instanceof types.ImmutableObject
x.val()
else
@@ -89,7 +91,7 @@ module.exports = (HB)->
jsonType.val(name, o, 'immutable')
enumerable: true
configurable: false
new JsonWrapper _jsonType
new JsonTypeWrapper _jsonType
#
# Manages Object-like values.
@@ -109,6 +111,18 @@ module.exports = (HB)->
for name,o of initial_value
@val name, o, mutable
#
# Identifies this class.
# Use it to check whether this is a json-type or something else.
#
# @example
# var x = yatta.val('unknown')
# if (x.type === "JsonType") {
# console.log JSON.stringify(x.toJson())
# }
#
type: "JsonType"
#
# Transform this to a Json and loose all the sharing-abilities (the new object will be a deep clone)!
# @return {Json}
@@ -128,7 +142,7 @@ module.exports = (HB)->
json
#
# @see Word.setReplaceManager
# @see WordType.setReplaceManager
# Sets the parent of this JsonType object.
#
setReplaceManager: (rm)->
@@ -168,7 +182,7 @@ module.exports = (HB)->
# @overload val(name)
# Get value of a property.
# @param {String} name Name of the object property.
# @return [JsonType|Word|String|Object] Depending on the value of the property. If mutable it will return a Operation-type object, if immutable it will return String/Object.
# @return [JsonType|WordType|String|Object] Depending on the value of the property. If mutable it will return a Operation-type object, if immutable it will return String/Object.
#
# @overload val(name, content)
# Set a new property.
@@ -198,7 +212,7 @@ module.exports = (HB)->
super name, obj
else
if typeof content is 'string'
word = HB.addOperation(new types.Word undefined).execute()
word = HB.addOperation(new types.WordType undefined).execute()
word.insertText 0, content
super name, word
else if content.constructor is Object
@@ -210,7 +224,7 @@ module.exports = (HB)->
super name, content
Object.defineProperty JsonType.prototype, 'value',
get : -> createJsonWrapper @
get : -> createJsonTypeWrapper @
set : (o)->
if o.constructor is {}.constructor
for o_name,o_obj of o

View File

@@ -6,6 +6,7 @@ module.exports = (HB)->
parser = basic_types.parser
#
# @nodoc
# Manages map like objects. E.g. Json-Type and XML attributes.
#
class MapManager extends types.Operation
@@ -42,6 +43,7 @@ module.exports = (HB)->
result
#
# @nodoc
# When a new property in a map manager is created, then the uids of the inserted Operations
# must be unique (think about concurrent operations). Therefore only an AddName operation is allowed to
# add a property in a MapManager. If two AddName operations on the same MapManager name happen concurrently
@@ -102,6 +104,7 @@ module.exports = (HB)->
new AddName uid, map_manager, name
#
# @nodoc
# Manages a list of Insert-type operations.
#
class ListManager extends types.Insert
@@ -123,6 +126,10 @@ module.exports = (HB)->
@end.execute()
super uid, prev, next, origin
#
# @private
# @see Operation.execute
#
execute: ()->
if @validateSavedOperations()
@beginning.setParent @
@@ -170,11 +177,12 @@ module.exports = (HB)->
o
#
# @nodoc
# Adds support for replace. The ReplaceManager manages Replaceable operations.
# Each Replaceable holds a value that is now replaceable.
#
# The Word-type has implemented support for replace
# @see Word
# The WordType-type has implemented support for replace
# @see WordType
#
class ReplaceManager extends ListManager
#
@@ -214,8 +222,9 @@ module.exports = (HB)->
@deleteListener 'addProperty', addPropertyListener
@on 'insert', addPropertyListener
super parent
#
# Get the value of this Word
# Get the value of this WordType
# @return {String}
#
val: ()->
@@ -256,6 +265,7 @@ module.exports = (HB)->
#
# @nodoc
# The ReplaceManager manages Replaceables.
# @see ReplaceManager
#
@@ -287,7 +297,7 @@ module.exports = (HB)->
#
# If possible set the replace manager in the content.
# @see Word.setReplaceManager
# @see WordType.setReplaceManager
#
execute: ()->
if not @validateSavedOperations()

View File

@@ -6,6 +6,7 @@ module.exports = (HB)->
parser = structured_types.parser
#
# @nodoc
# At the moment TextDelete type equals the Delete type in BasicTypes.
# @see BasicTypes.Delete
#
@@ -13,7 +14,8 @@ module.exports = (HB)->
parser["TextDelete"] = parser["Delete"]
#
# Extends the basic Insert type to an operation that holds a text value
# @nodoc
# Extends the basic Insert type to an operation that holds a text value
#
class TextInsert extends types.Insert
#
@@ -72,28 +74,47 @@ module.exports = (HB)->
new TextInsert content, uid, prev, next, origin
#
# Handles a Text-like data structures with support for insertText/deleteText at a word-position.
# Handles a WordType-like data structures with support for insertText/deleteText at a word-position.
# @note Currently, only Text is supported!
#
class Word extends types.ListManager
class WordType extends types.ListManager
#
# @private
# @param {Object} uid A unique identifier. If uid is undefined, a new uid will be created.
#
constructor: (uid, beginning, end, prev, next, origin)->
super uid, beginning, end, prev, next, origin
#
# Inserts a string into the word
# Identifies this class.
# Use it to check whether this is a word-type or something else.
#
# @example
# var x = yatta.val('unknown')
# if (x.type === "WordType") {
# console.log JSON.stringify(x.toJson())
# }
#
type: "WordType"
#
# Inserts a string into the word.
#
# @return {WordType} This WordType object.
#
insertText: (position, content)->
o = @getOperationByPosition position
for c in content
op = new TextInsert c, undefined, o.prev_cl, o
HB.addOperation(op).execute()
@
#
# Deletes a part of the word.
#
# @return {WordType} This WordType object
#
deleteText: (position, length)->
o = @getOperationByPosition position
@@ -106,25 +127,28 @@ module.exports = (HB)->
while not (o instanceof types.Delimiter) and o.isDeleted()
o = o.next_cl
delete_ops.push d._encode()
delete_ops
@
#
# Replace the content of this word with another one. Concurrent replacements are not merged!
# Only one of the replacements will be used.
#
# Can only be used if the ReplaceManager was set!
# @see Word.setReplaceManager
# @return {WordType} Returns the new WordType object.
#
replaceText: (text)->
# Can only be used if the ReplaceManager was set!
# @see WordType.setReplaceManager
if @replace_manager?
word = HB.addOperation(new Word undefined).execute()
word = HB.addOperation(new WordType undefined).execute()
word.insertText 0, text
@replace_manager.replace(word)
word
else
throw new Error "This type is currently not maintained by a ReplaceManager!"
#
# @returns [Json] A Json object.
# Get the String-representation of this word.
# @return {String} The String-representation of this object.
#
val: ()->
c = for o in @toArray()
@@ -135,7 +159,15 @@ module.exports = (HB)->
c.join('')
#
# In most cases you would embed a Word in a Replaceable, wich is handled by the ReplaceManager in order
# Same as WordType.val
# @see WordType.val
#
toString: ()->
@val()
#
# @private
# In most cases you would embed a WordType in a Replaceable, wich is handled by the ReplaceManager in order
# to provide replace functionality.
#
setReplaceManager: (op)->
@@ -145,7 +177,11 @@ module.exports = (HB)->
@replace_manager?.callEvent 'change'
#
# Bind this Word to a textfield.
# Bind this WordType to a textfield or input field.
#
# @example
# var textbox = document.getElementById("textfield");
# yatta.bind(textbox);
#
bind: (textfield)->
word = @
@@ -250,11 +286,12 @@ module.exports = (HB)->
#
# @private
# Encode this operation in such a way that it can be parsed by remote peers.
#
_encode: ()->
json = {
'type': "Word"
'type': "WordType"
'uid' : @getUid()
'beginning' : @beginning.getUid()
'end' : @end.getUid()
@@ -267,7 +304,7 @@ module.exports = (HB)->
json["origin"] = @origin.getUid()
json
parser['Word'] = (json)->
parser['WordType'] = (json)->
{
'uid' : uid
'beginning' : beginning
@@ -276,11 +313,11 @@ module.exports = (HB)->
'next': next
'origin' : origin
} = json
new Word uid, beginning, end, prev, next, origin
new WordType uid, beginning, end, prev, next, origin
types['TextInsert'] = TextInsert
types['TextDelete'] = TextDelete
types['Word'] = Word
types['WordType'] = WordType
structured_types

View File

@@ -1,10 +1,11 @@
exports['IwcConnector'] =
require './Connectors/IwcConnector'
exports['TestConnector'] =
require './Connectors/TestConnector'
exports['JsonYatta'] =
require './Frameworks/JsonYatta'
exports['TextYatta'] =
require './Frameworks/TextYatta'
exports['JsonFramework'] =
require './Frameworks/JsonFramework'
exports['TextFramework'] =
require './Frameworks/TextFramework'