Compare commits

..

5 Commits

Author SHA1 Message Date
Kevin Jahns
73e53e3dcc Deploy 12.3.0 2017-05-08 12:40:26 +02:00
Kevin Jahns
222ffea11e Deploy 12.2.1 2017-05-03 18:00:53 +02:00
Kevin Jahns
c3d21b5c8e Deploy 12.2.0 2017-05-03 16:17:06 +02:00
Kevin Jahns
3eafd78710 Added Monaco editor example 2017-04-18 18:23:17 +02:00
Kevin Jahns
e30e16c91b add serviceworker demo 2017-04-11 16:20:13 +02:00
13 changed files with 457 additions and 209 deletions

1
.gitignore vendored
View File

@@ -1 +1,2 @@
node_modules
bower_components bower_components

View File

@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id="monacoContainer"></div>
<style>
#monacoContainer {
position: fixed;
top: 0;
left: 0;
height: 100%;
width: 100%;
}
</style>
<script src="../bower_components/yjs/y.es6"></script>
<script src="../bower_components/y-array/y-array.es6"></script>
<script src="../bower_components/y-text/y-text.es6"></script>
<script src="../bower_components/y-websockets-client/y-websockets-client.es6"></script>
<script src="../bower_components/y-memory/y-memory.es6"></script>
<script src="../node_modules/monaco-editor/min/vs/loader.js"></script>
<script src="./index.js"></script>
</body>
</html>

31
Examples/Monaco/index.js Normal file
View File

@@ -0,0 +1,31 @@
/* global Y */
require.config({ paths: { 'vs': '../node_modules/monaco-editor/min/vs' }})
require(['vs/editor/editor.main'], function() {
// Initialize a shared object. This function call returns a promise!
Y({
db: {
name: 'memory'
},
connector: {
name: 'websockets-client',
room: 'monaco-example'
},
sourceDir: '/bower_components',
share: {
monaco: 'Text' // y.share.monaco is of type Y.Text
}
}).then(function (y) {
window.yMonaco = y
// Create Monaco editor
var editor = monaco.editor.create(document.getElementById('monacoContainer'), {
language: 'javascript'
})
// Bind to y.share.monaco
y.share.monaco.bindMonaco(editor)
})
})

View File

