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:
DadaMonad 2015-01-16 13:36:15 +00:00
parent 6b46500325
commit b647b2af58
20 changed files with 543 additions and 1254 deletions

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

View File

@ -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.

View File

@ -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>

View File

@ -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","");
};

View File

@ -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
```

View File

@ -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>

View File

@ -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

View File

@ -49,5 +49,5 @@ window.onload = function(){
}
}
});
yatta.val("textfield","");
yatta.val("textfield","", "mutable");
};

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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)->

View File

@ -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)

View File

@ -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

File diff suppressed because one or more lines are too long