From 3f5f3cae971528e8fa4830bbdf468e8f9778f8e2 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 13 Aug 2014 02:00:52 +0200 Subject: [PATCH] experimenting with peerjs --- build/browser/Connectors/PeerJsConnector.js | 102 ++++++++++ .../browser/Connectors/PeerJsConnector.min.js | 1 + build/node/Connectors/PeerJsConnector.js | 2 + build/node/Connectors/PeerJsConnector.js.map | 1 + build/node/Types/XmlTypes.js.map | 2 +- examples/IwcJson/README.md | 2 +- examples/PeerJsJson/PeerJsJson.html | 14 ++ examples/PeerJsJson/README.md | 177 ++++++++++++++++++ examples/PeerJsJson/index.js | 130 +++++++++++++ lib/Connectors/PeerJsConnector.coffee | 83 ++++++++ package.json | 2 +- 11 files changed, 513 insertions(+), 3 deletions(-) create mode 100644 build/browser/Connectors/PeerJsConnector.js create mode 100644 build/browser/Connectors/PeerJsConnector.min.js create mode 100644 build/node/Connectors/PeerJsConnector.js create mode 100755 build/node/Connectors/PeerJsConnector.js.map create mode 100644 examples/PeerJsJson/PeerJsJson.html create mode 100644 examples/PeerJsJson/README.md create mode 100644 examples/PeerJsJson/index.js create mode 100644 lib/Connectors/PeerJsConnector.coffee diff --git a/build/browser/Connectors/PeerJsConnector.js b/build/browser/Connectors/PeerJsConnector.js new file mode 100644 index 00000000..07c8fe3a --- /dev/null +++ b/build/browser/Connectors/PeerJsConnector.js @@ -0,0 +1,102 @@ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;ot;t++)e=r[t],console.log("sent op"),i.push(e.send({op:n}));return i}},n.prototype.receive=function(n){return n.uid.creator!==this.HB.getUserId()?this.engine.applyOp(n):void 0},n}(),t.on("open",function(t){return console.log(t),n(e,t)})},e.exports=t,"undefined"!=typeof window&&null!==window&&(window.createPeerJsConnector=t)},{}]},{},[1]); \ No newline at end of file diff --git a/build/node/Connectors/PeerJsConnector.js b/build/node/Connectors/PeerJsConnector.js new file mode 100644 index 00000000..3ded859f --- /dev/null +++ b/build/node/Connectors/PeerJsConnector.js @@ -0,0 +1,2 @@ +(function(){var n;n=function(n){var e,t;return t=new Peer({key:"h7nlefbgavh1tt9"}),e=function(){function n(n,e,o,i){var r;this.engine=n,this.HB=e,this.execution_listener=o,this.yatta=i,this.peer=t,this.connections=[],this.peer.on("connection",function(n){return function(e){return console.log("received conn"),n.addConnection(e)}}(this)),r=function(n){return function(e){return n.send(e)}}(this),this.execution_listener.push(r)}return n.prototype.connectToPeer=function(n){return this.addConnection(t.connect(n))},n.prototype.addConnection=function(n){var e;return this.connections.push(n),n.on("data",function(n){return function(e){if(console.log("data: "+e),null!=e.HB)return n.engine.applyOpsCheckDouble(e.HB);if(null!=e.op)return n.engine.applyOp(e.op);throw new Error("Can't parse this operation")}}(this)),e=function(e){return function(){return console.log("sending..."),n.send({HB:e.yatta.getHistoryBuffer()._encode()})}}(this),setTimeout(e,1e3)},n.prototype.send=function(n){var e,t,o,i,r;if(n.uid.creator===this.HB.getUserId()&&"string"!=typeof n.uid.op_number){for(console.log("trying to send ops"),i=this.connections,r=[],t=0,o=i.length;o>t;t++)e=i[t],console.log("sent op"),r.push(e.send({op:n}));return r}},n.prototype.receive=function(n){return n.uid.creator!==this.HB.getUserId()?this.engine.applyOp(n):void 0},n}(),t.on("open",function(t){return console.log(t),n(e,t)})},module.exports=n,"undefined"!=typeof window&&null!==window&&(window.createPeerJsConnector=n)}).call(this); +//# sourceMappingURL=../Connectors/PeerJsConnector.js.map \ No newline at end of file diff --git a/build/node/Connectors/PeerJsConnector.js.map b/build/node/Connectors/PeerJsConnector.js.map new file mode 100755 index 00000000..cc48bd6a --- /dev/null +++ b/build/node/Connectors/PeerJsConnector.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Connectors/PeerJsConnector.js","sources":["Connectors/PeerJsConnector.coffee"],"names":[],"mappings":"CAIA,WAAA,GAAA,EAAA,GAAwB,SAAC,GAEvB,GAAA,GAAA,QAAA,GAAW,GAAA,OAAM,IAAK,oBAKhB,EAAA,WAQS,QAAA,GAAE,EAAS,EAAK,EAAqB,GAEhD,GAAA,EAFY,MAAC,OAAA,EAAQ,KAAC,GAAA,EAAI,KAAC,mBAAA,EAAoB,KAAC,MAAA,EAEhD,KAAC,KAAO,EACR,KAAC,eAED,KAAC,KAAK,GAAG,aAAc,SAAA,SAAA,UAAC,SACtB,SAAQ,IAAI,iBACZ,EAAC,cAAc,KAFM,OAMvB,EAAQ,SAAA,SAAA,UAAC,SACP,GAAC,KAAK,KADA,MAER,KAAC,mBAAmB,KAAK,SAb3B,GAAA,UAeA,cAAe,SAAC,SACd,MAAC,cAAc,EAAK,QAAQ,KAhB9B,EAAA,UAkBA,cAAe,SAAC,GACd,GAAA,SAAA,MAAC,YAAY,KAAK,GAElB,EAAK,GAAG,OAAQ,SAAA,SAAA,UAAC,GAEf,GADA,QAAQ,IAAK,SAAO,GACjB,MAAA,EAAA,SACD,GAAC,OAAO,oBAAoB,EAAK,GAC9B,IAAG,MAAA,EAAA,SACN,GAAC,OAAO,QAAQ,EAAK,GAErB,MAAU,IAAA,OAAM,gCAPJ,OAShB,EAAS,SAAA,SAAA,kBACP,SAAQ,IAAI,cACZ,EAAK,MACH,GAAI,EAAC,MAAM,mBAAmB,cAHzB,MAIT,WAAW,EAAQ,MAlCrB,EAAA,UAwCA,KAAM,SAAC,GACL,GAAA,GAAA,EAAA,EAAA,EAAA,CAAA,IAAG,EAAE,IAAI,UAAW,KAAC,GAAG,aAA6C,gBAA5B,GAAS,IAAI,UAAtD,KACE,QAAQ,IAAI,sBACZ,EAAA,KAAA,YAAA,KAAA,EAAA,EAAA,EAAA,EAAA,OAAA,EAAA,EAAA,WACE,QAAQ,IAAI,WAAZ,EAAA,KACA,EAAK,MACH,GAAI,gBA9CZ,EAAA,UAoDA,QAAS,SAAC,GACR,MAAG,GAAE,IAAI,UAAa,KAAC,GAAG,YACxB,KAAC,OAAO,QAAQ,GADlB,aAGJ,EAAK,GAAG,OAAQ,SAAC,SACf,SAAQ,IAAI,GACZ,EAAS,EAAiB,MAG9B,OAAO,QAAU,8CACjB,OAAQ,sBAAwB","sourcesContent":["\n#\n# @param {Function} callback The callback is called when the connector is initialized.\n#\ncreatePeerJsConnector = (callback)->\n\n peer = new Peer {key: 'h7nlefbgavh1tt9'}\n\n #\n # @see http://peerjs.com\n #\n class PeerJsConnector\n\n #\n # @param {Engine} engine The transformation engine\n # @param {HistoryBuffer} HB\n # @param {Array} execution_listener You must ensure that whenever an operation is executed, every function in this Array is called.\n # @param {Yatta} yatta The Yatta framework.\n #\n constructor: (@engine, @HB, @execution_listener, @yatta)->\n\n @peer = peer\n @connections = []\n\n @peer.on 'connection', (conn)=>\n console.log \"received conn\"\n @addConnection conn\n\n\n\n send_ = (o)=>\n @send o\n @execution_listener.push send_\n\n connectToPeer: (id)->\n @addConnection peer.connect id\n\n addConnection: (conn)->\n @connections.push conn\n\n conn.on 'data', (data)=>\n console.log \"data: #{data}\"\n if data.HB?\n @engine.applyOpsCheckDouble data.HB\n else if data.op?\n @engine.applyOp data.op\n else\n throw new Error \"Can't parse this operation\"\n\n sendHB = ()=>\n console.log \"sending...\"\n conn.send\n HB: @yatta.getHistoryBuffer()._encode()\n setTimeout sendHB, 1000\n\n #\n # This function is called whenever an operation was executed.\n # @param {Operation} o The operation that was executed.\n #\n send: (o)->\n if o.uid.creator is @HB.getUserId() and (typeof o.uid.op_number isnt \"string\")\n console.log \"trying to send ops\"\n for conn in @connections\n console.log \"sent op\"\n conn.send\n op: o\n\n #\n # This function is called whenever an operation was received from another peer.\n # @param {Operation} o The operation that was received.\n #\n receive: (o)->\n if o.uid.creator isnt @HB.getUserId()\n @engine.applyOp o\n\n peer.on 'open', (id)->\n console.log id\n callback PeerJsConnector, id\n\n\nmodule.exports = createPeerJsConnector\nwindow?.createPeerJsConnector = createPeerJsConnector\n\n"],"sourceRoot":"/source/"} \ No newline at end of file diff --git a/build/node/Types/XmlTypes.js.map b/build/node/Types/XmlTypes.js.map index dd91a270..cf7d76c2 100755 --- a/build/node/Types/XmlTypes.js.map +++ b/build/node/Types/XmlTypes.js.map @@ -1 +1 @@ -{"version":3,"file":"Types/XmlTypes.js","sources":["Types/XmlTypes.coffee"],"names":[],"mappings":"CA8KkB","sourcesContent":[""],"sourceRoot":"/source/"} \ No newline at end of file +{"version":3,"file":"Types/XmlTypes.js","sources":["Types/XmlTypes.coffee"],"names":[],"mappings":"CAwFuB","sourcesContent":[""],"sourceRoot":"/source/"} \ No newline at end of file diff --git a/examples/IwcJson/README.md b/examples/IwcJson/README.md index a0717738..d345e239 100644 --- a/examples/IwcJson/README.md +++ b/examples/IwcJson/README.md @@ -9,7 +9,7 @@ First you have to include the following libraries in your widget file: ``` -A working widget implementation is maintained [here](./IwcJson.xml) and the js-file is [here](./IwcJson.js) +A working widget implementation is [IwcJson.xml](./IwcJson.xml) and the js-file is [index.js](./index.js) ```js diff --git a/examples/PeerJsJson/PeerJsJson.html b/examples/PeerJsJson/PeerJsJson.html new file mode 100644 index 00000000..264c92a3 --- /dev/null +++ b/examples/PeerJsJson/PeerJsJson.html @@ -0,0 +1,14 @@ + + + + + PeerJs Json Example + + + + + + + + + diff --git a/examples/PeerJsJson/README.md b/examples/PeerJsJson/README.md new file mode 100644 index 00000000..4c4b4bf6 --- /dev/null +++ b/examples/PeerJsJson/README.md @@ -0,0 +1,177 @@ +## IWC + JSON Example +Here, I will give a short overview on how to use the IwcJson Framework in Role-SDK widgets. +First you have to include the following libraries in your widget file: +``` + + + + + + +``` +A working widget implementation is [IwcJson.xml](./IwcJson.xml) and the js-file is [index.js](./index.js) + + +```js +function init(){ + createPeerJsConnector(function(Connector, user_id){ +``` + + +yatta is the shared json object. If you change something on this object, +it will be instantly shared with all the other collaborators. + + +```js + yatta = new JsonYatta(user_id, Connector); +``` + + +Add a integer-property like this + + +```js + yatta.val('x', 7); +``` + + +Get the value of property x like this + + +```js + console.log(yatta.val('x') === 7); // true +``` + + +A string property can be either mutable or immutable. + + +```js + yatta.val('mutable_string', "text", "mutable"); + yatta.val('immutable_string', "text", "immutable"); + + console.log(yatta.val('immutable_string') === "text"); // true + yatta.val('mutable_string').insertText(2,"XXX"); // position, string + yatta.val('mutable_string').deleteText(0,1); // position, deletion length + console.log(yatta.val('mutable_string').val() === "eXXXxt"); // true +``` + + +You can omit the mutable - parameter. In that case the default will be used. +Initially the default is 'mutable'. You can set it like this: + + +```js + yatta.setMutableDefault('mutable'); + // or + yatta.setMutableDefault('immutable'); + + yatta.val('new_string', "string"); + console.log(yatta.val('new_string') === "string"); // true +``` + + +yatta is chainable: + + +```js + yatta.val('a', 4).val('b',5); + console.log(yatta.val('a') === 4); // true + console.log(yatta.val('b') === 5); // true +``` + + +You can alse set objects. + + +```js + yatta.val('object', {a : {b : "b"}, c : { d : 5 }}); + console.log(yatta.val('object').val('c').val('d') === 5); // true +``` + + +Lists are always immutable. + + +```js + yatta.val('list', [1,2,3]); + console.log(yatta.val('list')[2] === 3); // true +``` + + +But there is a much more convenient way! + + +```js + console.log(yatta.value.list[2] === 3) // true + yatta.value.list = [3,4,5] + console.log(yatta.val('list')[2] === 5) // true + yatta.value.object = {c : 4} + console.log(yatta.value.object.c === 4) // true +``` + + +The downside is that you are only allowed to overwrite existing properties. + + +```js + yatta.value.newProperty = "Awesome" + console.log(yatta.value.newProperty !== "Awesome") // true, yatta.value.newProperty is undefined. +``` + + +So, how do we create new properties? + + +```js + yatta.value = {newProperty : "Awesome"} + console.log(yatta.value.newProperty === "Awesome") // true, it's awesome ;) +``` + + +This is stupid! I don't want to overwrite all my existing properties! +Very well.. The solution is that we merge yatta.value with the new assignment. +For example: assuming we want to overwrite yatta.value with some object o. +Then these two rules apply: +* The result has all properties of o +* The result has all properties of yatta.value if they don't occur under the same property-name in o + + +```js + yatta.value = {newProperty : {Awesome : true }} + console.log(yatta.value.list[2] === 5) // true, old value list still exists. + console.log(yatta.value.newProperty.Awesome === true) // true, newProperty is overwritten. +``` + + +Consider this case. + + +```js + yatta.value = {newProperty : { x : 4} } + console.log(yatta.value.newProperty.Awesome == null) // true, newProperty was replaced, therefore it is now undefined +``` + + +Did you notice that you always set immutable objects if you set properties like this? +Even if the default is 'mutable'. If you want to work with mutable objects you have to work with .val(). + +One last thing. You are only allowed to set properties like this `yatta.value = o`. +Yatta can't observe if you overwrite object references `yatta = "Awesome"`. + + +```js + w = yatta.value.newProperty + w = "Awesome" + console.log(yatta.value.newProperty !== "Awesome") // true, still not awesome.. +``` + + +Please also read [JsonWrapper](https://rawgit.com/DadaMonad/Yatta/master/doc/class/JsonWrapper.html) + + +```js + }) +} +window.onload = init +``` diff --git a/examples/PeerJsJson/index.js b/examples/PeerJsJson/index.js new file mode 100644 index 00000000..c2187d5a --- /dev/null +++ b/examples/PeerJsJson/index.js @@ -0,0 +1,130 @@ + +/** + ## IWC + JSON Example + Here, I will give a short overview on how to use the IwcJson Framework in Role-SDK widgets. + First you have to include the following libraries in your widget file: + ``` + + + + + + + ``` +A working widget implementation is [IwcJson.xml](./IwcJson.xml) and the js-file is [index.js](./index.js) + */ +function init(){ + createPeerJsConnector(function(Connector, user_id){ + /** + 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 JsonYatta(user_id, Connector); + + /** + Add a integer-property like this + */ + yatta.val('x', 7); + + /** + Get the value of property x like this + */ + console.log(yatta.val('x') === 7); // true + + /** + A string property can be either mutable or immutable. + */ + yatta.val('mutable_string', "text", "mutable"); + yatta.val('immutable_string', "text", "immutable"); + + console.log(yatta.val('immutable_string') === "text"); // true + yatta.val('mutable_string').insertText(2,"XXX"); // position, string + yatta.val('mutable_string').deleteText(0,1); // position, deletion length + console.log(yatta.val('mutable_string').val() === "eXXXxt"); // true + + /** + You can omit the mutable - parameter. In that case the default will be used. + Initially the default is 'mutable'. You can set it like this: + */ + yatta.setMutableDefault('mutable'); + // or + yatta.setMutableDefault('immutable'); + + yatta.val('new_string', "string"); + console.log(yatta.val('new_string') === "string"); // true + + /** + yatta is chainable: + */ + yatta.val('a', 4).val('b',5); + console.log(yatta.val('a') === 4); // true + console.log(yatta.val('b') === 5); // true + + /** + You can alse set objects. + */ + yatta.val('object', {a : {b : "b"}, c : { d : 5 }}); + console.log(yatta.val('object').val('c').val('d') === 5); // true + + /** + Lists are always immutable. + */ + yatta.val('list', [1,2,3]); + console.log(yatta.val('list')[2] === 3); // true + + /** + But there is a much more convenient way! + */ + console.log(yatta.value.list[2] === 3) // true + yatta.value.list = [3,4,5] + console.log(yatta.val('list')[2] === 5) // true + yatta.value.object = {c : 4} + console.log(yatta.value.object.c === 4) // true + + /** + The downside is that you are only allowed to overwrite existing properties. + */ + yatta.value.newProperty = "Awesome" + console.log(yatta.value.newProperty !== "Awesome") // true, yatta.value.newProperty is undefined. + + /** + So, how do we create new properties? + */ + yatta.value = {newProperty : "Awesome"} + console.log(yatta.value.newProperty === "Awesome") // true, it's awesome ;) + + /** + This is stupid! I don't want to overwrite all my existing properties! + Very well.. The solution is that we merge yatta.value with the new assignment. + For example: assuming we want to overwrite yatta.value with some object o. + Then these two rules apply: + * The result has all properties of o + * The result has all properties of yatta.value if they don't occur under the same property-name in o + */ + yatta.value = {newProperty : {Awesome : true }} + console.log(yatta.value.list[2] === 5) // true, old value list still exists. + console.log(yatta.value.newProperty.Awesome === true) // true, newProperty is overwritten. + + /** + Consider this case. + */ + yatta.value = {newProperty : { x : 4} } + console.log(yatta.value.newProperty.Awesome == null) // true, newProperty was replaced, therefore it is now undefined + + /** + Did you notice that you always set immutable objects if you set properties like this? + Even if the default is 'mutable'. If you want to work with mutable objects you have to work with .val(). + + One last thing. You are only allowed to set properties like this `yatta.value = o`. + Yatta can't observe if you overwrite object references `yatta = "Awesome"`. + */ + w = yatta.value.newProperty + w = "Awesome" + console.log(yatta.value.newProperty !== "Awesome") // true, still not awesome.. + + /** + Please also read [JsonWrapper](https://rawgit.com/DadaMonad/Yatta/master/doc/class/JsonWrapper.html) + */ + }) +} +window.onload = init diff --git a/lib/Connectors/PeerJsConnector.coffee b/lib/Connectors/PeerJsConnector.coffee new file mode 100644 index 00000000..52c92cfb --- /dev/null +++ b/lib/Connectors/PeerJsConnector.coffee @@ -0,0 +1,83 @@ + +# +# @param {Function} callback The callback is called when the connector is initialized. +# +createPeerJsConnector = (callback)-> + + peer = new Peer {key: 'h7nlefbgavh1tt9'} + + # + # @see http://peerjs.com + # + class PeerJsConnector + + # + # @param {Engine} engine The transformation engine + # @param {HistoryBuffer} HB + # @param {Array} execution_listener You must ensure that whenever an operation is executed, every function in this Array is called. + # @param {Yatta} yatta The Yatta framework. + # + constructor: (@engine, @HB, @execution_listener, @yatta)-> + + @peer = peer + @connections = [] + + @peer.on 'connection', (conn)=> + console.log "received conn" + @addConnection conn + + + + send_ = (o)=> + @send o + @execution_listener.push send_ + + connectToPeer: (id)-> + @addConnection peer.connect id + + addConnection: (conn)-> + @connections.push conn + + conn.on 'data', (data)=> + console.log "data: #{data}" + if data.HB? + @engine.applyOpsCheckDouble data.HB + else if data.op? + @engine.applyOp data.op + else + throw new Error "Can't parse this operation" + + sendHB = ()=> + console.log "sending..." + conn.send + HB: @yatta.getHistoryBuffer()._encode() + setTimeout sendHB, 1000 + + # + # This function is called whenever an operation was executed. + # @param {Operation} o The operation that was executed. + # + send: (o)-> + if o.uid.creator is @HB.getUserId() and (typeof o.uid.op_number isnt "string") + console.log "trying to send ops" + for conn in @connections + console.log "sent op" + conn.send + op: o + + # + # This function is called whenever an operation was received from another peer. + # @param {Operation} o The operation that was received. + # + receive: (o)-> + if o.uid.creator isnt @HB.getUserId() + @engine.applyOp o + + peer.on 'open', (id)-> + console.log id + callback PeerJsConnector, id + + +module.exports = createPeerJsConnector +window?.createPeerJsConnector = createPeerJsConnector + diff --git a/package.json b/package.json index 18e4d836..41ff2aa1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yatta", - "version": "0.0.2", + "version": "0.0.3", "description": "A Framework that enables Real-Time Collaboration on arbitrary data structures.", "main": "./build/node/index", "directories": {