There are now "Pseudo operations" that are not sent, and get be queried by a special parameter with the HB.getOperation. This will reduce the number of operations that are sent and is necessary for the Array implementation, that I plan to implement in the near future
This commit is contained in:
+106
-176
File diff suppressed because one or more lines are too long
+106
-176
File diff suppressed because one or more lines are too long
+116
-208
File diff suppressed because one or more lines are too long
+115
-185
File diff suppressed because one or more lines are too long
+1
-9
@@ -2,12 +2,4 @@
|
||||
|
||||
Here you find some (hopefully) usefull examples on how to use Yatta!
|
||||
|
||||
## Tutorials
|
||||
* [PeerJs-Json Tutorial](./PeerJs-Json/) Tutorial on how to use Yatta! with Json and PeerJs Connector.
|
||||
* [IWC Tutorial](./Iwc/) Tutorial on how to use IWC Connector.
|
||||
|
||||
## Demos
|
||||
* [Text Editing](http://dadamonad.github.io/Yatta/examples/TextEditing/) Simple collaborative text editing demo with PeerJs and Text Framework.
|
||||
* [XML Example](http://dadamonad.github.io/Yatta/examples/XmlExample) Collaboratively manipulate the dom with native dom-features and jQuery.
|
||||
* [IWC Demo](./IwcDemo/) More IWC example widgets.
|
||||
|
||||
Please note, that the XMPP Connector is the best supported Connector at the moment.
|
||||
@@ -1,23 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset=utf-8 />
|
||||
<title>PeerJs Json Example</title>
|
||||
<script src="../../../Connector/xmpp-connector/strophe.js"></script>
|
||||
<script src="../../../Connector/bower_components/strophejs-plugins/muc/strophe.muc.js"></script>
|
||||
<script src="../../../Connector/xmpp-connector/xmpp-connector.js"></script>
|
||||
<script src="../../build/browser/yatta.js"></script>
|
||||
<script src="./index.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1> PeerJs + Json Tutorial</h1>
|
||||
<p> Collaborative Json editing with <a href="https://github.com/DadaMonad/Yatta/">Yatta</a>
|
||||
and <a href="http://peerjs.com/">PeerJs</a> (WebRTC). </p>
|
||||
|
||||
<textarea style="width:80%;" rows=40 id="textfield"></textarea>
|
||||
|
||||
<p> <a href="https://github.com/DadaMonad/Yatta/">Yatta</a> is a Framework for Real-Time collaboration on arbitrary data structures.
|
||||
You can find the code for this example <a href="https://github.com/DadaMonad/Yatta/tree/master/examples/PeerJs-Json">here</a>.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,53 +0,0 @@
|
||||
|
||||
/**
|
||||
## PeerJs + JSON Example
|
||||
Here, I will give a short overview on how to enable collaborative json with the
|
||||
[PeerJs](http://peerjs.com/) Connector and the Json Framework. Open
|
||||
[index.html](http://dadamonad.github.io/Yatta/examples/PeerJs-Json/index.html) in your Browser and
|
||||
use the console to explore Yatta!
|
||||
|
||||
[PeerJs](http://peerjs.com) 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.
|
||||
|
||||
First you have to include the following libraries in your html file:
|
||||
```
|
||||
<script src="http://cdn.peerjs.com/0.3/peer.js"></script>
|
||||
<script src="../../build/browser/Frameworks/JsonFramework.js"></script>
|
||||
<script src="../../build/browser/Connectors/PeerJsConnector.js"></script>
|
||||
<script src="./index.js"></script>
|
||||
```
|
||||
### Create Connector
|
||||
|
||||
The PeerJs Framework requires an API key, or you need to set up your own PeerJs server.
|
||||
Get an API key from the [Website](http://peerjs.com/peerserver).
|
||||
The first parameter of `createPeerJsConnector` is forwarded as the options object in PeerJs.
|
||||
Therefore, you may also specify the server/port here, if you have set up your own server.
|
||||
*/
|
||||
var yatta, yattaHandler;
|
||||
|
||||
/**
|
||||
This will connect to the server owned by the peerjs team.
|
||||
For now, you can use my API key.
|
||||
*/
|
||||
|
||||
connector = new XMPPConnector();
|
||||
|
||||
/**
|
||||
### Yatta
|
||||
yatta is the shared json object. If you change something on this object,
|
||||
it will be instantly shared with all the other collaborators.
|
||||
*/
|
||||
yatta = new Yatta(connector);
|
||||
|
||||
window.onload = function(){
|
||||
var textbox = document.getElementById("textfield");
|
||||
yatta.observe(function(events){
|
||||
for(var i=0; i<events.length; i++){
|
||||
var event = events[i];
|
||||
if(event.name === "textfield" && event.type !== "delete"){
|
||||
yatta.val("textfield").bind(textbox);
|
||||
}
|
||||
}
|
||||
});
|
||||
yatta.val("textfield","");
|
||||
};
|
||||
@@ -1,106 +0,0 @@
|
||||
## Text Editing Example
|
||||
Here, I will give a short overview on how to enable collaborative text editing with the
|
||||
[PeerJs](http://peerjs.com/) Connector and the TextFramework Framework.
|
||||
|
||||
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. In this example we will encode
|
||||
the client-id to which this client shall connect, in the url.
|
||||
It should look like this: http://../index.html?user_id
|
||||
|
||||
First you have to include the following libraries in your html file:
|
||||
```
|
||||
<script src="http://cdn.peerjs.com/0.3/peer.js"></script>
|
||||
<script src="../../build/browser/Frameworks/TextFramework.js"></script>
|
||||
<script src="../../build/browser/Connectors/PeerJsConnector.js"></script>
|
||||
<script src="./index.js"></script>
|
||||
```
|
||||
Open [index.html](./index.html) in order to start collaboration.
|
||||
|
||||
|
||||
```js
|
||||
var yatta;
|
||||
|
||||
function init(){
|
||||
```
|
||||
|
||||
|
||||
First create the connector - the underlaying communication protocol.
|
||||
Here, we use the PeerJs connector. Its first parameter is the API key that you need to specify (see [website](http://peerjs.com/))
|
||||
|
||||
|
||||
This will connect to the server owned by the peerjs team.
|
||||
For now, you can use my API key.
|
||||
|
||||
|
||||
```js
|
||||
// var conn = {key: 'h7nlefbgavh1tt9'};
|
||||
```
|
||||
|
||||
|
||||
This will connect to one of my peerjs instances.
|
||||
I can't guaranty that this will be always up. This is why you should use the previous method with the api key,
|
||||
or set up your own server.
|
||||
|
||||
|
||||
```js
|
||||
var conn = {
|
||||
host: "terrific-peerjs.herokuapp.com",
|
||||
port: "", // this works because heroku can forward to the right port.
|
||||
// debug: true,
|
||||
};
|
||||
|
||||
Y.createPeerJsConnector(conn, function(Connector, user_id){
|
||||
```
|
||||
|
||||
|
||||
TextFramework is a shared text object. If you change something on this object,
|
||||
it will be instantaneously shared with all the other collaborators.
|
||||
|
||||
|
||||
```js
|
||||
yatta = new Y.TextFramework(user_id, Connector);
|
||||
```
|
||||
|
||||
|
||||
Get the url of this frame. If it has a url-encoded parameter
|
||||
we will connect to the foreign peer.
|
||||
|
||||
|
||||
```js
|
||||
var url = window.location.href;
|
||||
var peer_id = location.search
|
||||
var url = url.substring(0,-peer_id.length);
|
||||
peer_id = peer_id.substring(1);
|
||||
```
|
||||
|
||||
|
||||
Set the shareable link.
|
||||
|
||||
|
||||
```js
|
||||
document.getElementById("peer_link").setAttribute("href",url+"?"+user_id);
|
||||
```
|
||||
|
||||
|
||||
Connect to other peer.
|
||||
|
||||
|
||||
```js
|
||||
if (peer_id.length > 0){
|
||||
yatta.connector.connectToPeer(peer_id);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Bind yatta to the textfield.
|
||||
|
||||
The .bind property is a method of the Word class. You can also use it with all the other Frameworks in Yatta (e.g. Json).
|
||||
|
||||
|
||||
```js
|
||||
var textbox = document.getElementById("textfield");
|
||||
yatta.bind(textbox);
|
||||
});
|
||||
}
|
||||
window.onload = init
|
||||
```
|
||||
@@ -1,22 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset=utf-8 />
|
||||
<title>PeerJs Json Example</title>
|
||||
<script src="../../bower_components/peerjs/peer.js"></script>
|
||||
<script src="../../bower_components/connector/peerjs-connector/peerjs-connector.js"></script>
|
||||
<script src="../../build/browser/yatta.js"></script>
|
||||
<script src="./index.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1> Text Editing Demo</h1>
|
||||
<p> Collaborative text editing with <a href="https://github.com/DadaMonad/Yatta/">Yatta</a>
|
||||
and <a href="http://peerjs.com/">PeerJs</a> (WebRTC). Open this link in other browsers: <a id="peer_link" target="_blank">Drop me </a> </p>
|
||||
|
||||
<textarea style="width:80%;" rows=40 id="textfield"></textarea>
|
||||
|
||||
<p> <a href="https://github.com/DadaMonad/Yatta/">Yatta</a> is a Framework for Real-Time collaboration on arbitrary data structures.
|
||||
You can find the code for this example <a href="https://github.com/DadaMonad/Yatta/tree/master/examples/TextEditing">here</a>.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,92 +0,0 @@
|
||||
|
||||
/**
|
||||
## Text Editing Example
|
||||
Here, I will give a short overview on how to enable collaborative text editing with the
|
||||
[PeerJs](http://peerjs.com/) Connector and the TextFramework Framework.
|
||||
|
||||
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. In this example we will encode
|
||||
the client-id to which this client shall connect, in the url.
|
||||
It should look like this: http://../index.html?user_id
|
||||
|
||||
First you have to include the following libraries in your html file:
|
||||
```
|
||||
<script src="../../bower_components/peerjs/peer.js"></script>
|
||||
<script src="../../bower_components/connector/peerjs-connector/peerjs-connector.js"></script>
|
||||
<script src="../../yatta.js"></script>
|
||||
<script src="./index.js"></script>
|
||||
```
|
||||
Open [index.html](./index.html) in order to start collaboration.
|
||||
*/
|
||||
var yatta;
|
||||
var connector;
|
||||
|
||||
function init(){
|
||||
/**
|
||||
First create the connector - the underlaying communication protocol.
|
||||
Here, we use the PeerJs connector. Its first parameter is the API key that you need to specify (see [website](http://peerjs.com/))
|
||||
*/
|
||||
|
||||
/**
|
||||
This will connect to the server owned by the peerjs team.
|
||||
For now, you can use my API key.
|
||||
*/
|
||||
var options = {key: 'h7nlefbgavh1tt9'};
|
||||
|
||||
/**
|
||||
This will connect to one of my peerjs instances.
|
||||
I can't guaranty that this will be always up. This is why you should use the previous method with the api key,
|
||||
or set up your own server.
|
||||
*/
|
||||
/*var options = {
|
||||
host: "terrific-peerjs.herokuapp.com",
|
||||
port: "", // this works because heroku can forward to the right port.
|
||||
// debug: true,
|
||||
};*/
|
||||
var user_id = Math.ceil(Math.random()*1000);
|
||||
connector = new PeerJsConnector(user_id, options);
|
||||
/**
|
||||
TextFramework is a shared text object. If you change something on this object,
|
||||
it will be instantaneously shared with all the other collaborators.
|
||||
*/
|
||||
yatta = new Yatta(connector);
|
||||
yatta.val()
|
||||
|
||||
/**
|
||||
Get the url of this frame. If it has a url-encoded parameter
|
||||
we will connect to the foreign peer.
|
||||
*/
|
||||
var url = window.location.href;
|
||||
var peer_id = location.search
|
||||
var url = url.substring(0,-peer_id.length);
|
||||
peer_id = peer_id.substring(1);
|
||||
|
||||
/**
|
||||
Set the shareable link.
|
||||
*/
|
||||
document.getElementById("peer_link").setAttribute("href",url+"?"+user_id);
|
||||
|
||||
/**
|
||||
Connect to other peer.
|
||||
*/
|
||||
if (peer_id.length > 0){
|
||||
yatta.connector.join(peer_id);
|
||||
}
|
||||
|
||||
/**
|
||||
Bind yatta to the textfield.
|
||||
|
||||
The .bind property is a method of the Word class. You can also use it with all the other Frameworks in Yatta (e.g. Json).
|
||||
*/
|
||||
var textbox = document.getElementById("textfield");
|
||||
function textbind(){
|
||||
yatta.val("textbox").bind(textbox);
|
||||
}
|
||||
if(peer_id.length > 0){
|
||||
connector.whenSynced([textbind]);
|
||||
} else {
|
||||
yatta.val("textbox",textbox.value)
|
||||
textbind()
|
||||
}
|
||||
}
|
||||
window.onload = init
|
||||
@@ -49,5 +49,5 @@ window.onload = function(){
|
||||
}
|
||||
}
|
||||
});
|
||||
yatta.val("textfield","");
|
||||
yatta.val("textfield","", "mutable");
|
||||
};
|
||||
@@ -152,10 +152,17 @@ class HistoryBuffer
|
||||
#
|
||||
# Retrieve an operation from a unique id.
|
||||
#
|
||||
# when uid has a "sub" property, the value of it will be applied
|
||||
# on the operations retrieveSub method (which must! be defined)
|
||||
#
|
||||
getOperation: (uid)->
|
||||
if uid.uid?
|
||||
uid = uid.uid
|
||||
@buffer[uid.creator]?[uid.op_number]
|
||||
o = @buffer[uid.creator]?[uid.op_number]
|
||||
if uid.sub? and o?
|
||||
o.retrieveSub uid.sub
|
||||
else
|
||||
o
|
||||
|
||||
#
|
||||
# Add an operation to the HB. Note that this will not link it against
|
||||
|
||||
+34
-23
@@ -31,6 +31,9 @@ module.exports = (HB)->
|
||||
|
||||
type: "Operation"
|
||||
|
||||
retrieveSub: ()->
|
||||
throw new Error "sub properties are not enable on this operation type!"
|
||||
|
||||
#
|
||||
# Add an event listener. It depends on the operation which events are supported.
|
||||
# @param {Function} f f is executed in case the event fires.
|
||||
@@ -101,7 +104,16 @@ module.exports = (HB)->
|
||||
# Computes a unique identifier (uid) that identifies this operation.
|
||||
#
|
||||
getUid: ()->
|
||||
@uid
|
||||
if not @uid.noOperation?
|
||||
@uid
|
||||
else
|
||||
@uid.alt # could be (safely) undefined
|
||||
|
||||
cloneUid: ()->
|
||||
uid = {}
|
||||
for n,v of @getUid()
|
||||
uid[n] = v
|
||||
uid
|
||||
|
||||
dontSync: ()->
|
||||
@uid.doSync = false
|
||||
@@ -119,9 +131,10 @@ module.exports = (HB)->
|
||||
# There is only one other place, where this can be done - before an Insertion
|
||||
# is executed (because we need the creator_id)
|
||||
@uid = HB.getNextOperationIdentifier()
|
||||
HB.addOperation @
|
||||
for l in execution_listener
|
||||
l @_encode()
|
||||
if not @uid.noOperation?
|
||||
HB.addOperation @
|
||||
for l in execution_listener
|
||||
l @_encode()
|
||||
@
|
||||
|
||||
#
|
||||
@@ -180,7 +193,6 @@ module.exports = (HB)->
|
||||
success
|
||||
|
||||
|
||||
|
||||
#
|
||||
# @nodoc
|
||||
# A simple Delete-type operation that deletes an operation.
|
||||
@@ -249,7 +261,8 @@ module.exports = (HB)->
|
||||
# @param {Operation} prev_cl The predecessor of this operation in the complete-list (cl)
|
||||
# @param {Operation} next_cl The successor of this operation in the complete-list (cl)
|
||||
#
|
||||
constructor: (uid, prev_cl, next_cl, origin)->
|
||||
constructor: (uid, prev_cl, next_cl, origin, parent)->
|
||||
@saveOperation 'parent', parent
|
||||
@saveOperation 'prev_cl', prev_cl
|
||||
@saveOperation 'next_cl', next_cl
|
||||
if origin?
|
||||
@@ -283,7 +296,6 @@ module.exports = (HB)->
|
||||
@prev_cl.applyDelete()
|
||||
|
||||
cleanup: ()->
|
||||
# TODO: Debugging
|
||||
if @next_cl.isDeleted()
|
||||
# delete all ops that delete this insertion
|
||||
for d in @deleted_by
|
||||
@@ -300,8 +312,9 @@ module.exports = (HB)->
|
||||
@prev_cl.next_cl = @next_cl
|
||||
@next_cl.prev_cl = @prev_cl
|
||||
super
|
||||
else if @next_cl? and @prev_cl?
|
||||
throw new Error "This insertion was not supposed to be deleted!"
|
||||
# else
|
||||
# Someone inserted something in the meantime.
|
||||
# Remember: this can only be garbage collected when next_cl is deleted
|
||||
|
||||
#
|
||||
# @private
|
||||
@@ -324,6 +337,13 @@ module.exports = (HB)->
|
||||
if not @validateSavedOperations()
|
||||
return false
|
||||
else
|
||||
if @parent?
|
||||
if not @prev_cl?
|
||||
@prev_cl = @parent.beginning
|
||||
if not @origin?
|
||||
@origin = @parent.beginning
|
||||
if not @next_cl?
|
||||
@next_cl = @parent.end
|
||||
if @prev_cl?
|
||||
distance_to_origin = @getDistanceToOrigin() # most cases: 0
|
||||
o = @prev_cl.next_cl
|
||||
@@ -419,8 +439,8 @@ module.exports = (HB)->
|
||||
# @param {Object} uid A unique identifier. If uid is undefined, a new uid will be created.
|
||||
# @param {Object} content
|
||||
#
|
||||
constructor: (uid, @content, prev, next, origin)->
|
||||
super uid, prev, next, origin
|
||||
constructor: (uid, @content)->
|
||||
super uid
|
||||
|
||||
type: "ImmutableObject"
|
||||
|
||||
@@ -439,23 +459,14 @@ module.exports = (HB)->
|
||||
'uid' : @getUid()
|
||||
'content' : @content
|
||||
}
|
||||
if @prev_cl?
|
||||
json['prev'] = @prev_cl.getUid()
|
||||
if @next_cl?
|
||||
json['next'] = @next_cl.getUid()
|
||||
if @origin? # and @origin isnt @prev_cl
|
||||
json["origin"] = @origin().getUid()
|
||||
json
|
||||
|
||||
parser['ImmutableObject'] = (json)->
|
||||
{
|
||||
'uid' : uid
|
||||
'content' : content
|
||||
'prev': prev
|
||||
'next': next
|
||||
'origin' : origin
|
||||
} = json
|
||||
new ImmutableObject uid, content, prev, next, origin
|
||||
new ImmutableObject uid, content
|
||||
|
||||
#
|
||||
# @nodoc
|
||||
@@ -469,11 +480,11 @@ module.exports = (HB)->
|
||||
# @param {Operation} prev_cl The predecessor of this operation in the complete-list (cl)
|
||||
# @param {Operation} next_cl The successor of this operation in the complete-list (cl)
|
||||
#
|
||||
constructor: (uid, prev_cl, next_cl, origin)->
|
||||
constructor: (prev_cl, next_cl, origin)->
|
||||
@saveOperation 'prev_cl', prev_cl
|
||||
@saveOperation 'next_cl', next_cl
|
||||
@saveOperation 'origin', prev_cl
|
||||
super uid
|
||||
super {noOperation: true}
|
||||
|
||||
type: "Delimiter"
|
||||
|
||||
|
||||
@@ -33,9 +33,7 @@ module.exports = (HB)->
|
||||
#
|
||||
val: (name, content)->
|
||||
if content?
|
||||
if not @map[name]?
|
||||
(new AddName undefined, @, name).execute()
|
||||
@map[name].replace content
|
||||
@retrieveSub(name).replace content
|
||||
@
|
||||
else if name?
|
||||
prop = @map[name]
|
||||
@@ -60,85 +58,22 @@ module.exports = (HB)->
|
||||
delete: (name)->
|
||||
@map[name]?.deleteContent()
|
||||
@
|
||||
#
|
||||
# @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
|
||||
# only one will AddName operation will be executed.
|
||||
#
|
||||
class AddName extends types.Operation
|
||||
|
||||
#
|
||||
# @param {Object} uid A unique identifier. If uid is undefined, a new uid will be created.
|
||||
# @param {Object} map_manager Uid or reference to the MapManager.
|
||||
# @param {String} name Name of the property that will be added.
|
||||
#
|
||||
constructor: (uid, map_manager, @name)->
|
||||
@saveOperation 'map_manager', map_manager
|
||||
super uid
|
||||
|
||||
type: "AddName"
|
||||
|
||||
applyDelete: ()->
|
||||
super()
|
||||
|
||||
cleanup: ()->
|
||||
super()
|
||||
|
||||
#
|
||||
# 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
|
||||
else
|
||||
# helper for cloning an object
|
||||
clone = (o)->
|
||||
p = {}
|
||||
for name,value of o
|
||||
p[name] = value
|
||||
p
|
||||
uid_r = clone(@map_manager.getUid())
|
||||
uid_r.doSync = false
|
||||
uid_r.op_number = "_#{uid_r.op_number}_RM_#{@name}"
|
||||
if not HB.getOperation(uid_r)?
|
||||
uid_beg = clone(uid_r)
|
||||
uid_beg.op_number = "#{uid_r.op_number}_beginning"
|
||||
uid_end = clone(uid_r)
|
||||
uid_end.op_number = "#{uid_r.op_number}_end"
|
||||
beg = (new types.Delimiter uid_beg, undefined, uid_end).execute()
|
||||
end = (new types.Delimiter uid_end, beg, undefined).execute()
|
||||
event_properties =
|
||||
name: @name
|
||||
event_this = @map_manager
|
||||
@map_manager.map[@name] = new ReplaceManager event_properties, event_this, uid_r, beg, end
|
||||
@map_manager.map[@name].setParent @map_manager, @name
|
||||
(@map_manager.map[@name].add_name_ops ?= []).push @
|
||||
@map_manager.map[@name].execute()
|
||||
super
|
||||
|
||||
#
|
||||
# Encode this operation in such a way that it can be parsed by remote peers.
|
||||
#
|
||||
_encode: ()->
|
||||
{
|
||||
'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
|
||||
retrieveSub: (property_name)->
|
||||
if not @map[property_name]?
|
||||
event_properties =
|
||||
name: property_name
|
||||
event_this = @
|
||||
map_uid = @cloneUid()
|
||||
map_uid.sub = property_name
|
||||
rm_uid =
|
||||
noOperation: true
|
||||
alt: map_uid
|
||||
rm = new ReplaceManager event_properties, event_this, rm_uid # this operation shall not be saved in the HB
|
||||
@map[property_name] = rm
|
||||
rm.setParent @, property_name
|
||||
rm.execute()
|
||||
@map[property_name]
|
||||
|
||||
#
|
||||
# @nodoc
|
||||
@@ -151,17 +86,13 @@ module.exports = (HB)->
|
||||
# @param {Object} uid A unique identifier. If uid is undefined, a new uid will be created.
|
||||
# @param {Delimiter} beginning Reference or Object.
|
||||
# @param {Delimiter} end Reference or Object.
|
||||
constructor: (uid, beginning, end, prev, next, origin)->
|
||||
if beginning? and end?
|
||||
@saveOperation 'beginning', beginning
|
||||
@saveOperation 'end', end
|
||||
else
|
||||
@beginning = new types.Delimiter undefined, undefined, undefined
|
||||
@end = new types.Delimiter undefined, @beginning, undefined
|
||||
@beginning.next_cl = @end
|
||||
@beginning.execute()
|
||||
@end.execute()
|
||||
super uid, prev, next, origin
|
||||
constructor: (uid)->
|
||||
@beginning = new types.Delimiter undefined, undefined
|
||||
@end = new types.Delimiter @beginning, undefined
|
||||
@beginning.next_cl = @end
|
||||
@beginning.execute()
|
||||
@end.execute()
|
||||
super uid
|
||||
|
||||
type: "ListManager"
|
||||
|
||||
@@ -236,10 +167,10 @@ module.exports = (HB)->
|
||||
# @param {Object} uid A unique identifier. If uid is undefined, a new uid will be created.
|
||||
# @param {Delimiter} beginning Reference or Object.
|
||||
# @param {Delimiter} end Reference or Object.
|
||||
constructor: (@event_properties, @event_this, uid, beginning, end, prev, next, origin)->
|
||||
constructor: (@event_properties, @event_this, uid, beginning, end)->
|
||||
if not @event_properties['object']?
|
||||
@event_properties['object'] = @event_this
|
||||
super uid, beginning, end, prev, next, origin
|
||||
super uid, beginning, end
|
||||
|
||||
type: "ReplaceManager"
|
||||
|
||||
@@ -248,10 +179,6 @@ module.exports = (HB)->
|
||||
while o?
|
||||
o.applyDelete()
|
||||
o = o.next_cl
|
||||
# if this was created by an AddName operation, delete it too
|
||||
if @add_name_ops?
|
||||
for o in @add_name_ops
|
||||
o.applyDelete()
|
||||
super()
|
||||
|
||||
cleanup: ()->
|
||||
@@ -312,25 +239,8 @@ module.exports = (HB)->
|
||||
'beginning' : @beginning.getUid()
|
||||
'end' : @end.getUid()
|
||||
}
|
||||
if @prev_cl? and @next_cl?
|
||||
json['prev'] = @prev_cl.getUid()
|
||||
json['next'] = @next_cl.getUid()
|
||||
if @origin? # TODO: do this everywhere: and @origin isnt @prev_cl
|
||||
json["origin"] = @origin().getUid()
|
||||
json
|
||||
|
||||
parser["ReplaceManager"] = (json)->
|
||||
{
|
||||
'uid' : uid
|
||||
'prev': prev
|
||||
'next': next
|
||||
'origin' : origin
|
||||
'beginning' : beginning
|
||||
'end' : end
|
||||
} = json
|
||||
new ReplaceManager uid, beginning, end, prev, next, origin
|
||||
|
||||
|
||||
#
|
||||
# @nodoc
|
||||
# The ReplaceManager manages Replaceables.
|
||||
@@ -346,9 +256,7 @@ module.exports = (HB)->
|
||||
constructor: (content, parent, uid, prev, next, origin, is_deleted)->
|
||||
@saveOperation 'content', content
|
||||
@saveOperation 'parent', parent
|
||||
if not (prev? and next?)
|
||||
throw new Error "You must define prev, and next for Replaceable-types!"
|
||||
super uid, prev, next, origin
|
||||
super uid, prev, next, origin # Parent is already saved by Replaceable
|
||||
@is_deleted = is_deleted
|
||||
|
||||
type: "Replaceable"
|
||||
@@ -415,20 +323,19 @@ module.exports = (HB)->
|
||||
{
|
||||
'type': "Replaceable"
|
||||
'content': @content?.getUid()
|
||||
'replace_manager' : @parent.getUid()
|
||||
'parent' : @parent.getUid()
|
||||
'prev': @prev_cl.getUid()
|
||||
'next': @next_cl.getUid()
|
||||
'origin' : @origin.getUid()
|
||||
'uid' : @getUid()
|
||||
'is_deleted': @is_deleted
|
||||
}
|
||||
if @origin? and @origin isnt @prev_cl
|
||||
json["origin"] = @origin.getUid()
|
||||
json
|
||||
|
||||
parser["Replaceable"] = (json)->
|
||||
{
|
||||
'content' : content
|
||||
'replace_manager' : parent
|
||||
'parent' : parent
|
||||
'uid' : uid
|
||||
'prev': prev
|
||||
'next': next
|
||||
|
||||
+10
-23
@@ -22,14 +22,12 @@ module.exports = (HB)->
|
||||
# @param {String} content The content of this Insert-type Operation. Usually you restrict the length of content to size 1
|
||||
# @param {Object} uid A unique identifier. If uid is undefined, a new uid will be created.
|
||||
#
|
||||
constructor: (content, uid, prev, next, origin)->
|
||||
constructor: (content, uid, prev, next, origin, parent)->
|
||||
if content?.uid?.creator
|
||||
@saveOperation 'content', content
|
||||
else
|
||||
@content = content
|
||||
if not (prev? and next?)
|
||||
throw new Error "You must define prev, and next for TextInsert-types!"
|
||||
super uid, prev, next, origin
|
||||
super uid, prev, next, origin, parent
|
||||
|
||||
type: "TextInsert"
|
||||
|
||||
@@ -78,13 +76,14 @@ module.exports = (HB)->
|
||||
'uid' : @getUid()
|
||||
'prev': @prev_cl.getUid()
|
||||
'next': @next_cl.getUid()
|
||||
'origin': @origin.getUid()
|
||||
'parent': @parent.getUid()
|
||||
}
|
||||
|
||||
if @content?.getUid?
|
||||
json['content'] = @content.getUid()
|
||||
else
|
||||
json['content'] = @content
|
||||
if @origin isnt @prev_cl
|
||||
json["origin"] = @origin.getUid()
|
||||
json
|
||||
|
||||
parser["TextInsert"] = (json)->
|
||||
@@ -94,8 +93,9 @@ module.exports = (HB)->
|
||||
'prev': prev
|
||||
'next': next
|
||||
'origin' : origin
|
||||
'parent' : parent
|
||||
} = json
|
||||
new TextInsert content, uid, prev, next, origin
|
||||
new TextInsert content, uid, prev, next, origin, parent
|
||||
|
||||
#
|
||||
# Handles a WordType-like data structures with support for insertText/deleteText at a word-position.
|
||||
@@ -107,9 +107,9 @@ module.exports = (HB)->
|
||||
# @private
|
||||
# @param {Object} uid A unique identifier. If uid is undefined, a new uid will be created.
|
||||
#
|
||||
constructor: (uid, beginning, end, prev, next, origin)->
|
||||
constructor: (uid)->
|
||||
@textfields = []
|
||||
super uid, beginning, end, prev, next, origin
|
||||
super uid
|
||||
|
||||
#
|
||||
# Identifies this class.
|
||||
@@ -331,27 +331,14 @@ module.exports = (HB)->
|
||||
json = {
|
||||
'type': "WordType"
|
||||
'uid' : @getUid()
|
||||
'beginning' : @beginning.getUid()
|
||||
'end' : @end.getUid()
|
||||
}
|
||||
if @prev_cl?
|
||||
json['prev'] = @prev_cl.getUid()
|
||||
if @next_cl?
|
||||
json['next'] = @next_cl.getUid()
|
||||
if @origin? # and @origin isnt @prev_cl
|
||||
json["origin"] = @origin().getUid()
|
||||
json
|
||||
|
||||
parser['WordType'] = (json)->
|
||||
{
|
||||
'uid' : uid
|
||||
'beginning' : beginning
|
||||
'end' : end
|
||||
'prev': prev
|
||||
'next': next
|
||||
'origin' : origin
|
||||
} = json
|
||||
new WordType uid, beginning, end, prev, next, origin
|
||||
new WordType uid
|
||||
|
||||
types['TextInsert'] = TextInsert
|
||||
types['TextDelete'] = TextDelete
|
||||
|
||||
@@ -97,22 +97,6 @@ describe "JsonFramework", ->
|
||||
expect(change2).to.equal 8
|
||||
###
|
||||
|
||||
it "has a JsonTypeWrapper", ->
|
||||
y = this.yTest.getSomeUser()
|
||||
y.val('x',"dtrn", 'immutable')
|
||||
y.val('set',{x:"x"}, 'immutable')
|
||||
w = y.value
|
||||
w.x
|
||||
w.set = {y:""}
|
||||
w.x
|
||||
w.set
|
||||
w.set.x
|
||||
expect(w.x).to.equal("dtrn")
|
||||
expect(w.set.x).to.equal("x")
|
||||
y.value.x = {q:4}
|
||||
expect(y.value.x.q).to.equal(4)
|
||||
|
||||
|
||||
it "has a working test suite", ->
|
||||
@yTest.compareAll()
|
||||
|
||||
@@ -131,17 +115,17 @@ describe "JsonFramework", ->
|
||||
expect(test.getContent(0)).to.deep.equal(@yTest.getContent(1))
|
||||
|
||||
it "can handle creaton of complex json (1)", ->
|
||||
@yTest.users[0].val('a', 'q')
|
||||
@yTest.users[2].val('a', 't')
|
||||
@yTest.users[0].val('a', 'q', "mutable")
|
||||
@yTest.users[1].val('a', 't', "mutable")
|
||||
@yTest.compareAll()
|
||||
q = @yTest.users[1].val('a')
|
||||
q = @yTest.users[2].val('a')
|
||||
q.insertText(0,'A')
|
||||
@yTest.compareAll()
|
||||
expect(@yTest.getSomeUser().value.a.val()).to.equal("At")
|
||||
|
||||
it "can handle creaton of complex json (2)", ->
|
||||
@yTest.getSomeUser().val('x', {'a':'b'})
|
||||
@yTest.getSomeUser().val('a', {'a':{q:"dtrndtrtdrntdrnrtdnrtdnrtdnrtdnrdnrdt"}})
|
||||
@yTest.getSomeUser().val('a', {'a':{q:"dtrndtrtdrntdrnrtdnrtdnrtdnrtdnrdnrdt"}}, "mutable")
|
||||
@yTest.getSomeUser().val('b', {'a':{}})
|
||||
@yTest.getSomeUser().val('c', {'a':'c'})
|
||||
@yTest.getSomeUser().val('c', {'a':'b'})
|
||||
@@ -176,7 +160,7 @@ describe "JsonFramework", ->
|
||||
expect(change.name).to.equal("newStuff")
|
||||
last_task = "observer1"
|
||||
u.observe observer1
|
||||
u.val("newStuff","someStuff")
|
||||
u.val("newStuff","someStuff","mutable")
|
||||
expect(last_task).to.equal("observer1")
|
||||
u.unobserve observer1
|
||||
|
||||
@@ -196,7 +180,7 @@ describe "JsonFramework", ->
|
||||
u.unobserve observer2
|
||||
|
||||
it "Observers work on JSON Types (update type observers, local and foreign)", ->
|
||||
u = @yTest.users[0].val("newStuff","oldStuff").val("moreStuff","moreOldStuff")
|
||||
u = @yTest.users[0].val("newStuff","oldStuff","mutable").val("moreStuff","moreOldStuff","mutable")
|
||||
@yTest.flushAll()
|
||||
last_task = null
|
||||
observer1 = (changes)->
|
||||
@@ -231,7 +215,7 @@ describe "JsonFramework", ->
|
||||
|
||||
|
||||
it "Observers work on JSON Types (delete type observers, local and foreign)", ->
|
||||
u = @yTest.users[0].val("newStuff","oldStuff").val("moreStuff","moreOldStuff")
|
||||
u = @yTest.users[0].val("newStuff","oldStuff","mutable").val("moreStuff","moreOldStuff","mutable")
|
||||
@yTest.flushAll()
|
||||
last_task = null
|
||||
observer1 = (changes)->
|
||||
|
||||
@@ -12,9 +12,9 @@ Connector = require "../bower_components/connector/lib/test-connector/test-conne
|
||||
module.exports = class Test
|
||||
constructor: (@name_suffix = "")->
|
||||
@number_of_test_cases_multiplier = 1
|
||||
@repeat_this = 1 * @number_of_test_cases_multiplier
|
||||
@doSomething_amount = 50 * @number_of_test_cases_multiplier
|
||||
@number_of_engines = 4 + @number_of_test_cases_multiplier - 1
|
||||
@repeat_this = 503 * @number_of_test_cases_multiplier
|
||||
@doSomething_amount = 5 * @number_of_test_cases_multiplier
|
||||
@number_of_engines = 3 + @number_of_test_cases_multiplier - 1
|
||||
|
||||
@time = 0 # denotes to the time when run was started
|
||||
@ops = 0 # number of operations (used with @time)
|
||||
|
||||
@@ -17,9 +17,7 @@ class TextTest extends Test
|
||||
|
||||
makeNewUser: (userId)->
|
||||
conn = new Connector userId
|
||||
y = new Yatta conn
|
||||
y.val("TextTest","","mutable")
|
||||
y
|
||||
new Yatta conn
|
||||
|
||||
getRandomRoot: (user_num)->
|
||||
@users[user_num].val("TextTest")
|
||||
@@ -35,6 +33,8 @@ describe "TextFramework", ->
|
||||
test_user_connector = new Connector 'test_user'
|
||||
@test_user = @yTest.makeNewUser 'test_user', test_user_connector
|
||||
test_user_connector.join @users[0].connector
|
||||
@users[0].val("TextTest","","mutable")
|
||||
@yTest.flushAll()
|
||||
done()
|
||||
|
||||
it "simple multi-char insert", ->
|
||||
@@ -46,7 +46,7 @@ describe "TextFramework", ->
|
||||
expect(u.val()).to.equal("abcxyz")
|
||||
|
||||
it "Observers work on shared Text (insert type observers, local and foreign)", ->
|
||||
u = @yTest.users[0].val("TextTest","my awesome Text").val("TextTest")
|
||||
u = @yTest.users[0].val("TextTest","my awesome Text","mutable").val("TextTest")
|
||||
@yTest.flushAll()
|
||||
last_task = null
|
||||
observer1 = (changes)->
|
||||
@@ -80,7 +80,7 @@ describe "TextFramework", ->
|
||||
u.unobserve observer2
|
||||
|
||||
it "Observers work on shared Text (delete type observers, local and foreign)", ->
|
||||
u = @yTest.users[0].val("TextTest","my awesome Text").val("TextTest")
|
||||
u = @yTest.users[0].val("TextTest","my awesome Text","mutable").val("TextTest")
|
||||
@yTest.flushAll()
|
||||
last_task = null
|
||||
observer1 = (changes)->
|
||||
|
||||
+1
-2
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user