added tests for observer types
This commit is contained in:
parent
2f47ad9e3a
commit
fd1128d456
13
README.md
13
README.md
@ -1,11 +1,11 @@
|
||||
|
||||
# 
|
||||
|
||||
A Real-Time web framework that manages concurrency control for arbitrary data structures.
|
||||
A Real-Time web framework that manages concurrency control for arbitrary data types.
|
||||
Yatta! provides similar functionality as [ShareJs](https://github.com/share/ShareJS) and [OpenCoweb](https://github.com/opencoweb/coweb),
|
||||
but does not require you to understand how the internals work. The predefined data structures provide a simple API to access your shared data structures.
|
||||
but does not require you to understand how the internals work. The predefined data types provide a simple API to access your shared data types.
|
||||
|
||||
Predefined data structures:
|
||||
Predefined data types:
|
||||
* Text - [Collaborative Text Editing Example](http://dadamonad.github.io/Yatta/examples/TextEditing/)
|
||||
* Json - [Tutorial](http://dadamonad.github.io/Yatta/examples/PeerJs-Json/)
|
||||
* XML - [XML Example](http://dadamonad.github.io/Yatta/examples/XmlExample/) Collaboratively manipulate the dom with native dom-features and jQuery.
|
||||
@ -27,6 +27,9 @@ Either clone this git repository, install it with [bower](http://bower.io/), or
|
||||
bower install Yatta
|
||||
```
|
||||
Then you include the libraries directly from the installation folder.
|
||||
```
|
||||
<script src="./bower_components/yatta.js"></script>
|
||||
```
|
||||
|
||||
### Npm
|
||||
```
|
||||
@ -35,9 +38,7 @@ npm install yatta --save
|
||||
|
||||
And use it like this with *npm*:
|
||||
```
|
||||
Y = require("yatta");
|
||||
Y.createPeerJsConnector({key: 'xxx'}, function(Connector, user_id){
|
||||
yatta = new Y.JsonFramework(user_id, Connector);
|
||||
Yatta = require("yatta");
|
||||
```
|
||||
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@ -24,7 +24,7 @@
|
||||
};
|
||||
|
||||
Operation.prototype.unobserve = function(f) {
|
||||
return this.event_listeners.filter(function(g) {
|
||||
return this.event_listeners = this.event_listeners.filter(function(g) {
|
||||
return f !== g;
|
||||
});
|
||||
};
|
||||
@ -156,9 +156,13 @@
|
||||
};
|
||||
|
||||
Delete.prototype.execute = function() {
|
||||
var res;
|
||||
if (this.validateSavedOperations()) {
|
||||
this.deletes.applyDelete(this);
|
||||
return Delete.__super__.execute.apply(this, arguments);
|
||||
res = Delete.__super__.execute.apply(this, arguments);
|
||||
if (res) {
|
||||
this.deletes.applyDelete(this);
|
||||
}
|
||||
return res;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@ -307,7 +311,8 @@
|
||||
type: "insert",
|
||||
position: this.getPosition(),
|
||||
object: this.parent,
|
||||
changed_by: this.uid.creator
|
||||
changed_by: this.uid.creator,
|
||||
value: this.content
|
||||
}
|
||||
]) : void 0;
|
||||
};
|
||||
|
File diff suppressed because one or more lines are too long
@ -195,22 +195,21 @@
|
||||
|
||||
ListManager.prototype.getOperationByPosition = function(position) {
|
||||
var o;
|
||||
o = this.beginning.next_cl;
|
||||
if ((position > 0 || o.isDeleted()) && !(o instanceof types.Delimiter)) {
|
||||
while (o.isDeleted() && !(o instanceof types.Delimiter)) {
|
||||
o = o.next_cl;
|
||||
o = this.beginning;
|
||||
while (true) {
|
||||
if (o instanceof types.Delimiter && (o.prev_cl != null)) {
|
||||
o = o.prev_cl;
|
||||
while (o.isDeleted() || !(o instanceof types.Delimiter)) {
|
||||
o = o.prev_cl;
|
||||
}
|
||||
break;
|
||||
}
|
||||
while (true) {
|
||||
if (o instanceof types.Delimiter) {
|
||||
break;
|
||||
}
|
||||
if (position <= 0 && !o.isDeleted()) {
|
||||
break;
|
||||
}
|
||||
o = o.next_cl;
|
||||
if (!o.isDeleted()) {
|
||||
position -= 1;
|
||||
}
|
||||
if (position <= 0 && !o.isDeleted()) {
|
||||
break;
|
||||
}
|
||||
o = o.next_cl;
|
||||
if (!o.isDeleted()) {
|
||||
position -= 1;
|
||||
}
|
||||
}
|
||||
return o;
|
||||
|
File diff suppressed because one or more lines are too long
@ -147,15 +147,14 @@
|
||||
};
|
||||
|
||||
WordType.prototype.insertText = function(position, content) {
|
||||
var ith, left;
|
||||
var ith;
|
||||
ith = this.getOperationByPosition(position);
|
||||
left = ith.prev_cl;
|
||||
return this.insertAfter(left, content);
|
||||
return this.insertAfter(ith, content);
|
||||
};
|
||||
|
||||
WordType.prototype.deleteText = function(position, length) {
|
||||
var d, delete_ops, i, o, _i;
|
||||
o = this.getOperationByPosition(position);
|
||||
o = this.getOperationByPosition(position + 1);
|
||||
delete_ops = [];
|
||||
for (i = _i = 0; 0 <= length ? _i < length : _i > length; i = 0 <= length ? ++_i : --_i) {
|
||||
if (o instanceof types.Delimiter) {
|
||||
|
File diff suppressed because one or more lines are too long
@ -42,7 +42,7 @@
|
||||
module.exports = createYatta;
|
||||
|
||||
if ((typeof window !== "undefined" && window !== null) && (window.Yatta == null)) {
|
||||
window.Yatta = Yatta;
|
||||
window.Yatta = createYatta;
|
||||
}
|
||||
|
||||
}).call(this);
|
||||
|
@ -1 +1 @@
|
||||
{"version":3,"sources":["Yatta.coffee"],"names":[],"mappings":"AACA;AAAA,MAAA,4EAAA;IAAA;mSAAA;;AAAA,EAAA,wBAAA,GAA2B,OAAA,CAAQ,mBAAR,CAA3B,CAAA;;AAAA,EACA,aAAA,GAAgB,OAAA,CAAQ,iBAAR,CADhB,CAAA;;AAAA,EAEA,MAAA,GAAS,OAAA,CAAQ,UAAR,CAFT,CAAA;;AAAA,EAGA,cAAA,GAAiB,OAAA,CAAQ,oBAAR,CAHjB,CAAA;;AAAA,EAKA,WAAA,GAAc,SAAC,SAAD,GAAA;AACZ,QAAA,uCAAA;AAAA,IAAA,OAAA,GAAU,SAAS,CAAC,EAApB,CAAA;AAAA,IACA,EAAA,GAAS,IAAA,aAAA,CAAc,OAAd,CADT,CAAA;AAAA,IAEA,YAAA,GAAe,wBAAA,CAAyB,EAAzB,CAFf,CAAA;AAAA,IAGA,KAAA,GAAQ,YAAY,CAAC,KAHrB,CAAA;AAAA,IAYM;AAMJ,8BAAA,CAAA;;AAAa,MAAA,eAAA,GAAA;AACX,QAAA,IAAC,CAAA,SAAD,GAAa,SAAb,CAAA;AAAA,QACA,IAAC,CAAA,EAAD,GAAM,EADN,CAAA;AAAA,QAEA,IAAC,CAAA,KAAD,GAAS,KAFT,CAAA;AAAA,QAGA,IAAC,CAAA,MAAD,GAAc,IAAA,MAAA,CAAO,IAAC,CAAA,EAAR,EAAY,YAAY,CAAC,MAAzB,CAHd,CAAA;AAAA,QAIA,cAAA,CAAe,IAAC,CAAA,SAAhB,EAA2B,IAAC,CAAA,MAA5B,EAAoC,IAAC,CAAA,EAArC,EAAyC,YAAY,CAAC,kBAAtD,CAJA,CAAA;AAAA,QAKA,wCAAA,SAAA,CALA,CADW;MAAA,CAAb;;AAAA,sBAQA,YAAA,GAAc,SAAA,GAAA;eACZ,IAAC,CAAA,UADW;MAAA,CARd,CAAA;;mBAAA;;OANkB,KAAK,CAAC,SAZ1B,CAAA;AA6BA,WAAW,IAAA,KAAA,CAAM,EAAE,CAAC,2BAAH,CAAA,CAAN,CAAuC,CAAC,OAAxC,CAAA,CAAX,CA9BY;EAAA,CALd,CAAA;;AAAA,EAqCA,MAAM,CAAC,OAAP,GAAiB,WArCjB,CAAA;;AAsCA,EAAA,IAAG,kDAAA,IAAgB,sBAAnB;AACE,IAAA,MAAM,CAAC,KAAP,GAAe,KAAf,CADF;GAtCA;AAAA","file":"Yatta.js","sourceRoot":"/source/","sourcesContent":["\njson_types_uninitialized = require \"./Types/JsonTypes\"\nHistoryBuffer = require \"./HistoryBuffer\"\nEngine = require \"./Engine\"\nadaptConnector = require \"./ConnectorAdapter\"\n\ncreateYatta = (connector)->\n user_id = connector.id # TODO: change to getUniqueId()\n HB = new HistoryBuffer user_id\n type_manager = json_types_uninitialized HB\n types = type_manager.types\n\n #\n # Framework for Json data-structures.\n # Known values that are supported:\n # * String\n # * Integer\n # * Array\n #\n class Yatta extends types.JsonType\n\n #\n # @param {String} user_id Unique id of the peer.\n # @param {Connector} Connector the connector class.\n #\n constructor: ()->\n @connector = connector\n @HB = HB\n @types = types\n @engine = new Engine @HB, type_manager.parser\n adaptConnector @connector, @engine, @HB, type_manager.execution_listener\n super\n\n getConnector: ()->\n @connector\n\n return new Yatta(HB.getReservedUniqueIdentifier()).execute()\n\nmodule.exports = createYatta\nif window? and not window.Yatta?\n window.Yatta = Yatta\n"]}
|
||||
{"version":3,"sources":["Yatta.coffee"],"names":[],"mappings":"AACA;AAAA,MAAA,4EAAA;IAAA;mSAAA;;AAAA,EAAA,wBAAA,GAA2B,OAAA,CAAQ,mBAAR,CAA3B,CAAA;;AAAA,EACA,aAAA,GAAgB,OAAA,CAAQ,iBAAR,CADhB,CAAA;;AAAA,EAEA,MAAA,GAAS,OAAA,CAAQ,UAAR,CAFT,CAAA;;AAAA,EAGA,cAAA,GAAiB,OAAA,CAAQ,oBAAR,CAHjB,CAAA;;AAAA,EAKA,WAAA,GAAc,SAAC,SAAD,GAAA;AACZ,QAAA,uCAAA;AAAA,IAAA,OAAA,GAAU,SAAS,CAAC,EAApB,CAAA;AAAA,IACA,EAAA,GAAS,IAAA,aAAA,CAAc,OAAd,CADT,CAAA;AAAA,IAEA,YAAA,GAAe,wBAAA,CAAyB,EAAzB,CAFf,CAAA;AAAA,IAGA,KAAA,GAAQ,YAAY,CAAC,KAHrB,CAAA;AAAA,IAYM;AAMJ,8BAAA,CAAA;;AAAa,MAAA,eAAA,GAAA;AACX,QAAA,IAAC,CAAA,SAAD,GAAa,SAAb,CAAA;AAAA,QACA,IAAC,CAAA,EAAD,GAAM,EADN,CAAA;AAAA,QAEA,IAAC,CAAA,KAAD,GAAS,KAFT,CAAA;AAAA,QAGA,IAAC,CAAA,MAAD,GAAc,IAAA,MAAA,CAAO,IAAC,CAAA,EAAR,EAAY,YAAY,CAAC,MAAzB,CAHd,CAAA;AAAA,QAIA,cAAA,CAAe,IAAC,CAAA,SAAhB,EAA2B,IAAC,CAAA,MAA5B,EAAoC,IAAC,CAAA,EAArC,EAAyC,YAAY,CAAC,kBAAtD,CAJA,CAAA;AAAA,QAKA,wCAAA,SAAA,CALA,CADW;MAAA,CAAb;;AAAA,sBAQA,YAAA,GAAc,SAAA,GAAA;eACZ,IAAC,CAAA,UADW;MAAA,CARd,CAAA;;mBAAA;;OANkB,KAAK,CAAC,SAZ1B,CAAA;AA6BA,WAAW,IAAA,KAAA,CAAM,EAAE,CAAC,2BAAH,CAAA,CAAN,CAAuC,CAAC,OAAxC,CAAA,CAAX,CA9BY;EAAA,CALd,CAAA;;AAAA,EAqCA,MAAM,CAAC,OAAP,GAAiB,WArCjB,CAAA;;AAsCA,EAAA,IAAG,kDAAA,IAAgB,sBAAnB;AACE,IAAA,MAAM,CAAC,KAAP,GAAe,WAAf,CADF;GAtCA;AAAA","file":"Yatta.js","sourceRoot":"/source/","sourcesContent":["\njson_types_uninitialized = require \"./Types/JsonTypes\"\nHistoryBuffer = require \"./HistoryBuffer\"\nEngine = require \"./Engine\"\nadaptConnector = require \"./ConnectorAdapter\"\n\ncreateYatta = (connector)->\n user_id = connector.id # TODO: change to getUniqueId()\n HB = new HistoryBuffer user_id\n type_manager = json_types_uninitialized HB\n types = type_manager.types\n\n #\n # Framework for Json data-structures.\n # Known values that are supported:\n # * String\n # * Integer\n # * Array\n #\n class Yatta extends types.JsonType\n\n #\n # @param {String} user_id Unique id of the peer.\n # @param {Connector} Connector the connector class.\n #\n constructor: ()->\n @connector = connector\n @HB = HB\n @types = types\n @engine = new Engine @HB, type_manager.parser\n adaptConnector @connector, @engine, @HB, type_manager.execution_listener\n super\n\n getConnector: ()->\n @connector\n\n return new Yatta(HB.getReservedUniqueIdentifier()).execute()\n\nmodule.exports = createYatta\nif window? and not window.Yatta?\n window.Yatta = createYatta\n"]}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -178,7 +178,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div id='footer'>
|
||||
December 22, 14 14:36:45 by
|
||||
December 23, 14 11:09:09 by
|
||||
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
|
||||
Codo
|
||||
</a>
|
||||
|
@ -359,7 +359,7 @@ E.g.: let x = {a:[]}. Then x.a.push 1 wouldn't change anything</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id='footer'>
|
||||
December 22, 14 14:36:45 by
|
||||
December 23, 14 11:09:09 by
|
||||
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
|
||||
Codo
|
||||
</a>
|
||||
|
@ -139,7 +139,7 @@ console.log(w.newProperty == "Awesome") # true!</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div id='footer'>
|
||||
December 22, 14 14:36:45 by
|
||||
December 23, 14 11:09:09 by
|
||||
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
|
||||
Codo
|
||||
</a>
|
||||
|
@ -389,7 +389,7 @@ yatta.bind(textbox);</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div id='footer'>
|
||||
December 22, 14 14:36:45 by
|
||||
December 23, 14 11:09:09 by
|
||||
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
|
||||
Codo
|
||||
</a>
|
||||
|
@ -115,7 +115,7 @@ Known values that are supported:</p><ul>
|
||||
</div>
|
||||
</div>
|
||||
<div id='footer'>
|
||||
December 22, 14 14:36:45 by
|
||||
December 23, 14 11:09:09 by
|
||||
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
|
||||
Codo
|
||||
</a>
|
||||
|
@ -38,7 +38,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div id='footer'>
|
||||
December 22, 14 14:36:45 by
|
||||
December 23, 14 11:09:09 by
|
||||
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
|
||||
Codo
|
||||
</a>
|
||||
|
@ -76,7 +76,7 @@ But I would become really motivated if you gave me some feedback :) (<a href="ht
|
||||
</ul>
|
||||
<h2 id="support">Support</h2><p>Please report <em>any</em> issues to the <a href="https://github.com/DadaMonad/Yatta/issues">Github issue page</a>!
|
||||
I would appreciate if developers gave me feedback on how <em>convenient</em> the framework is, and if it is easy to use. Particularly the XML-support may not support every DOM-methods - if you encounter a method that does not cause any change on other peers,
|
||||
please state function name, and sample parameters. However, there are browser-specific features, that Yatta won't support.</p><h2 id="license">License</h2><p>Yatta! is licensed under the <a href="./LICENSE.txt">MIT License</a>.</p><a href="mailto:kevin.jahns@rwth-aachen.de">kevin.jahns@rwth-aachen.de</a>
|
||||
please state function name, and sample parameters. However, there are browser-specific features, that Yatta won't support.</p><h2 id="license">License</h2><p>Yatta! is licensed under the <a href="./LICENSE.txt">MIT License</a>.</p><a href="mailto:kevin.jahns@rwth-aachen.de">kevin.jahns@rwth-aachen.de</a>
|
||||
|
||||
|
||||
|
||||
@ -85,7 +85,7 @@ please state function name, and sample parameters. However, there are browser-sp
|
||||
</div>
|
||||
</div>
|
||||
<div id='footer'>
|
||||
December 22, 14 14:36:45 by
|
||||
December 23, 14 11:09:09 by
|
||||
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
|
||||
Codo
|
||||
</a>
|
||||
|
@ -106,7 +106,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div id='footer'>
|
||||
December 22, 14 14:36:45 by
|
||||
December 23, 14 11:09:09 by
|
||||
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
|
||||
Codo
|
||||
</a>
|
||||
|
@ -72,7 +72,7 @@
|
||||
</dl>
|
||||
</div>
|
||||
<div id='footer'>
|
||||
December 22, 14 14:36:45 by
|
||||
December 23, 14 11:09:09 by
|
||||
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
|
||||
Codo
|
||||
</a>
|
||||
|
@ -48,7 +48,7 @@
|
||||
</dl>
|
||||
</div>
|
||||
<div id='footer'>
|
||||
December 22, 14 14:36:45 by
|
||||
December 23, 14 11:09:09 by
|
||||
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
|
||||
Codo
|
||||
</a>
|
||||
|
@ -39,7 +39,7 @@
|
||||
</table>
|
||||
</div>
|
||||
<div id='footer'>
|
||||
December 22, 14 14:36:45 by
|
||||
December 23, 14 11:09:09 by
|
||||
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
|
||||
Codo
|
||||
</a>
|
||||
|
@ -39,7 +39,7 @@
|
||||
</table>
|
||||
</div>
|
||||
<div id='footer'>
|
||||
December 22, 14 14:36:45 by
|
||||
December 23, 14 11:09:09 by
|
||||
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
|
||||
Codo
|
||||
</a>
|
||||
|
@ -39,7 +39,7 @@
|
||||
</table>
|
||||
</div>
|
||||
<div id='footer'>
|
||||
December 22, 14 14:36:45 by
|
||||
December 23, 14 11:09:09 by
|
||||
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
|
||||
Codo
|
||||
</a>
|
||||
|
@ -39,7 +39,7 @@
|
||||
</table>
|
||||
</div>
|
||||
<div id='footer'>
|
||||
December 22, 14 14:36:45 by
|
||||
December 23, 14 11:09:09 by
|
||||
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
|
||||
Codo
|
||||
</a>
|
||||
|
@ -74,7 +74,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div id='footer'>
|
||||
December 22, 14 14:36:45 by
|
||||
December 23, 14 11:09:09 by
|
||||
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
|
||||
Codo
|
||||
</a>
|
||||
|
@ -277,12 +277,7 @@ module.exports = (HB)->
|
||||
garbagecollect = true
|
||||
super garbagecollect
|
||||
if callLater
|
||||
@parent.callEvent [
|
||||
type: "insert"
|
||||
position: @getPosition()
|
||||
object: @parent # TODO: You can combine getPosition + getParent in a more efficient manner! (only left Delimiter will hold @parent)
|
||||
changed_by: o.uid.creator
|
||||
]
|
||||
@callOperationSpecificDeleteEvents(o)
|
||||
if @next_cl?.isDeleted()
|
||||
# garbage collect next_cl
|
||||
@next_cl.applyDelete()
|
||||
@ -378,18 +373,27 @@ module.exports = (HB)->
|
||||
|
||||
@setParent @prev_cl.getParent() # do Insertions always have a parent?
|
||||
super # notify the execution_listeners
|
||||
@callOperationSpecificEvents()
|
||||
@callOperationSpecificInsertEvents()
|
||||
@
|
||||
|
||||
callOperationSpecificEvents: ()->
|
||||
callOperationSpecificInsertEvents: ()->
|
||||
@parent?.callEvent [
|
||||
type: "insert"
|
||||
position: @getPosition()
|
||||
object: @parent
|
||||
changed_by: @uid.creator
|
||||
changedBy: @uid.creator
|
||||
value: @content
|
||||
]
|
||||
|
||||
callOperationSpecificDeleteEvents: (o)->
|
||||
@parent.callEvent [
|
||||
type: "delete"
|
||||
position: @getPosition()
|
||||
object: @parent # TODO: You can combine getPosition + getParent in a more efficient manner! (only left Delimiter will hold @parent)
|
||||
length: 1
|
||||
changedBy: o.uid.creator
|
||||
]
|
||||
|
||||
#
|
||||
# Compute the position of this operation.
|
||||
#
|
||||
|
@ -145,7 +145,7 @@ module.exports = (HB)->
|
||||
that = @
|
||||
Object.observe @bound_json, (events)->
|
||||
for event in events
|
||||
if not event.changed_by? and (event.type is "add" or event.type = "update")
|
||||
if not event.changedBy? and (event.type is "add" or event.type = "update")
|
||||
# this event is not created by Yatta.
|
||||
that.val(event.name, event.object[event.name])
|
||||
@observe (events)->
|
||||
@ -162,7 +162,7 @@ module.exports = (HB)->
|
||||
type: 'update'
|
||||
name: event.name
|
||||
oldValue: oldVal
|
||||
changed_by: event.changed_by
|
||||
changedBy: event.changedBy
|
||||
else
|
||||
notifier.performChange 'add', ()->
|
||||
that.bound_json[event.name] = that.val(event.name)
|
||||
@ -172,7 +172,7 @@ module.exports = (HB)->
|
||||
type: 'add'
|
||||
name: event.name
|
||||
oldValue: oldVal
|
||||
changed_by:event.changed_by
|
||||
changedBy:event.changedBy
|
||||
@bound_json
|
||||
|
||||
#
|
||||
|
@ -35,29 +35,31 @@ module.exports = (HB)->
|
||||
if content?
|
||||
if not @map[name]?
|
||||
(new AddName undefined, @, name).execute()
|
||||
## TODO: del this
|
||||
if @map[name] == null
|
||||
qqq = @
|
||||
x = new AddName undefined, @, name
|
||||
x.execute()
|
||||
## endtodo
|
||||
@map[name].replace content
|
||||
@
|
||||
else if name?
|
||||
obj = @map[name]?.val()
|
||||
if obj instanceof types.ImmutableObject
|
||||
obj.val()
|
||||
prop = @map[name]
|
||||
if prop? and not prop.isContentDeleted()
|
||||
obj = prop.val()
|
||||
if obj instanceof types.ImmutableObject
|
||||
obj.val()
|
||||
else
|
||||
obj
|
||||
else
|
||||
obj
|
||||
undefined
|
||||
else
|
||||
result = {}
|
||||
for name,o of @map
|
||||
obj = o.val()
|
||||
if obj instanceof types.ImmutableObject or obj instanceof MapManager
|
||||
obj = obj.val()
|
||||
result[name] = obj
|
||||
if not o.isContentDeleted()
|
||||
obj = o.val()
|
||||
if obj instanceof types.ImmutableObject or obj instanceof MapManager
|
||||
obj = obj.val()
|
||||
result[name] = obj
|
||||
result
|
||||
|
||||
delete: (name)->
|
||||
@map[name]?.deleteContent()
|
||||
@
|
||||
#
|
||||
# @nodoc
|
||||
# When a new property in a map manager is created, then the uids of the inserted Operations
|
||||
@ -234,7 +236,9 @@ 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_porperties, @event_this, uid, beginning, end, prev, next, origin)->
|
||||
constructor: (@event_properties, @event_this, uid, beginning, end, prev, next, origin)->
|
||||
if not @event_properties['object']?
|
||||
@event_properties['object'] = @event_this
|
||||
super uid, beginning, end, prev, next, origin
|
||||
|
||||
type: "ReplaceManager"
|
||||
@ -262,8 +266,8 @@ module.exports = (HB)->
|
||||
#
|
||||
callEventDecorator: (events)->
|
||||
if not @isDeleted()
|
||||
for name,prop of @event_porperties
|
||||
for event in events
|
||||
for event in events
|
||||
for name,prop of @event_properties
|
||||
event[name] = prop
|
||||
@event_this.callEvent events
|
||||
undefined
|
||||
@ -276,7 +280,15 @@ module.exports = (HB)->
|
||||
#
|
||||
replace: (content, replaceable_uid)->
|
||||
o = @getLastOperation()
|
||||
(new Replaceable content, @, replaceable_uid, o, o.next_cl).execute()
|
||||
relp = (new Replaceable content, @, replaceable_uid, o, o.next_cl).execute()
|
||||
# TODO: delete repl (for debugging)
|
||||
undefined
|
||||
|
||||
isContentDeleted: ()->
|
||||
@getLastOperation().isDeleted()
|
||||
|
||||
deleteContent: ()->
|
||||
(new types.Delete undefined, @getLastOperation().uid).execute()
|
||||
undefined
|
||||
|
||||
#
|
||||
@ -347,13 +359,14 @@ module.exports = (HB)->
|
||||
@content
|
||||
|
||||
applyDelete: ()->
|
||||
res = super
|
||||
if @content?
|
||||
if @next_cl.type isnt "Delimiter"
|
||||
@content.deleteAllObservers()
|
||||
@content.applyDelete()
|
||||
@content.dontSync()
|
||||
@content = null
|
||||
super
|
||||
res
|
||||
|
||||
cleanup: ()->
|
||||
super
|
||||
@ -363,27 +376,35 @@ module.exports = (HB)->
|
||||
# TODO: consider doing this in a more consistent manner. This could also be
|
||||
# done with execute. But currently, there are no specital Insert-types for ListManager.
|
||||
#
|
||||
callOperationSpecificEvents: ()->
|
||||
callOperationSpecificInsertEvents: ()->
|
||||
if @next_cl.type is "Delimiter" and @prev_cl.type isnt "Delimiter"
|
||||
# this replaces another Replaceable
|
||||
old_value = @prev_cl.content
|
||||
@prev_cl.applyDelete()
|
||||
@parent.callEventDecorator [
|
||||
type: "update"
|
||||
changed_by: @uid.creator
|
||||
changedBy: @uid.creator
|
||||
oldValue: old_value
|
||||
]
|
||||
@prev_cl.applyDelete()
|
||||
else if @next_cl.type isnt "Delimiter"
|
||||
# This will never be recognized by the user, because another
|
||||
# This won't be recognized by the user, because another
|
||||
# concurrent operation is set as the current value of the RM
|
||||
@applyDelete()
|
||||
else # prev _and_ next are Delimiters. This is the first created Replaceable in the RM
|
||||
@parent.callEventDecorator [
|
||||
type: "add"
|
||||
changed_by: @uid.creator
|
||||
changedBy: @uid.creator
|
||||
]
|
||||
undefined
|
||||
|
||||
callOperationSpecificDeleteEvents: (o)->
|
||||
if @next_cl.type is "Delimiter"
|
||||
@parent.callEventDecorator [
|
||||
type: "delete"
|
||||
changedBy: o.uid.creator
|
||||
oldValue: @content
|
||||
]
|
||||
|
||||
#
|
||||
# Encode this operation in such a way that it can be parsed by remote peers.
|
||||
#
|
||||
|
@ -161,3 +161,106 @@ describe "JsonFramework", ->
|
||||
expect(@yTest.getSomeUser().val('null') is null).to.be.ok
|
||||
|
||||
|
||||
it "Observers work on JSON Types (add type observers, local and foreign)", ->
|
||||
u = @yTest.users[0]
|
||||
@yTest.flushAll()
|
||||
last_task = null
|
||||
observer1 = (changes)->
|
||||
expect(changes.length).to.equal(1)
|
||||
change = changes[0]
|
||||
expect(change.type).to.equal("add")
|
||||
expect(change.object).to.equal(u)
|
||||
expect(change.changedBy).to.equal('0')
|
||||
expect(change.name).to.equal("newStuff")
|
||||
last_task = "observer1"
|
||||
u.observe observer1
|
||||
u.val("newStuff","someStuff")
|
||||
expect(last_task).to.equal("observer1")
|
||||
u.unobserve observer1
|
||||
|
||||
observer2 = (changes)->
|
||||
expect(changes.length).to.equal(1)
|
||||
change = changes[0]
|
||||
expect(change.type).to.equal("add")
|
||||
expect(change.object).to.equal(u)
|
||||
expect(change.changedBy).to.equal('1')
|
||||
expect(change.name).to.equal("moreStuff")
|
||||
last_task = "observer2"
|
||||
u.observe observer2
|
||||
v = @yTest.users[1]
|
||||
v.val("moreStuff","someMoreStuff")
|
||||
@yTest.flushAll()
|
||||
expect(last_task).to.equal("observer2")
|
||||
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")
|
||||
@yTest.flushAll()
|
||||
last_task = null
|
||||
observer1 = (changes)->
|
||||
expect(changes.length).to.equal(1)
|
||||
change = changes[0]
|
||||
expect(change.type).to.equal("update")
|
||||
expect(change.object).to.equal(u)
|
||||
expect(change.changedBy).to.equal('0')
|
||||
expect(change.name).to.equal("newStuff")
|
||||
expect(change.oldValue.val()).to.equal("oldStuff")
|
||||
last_task = "observer1"
|
||||
u.observe observer1
|
||||
u.val("newStuff","someStuff")
|
||||
expect(last_task).to.equal("observer1")
|
||||
u.unobserve observer1
|
||||
|
||||
observer2 = (changes)->
|
||||
expect(changes.length).to.equal(1)
|
||||
change = changes[0]
|
||||
expect(change.type).to.equal("update")
|
||||
expect(change.object).to.equal(u)
|
||||
expect(change.changedBy).to.equal('1')
|
||||
expect(change.name).to.equal("moreStuff")
|
||||
expect(change.oldValue.val()).to.equal("moreOldStuff")
|
||||
last_task = "observer2"
|
||||
u.observe observer2
|
||||
v = @yTest.users[1]
|
||||
v.val("moreStuff","someMoreStuff")
|
||||
@yTest.flushAll()
|
||||
expect(last_task).to.equal("observer2")
|
||||
u.unobserve observer2
|
||||
|
||||
|
||||
it "Observers work on JSON Types (delete type observers, local and foreign)", ->
|
||||
u = @yTest.users[0].val("newStuff","oldStuff").val("moreStuff","moreOldStuff")
|
||||
@yTest.flushAll()
|
||||
last_task = null
|
||||
observer1 = (changes)->
|
||||
expect(changes.length).to.equal(1)
|
||||
change = changes[0]
|
||||
expect(change.type).to.equal("delete")
|
||||
expect(change.object).to.equal(u)
|
||||
expect(change.changedBy).to.equal('0')
|
||||
expect(change.name).to.equal("newStuff")
|
||||
expect(change.oldValue.val()).to.equal("oldStuff")
|
||||
last_task = "observer1"
|
||||
u.observe observer1
|
||||
u.delete("newStuff")
|
||||
expect(last_task).to.equal("observer1")
|
||||
u.unobserve observer1
|
||||
|
||||
observer2 = (changes)->
|
||||
expect(changes.length).to.equal(1)
|
||||
change = changes[0]
|
||||
expect(change.type).to.equal("delete")
|
||||
expect(change.object).to.equal(u)
|
||||
expect(change.changedBy).to.equal('1')
|
||||
expect(change.name).to.equal("moreStuff")
|
||||
expect(change.oldValue.val()).to.equal("moreOldStuff")
|
||||
last_task = "observer2"
|
||||
u.observe observer2
|
||||
v = @yTest.users[1]
|
||||
v.delete("moreStuff")
|
||||
@yTest.flushAll()
|
||||
expect(last_task).to.equal("observer2")
|
||||
u.unobserve observer2
|
||||
|
||||
|
||||
|
||||
|
@ -56,7 +56,7 @@ describe "TextFramework", ->
|
||||
expect(change.object).to.equal(u)
|
||||
expect(change.value).to.equal("a")
|
||||
expect(change.position).to.equal(1)
|
||||
expect(change.changed_by).to.equal('0')
|
||||
expect(change.changedBy).to.equal('0')
|
||||
last_task = "observer1"
|
||||
u.observe observer1
|
||||
u.insertText 1, "a"
|
||||
@ -70,7 +70,7 @@ describe "TextFramework", ->
|
||||
expect(change.object).to.equal(u)
|
||||
expect(change.value).to.equal("x")
|
||||
expect(change.position).to.equal(0)
|
||||
expect(change.changed_by).to.equal('1')
|
||||
expect(change.changedBy).to.equal('1')
|
||||
last_task = "observer2"
|
||||
u.observe observer2
|
||||
v = @yTest.users[1].val("TextTest")
|
||||
@ -90,10 +90,10 @@ describe "TextFramework", ->
|
||||
expect(change.object).to.equal(u)
|
||||
expect(change.position).to.equal(1)
|
||||
expect(change.length).to.equal(1)
|
||||
expect(change.changed_by).to.equal('0')
|
||||
expect(change.changedBy).to.equal('0')
|
||||
last_task = "observer1"
|
||||
u.observe observer1
|
||||
u.deleteText 1
|
||||
u.deleteText 1, 1
|
||||
expect(last_task).to.equal("observer1")
|
||||
u.unobserve observer1
|
||||
|
||||
@ -103,12 +103,12 @@ describe "TextFramework", ->
|
||||
expect(change.type).to.equal("delete")
|
||||
expect(change.object).to.equal(u)
|
||||
expect(change.position).to.equal(0)
|
||||
expect(change.length).to.equal(0)
|
||||
expect(change.changed_by).to.equal('1')
|
||||
expect(change.length).to.equal(1)
|
||||
expect(change.changedBy).to.equal('1')
|
||||
last_task = "observer2"
|
||||
u.observe observer2
|
||||
v = @yTest.users[1].val("TextTest")
|
||||
v.deleteText 0
|
||||
v.deleteText 0, 1
|
||||
@yTest.flushAll()
|
||||
expect(last_task).to.equal("observer2")
|
||||
u.unobserve observer2
|
||||
|
Loading…
x
Reference in New Issue
Block a user