This commit is contained in:
Kevin Jahns 2014-10-06 14:19:56 +02:00
commit d2f16eaa87
62 changed files with 2357 additions and 481 deletions

View File

@ -8,12 +8,13 @@ but does not require you to understand how the internals work. The predefined da
Predefined data structures:
* Text - [Collaborative Text Editing Example](http://dadamonad.github.io/Yatta/examples/TextEditing/) and [Source](./examples/TextEditing/)
* Json - [Tutorial](./examples/PeerJs-Json/)
* XML (coming soon)
* XML - [XML Example](./XmlExample) Collaboratively manipulate the dom with native dom-features and jQuery.
Unlike other frameworks, Yatta! supports P2P message propagation and is not bound to a specific communication protocol.
It is possible to add any communication protocol to Yatta. Currently it supports:
* [PeerJs](http://peerjs.com/) - A WebRTC Framework
* [SimpleWebRTC](http://simplewebrtc.com/) - Another WebRTC Framework
* [IWC](http://dbis.rwth-aachen.de/cms/projects/the-xmpp-experience#interwidget-communication) - Inter-widget Communication
## Use it!
@ -75,10 +76,16 @@ Yatta! is still in an early development phase. Don't expect that everything is w
But I would become really motivated if you gave me some feedback :) ([github](https://github.com/DadaMonad/Yatta/issues)).
### Current Issues
* XML support
* HTML editable tag
* More efficient representation of text.
* Use a better data structure for the History Buffer - it should be possible to use Arrays.
* SimpleRTC support
## Support
Please report any issues to the [Github issue page](https://github.com/DadaMonad/Yatta/issues)!
Please report _any_ issues to the [Github issue page](https://github.com/DadaMonad/Yatta/issues)!
I would appreciate if developers gave me feedback on how _convenient_ 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.
## License
Yatta! is licensed under the [MIT License](./LICENSE.txt).

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
!function n(e,t,r){function o(u,s){if(!t[u]){if(!e[u]){var c="function"==typeof require&&require;if(!s&&c)return c(u,!0);if(i)return i(u,!0);throw new Error("Cannot find module '"+u+"'")}var a=t[u]={exports:{}};e[u][0].call(a.exports,function(n){var t=e[u][1][n];return o(t?t:n)},a,a.exports,n,e,t,r)}return t[u].exports}for(var i="function"==typeof require&&require,u=0;u<r.length;u++)o(r[u]);return o}({1:[function(n,e){var t;t=function(){var n,e,t;return t=null,2===arguments.length?(t=new Peer(arguments[0]),e=arguments[1]):(t=new Peer(arguments[0],arguments[1]),t.on("error",function(n){throw new Error("Peerjs connector: "+n)}),t.on("disconnected",function(){throw new Error("Peerjs connector disconnected from signalling server. Cannot accept new connections. Not fatal, but not so good either..")}),e=arguments[2]),n=function(){function n(n,e,r,o){var i,u;this.engine=n,this.HB=e,this.execution_listener=r,this.yatta=o,this.peer=t,this.connections={},this.peer.on("connection",function(n){return function(e){return n.addConnection(e)}}(this)),u=function(n){return function(){var e,t,r,o;r=n.connections,o=[];for(t in r)e=r[t],o.push(e.send({sync_state_vector:n.HB.getOperationCounter()}));return o}}(this),setInterval(u,4e3),i=function(n){return function(e){var t,r,o,i;if(e.uid.creator===n.HB.getUserId()&&"string"!=typeof e.uid.op_number){o=n.connections,i=[];for(r in o)t=o[r],i.push(t.send({op:e}));return i}}}(this),this.execution_listener.push(i)}return n.prototype.connectToPeer=function(n){return null==this.connections[n]&&n!==this.yatta.getUserId()?this.addConnection(t.connect(n)):void 0},n.prototype.getAllConnectionIds=function(){var n,e;e=[];for(n in this.connections)e.push(n);return e},n.prototype.addConnection=function(n){var e,t,r;return this.connections[n.peer]=n,t=!1,e=!1,n.on("data",function(r){return function(o){var i,u,s,c,a;if("empty_message"===o);else if(null!=o.HB){if(t=!0,r.engine.applyOpsCheckDouble(o.HB),!o.initialized)return n.send({conns:r.getAllConnectionIds()})}else{if(null!=o.op)return r.engine.applyOp(o.op);if(null!=o.conns){for(c=o.conns,a=[],u=0,s=c.length;s>u;u++)i=c[u],a.push(r.connectToPeer(i));return a}if(null!=o.sync_state_vector)return n.send({HB:r.yatta.getHistoryBuffer()._encode(o.sync_state_vector),initialized:!0});if(null==o.state_vector)throw new Error("Can't parse this operation: "+o);if(!e)return n.send({HB:r.yatta.getHistoryBuffer()._encode(o.state_vector),initialized:!1}),e=!0}}}(this)),r=function(e){return function(){return n.send({state_vector:e.HB.getOperationCounter()}),t?void 0:setTimeout(r,100)}}(this),r()},n}(),t.on("open",function(t){return e(n,t)})},e.exports=t,"undefined"!=typeof window&&null!==window&&(null==window.Y&&(window.Y={}),window.Y.createPeerJsConnector=t)},{}]},{},[1]);
!function n(e,t,r){function o(s,u){if(!t[s]){if(!e[s]){var c="function"==typeof require&&require;if(!u&&c)return c(s,!0);if(i)return i(s,!0);throw new Error("Cannot find module '"+s+"'")}var a=t[s]={exports:{}};e[s][0].call(a.exports,function(n){var t=e[s][1][n];return o(t?t:n)},a,a.exports,n,e,t,r)}return t[s].exports}for(var i="function"==typeof require&&require,s=0;s<r.length;s++)o(r[s]);return o}({1:[function(n,e){var t;t=function(){var n,e,t;return t=null,2===arguments.length?(t=new Peer(arguments[0]),e=arguments[1]):(t=new Peer(arguments[0],arguments[1]),t.on("error",function(n){throw new Error("Peerjs connector: "+n)}),t.on("disconnected",function(){throw new Error("Peerjs connector disconnected from signalling server. Cannot accept new connections. Not fatal, but not so good either..")}),e=arguments[2]),n=function(){function n(n,e,r,o){var i,s;this.engine=n,this.HB=e,this.execution_listener=r,this.yatta=o,this.peer=t,this.connections={},this.new_connection_listeners=[],this.peer.on("connection",function(n){return function(e){return n.addConnection(e)}}(this)),s=function(n){return function(){var e,t,r,o;r=n.connections,o=[];for(t in r)e=r[t],o.push(e.send({sync_state_vector:n.HB.getOperationCounter()}));return o}}(this),setInterval(s,4e3),i=function(n){return function(e){var t,r,o,i;if(e.uid.creator===n.HB.getUserId()&&"string"!=typeof e.uid.op_number){o=n.connections,i=[];for(r in o)t=o[r],i.push(t.send({op:e}));return i}}}(this),this.execution_listener.push(i)}return n.prototype.connectToPeer=function(n){return null==this.connections[n]&&n!==this.yatta.getUserId()?this.addConnection(t.connect(n)):void 0},n.prototype.getAllConnectionIds=function(){var n,e;e=[];for(n in this.connections)e.push(n);return e},n.prototype.onNewConnection=function(n){return this.new_connection_listeners.push(n)},n.prototype.addConnection=function(n){var e,t,r;return this.connections[n.peer]=n,t=!1,e=!1,n.on("data",function(r){return function(o){var i,s,u,c,a;if("empty_message"===o);else if(null!=o.HB){if(t=!0,r.engine.applyOpsCheckDouble(o.HB),!o.initialized)return n.send({conns:r.getAllConnectionIds()}),r.new_connection_listeners.map(function(e){return e(n)})}else{if(null!=o.op)return r.engine.applyOp(o.op);if(null!=o.conns){for(c=o.conns,a=[],s=0,u=c.length;u>s;s++)i=c[s],a.push(r.connectToPeer(i));return a}if(null!=o.sync_state_vector)return n.send({HB:r.yatta.getHistoryBuffer()._encode(o.sync_state_vector),initialized:!0});if(null==o.state_vector)throw new Error("Can't parse this operation: "+o);if(!e)return n.send({HB:r.yatta.getHistoryBuffer()._encode(o.state_vector),initialized:!1}),e=!0}}}(this)),r=function(e){return function(){return n.send({state_vector:e.HB.getOperationCounter()}),t?void 0:setTimeout(r,100)}}(this),r()},n}(),t.on("open",function(t){return e(n,t)})},e.exports=t,"undefined"!=typeof window&&null!==window&&(null==window.Y&&(window.Y={}),window.Y.createPeerJsConnector=t)},{}]},{},[1]);

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -26,6 +26,7 @@
this.yatta = yatta;
this.peer = peer;
this.connections = {};
this.new_connection_listeners = [];
this.peer.on('connection', (function(_this) {
return function(conn) {
return _this.addConnection(conn);
@ -80,6 +81,10 @@
return _results;
};
PeerJsConnector.prototype.onNewConnection = function(f) {
return this.new_connection_listeners.push(f);
};
PeerJsConnector.prototype.addConnection = function(conn) {
var initialized_him, initialized_me, sendStateVector;
this.connections[conn.peer] = conn;
@ -94,9 +99,12 @@
initialized_me = true;
_this.engine.applyOpsCheckDouble(data.HB);
if (!data.initialized) {
return conn.send({
conn.send({
conns: _this.getAllConnectionIds()
});
return _this.new_connection_listeners.map(function(f) {
return f(conn);
});
}
} else if (data.op != null) {
return _this.engine.applyOp(data.op);

File diff suppressed because one or more lines are too long

View File

@ -218,12 +218,13 @@
Insert.prototype.type = "Insert";
Insert.prototype.applyDelete = function(o) {
var garbagecollect, _ref;
var callLater, garbagecollect, _ref;
if (this.deleted_by == null) {
this.deleted_by = [];
}
callLater = false;
if ((this.parent != null) && !this.isDeleted()) {
this.parent.callEvent("delete", this, o);
callLater = true;
}
if (o != null) {
this.deleted_by.push(o);
@ -233,6 +234,9 @@
garbagecollect = true;
}
Insert.__super__.applyDelete.call(this, garbagecollect);
if (callLater) {
this.parent.callEvent("delete", this, o);
}
if ((_ref = this.next_cl) != null ? _ref.isDeleted() : void 0) {
return this.next_cl.applyDelete();
}

File diff suppressed because one or more lines are too long

View File

@ -47,8 +47,22 @@
};
TextInsert.prototype.applyDelete = function() {
this.content = null;
return TextInsert.__super__.applyDelete.apply(this, arguments);
TextInsert.__super__.applyDelete.apply(this, arguments);
if (this.content instanceof types.Operation) {
this.content.applyDelete();
}
return this.content = null;
};
TextInsert.prototype.execute = function() {
if (!this.validateSavedOperations()) {
return false;
} else {
if (this.content instanceof types.Operation) {
this.content.insert_parent = this;
}
return TextInsert.__super__.execute.call(this);
}
};
TextInsert.prototype.val = function(current_position) {

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
(function() {
var json_types_uninitialized, proxy_token,
var dont_proxy, json_types_uninitialized, proxy_token, _proxy,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
@ -7,27 +7,45 @@
proxy_token = false;
dont_proxy = function(f) {
var e;
proxy_token = true;
try {
f();
} catch (_error) {
e = _error;
proxy_token = false;
throw new Error(e);
}
return proxy_token = false;
};
_proxy = function(f_name, f) {
var old_f;
old_f = this[f_name];
if (old_f != null) {
return this[f_name] = function() {
var args, that, _ref;
if (!proxy_token && !((_ref = this._yatta) != null ? _ref.isDeleted() : void 0)) {
that = this;
args = arguments;
return dont_proxy(function() {
f.apply(that, args);
return old_f.apply(that, args);
});
} else {
return old_f.apply(this, arguments);
}
};
}
};
if (typeof Element !== "undefined" && Element !== null) {
Element.prototype._proxy = function(f_name, f) {
var old_f;
old_f = this[f_name];
if (old_f != null) {
return this[f_name] = function() {
if (!proxy_token) {
proxy_token = true;
old_f.apply(this, arguments);
f.apply(this, arguments);
return proxy_token = false;
} else {
return old_f.apply(this, arguments);
}
};
}
};
Element.prototype._proxy = _proxy;
}
module.exports = function(HB) {
var XmlType, json_types, parser, types;
var TextNodeType, XmlType, json_types, parser, types;
json_types = json_types_uninitialized(HB);
types = json_types.types;
parser = json_types.parser;
@ -35,7 +53,7 @@
__extends(XmlType, _super);
function XmlType(uid, tagname, attributes, elements, xml) {
var attr, element, i, n, word, _i, _j, _len, _ref, _ref1;
var attr, d, element, i, n, word, _i, _j, _len, _ref, _ref1, _ref2;
this.tagname = tagname;
this.xml = xml;
@ -48,12 +66,18 @@
prev = prev.prev_cl
next = prev.next_cl
*/
XmlType.__super__.constructor.call(this);
XmlType.__super__.constructor.call(this, uid);
if (((_ref = this.xml) != null ? _ref._yatta : void 0) != null) {
d = new types.Delete(void 0, this.xml._yatta);
HB.addOperation(d).execute();
this.xml._yatta = null;
}
if ((attributes != null) && (elements != null)) {
this.saveOperation('attributes', attributes);
this.saveOperation('elements', elements);
} else if ((attributes == null) && (elements == null)) {
this.attributes = new types.JsonType();
this.attributes.setMutableDefault('immutable');
HB.addOperation(this.attributes).execute();
this.elements = new types.WordType();
this.elements.parent = this;
@ -63,17 +87,16 @@
}
if (this.xml != null) {
this.tagname = this.xml.tagName;
for (i = _i = 0, _ref = this.xml.attributes.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
for (i = _i = 0, _ref1 = this.xml.attributes.length; 0 <= _ref1 ? _i < _ref1 : _i > _ref1; i = 0 <= _ref1 ? ++_i : --_i) {
attr = xml.attributes[i];
this.attributes.val(attr.name, attr.value);
}
_ref1 = this.xml.childNodes;
for (_j = 0, _len = _ref1.length; _j < _len; _j++) {
n = _ref1[_j];
_ref2 = this.xml.childNodes;
for (_j = 0, _len = _ref2.length; _j < _len; _j++) {
n = _ref2[_j];
if (n.nodeType === n.TEXT_NODE) {
word = new types.WordType();
word = new TextNodeType(void 0, n);
HB.addOperation(word).execute();
word.push(n.textContent);
this.elements.push(word);
} else if (n.nodeType === n.ELEMENT_NODE) {
element = new XmlType(void 0, void 0, void 0, void 0, n);
@ -90,10 +113,14 @@
XmlType.prototype.type = "XmlType";
XmlType.prototype.applyDelete = function() {
this.attributes.applyDelete();
this.elements.applyDelete();
return XmlType.__super__.applyDelete.call(this);
XmlType.prototype.applyDelete = function(op) {
if ((this.insert_parent != null) && !this.insert_parent.isDeleted()) {
return this.insert_parent.applyDelete(op);
} else {
this.attributes.applyDelete();
this.elements.applyDelete();
return XmlType.__super__.applyDelete.apply(this, arguments);
}
};
XmlType.prototype.cleanup = function() {
@ -101,28 +128,158 @@
};
XmlType.prototype.setXmlProxy = function() {
var insertBefore, that;
var findNode, insertBefore, removeChild, renewClassList, that;
this.xml._yatta = this;
that = this;
insertBefore = function(insertedNode, adjacentNode) {
var element, next, prev;
next = adjacentNode != null ? adjacentNode._yatta : void 0;
this.elements.on('insert', function(event, op) {
var newNode, right, rightNode;
if (op.creator !== HB.getUserId() && this === that.elements) {
newNode = op.content.val();
right = op.next_cl;
while ((right != null) && right.isDeleted()) {
right = right.next_cl;
}
rightNode = null;
if (right.type !== 'Delimiter') {
rightNode = right.val().val();
}
return dont_proxy(function() {
return that.xml.insertBefore(newNode, rightNode);
});
}
});
this.elements.on('delete', function(event, op) {
var del_op, deleted;
del_op = op.deleted_by[0];
if ((del_op != null) && del_op.creator !== HB.getUserId() && this === that.elements) {
deleted = op.content.val();
return dont_proxy(function() {
return that.xml.removeChild(deleted);
});
}
});
this.attributes.on(['addProperty', 'change'], function(event, property_name, op) {
if (op.creator !== HB.getUserId() && this === that.attributes) {
return dont_proxy(function() {
var newval;
newval = op.val().val();
if (newval != null) {
return that.xml.setAttribute(property_name, op.val().val());
} else {
return that.xml.removeAttribute(property_name);
}
});
}
});
findNode = function(child) {
var elem;
if (child == null) {
throw new Error("you must specify a parameter!");
}
child = child._yatta;
elem = that.elements.beginning.next_cl;
while (elem.type !== 'Delimiter' && elem.content !== child) {
elem = elem.next_cl;
}
if (elem.type === 'Delimiter') {
return false;
} else {
return elem;
}
};
insertBefore = function(insertedNode_s, adjacentNode) {
var child, element, inserted_nodes, next, prev, _results;
next = null;
if (adjacentNode != null) {
next = findNode(adjacentNode);
}
prev = null;
if (next != null) {
if (next) {
prev = next.prev_cl;
} else {
prev = this._yatta.elements.end.prev_cl;
while (prev.isDeleted()) {
prev = prev.prev_cl;
}
}
inserted_nodes = null;
if (insertedNode_s.nodeType === insertedNode_s.DOCUMENT_FRAGMENT_NODE) {
child = insertedNode_s.lastChild;
_results = [];
while (child != null) {
element = new XmlType(void 0, void 0, void 0, void 0, child);
HB.addOperation(element).execute();
that.elements.insertAfter(prev, element);
_results.push(child = child.previousSibling);
}
return _results;
} else {
element = new XmlType(void 0, void 0, void 0, void 0, insertedNode_s);
HB.addOperation(element).execute();
return that.elements.insertAfter(prev, element);
}
element = new XmlType(void 0, void 0, void 0, void 0, insertedNode);
HB.addOperation(element).execute();
return that.elements.insertAfter(prev, element);
};
this.xml._proxy('insertBefore', insertBefore);
this.xml._proxy('appendChild', insertBefore);
this.xml._proxy('removeAttribute', function(name) {
return that.attributes.val(name, void 0);
});
return this.xml._proxy('removeChild', function(node) {});
this.xml._proxy('setAttribute', function(name, value) {
return that.attributes.val(name, value);
});
renewClassList = function(newclass) {
var dont_do_it, elem, value, _i, _len;
dont_do_it = false;
if (newclass != null) {
for (_i = 0, _len = this.length; _i < _len; _i++) {
elem = this[_i];
if (newclass === elem) {
dont_do_it = true;
}
}
}
value = Array.prototype.join.call(this, " ");
if ((newclass != null) && !dont_do_it) {
value += " " + newclass;
}
return that.attributes.val('class', value);
};
_proxy.call(this.xml.classList, 'add', renewClassList);
_proxy.call(this.xml.classList, 'remove', renewClassList);
this.xml.__defineSetter__('className', function(val) {
return this.setAttribute('class', val);
});
this.xml.__defineGetter__('className', function() {
return that.attributes.val('class');
});
this.xml.__defineSetter__('textContent', function(val) {
var elem, remove, text_node;
elem = that.xml.firstChild;
while (elem != null) {
remove = elem;
elem = elem.nextSibling;
that.xml.removeChild(remove);
}
if (val !== "") {
text_node = document.createTextNode(val);
return that.xml.appendChild(text_node);
}
});
removeChild = function(node) {
var d, elem;
elem = findNode(node);
if (!elem) {
throw new Error("You are only allowed to delete existing (direct) child elements!");
}
d = new types.Delete(void 0, elem);
HB.addOperation(d).execute();
return node._yatta = null;
};
this.xml._proxy('removeChild', removeChild);
return this.xml._proxy('replaceChild', function(insertedNode, replacedNode) {
insertBefore.call(this, insertedNode, replacedNode);
return removeChild.call(this, replacedNode);
});
};
XmlType.prototype.val = function(enforce) {
@ -145,11 +302,11 @@
e = this.elements.beginning.next_cl;
while (e.type !== "Delimiter") {
n = e.content;
if (!n.isDeleted()) {
if (!e.isDeleted() && (e.content != null)) {
if (n.type === "XmlType") {
this.xml.appendChild(n.val(enforce));
} else if (n.type === "WordType") {
text_node = document.createTextNode(n.val());
} else if (n.type === "TextNodeType") {
text_node = n.val();
this.xml.appendChild(text_node);
} else {
throw new Error("Internal structure cannot be transformed to dom");
@ -164,7 +321,7 @@
};
XmlType.prototype.execute = function() {
return XmlType.__super__.execute.apply(this, arguments);
return XmlType.__super__.execute.call(this);
};
@ -200,6 +357,49 @@
uid = json['uid'], attributes = json['attributes'], elements = json['elements'], tagname = json['tagname'];
return new XmlType(uid, tagname, attributes, elements, void 0);
};
TextNodeType = (function(_super) {
__extends(TextNodeType, _super);
function TextNodeType(uid, content) {
var d;
if (content._yatta != null) {
d = new types.Delete(void 0, content._yatta);
HB.addOperation(d).execute();
content._yatta = null;
}
content._yatta = this;
TextNodeType.__super__.constructor.call(this, uid, content);
}
TextNodeType.prototype.applyDelete = function(op) {
if ((this.insert_parent != null) && !this.insert_parent.isDeleted()) {
return this.insert_parent.applyDelete(op);
} else {
return TextNodeType.__super__.applyDelete.apply(this, arguments);
}
};
TextNodeType.prototype.type = "TextNodeType";
TextNodeType.prototype._encode = function() {
var json;
json = {
'type': this.type,
'uid': this.getUid(),
'content': this.content.textContent
};
return json;
};
return TextNodeType;
})(types.ImmutableObject);
parser['TextNodeType'] = function(json) {
var content, textnode, uid;
uid = json['uid'], content = json['content'];
textnode = document.createTextNode(content);
return new TextNodeType(uid, textnode);
};
types['XmlType'] = XmlType;
return json_types;
};

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

@ -6,7 +6,7 @@
</head>
<body>
<div id="mocha"></div>
<div id="test_dom" test_attribute="the test"><p id="removeme">remove me</p><p>This is a test object for <b>XmlFramework</b></p></div>
<div id="test_dom" test_attribute="the test" class="stuffy" style="color: blue"><p id="replaceme">replace me</p><p id="removeme">remove me</p><p>This is a test object for <b>XmlFramework</b></p><span class="span_element"><p>span</p></span></div>
<script src="../../node_modules/mocha/mocha.js" class="awesome"></script>
<script>
mocha.setup('bdd');

View File

@ -100,11 +100,6 @@
XmlFramework
</a>
</li>
<li>
<a href='class/XmlType.html'>
XmlType
</a>
</li>
</ul>
</ul>
</div>
@ -257,21 +252,17 @@
(lib&#47;Frameworks)
</small>
</li>
<li>
<a href='file/lib/Types/XmlTypes.coffee.html'>
XmlTypes.coffee
</a>
<small>
(lib&#47;Types)
</small>
</li>
</ul>
</ul>
</div>
</div>
</div>
<div id='footer'>
<<<<<<< HEAD
October 06, 14 10:34:33 by
=======
October 06, 14 10:16:21 by
>>>>>>> XML-Support
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
Codo
</a>

View File

@ -302,7 +302,11 @@ data from the received intent.</p>
</div>
</div>
<div id='footer'>
<<<<<<< HEAD
October 06, 14 10:34:33 by
=======
October 06, 14 10:16:21 by
>>>>>>> XML-Support
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
Codo
</a>

View File

@ -335,7 +335,11 @@ JsonFramework was initialized (Depending on the HistoryBuffer implementation).</
</div>
</div>
<div id='footer'>
<<<<<<< HEAD
October 06, 14 10:34:33 by
=======
October 06, 14 10:16:21 by
>>>>>>> XML-Support
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
Codo
</a>

View File

@ -466,7 +466,11 @@ if (x.type === &quot;JsonType&quot;) {
</div>
</div>
<div id='footer'>
<<<<<<< HEAD
October 06, 14 10:34:33 by
=======
October 06, 14 10:16:21 by
>>>>>>> XML-Support
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
Codo
</a>

View File

@ -139,7 +139,11 @@ console.log(w.newProperty == &quot;Awesome&quot;) # true!</code></pre>
</div>
</div>
<div id='footer'>
<<<<<<< HEAD
October 06, 14 10:34:33 by
=======
October 06, 14 10:16:21 by
>>>>>>> XML-Support
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
Codo
</a>

View File

@ -72,6 +72,17 @@ user-id of the peer (browser/client). And then you can connect to it.</p>
Receive the id of every connected peer.
</span>
</li>
<li>
<span class='signature'>
<a href='#onNewConnection-dynamic'>
#
(void)
<b>onNewConnection</b><span>(f)</span>
</a>
</span>
<span class='desc'>
</span>
</li>
<li>
<span class='signature'>
<a href='#addConnection-dynamic'>
@ -194,6 +205,15 @@ on how to do that with urls.</p>
</li>
</ul>
</div>
</div>
<div class='method_details'>
<p class='signature' id='onNewConnection-dynamic'>
#
(void)
<b>onNewConnection</b><span>(f)</span>
<br>
</p>
</div>
<div class='method_details'>
<p class='signature' id='addConnection-dynamic'>
@ -222,7 +242,11 @@ on how to do that with urls.</p>
</div>
</div>
<div id='footer'>
<<<<<<< HEAD
October 06, 14 10:34:33 by
=======
October 06, 14 10:16:21 by
>>>>>>> XML-Support
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
Codo
</a>

View File

@ -356,7 +356,11 @@ JsonFramework was initialized (Depending on the HistoryBuffer implementation).</
</div>
</div>
<div id='footer'>
<<<<<<< HEAD
October 06, 14 10:34:33 by
=======
October 06, 14 10:16:21 by
>>>>>>> XML-Support
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
Codo
</a>

View File

@ -455,7 +455,11 @@ yatta.bind(textbox);</code></pre>
</div>
</div>
<div id='footer'>
<<<<<<< HEAD
October 06, 14 10:34:33 by
=======
October 06, 14 10:16:21 by
>>>>>>> XML-Support
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
Codo
</a>

View File

@ -303,7 +303,11 @@ JsonFramework was initialized (Depending on the HistoryBuffer implementation).</
</div>
</div>
<div id='footer'>
<<<<<<< HEAD
October 06, 14 10:34:33 by
=======
October 06, 14 10:16:21 by
>>>>>>> XML-Support
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
Codo
</a>

View File

@ -44,8 +44,9 @@
<p>Manages XML types
Not supported:</p><ul>
<li>Attribute nodes</li>
<li>Real replace of child elements (to much overhead). Currently, the new element is inserted after the &#39;replaced&#39; element, and then it is deleted.
*</li>
<li>Real replace of child elements (to much overhead). Currently, the new element is inserted after the &#39;replaced&#39; element, and then it is deleted.</li>
<li>Namespaces (*NS)</li>
<li>Browser specific methods (webkit-* operations)</li>
</ul>
</div>
@ -122,7 +123,6 @@ Use it in order to check whether this is an xml-type or something else.</p>
</a>
</span>
<span class='desc'>
If possible set the replace manager in the content.
</span>
</li>
<li>
@ -208,17 +208,7 @@ Use it in order to check whether this is an xml-type or something else.</p>
<b>execute</b><span>()</span>
<br>
</p>
<div class='docstring'>
<p>If possible set the replace manager in the content.</p>
</div>
<div class='tags'>
<h3>See also:</h3>
<ul class='see'>
<li>
<a href='WordType.setReplaceManager'>WordType.setReplaceManager</a>
</li>
</ul>
</div>
</div>
<div class='method_details'>
<p class='signature' id='getParent-dynamic'>
@ -258,7 +248,11 @@ This result can be send to other clients.</p>
</div>
</div>
<div id='footer'>
<<<<<<< HEAD
October 06, 14 10:34:33 by
=======
October 02, 14 15:16:56 by
>>>>>>> XML-Support
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
Codo
</a>

View File

@ -99,18 +99,6 @@
</small>
<small class='namespace'>
</small>
</li>
<li>
<a href='class/XmlType.html' target='main'>
XmlType
</a>
<small class='parent'>
<
types.Insert
</small>
<small class='namespace'>
</small>
</li>
</ul>

View File

@ -38,7 +38,11 @@
</div>
</div>
<div id='footer'>
<<<<<<< HEAD
October 06, 14 10:34:33 by
=======
October 06, 14 10:16:21 by
>>>>>>> XML-Support
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
Codo
</a>

View File

@ -70,7 +70,11 @@ But I would become really motivated if you gave me some feedback :) (<a href="ht
<ul>
<li>XML support</li>
</ul>
<<<<<<< HEAD
<h2 id="support">Support</h2><p>Please report any issues to the <a href="https://github.com/DadaMonad/Yatta/issues">Github issue page</a>!</p><h2 id="license">License</h2><p>Yatta! is licensed under the <a href="./LICENSE.txt">MIT License</a>.</p><a href="&#x6d;&#97;&#x69;&#x6c;&#x74;&#111;&#x3a;&#107;&#101;&#118;&#x69;&#110;&#x2e;&#x6a;&#97;&#x68;&#x6e;&#x73;&#x40;&#x72;&#x77;&#116;&#104;&#x2d;&#x61;&#x61;&#99;&#104;&#101;&#110;&#46;&#100;&#101;">&#107;&#101;&#118;&#x69;&#110;&#x2e;&#x6a;&#97;&#x68;&#x6e;&#x73;&#x40;&#x72;&#x77;&#116;&#104;&#x2d;&#x61;&#x61;&#99;&#104;&#101;&#110;&#46;&#100;&#101;</a>
=======
<h2 id="support">Support</h2><p>Please report any issues to the <a href="https://github.com/DadaMonad/Yatta/issues">Github issue page</a>!</p><h2 id="license">License</h2><p>Yatta! is licensed under the <a href="./LICENSE.txt">MIT License</a>.</p><a href="&#109;&#97;&#x69;&#x6c;&#x74;&#x6f;&#58;&#107;&#x65;&#x76;&#x69;&#x6e;&#x2e;&#x6a;&#x61;&#x68;&#x6e;&#115;&#x40;&#114;&#x77;&#x74;&#x68;&#x2d;&#x61;&#x61;&#99;&#104;&#x65;&#x6e;&#x2e;&#x64;&#x65;">&#107;&#x65;&#x76;&#x69;&#x6e;&#x2e;&#x6a;&#x61;&#x68;&#x6e;&#115;&#x40;&#114;&#x77;&#x74;&#x68;&#x2d;&#x61;&#x61;&#99;&#104;&#x65;&#x6e;&#x2e;&#x64;&#x65;</a>
>>>>>>> XML-Support
@ -79,7 +83,11 @@ But I would become really motivated if you gave me some feedback :) (<a href="ht
</div>
</div>
<div id='footer'>
<<<<<<< HEAD
October 06, 14 10:34:33 by
=======
October 06, 14 10:16:21 by
>>>>>>> XML-Support
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
Codo
</a>

View File

@ -100,7 +100,11 @@
</div>
</div>
<div id='footer'>
<<<<<<< HEAD
October 06, 14 10:34:33 by
=======
October 06, 14 10:16:21 by
>>>>>>> XML-Support
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
Codo
</a>

View File

@ -158,7 +158,11 @@
</div>
</div>
<div id='footer'>
<<<<<<< HEAD
October 06, 14 10:34:33 by
=======
October 06, 14 10:16:21 by
>>>>>>> XML-Support
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
Codo
</a>

View File

@ -39,7 +39,11 @@
</table>
</div>
<div id='footer'>
<<<<<<< HEAD
October 06, 14 10:34:33 by
=======
October 06, 14 10:16:21 by
>>>>>>> XML-Support
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
Codo
</a>

View File

@ -48,7 +48,11 @@
</dl>
</div>
<div id='footer'>
<<<<<<< HEAD
October 06, 14 10:34:33 by
=======
October 06, 14 10:16:21 by
>>>>>>> XML-Support
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
Codo
</a>

View File

@ -60,7 +60,11 @@
</dl>
</div>
<div id='footer'>
<<<<<<< HEAD
October 06, 14 10:34:33 by
=======
October 06, 14 10:16:21 by
>>>>>>> XML-Support
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
Codo
</a>

View File

@ -60,7 +60,11 @@
</dl>
</div>
<div id='footer'>
<<<<<<< HEAD
October 06, 14 10:34:33 by
=======
October 06, 14 10:16:21 by
>>>>>>> XML-Support
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
Codo
</a>

View File

@ -60,7 +60,11 @@
</dl>
</div>
<div id='footer'>
<<<<<<< HEAD
October 06, 14 10:34:33 by
=======
October 06, 14 10:16:21 by
>>>>>>> XML-Support
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
Codo
</a>

View File

@ -48,7 +48,11 @@
</dl>
</div>
<div id='footer'>
<<<<<<< HEAD
October 06, 14 10:34:33 by
=======
October 06, 14 10:16:21 by
>>>>>>> XML-Support
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
Codo
</a>

View File

@ -39,7 +39,11 @@
</table>
</div>
<div id='footer'>
<<<<<<< HEAD
October 06, 14 10:34:33 by
=======
October 06, 14 10:16:21 by
>>>>>>> XML-Support
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
Codo
</a>

View File

@ -39,7 +39,11 @@
</table>
</div>
<div id='footer'>
<<<<<<< HEAD
October 06, 14 10:34:33 by
=======
October 06, 14 10:16:21 by
>>>>>>> XML-Support
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
Codo
</a>

View File

@ -39,7 +39,11 @@
</table>
</div>
<div id='footer'>
<<<<<<< HEAD
October 06, 14 10:34:33 by
=======
October 06, 14 10:16:21 by
>>>>>>> XML-Support
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
Codo
</a>

View File

@ -39,7 +39,11 @@
</table>
</div>
<div id='footer'>
<<<<<<< HEAD
October 06, 14 10:34:33 by
=======
October 06, 14 10:16:21 by
>>>>>>> XML-Support
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
Codo
</a>

View File

@ -53,15 +53,34 @@ But yatta&#39;s proxy may be called only once!</p>
<div class='tags'>
</div>
</dd>
<dt id='Element.prototype._proxy-variable'>
Element.prototype._proxy
=
</dt>
<dd>
<pre><code class='coffeescript'>_proxy</code></pre>
</dd>
</dl>
<h2>Method Summary</h2>
<ul class='summary'>
<li>
<span class='signature'>
<a href='#Element-'>
<a href='#dont_proxy-'>
~
(void)
<b>Element</b><span>(f_name, f)</span>
<b>dont_proxy</b><span>(f)</span>
</a>
</span>
<span class='desc'>
</span>
</li>
<li>
<span class='signature'>
<a href='#_proxy-'>
~
(void)
<b>_proxy</b><span>(f_name, f)</span>
</a>
</span>
<span class='desc'>
@ -71,10 +90,19 @@ But yatta&#39;s proxy may be called only once!</p>
<h2>Method Details</h2>
<div class='methods'>
<div class='method_details'>
<p class='signature' id='Element-'>
<p class='signature' id='dont_proxy-'>
~
(void)
<b>Element</b><span>(f_name, f)</span>
<b>dont_proxy</b><span>(f)</span>
<br>
</p>
</div>
<div class='method_details'>
<p class='signature' id='_proxy-'>
~
(void)
<b>_proxy</b><span>(f_name, f)</span>
<br>
</p>
@ -82,7 +110,11 @@ But yatta&#39;s proxy may be called only once!</p>
</div>
</div>
<div id='footer'>
<<<<<<< HEAD
October 06, 14 10:34:33 by
=======
October 02, 14 15:16:56 by
>>>>>>> XML-Support
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
Codo
</a>

View File

@ -37,7 +37,11 @@
</table>
</div>
<div id='footer'>
<<<<<<< HEAD
October 06, 14 10:34:33 by
=======
October 06, 14 10:16:21 by
>>>>>>> XML-Support
<a href='https://github.com/coffeedoc/codo' title='CoffeeScript API documentation generator'>
Codo
</a>

View File

@ -153,14 +153,6 @@
lib&#47;Types
</small>
</li>
<li>
<a href='file/lib/Types/XmlTypes.coffee.html' target='main'>
XmlTypes.coffee
</a>
<small class='namespace'>
lib&#47;Types
</small>
</li>
</ul>
<li>

File diff suppressed because one or more lines are too long

View File

@ -30,19 +30,11 @@
</div>
<ul>
<li>
<a href='file/lib/Types/XmlTypes.coffee.html#Element-' target='main' title='Element'>
~Element
</a>
<small>
(lib&#47;Types&#47;XmlTypes.coffee)
</small>
</li>
<li>
<a href='class/XmlType.html#_encode-dynamic' target='main' title='_encode'>
<a href='class/WordType.html#_encode-dynamic' target='main' title='_encode'>
#_encode
</a>
<small>
(XmlType)
(WordType)
</small>
</li>
<li>
@ -53,14 +45,6 @@
(JsonType)
</small>
</li>
<li>
<a href='class/WordType.html#_encode-dynamic' target='main' title='_encode'>
#_encode
</a>
<small>
(WordType)
</small>
</li>
<li>
<a href='class/PeerJsConnector.html#addConnection-dynamic' target='main' title='addConnection'>
#addConnection
@ -69,6 +53,14 @@
(PeerJsConnector)
</small>
</li>
<li>
<a href='class/WordType.html#applyDelete-dynamic' target='main' title='applyDelete'>
#applyDelete
</a>
<small>
(WordType)
</small>
</li>
<li>
<a href='class/JsonType.html#applyDelete-dynamic' target='main' title='applyDelete'>
#applyDelete
@ -78,16 +70,8 @@
</small>
</li>
<li>
<a href='class/XmlType.html#applyDelete-dynamic' target='main' title='applyDelete'>
#applyDelete
</a>
<small>
(XmlType)
</small>
</li>
<li>
<a href='class/WordType.html#applyDelete-dynamic' target='main' title='applyDelete'>
#applyDelete
<a href='class/WordType.html#bind-dynamic' target='main' title='bind'>
#bind
</a>
<small>
(WordType)
@ -102,8 +86,8 @@
</small>
</li>
<li>
<a href='class/WordType.html#bind-dynamic' target='main' title='bind'>
#bind
<a href='class/WordType.html#cleanup-dynamic' target='main' title='cleanup'>
#cleanup
</a>
<small>
(WordType)
@ -117,22 +101,6 @@
(JsonType)
</small>
</li>
<li>
<a href='class/WordType.html#cleanup-dynamic' target='main' title='cleanup'>
#cleanup
</a>
<small>
(WordType)
</small>
</li>
<li>
<a href='class/XmlType.html#cleanup-dynamic' target='main' title='cleanup'>
#cleanup
</a>
<small>
(XmlType)
</small>
</li>
<li>
<a href='class/PeerJsConnector.html#connectToPeer-dynamic' target='main' title='connectToPeer'>
#connectToPeer
@ -141,30 +109,6 @@
(PeerJsConnector)
</small>
</li>
<li>
<a href='class/WordType.html#constructor-dynamic' target='main' title='constructor'>
#constructor
</a>
<small>
(WordType)
</small>
</li>
<li>
<a href='class/XmlType.html#constructor-dynamic' target='main' title='constructor'>
#constructor
</a>
<small>
(XmlType)
</small>
</li>
<li>
<a href='class/PeerJsConnector.html#constructor-dynamic' target='main' title='constructor'>
#constructor
</a>
<small>
(PeerJsConnector)
</small>
</li>
<li>
<a href='class/IwcConnector.html#constructor-dynamic' target='main' title='constructor'>
#constructor
@ -173,6 +117,22 @@
(IwcConnector)
</small>
</li>
<li>
<a href='class/JsonFramework.html#constructor-dynamic' target='main' title='constructor'>
#constructor
</a>
<small>
(JsonFramework)
</small>
</li>
<li>
<a href='class/PeerJsConnector.html#constructor-dynamic' target='main' title='constructor'>
#constructor
</a>
<small>
(PeerJsConnector)
</small>
</li>
<li>
<a href='class/JsonType.html#constructor-dynamic' target='main' title='constructor'>
#constructor
@ -197,6 +157,14 @@
(XmlFramework)
</small>
</li>
<li>
<a href='class/WordType.html#constructor-dynamic' target='main' title='constructor'>
#constructor
</a>
<small>
(WordType)
</small>
</li>
<li>
<a href='class/TextFramework.html#constructor-dynamic' target='main' title='constructor'>
#constructor
@ -205,14 +173,6 @@
(TextFramework)
</small>
</li>
<li>
<a href='class/JsonFramework.html#constructor-dynamic' target='main' title='constructor'>
#constructor
</a>
<small>
(JsonFramework)
</small>
</li>
<li>
<a href='file/lib/Connectors/IwcConnector.coffee.html#createIwcConnector-' target='main' title='createIwcConnector'>
~createIwcConnector
@ -253,14 +213,6 @@
(TextFramework)
</small>
</li>
<li>
<a href='class/XmlType.html#execute-dynamic' target='main' title='execute'>
#execute
</a>
<small>
(XmlType)
</small>
</li>
<li>
<a href='class/PeerJsConnector.html#getAllConnectionIds-dynamic' target='main' title='getAllConnectionIds'>
#getAllConnectionIds
@ -269,14 +221,6 @@
(PeerJsConnector)
</small>
</li>
<li>
<a href='class/TextFramework.html#getConnector-dynamic' target='main' title='getConnector'>
#getConnector
</a>
<small>
(TextFramework)
</small>
</li>
<li>
<a href='class/JsonFramework.html#getConnector-dynamic' target='main' title='getConnector'>
#getConnector
@ -285,6 +229,14 @@
(JsonFramework)
</small>
</li>
<li>
<a href='class/TextFramework.html#getConnector-dynamic' target='main' title='getConnector'>
#getConnector
</a>
<small>
(TextFramework)
</small>
</li>
<li>
<a href='class/XmlFramework.html#getConnector-dynamic' target='main' title='getConnector'>
#getConnector
@ -293,6 +245,14 @@
(XmlFramework)
</small>
</li>
<li>
<a href='class/TextFramework.html#getHistoryBuffer-dynamic' target='main' title='getHistoryBuffer'>
#getHistoryBuffer
</a>
<small>
(TextFramework)
</small>
</li>
<li>
<a href='class/XmlFramework.html#getHistoryBuffer-dynamic' target='main' title='getHistoryBuffer'>
#getHistoryBuffer
@ -309,14 +269,6 @@
(JsonFramework)
</small>
</li>
<li>
<a href='class/TextFramework.html#getHistoryBuffer-dynamic' target='main' title='getHistoryBuffer'>
#getHistoryBuffer
</a>
<small>
(TextFramework)
</small>
</li>
<li>
<a href='class/JsonType.html#getParent-dynamic' target='main' title='getParent'>
#getParent
@ -325,14 +277,6 @@
(JsonType)
</small>
</li>
<li>
<a href='class/XmlType.html#getParent-dynamic' target='main' title='getParent'>
#getParent
</a>
<small>
(XmlType)
</small>
</li>
<li>
<a href='class/XmlFramework.html#getSharedObject-dynamic' target='main' title='getSharedObject'>
#getSharedObject
@ -357,14 +301,6 @@
(JsonFramework)
</small>
</li>
<li>
<a href='class/XmlFramework.html#getUserId-dynamic' target='main' title='getUserId'>
#getUserId
</a>
<small>
(XmlFramework)
</small>
</li>
<li>
<a href='class/TextFramework.html#getUserId-dynamic' target='main' title='getUserId'>
#getUserId
@ -373,6 +309,14 @@
(TextFramework)
</small>
</li>
<li>
<a href='class/XmlFramework.html#getUserId-dynamic' target='main' title='getUserId'>
#getUserId
</a>
<small>
(XmlFramework)
</small>
</li>
<li>
<a href='class/JsonFramework.html#getUserId-dynamic' target='main' title='getUserId'>
#getUserId
@ -405,14 +349,6 @@
(WordType)
</small>
</li>
<li>
<a href='class/XmlFramework.html#on-dynamic' target='main' title='on'>
#on
</a>
<small>
(XmlFramework)
</small>
</li>
<li>
<a href='class/TextFramework.html#on-dynamic' target='main' title='on'>
#on
@ -421,6 +357,14 @@
(TextFramework)
</small>
</li>
<li>
<a href='class/XmlFramework.html#on-dynamic' target='main' title='on'>
#on
</a>
<small>
(XmlFramework)
</small>
</li>
<li>
<a href='class/JsonFramework.html#on-dynamic' target='main' title='on'>
#on
@ -429,6 +373,14 @@
(JsonFramework)
</small>
</li>
<li>
<a href='class/PeerJsConnector.html#onNewConnection-dynamic' target='main' title='onNewConnection'>
#onNewConnection
</a>
<small>
(PeerJsConnector)
</small>
</li>
<li>
<a href='class/WordType.html#push-dynamic' target='main' title='push'>
#push
@ -485,14 +437,6 @@
(IwcConnector)
</small>
</li>
<li>
<a href='class/XmlFramework.html#setMutableDefault-dynamic' target='main' title='setMutableDefault'>
#setMutableDefault
</a>
<small>
(XmlFramework)
</small>
</li>
<li>
<a href='class/JsonFramework.html#setMutableDefault-dynamic' target='main' title='setMutableDefault'>
#setMutableDefault
@ -509,6 +453,14 @@
(JsonType)
</small>
</li>
<li>
<a href='class/XmlFramework.html#setMutableDefault-dynamic' target='main' title='setMutableDefault'>
#setMutableDefault
</a>
<small>
(XmlFramework)
</small>
</li>
<li>
<a href='class/JsonType.html#setReplaceManager-dynamic' target='main' title='setReplaceManager'>
#setReplaceManager
@ -525,14 +477,6 @@
(WordType)
</small>
</li>
<li>
<a href='class/XmlType.html#setXmlProxy-dynamic' target='main' title='setXmlProxy'>
#setXmlProxy
</a>
<small>
(XmlType)
</small>
</li>
<li>
<a href='class/XmlFramework.html#toJson-dynamic' target='main' title='toJson'>
#toJson
@ -565,14 +509,6 @@
(WordType)
</small>
</li>
<li>
<a href='class/TextFramework.html#val-dynamic' target='main' title='val'>
#val
</a>
<small>
(TextFramework)
</small>
</li>
<li>
<a href='class/WordType.html#val-dynamic' target='main' title='val'>
#val
@ -582,19 +518,19 @@
</small>
</li>
<li>
<a href='class/XmlType.html#val-dynamic' target='main' title='val'>
<a href='class/TextFramework.html#val-dynamic' target='main' title='val'>
#val
</a>
<small>
(XmlType)
(TextFramework)
</small>
</li>
<li>
<a href='class/JsonFramework.html#val-dynamic' target='main' title='val'>
<a href='class/XmlFramework.html#val-dynamic' target='main' title='val'>
#val
</a>
<small>
(JsonFramework)
(XmlFramework)
</small>
</li>
<li>
@ -606,11 +542,11 @@
</small>
</li>
<li>
<a href='class/XmlFramework.html#val-dynamic' target='main' title='val'>
<a href='class/JsonFramework.html#val-dynamic' target='main' title='val'>
#val
</a>
<small>
(XmlFramework)
(JsonFramework)
</small>
</li>
</ul>

View File

@ -194,7 +194,7 @@ Apply a 'change' - listener to a JsonType.
console.log("New value: " + show(this.val(property_name)) + ""); // 'this' is the object on which the property changed.
};
yatta.on('change', change);
yatta.val('mutable_string', "text", 'mutable'); // Property 'mutable_string' was replaced or changed!
yatta.val('mutable_string', "text", 'mutable'); // You changed the value of property 'mutable_string'!
```

View File

@ -156,7 +156,7 @@ Y.createPeerJsConnector("unique_id", {key: 'h7nlefbgavh1tt9'}, function(Connecto
console.log("New value: " + show(this.val(property_name)) + ""); // 'this' is the object on which the property changed.
};
yatta.on('change', change);
yatta.val('mutable_string', "text", 'mutable'); // Property 'mutable_string' was replaced or changed!
yatta.val('mutable_string', "text", 'mutable'); // You changed the value of property 'mutable_string'!
/**
'change' and 'addProperty' do also fire for nested properties.

View File

@ -8,5 +8,6 @@ Here you find some (hopefully) usefull examples on how to use Yatta!
## Demos
* [Text Editing](./TextEditing/) Simple collaborative text editing demo with PeerJs and Text Framework.
* [IWC Demo](./IwcDemo/) More IWC example widgets..
* [XML Example](./XmlExample) Collaboratively manipulate the dom with native dom-features and jQuery.
* [IWC Demo](./IwcDemo/) More IWC example widgets.

View File

@ -27,6 +27,49 @@ var yatta, yattaHandler;
Y.createPeerJsConnector({key: 'h7nlefbgavh1tt9'}, function(Connector, user_id){
yatta = new Y.XmlFramework(user_id, Connector);
console.log(yatta.getUserId());
```
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);
}
yatta.connector.onNewConnection(function(){
$("#collaborative").replaceWith(yatta.val())
});
yatta.val($("#collaborative")[0])
console.log(yatta.getUserId());
$("#collaborative").attr("contenteditable","true");
$("#collaborative")[0].onkeyup = function(){
console.log("dtrn");
console.log("rtdn");
}
$("p")[0].onfocus = function(){
console.log("nrtduiaenrtduiaer");
}
});
```

View File

@ -2,21 +2,43 @@
<html>
<head>
<meta charset=utf-8 />
<title>PeerJs Json Example</title>
<title>PeerJs Xml Example</title>
<script src="http://cdn.peerjs.com/0.3/peer.js"></script>
<script src="../../build/browser/Frameworks/XmlFramework.js"></script>
<script src="../../build/browser/Connectors/PeerJsConnector.js"></script>
<script src="./index.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<style>
/* Browser specific (not valid) styles to make preformatted text wrap */
code {
white-space: nowrap; /* css-3 */
}
</style>
</head>
<body>
<h1> PeerJs + Json Tutorial</h1>
<p> Collaborative Json editing with <a href="https://github.com/DadaMonad/Yatta/">Yatta</a>
<body id="collaborative">
<h1> PeerJs + XML Example</h1>
<p> Collaborative XML editing with <a href="https://github.com/DadaMonad/Yatta/">Yatta</a>
and <a href="http://peerjs.com/">PeerJs</a> (WebRTC). </p>
<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>.
You can find the code for this example <a href="https://github.com/DadaMonad/Yatta/tree/master/examples/XmlExample">here</a>.
<p>Open this link on other clients: <a id="peer_link" target="_blank">Drop me </a></p>. Share this link and build a peer-to-peer based network. Changes won't be recorded by any server.
<p>Open the console and use DOM and jQuery in order to manipulate the body-element of this website. You can see live changes on other clients.</p>
<h4>Sample code for the console </h4>
<nowrap>
<code>$("body").append('&lt;div id="div_tag"&gt;some new div tag &lt;/div&gt;');</code><br />
<code>$("#div_tag").empty();</code><br />
<code>$("&lt;p&gt;&lt;i&gt;This is inserted after every p tag&lt;/i&gt;&lt;/p&gt;").insertAfter("p");</code><br />
<code>$("p i").remove();</code><br />
</nowrap>
</p>
</body>
</html>

View File

@ -27,5 +27,32 @@ var yatta, yattaHandler;
Y.createPeerJsConnector({key: 'h7nlefbgavh1tt9'}, function(Connector, user_id){
yatta = new Y.XmlFramework(user_id, Connector);
console.log(yatta.getUserId());
/**
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.connectToPeer(peer_id);
}
yatta.connector.onNewConnection(function(){
$("#collaborative").replaceWith(yatta.val())
});
yatta.val($("#collaborative")[0])
console.log(yatta.getUserId());
});

View File

@ -39,6 +39,7 @@ createPeerJsConnector = ()->
@peer = peer
@connections = {}
@new_connection_listeners = []
@peer.on 'connection', (conn)=>
@addConnection conn
@ -82,6 +83,9 @@ createPeerJsConnector = ()->
for conn_id of @connections
conn_id
onNewConnection: (f)->
@new_connection_listeners.push f
#
# Adds an existing connection to this connector.
# @param conn {PeerJsConnection}
@ -105,6 +109,8 @@ createPeerJsConnector = ()->
if not data.initialized
conn.send
conns: @getAllConnectionIds()
@new_connection_listeners.map (f)->
f(conn)
else if data.op?
@engine.applyOp data.op
else if data.conns?

View File

@ -271,15 +271,18 @@ module.exports = (HB)->
#
applyDelete: (o)->
@deleted_by ?= []
callLater = false
if @parent? and not @isDeleted()
# call iff wasn't deleted earlyer
@parent.callEvent "delete", @, o
callLater = true
if o?
@deleted_by.push o
garbagecollect = false
if not (@prev_cl? and @next_cl?) or @prev_cl.isDeleted()
garbagecollect = true
super garbagecollect
if callLater
@parent.callEvent "delete", @, o
if @next_cl?.isDeleted()
# garbage collect next_cl
@next_cl.applyDelete()

View File

@ -43,8 +43,18 @@ module.exports = (HB)->
@content.length
applyDelete: ()->
super # no braces indeed!
if @content instanceof types.Operation
@content.applyDelete()
@content = null
super
execute: ()->
if not @validateSavedOperations()
return false
else
if @content instanceof types.Operation
@content.insert_parent = @
super()
#
# The result will be concatenated with the results from the other insert operations

View File

@ -5,19 +5,30 @@ json_types_uninitialized = require "./JsonTypes"
# For example xml.insertChild(dom) , wich inserts an element at the end, and xml.insertAfter(dom,null) wich does the same
# But yatta's proxy may be called only once!
proxy_token = false
Element?.prototype._proxy = (f_name, f)->
dont_proxy = (f)->
proxy_token = true
try
f()
catch e
proxy_token = false
throw new Error e
proxy_token = false
_proxy = (f_name, f)->
old_f = @[f_name]
if old_f?
@[f_name] = ()->
if not proxy_token
proxy_token = true
old_f.apply this, arguments
f.apply this, arguments
proxy_token = false
if not proxy_token and not @_yatta?.isDeleted()
that = this
args = arguments
dont_proxy ()->
f.apply that, args
old_f.apply that, args
else
old_f.apply this, arguments
#else
# @[f_name] = f
Element?.prototype._proxy = _proxy
module.exports = (HB)->
@ -30,7 +41,8 @@ module.exports = (HB)->
# Not supported:
# * Attribute nodes
# * Real replace of child elements (to much overhead). Currently, the new element is inserted after the 'replaced' element, and then it is deleted.
# *
# * Namespaces (*NS)
# * Browser specific methods (webkit-* operations)
class XmlType extends types.Insert
constructor: (uid, @tagname, attributes, elements, @xml)->
@ -44,13 +56,20 @@ module.exports = (HB)->
next = prev.next_cl
###
super()
super(uid)
if @xml?._yatta?
d = new types.Delete undefined, @xml._yatta
HB.addOperation(d).execute()
@xml._yatta = null
if attributes? and elements?
@saveOperation 'attributes', attributes
@saveOperation 'elements', elements
else if (not attributes?) and (not elements?)
@attributes = new types.JsonType()
@attributes.setMutableDefault 'immutable'
HB.addOperation(@attributes).execute()
@elements = new types.WordType()
@elements.parent = @
@ -65,9 +84,8 @@ module.exports = (HB)->
@attributes.val(attr.name, attr.value)
for n in @xml.childNodes
if n.nodeType is n.TEXT_NODE
word = new types.WordType()
word = new TextNodeType(undefined, n)
HB.addOperation(word).execute()
word.push n.textContent
@elements.push word
else if n.nodeType is n.ELEMENT_NODE
element = new XmlType undefined, undefined, undefined, undefined, n
@ -84,10 +102,13 @@ module.exports = (HB)->
#
type: "XmlType"
applyDelete: ()->
@attributes.applyDelete()
@elements.applyDelete()
super()
applyDelete: (op)->
if @insert_parent? and not @insert_parent.isDeleted()
@insert_parent.applyDelete op
else
@attributes.applyDelete()
@elements.applyDelete()
super
cleanup: ()->
super()
@ -95,22 +116,131 @@ module.exports = (HB)->
setXmlProxy: ()->
@xml._yatta = @
that = @
insertBefore = (insertedNode, adjacentNode)->
next = adjacentNode?._yatta
@elements.on 'insert', (event, op)->
if op.creator isnt HB.getUserId() and this is that.elements
newNode = op.content.val()
right = op.next_cl
while right? and right.isDeleted()
right = right.next_cl
rightNode = null
if right.type isnt 'Delimiter'
rightNode = right.val().val()
dont_proxy ()->
that.xml.insertBefore newNode, rightNode
@elements.on 'delete', (event, op)->
del_op = op.deleted_by[0]
if del_op? and del_op.creator isnt HB.getUserId() and this is that.elements
deleted = op.content.val()
dont_proxy ()->
that.xml.removeChild deleted
@attributes.on ['addProperty', 'change'], (event, property_name, op)->
if op.creator isnt HB.getUserId() and this is that.attributes
dont_proxy ()->
newval = op.val().val()
if newval?
that.xml.setAttribute(property_name, op.val().val())
else
that.xml.removeAttribute(property_name)
## Here are all methods that proxy the behavior of the xml
# you want to find a specific child element. Since they are carried by an Insert-Type, you want to find that Insert-Operation.
# @param child {DomElement} Dom element.
# @return {InsertType} This carries the XmlType that represents the DomElement (child). false if i couldn't find it.
#
findNode = (child)->
if not child?
throw new Error "you must specify a parameter!"
child = child._yatta
elem = that.elements.beginning.next_cl
while elem.type isnt 'Delimiter' and elem.content isnt child
elem = elem.next_cl
if elem.type is 'Delimiter'
false
else
elem
insertBefore = (insertedNode_s, adjacentNode)->
next = null
if adjacentNode?
next = findNode adjacentNode
prev = null
if next?
if next
prev = next.prev_cl
else
prev = @_yatta.elements.end.prev_cl
element = new XmlType undefined, undefined, undefined, undefined, insertedNode
HB.addOperation(element).execute()
that.elements.insertAfter prev, element
while prev.isDeleted()
prev = prev.prev_cl
inserted_nodes = null
if insertedNode_s.nodeType is insertedNode_s.DOCUMENT_FRAGMENT_NODE
child = insertedNode_s.lastChild
while child?
element = new XmlType undefined, undefined, undefined, undefined, child
HB.addOperation(element).execute()
that.elements.insertAfter prev, element
child = child.previousSibling
else
element = new XmlType undefined, undefined, undefined, undefined, insertedNode_s
HB.addOperation(element).execute()
that.elements.insertAfter prev, element
@xml._proxy 'insertBefore', insertBefore
@xml._proxy 'appendChild', insertBefore
@xml._proxy 'removeAttribute', (name)->
that.attributes.val(name, undefined)
@xml._proxy 'removeChild', (node)->
@xml._proxy 'setAttribute', (name, value)->
that.attributes.val name, value
renewClassList = (newclass)->
dont_do_it = false
if newclass?
for elem in this
if newclass is elem
dont_do_it = true
value = Array.prototype.join.call this, " "
if newclass? and not dont_do_it
value += " "+newclass
that.attributes.val('class', value )
_proxy.call @xml.classList, 'add', renewClassList
_proxy.call @xml.classList, 'remove', renewClassList
@xml.__defineSetter__ 'className', (val)->
@setAttribute('class', val)
@xml.__defineGetter__ 'className', ()->
that.attributes.val('class')
@xml.__defineSetter__ 'textContent', (val)->
# remove all nodes
elem = that.xml.firstChild
while elem?
remove = elem
elem = elem.nextSibling
that.xml.removeChild remove
# insert word content
if val isnt ""
text_node = document.createTextNode val
that.xml.appendChild text_node
removeChild = (node)->
elem = findNode node
if not elem
throw new Error "You are only allowed to delete existing (direct) child elements!"
d = new types.Delete undefined, elem
HB.addOperation(d).execute()
node._yatta = null
@xml._proxy 'removeChild', removeChild
@xml._proxy 'replaceChild', (insertedNode, replacedNode)->
insertBefore.call this, insertedNode, replacedNode
removeChild.call this, replacedNode
val: (enforce = false)->
@ -128,11 +258,11 @@ module.exports = (HB)->
e = @elements.beginning.next_cl
while e.type isnt "Delimiter"
n = e.content
if not n.isDeleted()
if not e.isDeleted() and e.content? # TODO: how can this happen? Probably because listeners
if n.type is "XmlType"
@xml.appendChild n.val(enforce)
else if n.type is "WordType"
text_node = document.createTextNode n.val()
else if n.type is "TextNodeType"
text_node = n.val()
@xml.appendChild text_node
else
throw new Error "Internal structure cannot be transformed to dom"
@ -141,13 +271,8 @@ module.exports = (HB)->
@xml
#
# If possible set the replace manager in the content.
# @see WordType.setReplaceManager
#
execute: ()->
super
super()
###
if not @validateSavedOperations()
return false
@ -177,7 +302,6 @@ module.exports = (HB)->
'elements' : @elements.getUid()
'tagname' : @tagname
'uid' : @getUid()
}
json
@ -191,6 +315,51 @@ module.exports = (HB)->
new XmlType uid, tagname, attributes, elements, undefined
#
# @nodoc
# Defines an object that is cannot be changed. You can use this to set an immutable string, or a number.
#
class TextNodeType extends types.ImmutableObject
#
# @param {Object} uid A unique identifier. If uid is undefined, a new uid will be created.
# @param {Object} content
#
constructor: (uid, content)->
if content._yatta?
d = new types.Delete undefined, content._yatta
HB.addOperation(d).execute()
content._yatta = null
content._yatta = @
super uid, content
applyDelete: (op)->
if @insert_parent? and not @insert_parent.isDeleted()
@insert_parent.applyDelete op
else
super
type: "TextNodeType"
#
# Encode this operation in such a way that it can be parsed by remote peers.
#
_encode: ()->
json = {
'type': @type
'uid' : @getUid()
'content' : @content.textContent
}
json
parser['TextNodeType'] = (json)->
{
'uid' : uid
'content' : content
} = json
textnode = document.createTextNode content
new TextNodeType uid, textnode
types['XmlType'] = XmlType

View File

@ -36,23 +36,43 @@ describe "XmlFramework", ->
@users = @yTest.users
###
@test_user = @yTest.makeNewUser 0, (Connector_uninitialized [])
@dom = $("#test_dom")[0]
test_users = []
connector = (Connector_uninitialized test_users)
@test_user = @yTest.makeNewUser 0, connector
test_users.push @test_user
# test_user_listen listens to the actions of test_user. He will update his dom when he receives from test_user.
@test_user_listen = @yTest.makeNewUser 2, connector
test_users.push @test_user_listen
@test_user2 = @yTest.makeNewUser 1, (Connector_uninitialized [])
$("#test_dom").replaceWith('<div id="test_dom" test_attribute="the test" class="stuffy" style="color: blue"><p id="replaceme">replace me</p><p id="removeme">remove me</p><p>This is a test object for <b>XmlFramework</b></p><span class="span_element"><p>span</p></span></div>')
@$dom = $("#test_dom")
@dom = @$dom[0]
@test_user.val(@dom)
@test_user_listen.getConnector().flushAll()
@test_user_listen_dom = @test_user_listen.val()
@check = ()=>
dom_ = @test_user.val(true)
expect(dom_.outerHTML).to.equal(@dom.outerHTML)
dom_ = @dom.outerHTML
# now test if other collaborators can parse the HB and result in the same content
hb = @test_user.HB._encode()
@test_user2.engine.applyOps(hb)
dom2 = @test_user2.val()
expect(dom_).to.equal(dom2.outerHTML)
@test_user_listen.getConnector().flushAll()
expect(dom_).to.equal(@test_user_listen_dom.outerHTML)
done()
it "can transform to a new real Dom element", ->
dom_ = @test_user.val(true)
expect(dom_ isnt @dom).to.be.true
@check()
it "supports dom.insertBefore", ->
newdom = $("<p>dtrn</p>")[0]
newdom2 = $("<p>dtrn2</p>")[0]
n = $("#removeme")[0]
@dom.insertBefore(newdom, null)
@dom.insertBefore(newdom2, n)
@check()
it "supports dom.appendChild", ->
@ -60,20 +80,117 @@ describe "XmlFramework", ->
@dom.appendChild(newdom)
@check()
it "supports dom.setAttribute", ->
@dom.setAttribute("test_attribute", "newVal")
@check()
it "supports dom.removeAttribute", ->
@dom.removeAttribute("test_attribute")
@check()
it "supports dom.removeAttribute", ->
it "supports dom.removeChild", ->
@dom.removeChild($("#removeme")[0])
expect($("#removeme").length).to.equal(0)
@check()
it "supports dom.replaceChild", ->
newdom = $("<p>replaced</p>")[0]
replace = $("#replaceme")[0]
@dom.replaceChild(newdom,replace)
expect($("#replaceme").length).to.equal(0)
@check()
it "supports dom.classList.add", ->
@dom.classList.add "classy"
@check()
it "supports dom.textcontent", -> #TODO!!!!
@dom.classList.add "classy"
@check()
it "supports jquery.addClass", ->
@$dom.addClass("testy")
@check()
it "supports jquery.after", ->
d = $("#test_dom p")
d.after("<div class=\"inserted_after\">after</div>")
@check()
it "supports jquery.append", ->
d = $("#test_dom p")
d.after("<b>appended</b>")
@check()
it "supports jquery.appendTo", ->
$("<b>appendedTo</b>").appendTo("#test_dom p")
$("p").appendTo("#test_dom")
@check()
it "supports jquery.before", ->
d = $("#test_dom p")
d.before("<div>before</div>")
@check()
it "supports jquery.detach", ->
d = $(".inserted_after")
d.detach()
@check()
it "supports jquery.empty", ->
d = $("#test_dom p")
d.empty()
@check()
it "supports jquery.insertAfter", ->
$("<p>after span</p>").insertAfter(".span_element")
@check()
it "supports jquery.insertBefore", ->
$("<p>before span</p>").insertBefore(".span_element")
@check()
it "supports jquery.prepend", ->
d = $("#test_dom div")
d.prepend("<p>prepended</p>")
@check()
it "supports jquery.prependTo", ->
$("<p atone=false attwo=\"dtrn\" class=\"attr_node sudo su\">prepended to</p>").prependTo("#test_dom div")
@check()
it "supports jquery.remove", ->
d = $("#test_dom b")
d.remove()
@check()
it "supports jquery.removeAttr", ->
d = $(".attr_node")
d.removeAttr("attwo")
@check()
it "supports jquery.removeClass", ->
d = $(".attr_node")
d.removeClass("sudo")
@check()
it "supports jquery.attr", ->
d = $(".attr_node")
d.attr("atone", true)
@check()
it "supports jquery.replaceAll", ->
$("<span>New span content </span>").replaceAll("#test_dom div")
@check()
it "supports jquery.replaceWith", ->
d = $("#test_dom span")
d.replaceWith("<div>me is div again </div>")
@check()