@@ -1,21 +1,31 @@
/* global Y, Quill */ /* global Y, Quill */
// register yjs service worker
if('serviceWorker' in navigator){
// Register service worker
// it is important to copy yjs-sw-template to the root directory!
navigator.serviceWorker.register('./yjs-sw-template.js').then(function(reg){
console.log("Yjs service worker registration succeeded. Scope is " + reg.scope);
}).catch(function(err){
console.error("Yjs service worker registration failed with error " + err);
})
}
// initialize a shared object. This function call returns a promise! // initialize a shared object. This function call returns a promise!
Y({ Y({
db: { db: {
name: 'memory' name: 'memory'
}, },
connector: { connector: {
name: 'webworker', name: 'serviceworker',
url: '../bower_components/y-webworker/yjs-webworker.js', room: 'ServiceWorkerExample2'
room: 'WebWorkerExample2'
}, },
sourceDir: '/bower_components', sourceDir: '/bower_components',
share: { share: {
richtext: 'Richtext' // y.share.richtext is of type Y.Richtext richtext: 'Richtext' // y.share.richtext is of type Y.Richtext
} }
}).then(function (y) { }).then(function (y) {
window.yQuill = y window.yServiceWorker = y
// create quill element // create quill element
window.quill = new Quill('#quill', { window.quill = new Quill('#quill', {

View File

@@ -0,0 +1,22 @@
/* eslint-env worker */
// copy and modify this file
self.DBConfig = {
name: 'indexeddb'
}
self.ConnectorConfig = {
name: 'websockets-client',
// url: '..',
options: {
jsonp: false
}
}
importScripts(
'/bower_components/yjs/y.js',
'/bower_components/y-memory/y-memory.js',
'/bower_components/y-indexeddb/y-indexeddb.js',
'/bower_components/y-websockets-client/y-websockets-client.js',
'/bower_components/y-serviceworker/yjs-sw-include.js'
)

10
Examples/package.json Normal file
View File

@@ -0,0 +1,10 @@
{
"name": "examples",
"version": "1.0.0",
"description": "",
"author": "Kevin Jahns",
"license": "MIT",
"dependencies": {
"monaco-editor": "^0.8.3"
}
}

View File

@@ -40,7 +40,7 @@ is a list of the modules we know of:
|[map](https://github.com/y-js/y-map) | A shared Map implementation. Maps from text to any stringify-able object | |[map](https://github.com/y-js/y-map) | A shared Map implementation. Maps from text to any stringify-able object |
|[array](https://github.com/y-js/y-array) | A shared Array implementation | |[array](https://github.com/y-js/y-array) | A shared Array implementation |
|[xml](https://github.com/y-js/y-xml) | An implementation of the DOM. You can create a two way binding to Browser DOM objects | |[xml](https://github.com/y-js/y-xml) | An implementation of the DOM. You can create a two way binding to Browser DOM objects |
|[text](https://github.com/y-js/y-text) | Collaborate on text. Supports two way binding to the [Ace Editor](https://ace.c9.io), [CodeMirror](https://codemirror.net/), textareas, input elements, and HTML elements (e.g. <*h1*>, or <*p*>) | |[text](https://github.com/y-js/y-text) | Collaborate on text. Supports two way binding to the [Ace Editor](https://ace.c9.io), [CodeMirror](https://codemirror.net/), [Monaco](https://github.com/Microsoft/monaco-editor), textareas, input elements, and HTML elements (e.g. <*h1*>, or <*p*>) |
|[richtext](https://github.com/y-js/y-richtext) | Collaborate on rich text. Supports two way binding to the [Quill Rich Text Editor](http://quilljs.com/)| |[richtext](https://github.com/y-js/y-richtext) | Collaborate on rich text. Supports two way binding to the [Quill Rich Text Editor](http://quilljs.com/)|
##### Other ##### Other
@@ -170,6 +170,11 @@ soon, if possible.
immediately going to be resolved, without waiting for any confirmation from immediately going to be resolved, without waiting for any confirmation from
the server. Use with caution. the server. Use with caution.
* Have a look at the used connector repository to see all available options. * Have a look at the used connector repository to see all available options.
* *Only if you know what you are doing:* Set
`options.connector.preferUntransformed = true` in order receive the shared
data untransformed. This is very efficient as the database content is simply
copied to this client. This does only work if this client receives content
from only one client.
* options.sourceDir (browser only) * options.sourceDir (browser only)
* Path where all y-* modules are stored * Path where all y-* modules are stored
* Defaults to `/bower_components` * Defaults to `/bower_components`

View File

@@ -1,6 +1,6 @@
{ {
"name": "yjs", "name": "yjs",
"version": "12.1.7", "version": "12.3.0",
"homepage": "y-js.org", "homepage": "y-js.org",
"authors": [ "authors": [
"Kevin Jahns <kevin.jahns@rwth-aachen.de>" "Kevin Jahns <kevin.jahns@rwth-aachen.de>"

539
y.es6
View File

@@ -1,161 +1,10 @@
/** /**
* yjs - A framework for real-time p2p shared editing on any data * yjs - A framework for real-time p2p shared editing on any data
* @version v12.1.6 * @version v12.2.1
* @link http://y-js.org * @link http://y-js.org
* @license MIT * @license MIT
*/ */
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Y = f()}})(function(){var define,module,exports;return (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);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Y = f()}})(function(){var define,module,exports;return (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);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
/**
* Helpers.
*/
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`.
*
* Options:
*
* - `long` verbose formatting [false]
*
* @param {String|Number} val
* @param {Object} options
* @throws {Error} throw an error if val is not a non-empty string or a number
* @return {String|Number}
* @api public
*/
module.exports = function (val, options) {
options = options || {}
var type = typeof val
if (type === 'string' && val.length > 0) {
return parse(val)
} else if (type === 'number' && isNaN(val) === false) {
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))
}
/**
* Parse the given `str` and return milliseconds.
*
* @param {String} str
* @return {Number}
* @api private
*/
function parse(str) {
str = String(str)
if (str.length > 10000) {
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)
if (!match) {
return
}
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
case 'days':
case 'day':
case 'd':
return n * d
case 'hours':
case 'hour':
case 'hrs':
case 'hr':
case 'h':
return n * h
case 'minutes':
case 'minute':
case 'mins':
case 'min':
case 'm':
return n * m
case 'seconds':
case 'second':
case 'secs':
case 'sec':
case 's':
return n * s
case 'milliseconds':
case 'millisecond':
case 'msecs':
case 'msec':
case 'ms':
return n
default:
return undefined
}
}
/**
* Short format for `ms`.
*
* @param {Number} ms
* @return {String}
* @api private
*/
function fmtShort(ms) {
if (ms >= d) {
return Math.round(ms / d) + 'd'
}
if (ms >= h) {
return Math.round(ms / h) + 'h'
}
if (ms >= m) {
return Math.round(ms / m) + 'm'
}
if (ms >= s) {
return Math.round(ms / s) + 's'
}
return ms + 'ms'
}
/**
* Long format for `ms`.
*
* @param {Number} ms
* @return {String}
* @api private
*/
function fmtLong(ms) {
return plural(ms, d, 'day') ||
plural(ms, h, 'hour') ||
plural(ms, m, 'minute') ||
plural(ms, s, 'second') ||
ms + ' ms'
}
/**
* Pluralization helper.
*/
function plural(ms, n, name) {
if (ms < n) {
return
}
if (ms < n * 1.5) {
return Math.floor(ms / n) + ' ' + name
}
return Math.ceil(ms / n) + ' ' + name + 's'
}
},{}],2:[function(require,module,exports){
(function (process){ (function (process){
/** /**
* This is the web browser implementation of `debug()`. * This is the web browser implementation of `debug()`.
@@ -345,7 +194,7 @@ function localstorage() {
}).call(this,require('_process')) }).call(this,require('_process'))
},{"./debug":3,"_process":4}],3:[function(require,module,exports){ },{"./debug":2,"_process":4}],2:[function(require,module,exports){
/** /**
* This is the common logic for both the Node.js and web browser * This is the common logic for both the Node.js and web browser
@@ -489,7 +338,7 @@ function enable(namespaces) {
exports.names = []; exports.names = [];
exports.skips = []; exports.skips = [];
var split = (namespaces || '').split(/[\s,]+/); var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/);
var len = split.length; var len = split.length;
for (var i = 0; i < len; i++) { for (var i = 0; i < len; i++) {
@@ -549,7 +398,158 @@ function coerce(val) {
return val; return val;
} }
},{"ms":1}],4:[function(require,module,exports){ },{"ms":3}],3:[function(require,module,exports){
/**
* Helpers.
*/
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`.
*
* Options:
*
* - `long` verbose formatting [false]
*
* @param {String|Number} val
* @param {Object} [options]
* @throws {Error} throw an error if val is not a non-empty string or a number
* @return {String|Number}
* @api public
*/
module.exports = function (val, options) {
options = options || {}
var type = typeof val
if (type === 'string' && val.length > 0) {
return parse(val)
} else if (type === 'number' && isNaN(val) === false) {
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))
}
/**
* Parse the given `str` and return milliseconds.
*
* @param {String} str
* @return {Number}
* @api private
*/
function parse(str) {
str = String(str)
if (str.length > 10000) {
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)
if (!match) {
return
}
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
case 'days':
case 'day':
case 'd':
return n * d
case 'hours':
case 'hour':
case 'hrs':
case 'hr':
case 'h':
return n * h
case 'minutes':
case 'minute':
case 'mins':
case 'min':
case 'm':
return n * m
case 'seconds':
case 'second':
case 'secs':
case 'sec':
case 's':
return n * s
case 'milliseconds':
case 'millisecond':
case 'msecs':
case 'msec':
case 'ms':
return n
default:
return undefined
}
}
/**
* Short format for `ms`.
*
* @param {Number} ms
* @return {String}
* @api private
*/
function fmtShort(ms) {
if (ms >= d) {
return Math.round(ms / d) + 'd'
}
if (ms >= h) {
return Math.round(ms / h) + 'h'
}
if (ms >= m) {
return Math.round(ms / m) + 'm'
}
if (ms >= s) {
return Math.round(ms / s) + 's'
}
return ms + 'ms'
}
/**
* Long format for `ms`.
*
* @param {Number} ms
* @return {String}
* @api private
*/
function fmtLong(ms) {
return plural(ms, d, 'day') ||
plural(ms, h, 'hour') ||
plural(ms, m, 'minute') ||
plural(ms, s, 'second') ||
ms + ' ms'
}
/**
* Pluralization helper.
*/
function plural(ms, n, name) {
if (ms < n) {
return
}
if (ms < n * 1.5) {
return Math.floor(ms / n) + ' ' + name
}
return Math.ceil(ms / n) + ' ' + name + 's'
}
},{}],4:[function(require,module,exports){
// shim for using process in browser // shim for using process in browser
var process = module.exports = {}; var process = module.exports = {};
@@ -561,35 +561,83 @@ var process = module.exports = {};
var cachedSetTimeout; var cachedSetTimeout;
var cachedClearTimeout; var cachedClearTimeout;
function defaultSetTimout() {
throw new Error('setTimeout has not been defined');
}
function defaultClearTimeout () {
throw new Error('clearTimeout has not been defined');
}
(function () { (function () {
try { try {
cachedSetTimeout = setTimeout; if (typeof setTimeout === 'function') {
} catch (e) { cachedSetTimeout = setTimeout;
cachedSetTimeout = function () { } else {
throw new Error('setTimeout is not defined'); cachedSetTimeout = defaultSetTimout;
} }
} catch (e) {
cachedSetTimeout = defaultSetTimout;
} }
try { try {
cachedClearTimeout = clearTimeout; if (typeof clearTimeout === 'function') {
} catch (e) { cachedClearTimeout = clearTimeout;
cachedClearTimeout = function () { } else {
throw new Error('clearTimeout is not defined'); cachedClearTimeout = defaultClearTimeout;
} }
} catch (e) {
cachedClearTimeout = defaultClearTimeout;
} }
} ()) } ())
function runTimeout(fun) { function runTimeout(fun) {
if (cachedSetTimeout === setTimeout) { if (cachedSetTimeout === setTimeout) {
//normal enviroments in sane situations
return setTimeout(fun, 0); return setTimeout(fun, 0);
} else {
return cachedSetTimeout.call(null, fun, 0);
} }
// if setTimeout wasn't available but was latter defined
if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
cachedSetTimeout = setTimeout;
return setTimeout(fun, 0);
}
try {
// when when somebody has screwed with setTimeout but no I.E. maddness
return cachedSetTimeout(fun, 0);
} catch(e){
try {
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
return cachedSetTimeout.call(null, fun, 0);
} catch(e){
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
return cachedSetTimeout.call(this, fun, 0);
}
}
} }
function runClearTimeout(marker) { function runClearTimeout(marker) {
if (cachedClearTimeout === clearTimeout) { if (cachedClearTimeout === clearTimeout) {
clearTimeout(marker); //normal enviroments in sane situations
} else { return clearTimeout(marker);
cachedClearTimeout.call(null, marker);
} }
// if clearTimeout wasn't available but was latter defined
if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
cachedClearTimeout = clearTimeout;
return clearTimeout(marker);
}
try {
// when when somebody has screwed with setTimeout but no I.E. maddness
return cachedClearTimeout(marker);
} catch (e){
try {
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
return cachedClearTimeout.call(null, marker);
} catch (e){
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
// Some versions of I.E. have different rules for clearTimeout vs setTimeout
return cachedClearTimeout.call(this, marker);
}
}
} }
var queue = []; var queue = [];
var draining = false; var draining = false;
@@ -672,6 +720,10 @@ process.off = noop;
process.removeListener = noop; process.removeListener = noop;
process.removeAllListeners = noop; process.removeAllListeners = noop;
process.emit = noop; process.emit = noop;
process.prependListener = noop;
process.prependOnceListener = noop;
process.listeners = function (name) { return [] }
process.binding = function (name) { process.binding = function (name) {
throw new Error('process.binding is not supported'); throw new Error('process.binding is not supported');
@@ -703,7 +755,6 @@ module.exports = function (Y/* :any */) {
syncingClients: Array<UserId>; syncingClients: Array<UserId>;
forwardToSyncingClients: boolean; forwardToSyncingClients: boolean;
debug: boolean; debug: boolean;
broadcastedHB: boolean;
syncStep2: Promise; syncStep2: Promise;
userId: UserId; userId: UserId;
send: Function; send: Function;
@@ -722,6 +773,11 @@ module.exports = function (Y/* :any */) {
if (opts == null) { if (opts == null) {
opts = {} opts = {}
} }
// Prefer to receive untransformed operations. This does only work if
// 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') { if (opts.role == null || opts.role === 'master') {
this.role = 'master' this.role = 'master'
} else if (opts.role === 'slave') { } else if (opts.role === 'slave') {
@@ -741,7 +797,6 @@ module.exports = function (Y/* :any */) {
this.syncingClients = [] this.syncingClients = []
this.forwardToSyncingClients = opts.forwardToSyncingClients !== false this.forwardToSyncingClients = opts.forwardToSyncingClients !== false
this.debug = opts.debug === true this.debug = opts.debug === true
this.broadcastedHB = false
this.syncStep2 = Promise.resolve() this.syncStep2 = Promise.resolve()
this.broadcastOpBuffer = [] this.broadcastOpBuffer = []
this.protocolVersion = 11 this.protocolVersion = 11
@@ -762,13 +817,13 @@ module.exports = function (Y/* :any */) {
} }
reconnect () { reconnect () {
this.log('reconnecting..') this.log('reconnecting..')
this.y.db.startGarbageCollector()
} }
disconnect () { disconnect () {
this.log('discronnecting..') this.log('discronnecting..')
this.connections = {} this.connections = {}
this.isSynced = false this.isSynced = false
this.currentSyncTarget = null this.currentSyncTarget = null
this.broadcastedHB = false
this.syncingClients = [] this.syncingClients = []
this.whenSyncedListeners = [] this.whenSyncedListeners = []
return this.y.db.stopGarbageCollector() return this.y.db.stopGarbageCollector()
@@ -780,7 +835,6 @@ module.exports = function (Y/* :any */) {
} }
this.isSynced = false this.isSynced = false
this.currentSyncTarget = null this.currentSyncTarget = null
this.broadcastedHB = false
this.findNextSyncTarget() this.findNextSyncTarget()
} }
setUserId (userId) { setUserId (userId) {
@@ -867,13 +921,17 @@ module.exports = function (Y/* :any */) {
this.y.db.requestTransaction(function *() { this.y.db.requestTransaction(function *() {
var stateSet = yield* this.getStateSet() var stateSet = yield* this.getStateSet()
var deleteSet = yield* this.getDeleteSet() var deleteSet = yield* this.getDeleteSet()
conn.send(syncUser, { var answer = {
type: 'sync step 1', type: 'sync step 1',
stateSet: stateSet, stateSet: stateSet,
deleteSet: deleteSet, deleteSet: deleteSet,
protocolVersion: conn.protocolVersion, protocolVersion: conn.protocolVersion,
auth: conn.authInfo auth: conn.authInfo
}) }
if (conn.preferUntransformed && Object.keys(stateSet).length === 0) {
answer.preferUntransformed = true
}
conn.send(syncUser, answer)
}) })
} else { } else {
if (!conn.isSynced) { if (!conn.isSynced) {
@@ -979,15 +1037,19 @@ module.exports = function (Y/* :any */) {
} }
var ds = yield* this.getDeleteSet() var ds = yield* this.getDeleteSet()
var ops = yield* this.getOperations(m.stateSet) var answer = {
conn.send(sender, {
type: 'sync step 2', type: 'sync step 2',
os: ops,
stateSet: currentStateSet, stateSet: currentStateSet,
deleteSet: ds, deleteSet: ds,
protocolVersion: this.protocolVersion, protocolVersion: this.protocolVersion,
auth: this.authInfo 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)
}
conn.send(sender, answer)
if (this.forwardToSyncingClients) { if (this.forwardToSyncingClients) {
conn.syncingClients.push(sender) conn.syncingClients.push(sender)
setTimeout(function () { setTimeout(function () {
@@ -1005,9 +1067,6 @@ module.exports = function (Y/* :any */) {
} }
}) })
} else if (message.type === 'sync step 2' && canWrite(auth)) { } else if (message.type === 'sync step 2' && canWrite(auth)) {
let conn = this
var broadcastHB = !this.broadcastedHB
this.broadcastedHB = true
var db = this.y.db var db = this.y.db
var defer = {} var defer = {}
defer.promise = new Promise(function (resolve) { defer.promise = new Promise(function (resolve) {
@@ -1017,7 +1076,15 @@ module.exports = function (Y/* :any */) {
let m /* :MessageSyncStep2 */ = message let m /* :MessageSyncStep2 */ = message
db.requestTransaction(function * () { db.requestTransaction(function * () {
yield* this.applyDeleteSet(m.deleteSet) yield* this.applyDeleteSet(m.deleteSet)
this.store.apply(m.os) if (m.osUntransformed != null) {
yield* this.applyOperationsUntransformed(m.osUntransformed, m.stateSet)
} else {
this.store.apply(m.os)
}
/*
* This just sends the complete hb after some time
* Mostly for debugging..
*
db.requestTransaction(function * () { db.requestTransaction(function * () {
var ops = yield* this.getOperations(m.stateSet) var ops = yield* this.getOperations(m.stateSet)
if (ops.length > 0) { if (ops.length > 0) {
@@ -1031,8 +1098,9 @@ module.exports = function (Y/* :any */) {
conn.broadcastOps(ops) conn.broadcastOps(ops)
} }
} }
defer.resolve()
}) })
*/
defer.resolve()
}) })
} else if (message.type === 'sync done') { } else if (message.type === 'sync done') {
var self = this var self = this
@@ -1204,7 +1272,7 @@ module.exports = function (Y) {
ps.push(self.users[name].y.db.whenTransactionsFinished()) ps.push(self.users[name].y.db.whenTransactionsFinished())
} }
Promise.all(ps).then(resolve, reject) Promise.all(ps).then(resolve, reject)
}, 0) }, 10)
}) })
}, },
flushOne: function flushOne () { flushOne: function flushOne () {
@@ -1314,11 +1382,15 @@ module.exports = function (Y) {
return Y.utils.globalRoom.flushAll() return Y.utils.globalRoom.flushAll()
} }
disconnect () { disconnect () {
var waitForMe = Promise.resolve()
if (!this.isDisconnected()) { if (!this.isDisconnected()) {
globalRoom.removeUser(this.userId) globalRoom.removeUser(this.userId)
super.disconnect() waitForMe = super.disconnect()
} }
return this.y.db.whenTransactionsFinished() var self = this
return waitForMe.then(function () {
return self.y.db.whenTransactionsFinished()
})
} }
flush () { flush () {
var self = this var self = this
@@ -1382,6 +1454,7 @@ module.exports = function (Y /* :any */) {
*/ */
constructor (y, opts) { constructor (y, opts) {
this.y = y this.y = y
this.dbOpts = opts
var os = this var os = this
this.userId = null this.userId = null
var resolve var resolve
@@ -1418,12 +1491,6 @@ module.exports = function (Y /* :any */) {
} }
this.gc1 = [] // first stage this.gc1 = [] // first stage
this.gc2 = [] // second stage -> after that, remove the op this.gc2 = [] // second stage -> after that, remove the op
this.gc = opts.gc == null || opts.gc
if (this.gc) {
this.gcTimeout = !opts.gcTimeout ? 50000 : opts.gcTimeout
} else {
this.gcTimeout = -1
}
function garbageCollect () { function garbageCollect () {
return os.whenTransactionsFinished().then(function () { return os.whenTransactionsFinished().then(function () {
@@ -1458,13 +1525,23 @@ module.exports = function (Y /* :any */) {
}) })
} }
this.garbageCollect = garbageCollect this.garbageCollect = garbageCollect
if (this.gcTimeout > 0) { this.startGarbageCollector()
garbageCollect()
}
this.repairCheckInterval = !opts.repairCheckInterval ? 6000 : opts.repairCheckInterval this.repairCheckInterval = !opts.repairCheckInterval ? 6000 : opts.repairCheckInterval
this.opsReceivedTimestamp = new Date() this.opsReceivedTimestamp = new Date()
this.startRepairCheck() this.startRepairCheck()
} }
startGarbageCollector () {
this.gc = this.dbOpts.gc == null || this.dbOpts.gc
if (this.gc) {
this.gcTimeout = !this.dbOpts.gcTimeout ? 50000 : this.dbOpts.gcTimeout
} else {
this.gcTimeout = -1
}
if (this.gcTimeout > 0) {
this.garbageCollect()
}
}
startRepairCheck () { startRepairCheck () {
var os = this var os = this
if (this.repairCheckInterval > 0) { if (this.repairCheckInterval > 0) {
@@ -3373,6 +3450,56 @@ module.exports = function (Y/* :any */) {
} }
return send.reverse() return send.reverse()
} }
/*
* Get the plain untransformed operations from the database.
* You can apply these operations using .applyOperationsUntransformed(ops)
*
*/
* getOperationsUntransformed () {
var ops = []
yield* this.os.iterate(this, null, null, function * (op) {
if (op.id[0] !== '_') {
ops.push(op)
}
})
return {
untransformed: ops
}
}
* applyOperationsUntransformed (m, stateSet) {
var ops = m.untransformed
for (var i = 0; i < ops.length; i++) {
var op = ops[i]
// create, and modify parent, if it is created implicitly
if (op.parent != null && op.parent[0] === '_') {
if (op.struct === 'Insert') {
// update parents .map/start/end properties
if (op.parentSub != null && op.left == null) {
// op is child of Map
let parent = yield* this.getOperation(op.parent)
parent.map[op.parentSub] = op.id
yield* this.setOperation(parent)
} else if (op.right == null || op.left == null) {
let parent = yield* this.getOperation(op.parent)
if (op.right == null) {
parent.end = Y.utils.getLastId(op)
}
if (op.left == null) {
parent.start = op.id
}
yield* this.setOperation(parent)
}
}
}
yield* this.os.put(op)
}
for (var user in stateSet) {
yield* this.ss.put({
id: [user],
clock: stateSet[user]
})
}
}
/* this is what we used before.. use this as a reference.. /* this is what we used before.. use this as a reference..
* makeOperationReady (startSS, op) { * makeOperationReady (startSS, op) {
op = Y.Struct[op.struct].encode(op) op = Y.Struct[op.struct].encode(op)
@@ -3436,6 +3563,20 @@ module.exports = function (Y/* :any */) {
module.exports = function (Y /* : any*/) { module.exports = function (Y /* : any*/) {
Y.utils = {} Y.utils = {}
Y.utils.bubbleEvent = function (type, event) {
type.eventHandler.callEventListeners(event)
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)
} else {
type = null
}
}
}
class EventListenerHandler { class EventListenerHandler {
constructor () { constructor () {
this.eventListeners = [] this.eventListeners = []
@@ -3460,7 +3601,11 @@ module.exports = function (Y /* : any*/) {
callEventListeners (event) { callEventListeners (event) {
for (var i = 0; i < this.eventListeners.length; i++) { for (var i = 0; i < this.eventListeners.length; i++) {
try { try {
this.eventListeners[i](event) var _event = {}
for (var name in event) {
_event[name] = event[name]
}
this.eventListeners[i](_event)
} catch (e) { } catch (e) {
console.error('Your observer threw an error. This error was caught so that Yjs still can ensure data consistency! In order to debug this error you have to check "Pause On Caught Exceptions"', e) console.error('Your observer threw an error. This error was caught so that Yjs still can ensure data consistency! In order to debug this error you have to check "Pause On Caught Exceptions"', e)
} }
@@ -4408,6 +4553,6 @@ class YConfig {
} }
} }
},{"./Connector.js":5,"./Connectors/Test.js":6,"./Database.js":7,"./Struct.js":8,"./Transaction.js":9,"./Utils.js":10,"debug":2}]},{},[11])(11) },{"./Connector.js":5,"./Connectors/Test.js":6,"./Database.js":7,"./Struct.js":8,"./Transaction.js":9,"./Utils.js":10,"debug":1}]},{},[11])(11)
}); });

File diff suppressed because one or more lines are too long

8
y.js

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long