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:
parent
6b46500325
commit
b647b2af58
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)->
|
||||
|
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user