Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8739fd3a9c | ||
|
|
b3b12958fa | ||
|
|
42aa7ec5c9 |
34
README.md
34
README.md
@@ -22,6 +22,7 @@ is a list of the modules we know of:
|
||||
|[webrtc](https://github.com/y-js/y-webrtc) | Propagate updates Browser2Browser via WebRTC|
|
||||
|[websockets](https://github.com/y-js/y-websockets-client) | Set up [a central server](https://github.com/y-js/y-websockets-client), and connect to it via websockets |
|
||||
|[xmpp](https://github.com/y-js/y-xmpp) | Propagate updates in a XMPP multi-user-chat room ([XEP-0045](http://xmpp.org/extensions/xep-0045.html))|
|
||||
|[ipfs](https://github.com/ipfs-labs/y-ipfs-connector) | Connector for the [Interplanetary File System](https://ipfs.io/)!|
|
||||
|[test](https://github.com/y-js/y-test) | A Connector for testing purposes. It is designed to simulate delays that happen in worst case scenarios|
|
||||
|
||||
##### Database adapters
|
||||
@@ -54,23 +55,23 @@ Install Yjs, and its modules with [bower](http://bower.io/), or
|
||||
[npm](https://www.npmjs.org/package/yjs).
|
||||
|
||||
### Bower
|
||||
```
|
||||
```sh
|
||||
bower install --save yjs y-array % add all y-* modules you want to use
|
||||
```
|
||||
You only need to include the `y.js` file. Yjs is able to automatically require
|
||||
missing modules.
|
||||
```
|
||||
```html
|
||||
<script src="./bower_components/yjs/y.js"></script>
|
||||
```
|
||||
|
||||
### Npm
|
||||
```
|
||||
```sh
|
||||
npm install --save yjs % add all y-* modules you want to use
|
||||
```
|
||||
|
||||
If you don't include via script tag, you have to explicitly include all modules!
|
||||
(Same goes for other module systems)
|
||||
```
|
||||
```js
|
||||
var Y = require('yjs')
|
||||
require('y-array')(Y) // add the y-array type to Yjs
|
||||
require('y-websockets-client')(Y)
|
||||
@@ -83,7 +84,7 @@ require('y-text')(Y)
|
||||
```
|
||||
|
||||
### ES6 Syntax
|
||||
```
|
||||
```js
|
||||
import Y from 'yjs'
|
||||
import yArray from 'y-array'
|
||||
import yWebsocketsClient from 'y-webrtc'
|
||||
@@ -97,7 +98,7 @@ Y.extend(yArray, yWebsocketsClient, yMemory, yArray, yMap, yText /*, .. */)
|
||||
|
||||
# Text editing example
|
||||
Install dependencies
|
||||
```
|
||||
```sh
|
||||
bower i yjs y-memory y-webrtc y-array y-text
|
||||
```
|
||||
|
||||
@@ -165,6 +166,25 @@ soon, if possible.
|
||||
endpoint of the used connector.
|
||||
* All of our connectors also have a default connection endpoint that you can
|
||||
use for development.
|
||||
* We provide basic authentification for all connectors. The value of
|
||||
`options.connector.auth` (this can be a passphase) is sent to all connected
|
||||
Yjs instances. `options.connector.checkAuth` may grant read or write access
|
||||
depending on the `auth` information.
|
||||
Example: A client specifies `options.connector.auth = 'superSecretPassword`.
|
||||
A server specifies
|
||||
```js
|
||||
options.connector.checkAuth = function (auth, yjsInstance, sender) {
|
||||
return new Promise(function (resolve, reject){
|
||||
if (auth === 'superSecretPassword') {
|
||||
resolve('write') // grant read-write access
|
||||
} else if (auth === 'different password') {
|
||||
resolve('read') // grant read-only access
|
||||
} else {
|
||||
reject('wrong password!') // reject connection
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
* Set `options.connector.generateUserId = true` in order to genenerate a
|
||||
userid, instead of receiving one from the server. This way the `Y(..)` is
|
||||
immediately going to be resolved, without waiting for any confirmation from
|
||||
@@ -180,7 +200,7 @@ soon, if possible.
|
||||
* Defaults to `/bower_components`
|
||||
* Not required when running on `nodejs` / `iojs`
|
||||
* When using nodejs you need to manually extend Yjs:
|
||||
```
|
||||
```js
|
||||
var Y = require('yjs')
|
||||
// you have to require a db, connector, and *all* types you use!
|
||||
require('y-memory')(Y)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "yjs",
|
||||
"version": "12.3.0",
|
||||
"version": "12.3.3",
|
||||
"homepage": "y-js.org",
|
||||
"authors": [
|
||||
"Kevin Jahns <kevin.jahns@rwth-aachen.de>"
|
||||
|
||||
140
y.es6
140
y.es6
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* yjs - A framework for real-time p2p shared editing on any data
|
||||
* @version v12.2.1
|
||||
* @version v12.3.2
|
||||
* @link http://y-js.org
|
||||
* @license MIT
|
||||
*/
|
||||
@@ -48,20 +48,20 @@ function useColors() {
|
||||
// NB: In an Electron preload script, document will be defined but not fully
|
||||
// initialized. Since we know we're in Chrome, we'll just detect this case
|
||||
// explicitly
|
||||
if (typeof window !== 'undefined' && window && typeof window.process !== 'undefined' && window.process.type === 'renderer') {
|
||||
if (typeof window !== 'undefined' && window.process && window.process.type === 'renderer') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// is webkit? http://stackoverflow.com/a/16459606/376773
|
||||
// document is undefined in react-native: https://github.com/facebook/react-native/pull/1632
|
||||
return (typeof document !== 'undefined' && document && 'WebkitAppearance' in document.documentElement.style) ||
|
||||
return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) ||
|
||||
// is firebug? http://stackoverflow.com/a/398120/376773
|
||||
(typeof window !== 'undefined' && window && window.console && (console.firebug || (console.exception && console.table))) ||
|
||||
(typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) ||
|
||||
// is firefox >= v31?
|
||||
// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
|
||||
(typeof navigator !== 'undefined' && navigator && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) ||
|
||||
(typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) ||
|
||||
// double check webkit in userAgent just in case we are in a worker
|
||||
(typeof navigator !== 'undefined' && navigator && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/));
|
||||
(typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -403,11 +403,11 @@ function coerce(val) {
|
||||
* Helpers.
|
||||
*/
|
||||
|
||||
var s = 1000
|
||||
var m = s * 60
|
||||
var h = m * 60
|
||||
var d = h * 24
|
||||
var y = d * 365.25
|
||||
var s = 1000;
|
||||
var m = s * 60;
|
||||
var h = m * 60;
|
||||
var d = h * 24;
|
||||
var y = d * 365.25;
|
||||
|
||||
/**
|
||||
* Parse or format the given `val`.
|
||||
@@ -423,18 +423,19 @@ var y = d * 365.25
|
||||
* @api public
|
||||
*/
|
||||
|
||||
module.exports = function (val, options) {
|
||||
options = options || {}
|
||||
var type = typeof val
|
||||
module.exports = function(val, options) {
|
||||
options = options || {};
|
||||
var type = typeof val;
|
||||
if (type === 'string' && val.length > 0) {
|
||||
return parse(val)
|
||||
return parse(val);
|
||||
} else if (type === 'number' && isNaN(val) === false) {
|
||||
return options.long ?
|
||||
fmtLong(val) :
|
||||
fmtShort(val)
|
||||
return options.long ? fmtLong(val) : fmtShort(val);
|
||||
}
|
||||
throw new Error('val is not a non-empty string or a valid number. val=' + JSON.stringify(val))
|
||||
}
|
||||
throw new Error(
|
||||
'val is not a non-empty string or a valid number. val=' +
|
||||
JSON.stringify(val)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse the given `str` and return milliseconds.
|
||||
@@ -445,53 +446,55 @@ module.exports = function (val, options) {
|
||||
*/
|
||||
|
||||
function parse(str) {
|
||||
str = String(str)
|
||||
if (str.length > 10000) {
|
||||
return
|
||||
str = String(str);
|
||||
if (str.length > 100) {
|
||||
return;
|
||||
}
|
||||
var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(str)
|
||||
var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(
|
||||
str
|
||||
);
|
||||
if (!match) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
var n = parseFloat(match[1])
|
||||
var type = (match[2] || 'ms').toLowerCase()
|
||||
var n = parseFloat(match[1]);
|
||||
var type = (match[2] || 'ms').toLowerCase();
|
||||
switch (type) {
|
||||
case 'years':
|
||||
case 'year':
|
||||
case 'yrs':
|
||||
case 'yr':
|
||||
case 'y':
|
||||
return n * y
|
||||
return n * y;
|
||||
case 'days':
|
||||
case 'day':
|
||||
case 'd':
|
||||
return n * d
|
||||
return n * d;
|
||||
case 'hours':
|
||||
case 'hour':
|
||||
case 'hrs':
|
||||
case 'hr':
|
||||
case 'h':
|
||||
return n * h
|
||||
return n * h;
|
||||
case 'minutes':
|
||||
case 'minute':
|
||||
case 'mins':
|
||||
case 'min':
|
||||
case 'm':
|
||||
return n * m
|
||||
return n * m;
|
||||
case 'seconds':
|
||||
case 'second':
|
||||
case 'secs':
|
||||
case 'sec':
|
||||
case 's':
|
||||
return n * s
|
||||
return n * s;
|
||||
case 'milliseconds':
|
||||
case 'millisecond':
|
||||
case 'msecs':
|
||||
case 'msec':
|
||||
case 'ms':
|
||||
return n
|
||||
return n;
|
||||
default:
|
||||
return undefined
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -505,18 +508,18 @@ function parse(str) {
|
||||
|
||||
function fmtShort(ms) {
|
||||
if (ms >= d) {
|
||||
return Math.round(ms / d) + 'd'
|
||||
return Math.round(ms / d) + 'd';
|
||||
}
|
||||
if (ms >= h) {
|
||||
return Math.round(ms / h) + 'h'
|
||||
return Math.round(ms / h) + 'h';
|
||||
}
|
||||
if (ms >= m) {
|
||||
return Math.round(ms / m) + 'm'
|
||||
return Math.round(ms / m) + 'm';
|
||||
}
|
||||
if (ms >= s) {
|
||||
return Math.round(ms / s) + 's'
|
||||
return Math.round(ms / s) + 's';
|
||||
}
|
||||
return ms + 'ms'
|
||||
return ms + 'ms';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -532,7 +535,7 @@ function fmtLong(ms) {
|
||||
plural(ms, h, 'hour') ||
|
||||
plural(ms, m, 'minute') ||
|
||||
plural(ms, s, 'second') ||
|
||||
ms + ' ms'
|
||||
ms + ' ms';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -541,12 +544,12 @@ function fmtLong(ms) {
|
||||
|
||||
function plural(ms, n, name) {
|
||||
if (ms < n) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
if (ms < n * 1.5) {
|
||||
return Math.floor(ms / n) + ' ' + name
|
||||
return Math.floor(ms / n) + ' ' + name;
|
||||
}
|
||||
return Math.ceil(ms / n) + ' ' + name + 's'
|
||||
return Math.ceil(ms / n) + ' ' + name + 's';
|
||||
}
|
||||
|
||||
},{}],4:[function(require,module,exports){
|
||||
@@ -736,9 +739,6 @@ process.chdir = function (dir) {
|
||||
process.umask = function() { return 0; };
|
||||
|
||||
},{}],5:[function(require,module,exports){
|
||||
/* @flow */
|
||||
'use strict'
|
||||
|
||||
function canRead (auth) { return auth === 'read' || auth === 'write' }
|
||||
function canWrite (auth) { return auth === 'write' }
|
||||
|
||||
@@ -777,7 +777,6 @@ module.exports = function (Y/* :any */) {
|
||||
// this client receives operations from only one other client.
|
||||
// In particular, this does not work with y-webrtc.
|
||||
// It will work with y-websockets-client
|
||||
this.preferUntransformed = opts.preferUntransformed || false
|
||||
if (opts.role == null || opts.role === 'master') {
|
||||
this.role = 'master'
|
||||
} else if (opts.role === 'slave') {
|
||||
@@ -817,7 +816,7 @@ module.exports = function (Y/* :any */) {
|
||||
}
|
||||
reconnect () {
|
||||
this.log('reconnecting..')
|
||||
this.y.db.startGarbageCollector()
|
||||
return this.y.db.startGarbageCollector()
|
||||
}
|
||||
disconnect () {
|
||||
this.log('discronnecting..')
|
||||
@@ -826,7 +825,8 @@ module.exports = function (Y/* :any */) {
|
||||
this.currentSyncTarget = null
|
||||
this.syncingClients = []
|
||||
this.whenSyncedListeners = []
|
||||
return this.y.db.stopGarbageCollector()
|
||||
this.y.db.stopGarbageCollector()
|
||||
return this.y.db.whenTransactionsFinished()
|
||||
}
|
||||
repair () {
|
||||
this.log('Repairing the state of Yjs. This can happen if messages get lost, and Yjs detects that something is wrong. If this happens often, please report an issue here: https://github.com/y-js/yjs/issues')
|
||||
@@ -928,9 +928,6 @@ module.exports = function (Y/* :any */) {
|
||||
protocolVersion: conn.protocolVersion,
|
||||
auth: conn.authInfo
|
||||
}
|
||||
if (conn.preferUntransformed && Object.keys(stateSet).length === 0) {
|
||||
answer.preferUntransformed = true
|
||||
}
|
||||
conn.send(syncUser, answer)
|
||||
})
|
||||
} else {
|
||||
@@ -1009,7 +1006,7 @@ module.exports = function (Y/* :any */) {
|
||||
}
|
||||
if (message.auth != null && this.connections[sender] != null) {
|
||||
// authenticate using auth in message
|
||||
var auth = this.checkAuth(message.auth, this.y)
|
||||
var auth = this.checkAuth(message.auth, this.y, sender)
|
||||
this.connections[sender].auth = auth
|
||||
auth.then(auth => {
|
||||
for (var f of this.userEventListeners) {
|
||||
@@ -1022,7 +1019,7 @@ module.exports = function (Y/* :any */) {
|
||||
})
|
||||
} else if (this.connections[sender] != null && this.connections[sender].auth == null) {
|
||||
// authenticate without otherwise
|
||||
this.connections[sender].auth = this.checkAuth(null, this.y)
|
||||
this.connections[sender].auth = this.checkAuth(null, this.y, sender)
|
||||
}
|
||||
if (this.connections[sender] != null && this.connections[sender].auth != null) {
|
||||
return this.connections[sender].auth.then((auth) => {
|
||||
@@ -1044,11 +1041,7 @@ module.exports = function (Y/* :any */) {
|
||||
protocolVersion: this.protocolVersion,
|
||||
auth: this.authInfo
|
||||
}
|
||||
if (message.preferUntransformed === true && Object.keys(m.stateSet).length === 0) {
|
||||
answer.osUntransformed = yield* this.getOperationsUntransformed()
|
||||
} else {
|
||||
answer.os = yield* this.getOperations(m.stateSet)
|
||||
}
|
||||
answer.os = yield* this.getOperations(m.stateSet)
|
||||
conn.send(sender, answer)
|
||||
if (this.forwardToSyncingClients) {
|
||||
conn.syncingClients.push(sender)
|
||||
@@ -2533,7 +2526,7 @@ module.exports = function (Y/* :any */) {
|
||||
send.push(Y.Struct[op.struct].encode(op))
|
||||
}
|
||||
}
|
||||
if (this.store.y.connector.isSynced && send.length > 0) { // TODO: && !this.store.forwardAppliedOperations (but then i don't send delete ops)
|
||||
if (send.length > 0) { // TODO: && !this.store.forwardAppliedOperations (but then i don't send delete ops)
|
||||
// is connected, and this is not going to be send in addOperation
|
||||
this.store.y.connector.broadcastOps(send)
|
||||
}
|
||||
@@ -3154,7 +3147,7 @@ module.exports = function (Y/* :any */) {
|
||||
}
|
||||
* addOperation (op) {
|
||||
yield* this.os.put(op)
|
||||
if (this.store.y.connector.isSynced && this.store.forwardAppliedOperations && typeof op.id[1] !== 'string') {
|
||||
if (this.store.forwardAppliedOperations && typeof op.id[1] !== 'string') {
|
||||
// is connected, and this is not going to be send in addOperation
|
||||
this.store.y.connector.broadcastOps([op])
|
||||
}
|
||||
@@ -3568,9 +3561,13 @@ module.exports = function (Y /* : any*/) {
|
||||
event.path = []
|
||||
while (type != null && type._deepEventHandler != null) {
|
||||
type._deepEventHandler.callEventListeners(event)
|
||||
if (type._parent != null && type._parentSub != null) {
|
||||
event.path = [type._parentSub].concat(event.path)
|
||||
type = type.os.getType(type._parent)
|
||||
var parent = null
|
||||
if (type._parent != null) {
|
||||
parent = type.os.getType(type._parent)
|
||||
}
|
||||
if (parent != null && parent._getPathToChild != null) {
|
||||
event.path = [parent._getPathToChild(type._model)].concat(event.path)
|
||||
type = parent
|
||||
} else {
|
||||
type = null
|
||||
}
|
||||
@@ -4015,7 +4012,20 @@ module.exports = function (Y /* : any*/) {
|
||||
Default class of custom types!
|
||||
*/
|
||||
class CustomType {
|
||||
|
||||
getPath () {
|
||||
var parent = null
|
||||
if (this._parent != null) {
|
||||
parent = this.os.getType(this._parent)
|
||||
}
|
||||
if (parent != null && parent._getPathToChild != null) {
|
||||
var firstKey = parent._getPathToChild(this._model)
|
||||
var parentKeys = parent.getPath()
|
||||
parentKeys.push(firstKey)
|
||||
return parentKeys
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
Y.utils.CustomType = CustomType
|
||||
|
||||
|
||||
Reference in New Issue
Block a user