Compare commits

..

19 Commits

Author SHA1 Message Date
Kevin Jahns
b1192fbb46 v13.0.0-32 -- distribution files 2017-11-14 21:31:19 -08:00
Kevin Jahns
2e3240b379 13.0.0-32 2017-11-14 21:31:11 -08:00
Kevin Jahns
2558652356 fix attribute filter (it used to filter everything) 2017-11-14 21:19:39 -08:00
Kevin Jahns
783cbd63fc 13.0.0-31 2017-11-14 20:44:12 -08:00
Kevin Jahns
41be80e751 fix y-xml server environment 2017-11-14 20:43:30 -08:00
Kevin Jahns
3d6050d8a2 13.0.0-30 2017-11-12 13:37:37 -08:00
Kevin Jahns
3d5ba7b4cc fix the case that a new transaction starts in an event listener (afterTransaction, observe, observeDeep) 2017-11-12 13:37:06 -08:00
Kevin Jahns
415b66607c fixed filtering 2017-11-10 19:04:00 -08:00
Kevin Jahns
05cd1d0575 13.0.0-29 2017-11-10 18:46:10 -08:00
Kevin Jahns
4edc22bedb remove prematurely commited dom-filter update 2017-11-10 18:45:41 -08:00
Kevin Jahns
16f84c67d5 13.0.0-28 2017-11-10 18:41:39 -08:00
Kevin Jahns
290d3c8ffe support undefined as an attribute value 2017-11-10 18:41:10 -08:00
Kevin Jahns
c51e8b46c2 13.0.0-27 2017-11-10 12:55:05 -08:00
Kevin Jahns
0cda1630d2 fix path bugs 2017-11-10 12:54:33 -08:00
Kevin Jahns
d232b883e9 13.0.0-26 2017-11-09 17:32:45 -08:00
Kevin Jahns
3a0e65403f fix undo scope 2017-11-09 17:31:58 -08:00
Kevin Jahns
224fff93ba 13.0.0-25 2017-11-08 17:31:50 -08:00
Kevin Jahns
4f55e8c655 fix event.path by using event.currentTarget 2017-11-08 17:31:12 -08:00
Kevin Jahns
a08624c04e implemented tree-walker 2017-11-08 13:40:36 -08:00
30 changed files with 12985 additions and 9317 deletions

View File

@@ -10,7 +10,7 @@ var y = new Y({
window.yChat = y window.yChat = y
let chatprotocol = y.get('chatprotocol', Y.Array) let chatprotocol = y.define('chatprotocol', Y.Array)
let chatcontainer = document.querySelector('#chat') let chatcontainer = document.querySelector('#chat')

View File

@@ -1,4 +1,15 @@
/* global Y */ /* global Y, HTMLElement, customElements */
class MagicTable extends HTMLElement {
constructor () {
super()
var shadow = this.attachShadow({mode: 'open'})
setTimeout(() => {
shadow.append(this.childNodes[0])
}, 1000)
}
}
customElements.define('magic-table', MagicTable)
// initialize a shared object. This function call returns a promise! // initialize a shared object. This function call returns a promise!
let y = new Y({ let y = new Y({
@@ -10,21 +21,21 @@ let y = new Y({
} }
}) })
window.yXml = y window.yXml = y
window.yXmlType = y.get('xml', Y.XmlFragment) window.yXmlType = y.define('xml', Y.XmlFragment)
window.onload = function () { window.onload = function () {
console.log('start!') console.log('start!')
// Bind children of XmlFragment to the document.body // Bind children of XmlFragment to the document.body
window.yXmlType.bindToDom(document.body) window.yXmlType.bindToDom(document.body)
} }
window.undoManager = new Y.utils.UndoManager(window.yXmlType) window.undoManager = new Y.utils.UndoManager(window.yXmlType, {
captureTimeout: 500
})
document.onkeydown = function interceptUndoRedo (e) { document.onkeydown = function interceptUndoRedo (e) {
if (e.keyCode === 90 && e.ctrlKey) { if (e.keyCode === 90 && e.metaKey) {
if (!e.shiftKey) { if (!e.shiftKey) {
console.info('Undo!')
window.undoManager.undo() window.undoManager.undo()
} else { } else {
console.info('Redo!')
window.undoManager.redo() window.undoManager.redo()
} }
e.preventDefault() e.preventDefault()

927
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{ {
"name": "yjs", "name": "yjs",
"version": "13.0.0-24", "version": "13.0.0-32",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@@ -1039,6 +1039,7 @@
"requires": { "requires": {
"anymatch": "1.3.0", "anymatch": "1.3.0",
"async-each": "1.0.1", "async-each": "1.0.1",
"fsevents": "1.1.2",
"glob-parent": "2.0.0", "glob-parent": "2.0.0",
"inherits": "2.0.3", "inherits": "2.0.3",
"is-binary-path": "1.0.1", "is-binary-path": "1.0.1",
@@ -2038,6 +2039,905 @@
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"dev": true "dev": true
}, },
"fsevents": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.2.tgz",
"integrity": "sha512-Sn44E5wQW4bTHXvQmvSHwqbuiXtduD6Rrjm2ZtUEGbyrig+nUH3t/QD4M4/ZXViY556TBpRgZkHLDx3JxPwxiw==",
"dev": true,
"optional": true,
"requires": {
"nan": "2.7.0",
"node-pre-gyp": "0.6.36"
},
"dependencies": {
"abbrev": {
"version": "1.1.0",
"bundled": true,
"dev": true,
"optional": true
},
"ajv": {
"version": "4.11.8",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"co": "4.6.0",
"json-stable-stringify": "1.0.1"
}
},
"ansi-regex": {
"version": "2.1.1",
"bundled": true,
"dev": true
},
"aproba": {
"version": "1.1.1",
"bundled": true,
"dev": true,
"optional": true
},
"are-we-there-yet": {
"version": "1.1.4",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"delegates": "1.0.0",
"readable-stream": "2.2.9"
}
},
"asn1": {
"version": "0.2.3",
"bundled": true,
"dev": true,
"optional": true
},
"assert-plus": {
"version": "0.2.0",
"bundled": true,
"dev": true,
"optional": true
},
"asynckit": {
"version": "0.4.0",
"bundled": true,
"dev": true,
"optional": true
},
"aws-sign2": {
"version": "0.6.0",
"bundled": true,
"dev": true,
"optional": true
},
"aws4": {
"version": "1.6.0",
"bundled": true,
"dev": true,
"optional": true
},
"balanced-match": {
"version": "0.4.2",
"bundled": true,
"dev": true
},
"bcrypt-pbkdf": {
"version": "1.0.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"tweetnacl": "0.14.5"
}
},
"block-stream": {
"version": "0.0.9",
"bundled": true,
"dev": true,
"requires": {
"inherits": "2.0.3"
}
},
"boom": {
"version": "2.10.1",
"bundled": true,
"dev": true,
"requires": {
"hoek": "2.16.3"
}
},
"brace-expansion": {
"version": "1.1.7",
"bundled": true,
"dev": true,
"requires": {
"balanced-match": "0.4.2",
"concat-map": "0.0.1"
}
},
"buffer-shims": {
"version": "1.0.0",
"bundled": true,
"dev": true
},
"caseless": {
"version": "0.12.0",
"bundled": true,
"dev": true,
"optional": true
},
"co": {
"version": "4.6.0",
"bundled": true,
"dev": true,
"optional": true
},
"code-point-at": {
"version": "1.1.0",
"bundled": true,
"dev": true
},
"combined-stream": {
"version": "1.0.5",
"bundled": true,
"dev": true,
"requires": {
"delayed-stream": "1.0.0"
}
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
"dev": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
"dev": true
},
"core-util-is": {
"version": "1.0.2",
"bundled": true,
"dev": true
},
"cryptiles": {
"version": "2.0.5",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"boom": "2.10.1"
}
},
"dashdash": {
"version": "1.14.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"assert-plus": "1.0.0"
},
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true
}
}
},
"debug": {
"version": "2.6.8",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"ms": "2.0.0"
}
},
"deep-extend": {
"version": "0.4.2",
"bundled": true,
"dev": true,
"optional": true
},
"delayed-stream": {
"version": "1.0.0",
"bundled": true,
"dev": true
},
"delegates": {
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true
},
"ecc-jsbn": {
"version": "0.1.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"jsbn": "0.1.1"
}
},
"extend": {
"version": "3.0.1",
"bundled": true,
"dev": true,
"optional": true
},
"extsprintf": {
"version": "1.0.2",
"bundled": true,
"dev": true
},
"forever-agent": {
"version": "0.6.1",
"bundled": true,
"dev": true,
"optional": true
},
"form-data": {
"version": "2.1.4",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"asynckit": "0.4.0",
"combined-stream": "1.0.5",
"mime-types": "2.1.15"
}
},
"fs.realpath": {
"version": "1.0.0",
"bundled": true,
"dev": true
},
"fstream": {
"version": "1.0.11",
"bundled": true,
"dev": true,
"requires": {
"graceful-fs": "4.1.11",
"inherits": "2.0.3",
"mkdirp": "0.5.1",
"rimraf": "2.6.1"
}
},
"fstream-ignore": {
"version": "1.0.5",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"fstream": "1.0.11",
"inherits": "2.0.3",
"minimatch": "3.0.4"
}
},
"gauge": {
"version": "2.7.4",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"aproba": "1.1.1",
"console-control-strings": "1.1.0",
"has-unicode": "2.0.1",
"object-assign": "4.1.1",
"signal-exit": "3.0.2",
"string-width": "1.0.2",
"strip-ansi": "3.0.1",
"wide-align": "1.1.2"
}
},
"getpass": {
"version": "0.1.7",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"assert-plus": "1.0.0"
},
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true
}
}
},
"glob": {
"version": "7.1.2",
"bundled": true,
"dev": true,
"requires": {
"fs.realpath": "1.0.0",
"inflight": "1.0.6",
"inherits": "2.0.3",
"minimatch": "3.0.4",
"once": "1.4.0",
"path-is-absolute": "1.0.1"
}
},
"graceful-fs": {
"version": "4.1.11",
"bundled": true,
"dev": true
},
"har-schema": {
"version": "1.0.5",
"bundled": true,
"dev": true,
"optional": true
},
"har-validator": {
"version": "4.2.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"ajv": "4.11.8",
"har-schema": "1.0.5"
}
},
"has-unicode": {
"version": "2.0.1",
"bundled": true,
"dev": true,
"optional": true
},
"hawk": {
"version": "3.1.3",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"boom": "2.10.1",
"cryptiles": "2.0.5",
"hoek": "2.16.3",
"sntp": "1.0.9"
}
},
"hoek": {
"version": "2.16.3",
"bundled": true,
"dev": true
},
"http-signature": {
"version": "1.1.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"assert-plus": "0.2.0",
"jsprim": "1.4.0",
"sshpk": "1.13.0"
}
},
"inflight": {
"version": "1.0.6",
"bundled": true,
"dev": true,
"requires": {
"once": "1.4.0",
"wrappy": "1.0.2"
}
},
"inherits": {
"version": "2.0.3",
"bundled": true,
"dev": true
},
"ini": {
"version": "1.3.4",
"bundled": true,
"dev": true,
"optional": true
},
"is-fullwidth-code-point": {
"version": "1.0.0",
"bundled": true,
"dev": true,
"requires": {
"number-is-nan": "1.0.1"
}
},
"is-typedarray": {
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true
},
"isarray": {
"version": "1.0.0",
"bundled": true,
"dev": true
},
"isstream": {
"version": "0.1.2",
"bundled": true,
"dev": true,
"optional": true
},
"jodid25519": {
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"jsbn": "0.1.1"
}
},
"jsbn": {
"version": "0.1.1",
"bundled": true,
"dev": true,
"optional": true
},
"json-schema": {
"version": "0.2.3",
"bundled": true,
"dev": true,
"optional": true
},
"json-stable-stringify": {
"version": "1.0.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"jsonify": "0.0.0"
}
},
"json-stringify-safe": {
"version": "5.0.1",
"bundled": true,
"dev": true,
"optional": true
},
"jsonify": {
"version": "0.0.0",
"bundled": true,
"dev": true,
"optional": true
},
"jsprim": {
"version": "1.4.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"assert-plus": "1.0.0",
"extsprintf": "1.0.2",
"json-schema": "0.2.3",
"verror": "1.3.6"
},
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true
}
}
},
"mime-db": {
"version": "1.27.0",
"bundled": true,
"dev": true
},
"mime-types": {
"version": "2.1.15",
"bundled": true,
"dev": true,
"requires": {
"mime-db": "1.27.0"
}
},
"minimatch": {
"version": "3.0.4",
"bundled": true,
"dev": true,
"requires": {
"brace-expansion": "1.1.7"
}
},
"minimist": {
"version": "0.0.8",
"bundled": true,
"dev": true
},
"mkdirp": {
"version": "0.5.1",
"bundled": true,
"dev": true,
"requires": {
"minimist": "0.0.8"
}
},
"ms": {
"version": "2.0.0",
"bundled": true,
"dev": true,
"optional": true
},
"node-pre-gyp": {
"version": "0.6.36",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"mkdirp": "0.5.1",
"nopt": "4.0.1",
"npmlog": "4.1.0",
"rc": "1.2.1",
"request": "2.81.0",
"rimraf": "2.6.1",
"semver": "5.3.0",
"tar": "2.2.1",
"tar-pack": "3.4.0"
}
},
"nopt": {
"version": "4.0.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"abbrev": "1.1.0",
"osenv": "0.1.4"
}
},
"npmlog": {
"version": "4.1.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"are-we-there-yet": "1.1.4",
"console-control-strings": "1.1.0",
"gauge": "2.7.4",
"set-blocking": "2.0.0"
}
},
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
"dev": true
},
"oauth-sign": {
"version": "0.8.2",
"bundled": true,
"dev": true,
"optional": true
},
"object-assign": {
"version": "4.1.1",
"bundled": true,
"dev": true,
"optional": true
},
"once": {
"version": "1.4.0",
"bundled": true,
"dev": true,
"requires": {
"wrappy": "1.0.2"
}
},
"os-homedir": {
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true
},
"os-tmpdir": {
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true
},
"osenv": {
"version": "0.1.4",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"os-homedir": "1.0.2",
"os-tmpdir": "1.0.2"
}
},
"path-is-absolute": {
"version": "1.0.1",
"bundled": true,
"dev": true
},
"performance-now": {
"version": "0.2.0",
"bundled": true,
"dev": true,
"optional": true
},
"process-nextick-args": {
"version": "1.0.7",
"bundled": true,
"dev": true
},
"punycode": {
"version": "1.4.1",
"bundled": true,
"dev": true,
"optional": true
},
"qs": {
"version": "6.4.0",
"bundled": true,
"dev": true,
"optional": true
},
"rc": {
"version": "1.2.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"deep-extend": "0.4.2",
"ini": "1.3.4",
"minimist": "1.2.0",
"strip-json-comments": "2.0.1"
},
"dependencies": {
"minimist": {
"version": "1.2.0",
"bundled": true,
"dev": true,
"optional": true
}
}
},
"readable-stream": {
"version": "2.2.9",
"bundled": true,
"dev": true,
"requires": {
"buffer-shims": "1.0.0",
"core-util-is": "1.0.2",
"inherits": "2.0.3",
"isarray": "1.0.0",
"process-nextick-args": "1.0.7",
"string_decoder": "1.0.1",
"util-deprecate": "1.0.2"
}
},
"request": {
"version": "2.81.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"aws-sign2": "0.6.0",
"aws4": "1.6.0",
"caseless": "0.12.0",
"combined-stream": "1.0.5",
"extend": "3.0.1",
"forever-agent": "0.6.1",
"form-data": "2.1.4",
"har-validator": "4.2.1",
"hawk": "3.1.3",
"http-signature": "1.1.1",
"is-typedarray": "1.0.0",
"isstream": "0.1.2",
"json-stringify-safe": "5.0.1",
"mime-types": "2.1.15",
"oauth-sign": "0.8.2",
"performance-now": "0.2.0",
"qs": "6.4.0",
"safe-buffer": "5.0.1",
"stringstream": "0.0.5",
"tough-cookie": "2.3.2",
"tunnel-agent": "0.6.0",
"uuid": "3.0.1"
}
},
"rimraf": {
"version": "2.6.1",
"bundled": true,
"dev": true,
"requires": {
"glob": "7.1.2"
}
},
"safe-buffer": {
"version": "5.0.1",
"bundled": true,
"dev": true
},
"semver": {
"version": "5.3.0",
"bundled": true,
"dev": true,
"optional": true
},
"set-blocking": {
"version": "2.0.0",
"bundled": true,
"dev": true,
"optional": true
},
"signal-exit": {
"version": "3.0.2",
"bundled": true,
"dev": true,
"optional": true
},
"sntp": {
"version": "1.0.9",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"hoek": "2.16.3"
}
},
"sshpk": {
"version": "1.13.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"asn1": "0.2.3",
"assert-plus": "1.0.0",
"bcrypt-pbkdf": "1.0.1",
"dashdash": "1.14.1",
"ecc-jsbn": "0.1.1",
"getpass": "0.1.7",
"jodid25519": "1.0.2",
"jsbn": "0.1.1",
"tweetnacl": "0.14.5"
},
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true
}
}
},
"string-width": {
"version": "1.0.2",
"bundled": true,
"dev": true,
"requires": {
"code-point-at": "1.1.0",
"is-fullwidth-code-point": "1.0.0",
"strip-ansi": "3.0.1"
}
},
"string_decoder": {
"version": "1.0.1",
"bundled": true,
"dev": true,
"requires": {
"safe-buffer": "5.0.1"
}
},
"stringstream": {
"version": "0.0.5",
"bundled": true,
"dev": true,
"optional": true
},
"strip-ansi": {
"version": "3.0.1",
"bundled": true,
"dev": true,
"requires": {
"ansi-regex": "2.1.1"
}
},
"strip-json-comments": {
"version": "2.0.1",
"bundled": true,
"dev": true,
"optional": true
},
"tar": {
"version": "2.2.1",
"bundled": true,
"dev": true,
"requires": {
"block-stream": "0.0.9",
"fstream": "1.0.11",
"inherits": "2.0.3"
}
},
"tar-pack": {
"version": "3.4.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"debug": "2.6.8",
"fstream": "1.0.11",
"fstream-ignore": "1.0.5",
"once": "1.4.0",
"readable-stream": "2.2.9",
"rimraf": "2.6.1",
"tar": "2.2.1",
"uid-number": "0.0.6"
}
},
"tough-cookie": {
"version": "2.3.2",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"punycode": "1.4.1"
}
},
"tunnel-agent": {
"version": "0.6.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"safe-buffer": "5.0.1"
}
},
"tweetnacl": {
"version": "0.14.5",
"bundled": true,
"dev": true,
"optional": true
},
"uid-number": {
"version": "0.0.6",
"bundled": true,
"dev": true,
"optional": true
},
"util-deprecate": {
"version": "1.0.2",
"bundled": true,
"dev": true
},
"uuid": {
"version": "3.0.1",
"bundled": true,
"dev": true,
"optional": true
},
"verror": {
"version": "1.3.6",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"extsprintf": "1.0.2"
}
},
"wide-align": {
"version": "1.1.2",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"string-width": "1.0.2"
}
},
"wrappy": {
"version": "1.0.2",
"bundled": true,
"dev": true
}
}
},
"function-bind": { "function-bind": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz",
@@ -2880,6 +3780,13 @@
"integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=", "integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=",
"dev": true "dev": true
}, },
"nan": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.7.0.tgz",
"integrity": "sha1-2Vv3IeyHfgjbJ27T/G63j5CDrUY=",
"dev": true,
"optional": true
},
"natural-compare": { "natural-compare": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@@ -3961,15 +4868,6 @@
"duplexer": "0.1.1" "duplexer": "0.1.1"
} }
}, },
"string_decoder": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
"dev": true,
"requires": {
"safe-buffer": "5.1.1"
}
},
"string-width": { "string-width": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
@@ -3991,6 +4889,15 @@
"resolved": "https://registry.npmjs.org/string.prototype.codepointat/-/string.prototype.codepointat-0.2.0.tgz", "resolved": "https://registry.npmjs.org/string.prototype.codepointat/-/string.prototype.codepointat-0.2.0.tgz",
"integrity": "sha1-aybpvTr8qnvjtCabUm3huCAArHg=" "integrity": "sha1-aybpvTr8qnvjtCabUm3huCAArHg="
}, },
"string_decoder": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
"dev": true,
"requires": {
"safe-buffer": "5.1.1"
}
},
"strip-ansi": { "strip-ansi": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",

View File

@@ -1,6 +1,6 @@
{ {
"name": "yjs", "name": "yjs",
"version": "13.0.0-24", "version": "13.0.0-32",
"description": "A framework for real-time p2p shared editing on any data", "description": "A framework for real-time p2p shared editing on any data",
"main": "./y.node.js", "main": "./y.node.js",
"browser": "./y.js", "browser": "./y.js",

View File

@@ -5,9 +5,13 @@ import commonjs from 'rollup-plugin-commonjs'
var pkg = require('./package.json') var pkg = require('./package.json')
export default { export default {
entry: 'src/Y.js', input: 'src/Y.js',
moduleName: 'Y', name: 'Y',
format: 'umd', sourcemap: true,
output: {
file: 'y.js',
format: 'umd'
},
plugins: [ plugins: [
nodeResolve({ nodeResolve({
main: true, main: true,
@@ -32,8 +36,6 @@ export default {
} }
}) })
], ],
dest: 'y.js',
sourceMap: true,
banner: ` banner: `
/** /**
* ${pkg.name} - ${pkg.description} * ${pkg.name} - ${pkg.description}

View File

@@ -3,9 +3,13 @@ import commonjs from 'rollup-plugin-commonjs'
var pkg = require('./package.json') var pkg = require('./package.json')
export default { export default {
entry: 'src/y-dist.cjs.js', input: 'src/y-dist.cjs.js',
moduleName: 'Y', nameame: 'Y',
format: 'cjs', sourcemap: true,
output: {
file: 'y.node.js',
format: 'cjs'
},
plugins: [ plugins: [
nodeResolve({ nodeResolve({
main: true, main: true,
@@ -14,8 +18,6 @@ export default {
}), }),
commonjs() commonjs()
], ],
dest: 'y.node.js',
sourceMap: true,
banner: ` banner: `
/** /**
* ${pkg.name} - ${pkg.description} * ${pkg.name} - ${pkg.description}

View File

@@ -3,9 +3,13 @@ import commonjs from 'rollup-plugin-commonjs'
import multiEntry from 'rollup-plugin-multi-entry' import multiEntry from 'rollup-plugin-multi-entry'
export default { export default {
entry: 'test/y-xml.tests.js', input: 'test/y-xml.tests.js',
moduleName: 'y-tests', name: 'y-tests',
format: 'umd', sourcemap: true,
output: {
file: 'y.test.js',
format: 'umd'
},
plugins: [ plugins: [
nodeResolve({ nodeResolve({
main: true, main: true,
@@ -14,7 +18,5 @@ export default {
}), }),
commonjs(), commonjs(),
multiEntry() multiEntry()
], ]
dest: 'y.test.js',
sourceMap: true
} }

View File

@@ -20,7 +20,13 @@ export default class ItemJSON extends Item {
this._content = new Array(len) this._content = new Array(len)
for (let i = 0; i < len; i++) { for (let i = 0; i < len; i++) {
const ctnt = decoder.readVarString() const ctnt = decoder.readVarString()
this._content[i] = JSON.parse(ctnt) let parsed
if (ctnt === 'undefined') {
parsed = undefined
} else {
parsed = JSON.parse(ctnt)
}
this._content[i] = parsed
} }
return missing return missing
} }
@@ -29,13 +35,20 @@ export default class ItemJSON extends Item {
let len = this._content.length let len = this._content.length
encoder.writeVarUint(len) encoder.writeVarUint(len)
for (let i = 0; i < len; i++) { for (let i = 0; i < len; i++) {
encoder.writeVarString(JSON.stringify(this._content[i])) let encoded
let content = this._content[i]
if (content === undefined) {
encoded = 'undefined'
} else {
encoded = JSON.stringify(content)
}
encoder.writeVarString(encoded)
} }
} }
_logString () { _logString () {
const left = this._left !== null ? this._left._lastId : null const left = this._left !== null ? this._left._lastId : null
const origin = this._origin !== null ? this._origin._lastId : null const origin = this._origin !== null ? this._origin._lastId : null
return `ItemJSON(id:${logID(this._id)},content:${JSON.stringify(this._content)},left:${logID(left)},origin:${logID(origin)},right:${logID(this._right)},parent:${logID(this._parent)},parentSub:${logID(this._parentSub)})` return `ItemJSON(id:${logID(this._id)},content:${JSON.stringify(this._content)},left:${logID(left)},origin:${logID(origin)},right:${logID(this._right)},parent:${logID(this._parent)},parentSub:${this._parentSub})`
} }
_splitAt (y, diff) { _splitAt (y, diff) {
if (diff === 0) { if (diff === 0) {

View File

@@ -26,7 +26,7 @@ export default class ItemString extends Item {
_logString () { _logString () {
const left = this._left !== null ? this._left._lastId : null const left = this._left !== null ? this._left._lastId : null
const origin = this._origin !== null ? this._origin._lastId : null const origin = this._origin !== null ? this._origin._lastId : null
return `ItemJSON(id:${logID(this._id)},content:${JSON.stringify(this._content)},left:${logID(left)},origin:${logID(origin)},right:${logID(this._right)},parent:${logID(this._parent)},parentSub:${logID(this._parentSub)})` return `ItemJSON(id:${logID(this._id)},content:${JSON.stringify(this._content)},left:${logID(left)},origin:${logID(origin)},right:${logID(this._right)},parent:${logID(this._parent)},parentSub:${this._parentSub})`
} }
_splitAt (y, diff) { _splitAt (y, diff) {
if (diff === 0) { if (diff === 0) {

View File

@@ -39,9 +39,35 @@ export default class Type extends Item {
this._eventHandler = new EventHandler() this._eventHandler = new EventHandler()
this._deepEventHandler = new EventHandler() this._deepEventHandler = new EventHandler()
} }
_callEventHandler (event) { getPathTo (type) {
const changedParentTypes = this._y._transaction.changedParentTypes if (type === this) {
this._eventHandler.callEventListeners(event) return []
}
const path = []
const y = this._y
while (type._parent !== this && this._parent !== y) {
let parent = type._parent
if (type._parentSub !== null) {
path.push(type._parentSub)
} else {
// parent is array-ish
for (let [i, child] of parent) {
if (child === type) {
path.push(i)
break
}
}
}
type = parent
}
if (this._parent !== this) {
throw new Error('The type is not a child of this node')
}
return path
}
_callEventHandler (transaction, event) {
const changedParentTypes = transaction.changedParentTypes
this._eventHandler.callEventListeners(transaction, event)
let type = this let type = this
while (type !== this._y) { while (type !== this._y) {
let events = changedParentTypes.get(type) let events = changedParentTypes.get(type)

View File

@@ -1,5 +1,6 @@
import Type from '../Struct/Type.js' import Type from '../Struct/Type.js'
import ItemJSON from '../Struct/ItemJSON.js' import ItemJSON from '../Struct/ItemJSON.js'
import ItemString from '../Struct/ItemString.js'
import { logID } from '../MessageHandler/messageToString.js' import { logID } from '../MessageHandler/messageToString.js'
import YEvent from '../Util/YEvent.js' import YEvent from '../Util/YEvent.js'
@@ -11,15 +12,19 @@ class YArrayEvent extends YEvent {
} }
export default class YArray extends Type { export default class YArray extends Type {
_callObserver (parentSubs, remote) { _callObserver (transaction, parentSubs, remote) {
this._callEventHandler(new YArrayEvent(this, remote)) this._callEventHandler(transaction, new YArrayEvent(this, remote))
} }
get (pos) { get (pos) {
let n = this._start let n = this._start
while (n !== null) { while (n !== null) {
if (!n._deleted) { if (!n._deleted) {
if (pos < n._length) { if (pos < n._length) {
return n._content[n._length - pos] if (n.constructor === ItemJSON || n.constructor === ItemString) {
return n._content[pos]
} else {
return n
}
} }
pos -= n._length pos -= n._length
} }
@@ -212,6 +217,6 @@ export default class YArray extends Type {
_logString () { _logString () {
const left = this._left !== null ? this._left._lastId : null const left = this._left !== null ? this._left._lastId : null
const origin = this._origin !== null ? this._origin._lastId : null const origin = this._origin !== null ? this._origin._lastId : null
return `YArray(id:${logID(this._id)},start:${logID(this._start)},left:${logID(left)},origin:${logID(origin)},right:${logID(this._right)},parent:${logID(this._parent)},parentSub:${logID(this._parentSub)})` return `YArray(id:${logID(this._id)},start:${logID(this._start)},left:${logID(left)},origin:${logID(origin)},right:${logID(this._right)},parent:${logID(this._parent)},parentSub:${this._parentSub})`
} }
} }

View File

@@ -13,8 +13,8 @@ class YMapEvent extends YEvent {
} }
export default class YMap extends Type { export default class YMap extends Type {
_callObserver (parentSubs, remote) { _callObserver (transaction, parentSubs, remote) {
this._callEventHandler(new YMapEvent(this, parentSubs, remote)) this._callEventHandler(transaction, new YMapEvent(this, parentSubs, remote))
} }
toJSON () { toJSON () {
const map = {} const map = {}
@@ -109,6 +109,6 @@ export default class YMap extends Type {
_logString () { _logString () {
const left = this._left !== null ? this._left._lastId : null const left = this._left !== null ? this._left._lastId : null
const origin = this._origin !== null ? this._origin._lastId : null const origin = this._origin !== null ? this._origin._lastId : null
return `YMap(id:${logID(this._id)},mapSize:${this._map.size},left:${logID(left)},origin:${logID(origin)},right:${logID(this._right)},parent:${logID(this._parent)},parentSub:${logID(this._parentSub)})` return `YMap(id:${logID(this._id)},mapSize:${this._map.size},left:${logID(left)},origin:${logID(origin)},right:${logID(this._right)},parent:${logID(this._parent)},parentSub:${this._parentSub})`
} }
} }

View File

@@ -62,6 +62,6 @@ export default class YText extends YArray {
_logString () { _logString () {
const left = this._left !== null ? this._left._lastId : null const left = this._left !== null ? this._left._lastId : null
const origin = this._origin !== null ? this._origin._lastId : null const origin = this._origin !== null ? this._origin._lastId : null
return `YText(id:${logID(this._id)},start:${logID(this._start)},left:${logID(left)},origin:${logID(origin)},right:${logID(this._right)},parent:${logID(this._parent)},parentSub:${logID(this._parentSub)})` return `YText(id:${logID(this._id)},start:${logID(this._start)},left:${logID(left)},origin:${logID(origin)},right:${logID(this._right)},parent:${logID(this._parent)},parentSub:${this._parentSub})`
} }
} }

View File

@@ -5,7 +5,7 @@ import YMap from '../YMap.js'
import YXmlFragment from './YXmlFragment.js' import YXmlFragment from './YXmlFragment.js'
export default class YXmlElement extends YXmlFragment { export default class YXmlElement extends YXmlFragment {
constructor (arg1, arg2) { constructor (arg1, arg2, _document) {
super() super()
this.nodeName = null this.nodeName = null
this._scrollElement = null this._scrollElement = null
@@ -13,7 +13,7 @@ export default class YXmlElement extends YXmlFragment {
this.nodeName = arg1.toUpperCase() this.nodeName = arg1.toUpperCase()
} else if (arg1 != null && arg1.nodeType != null && arg1.nodeType === arg1.ELEMENT_NODE) { } else if (arg1 != null && arg1.nodeType != null && arg1.nodeType === arg1.ELEMENT_NODE) {
this.nodeName = arg1.nodeName this.nodeName = arg1.nodeName
this._setDom(arg1) this._setDom(arg1, _document)
} else { } else {
this.nodeName = 'UNDEFINED' this.nodeName = 'UNDEFINED'
} }
@@ -26,14 +26,12 @@ export default class YXmlElement extends YXmlFragment {
struct.nodeName = this.nodeName struct.nodeName = this.nodeName
return struct return struct
} }
_setDom (dom) { _setDom (dom, _document) {
if (this._dom != null) { if (this._dom != null) {
throw new Error('Only call this method if you know what you are doing ;)') throw new Error('Only call this method if you know what you are doing ;)')
} else if (dom._yxml != null) { // TODO do i need to check this? - no.. but for dev purps.. } else if (dom._yxml != null) { // TODO do i need to check this? - no.. but for dev purps..
throw new Error('Already bound to an YXml type') throw new Error('Already bound to an YXml type')
} else { } else {
this._dom = dom
dom._yxml = this
// tag is already set in constructor // tag is already set in constructor
// set attributes // set attributes
let attrNames = [] let attrNames = []
@@ -46,8 +44,8 @@ export default class YXmlElement extends YXmlFragment {
let attrValue = dom.getAttribute(attrName) let attrValue = dom.getAttribute(attrName)
this.setAttribute(attrName, attrValue) this.setAttribute(attrName, attrValue)
} }
this.insertDomElements(0, Array.prototype.slice.call(dom.childNodes)) this.insertDomElements(0, Array.prototype.slice.call(dom.childNodes), _document)
this._bindToDom(dom) this._bindToDom(dom, _document)
return dom return dom
} }
} }
@@ -115,7 +113,6 @@ export default class YXmlElement extends YXmlFragment {
let dom = this._dom let dom = this._dom
if (dom == null) { if (dom == null) {
dom = _document.createElement(this.nodeName) dom = _document.createElement(this.nodeName)
this._dom = dom
dom._yxml = this dom._yxml = this
let attrs = this.getAttributes() let attrs = this.getAttributes()
for (let key in attrs) { for (let key in attrs) {
@@ -124,7 +121,7 @@ export default class YXmlElement extends YXmlFragment {
this.forEach(yxml => { this.forEach(yxml => {
dom.appendChild(yxml.getDom(_document)) dom.appendChild(yxml.getDom(_document))
}) })
this._bindToDom(dom) this._bindToDom(dom, _document)
} }
return dom return dom
} }

View File

@@ -9,7 +9,7 @@ import YXmlEvent from './YXmlEvent.js'
import { logID } from '../../MessageHandler/messageToString.js' import { logID } from '../../MessageHandler/messageToString.js'
import diff from 'fast-diff' import diff from 'fast-diff'
function domToYXml (parent, doms) { function domToYXml (parent, doms, _document) {
const types = [] const types = []
doms.forEach(d => { doms.forEach(d => {
if (d._yxml != null && d._yxml !== false) { if (d._yxml != null && d._yxml !== false) {
@@ -20,7 +20,7 @@ function domToYXml (parent, doms) {
if (d.nodeType === d.TEXT_NODE) { if (d.nodeType === d.TEXT_NODE) {
type = new YXmlText(d) type = new YXmlText(d)
} else if (d.nodeType === d.ELEMENT_NODE) { } else if (d.nodeType === d.ELEMENT_NODE) {
type = new YXmlFragment._YXmlElement(d, parent._domFilter) type = new YXmlFragment._YXmlElement(d, parent._domFilter, _document)
} else { } else {
throw new Error('Unsupported node!') throw new Error('Unsupported node!')
} }
@@ -33,6 +33,54 @@ function domToYXml (parent, doms) {
return types return types
} }
class YXmlTreeWalker {
constructor (root, f) {
this._filter = f || (() => true)
this._root = root
this._currentNode = root
this._firstCall = true
}
[Symbol.iterator] () {
return this
}
next () {
let n = this._currentNode
if (this._firstCall) {
this._firstCall = false
if (!n._deleted && this._filter(n)) {
return { value: n, done: false }
}
}
do {
if (!n._deleted && n.constructor === YXmlFragment._YXmlElement && n._start !== null) {
// walk down in the tree
n = n._start
} else {
// walk right or up in the tree
while (n !== this._root) {
if (n._right !== null) {
n = n._right
break
}
n = n._parent
}
if (n === this._root) {
n = null
}
}
if (n === this._root) {
break
}
} while (n !== null && (n._deleted || !this._filter(n)))
this._currentNode = n
if (n === null) {
return { done: true }
} else {
return { value: n, done: false }
}
}
}
export default class YXmlFragment extends YArray { export default class YXmlFragment extends YArray {
constructor () { constructor () {
super() super()
@@ -50,11 +98,40 @@ export default class YXmlFragment extends YArray {
} catch (e) { } catch (e) {
console.error(e) console.error(e)
} }
this._domObserver.takeRecords() if (this._domObserver !== null) {
this._domObserver.takeRecords()
}
token = true token = true
} }
} }
} }
createTreeWalker (filter) {
return new YXmlTreeWalker(this, filter)
}
/**
* Retrieve first element that matches *query*
* Similar to DOM's querySelector, but only accepts a subset of its queries
*
* Query support:
* - tagname
* TODO:
* - id
* - attribute
*/
querySelector (query) {
query = query.toUpperCase()
const iterator = new YXmlTreeWalker(this, element => element.nodeName === query)
const next = iterator.next()
if (next.done) {
return null
} else {
return next.value
}
}
querySelectorAll (query) {
query = query.toUpperCase()
return Array.from(new YXmlTreeWalker(this, element => element.nodeName === query))
}
enableSmartScrolling (scrollElement) { enableSmartScrolling (scrollElement) {
this._scrollElement = scrollElement this._scrollElement = scrollElement
this.forEach(xml => { this.forEach(xml => {
@@ -67,8 +144,8 @@ export default class YXmlFragment extends YArray {
xml.setDomFilter(f) xml.setDomFilter(f)
}) })
} }
_callObserver (parentSubs, remote) { _callObserver (transaction, parentSubs, remote) {
this._callEventHandler(new YXmlEvent(this, parentSubs, remote)) this._callEventHandler(transaction, new YXmlEvent(this, parentSubs, remote))
} }
toString () { toString () {
return this.map(xml => xml.toString()).join('') return this.map(xml => xml.toString()).join('')
@@ -87,113 +164,116 @@ export default class YXmlFragment extends YArray {
this._dom = null this._dom = null
} }
} }
insertDomElementsAfter (prev, doms) { insertDomElementsAfter (prev, doms, _document) {
const types = domToYXml(this, doms) const types = domToYXml(this, doms, _document)
this.insertAfter(prev, types) this.insertAfter(prev, types)
return types return types
} }
insertDomElements (pos, doms) { insertDomElements (pos, doms, _document) {
const types = domToYXml(this, doms) const types = domToYXml(this, doms, _document)
this.insert(pos, types) this.insert(pos, types)
return types return types
} }
getDom () { getDom () {
return this._dom return this._dom
} }
bindToDom (dom) { bindToDom (dom, _document) {
if (this._dom != null) { if (this._dom != null) {
this._unbindFromDom() this._unbindFromDom()
} }
if (dom._yxml != null) { if (dom._yxml != null) {
dom._yxml._unbindFromDom() dom._yxml._unbindFromDom()
} }
if (MutationObserver == null) {
throw new Error('Not able to bind to a DOM element, because MutationObserver is not available!')
}
dom.innerHTML = '' dom.innerHTML = ''
this._dom = dom
dom._yxml = this
this.forEach(t => { this.forEach(t => {
dom.insertBefore(t.getDom(), null) dom.insertBefore(t.getDom(_document), null)
}) })
this._bindToDom(dom) this._bindToDom(dom, _document)
} }
// binds to a dom element // binds to a dom element
// Only call if dom and YXml are isomorph // Only call if dom and YXml are isomorph
_bindToDom (dom) { _bindToDom (dom, _document) {
if (this._parent === null || this._parent._dom != null || typeof MutationObserver === 'undefined') { _document = _document || document
// only bind if parent did not already bind this._dom = dom
dom._yxml = this
// TODO: refine this..
if ((this.constructor !== YXmlFragment && this._parent !== this._y) || this._parent === null) {
// TODO: only top level YXmlFragment can bind. Also allow YXmlElements..
return return
} }
this._y.on('beforeTransaction', () => {
this._domObserverListener(this._domObserver.takeRecords())
})
this._y.on('beforeTransaction', beforeTransactionSelectionFixer) this._y.on('beforeTransaction', beforeTransactionSelectionFixer)
this._y.on('afterTransaction', afterTransactionSelectionFixer) this._y.on('afterTransaction', afterTransactionSelectionFixer)
// Apply Y.Xml events to dom // Apply Y.Xml events to dom
this.observeDeep(reflectChangesOnDom.bind(this)) this.observeDeep(events => {
reflectChangesOnDom.call(this, events, _document)
})
// Apply Dom changes on Y.Xml // Apply Dom changes on Y.Xml
this._domObserverListener = mutations => { if (typeof MutationObserver !== 'undefined') {
this._mutualExclude(() => { this._y.on('beforeTransaction', () => {
this._y.transact(() => { this._domObserverListener(this._domObserver.takeRecords())
let diffChildren = new Set() })
mutations.forEach(mutation => { this._domObserverListener = mutations => {
const dom = mutation.target this._mutualExclude(() => {
const yxml = dom._yxml this._y.transact(() => {
if (yxml == null) { let diffChildren = new Set()
// dom element is filtered mutations.forEach(mutation => {
return const dom = mutation.target
} const yxml = dom._yxml
switch (mutation.type) { if (yxml == null) {
case 'characterData': // dom element is filtered
var diffs = diff(yxml.toString(), dom.nodeValue) return
var pos = 0 }
for (var i = 0; i < diffs.length; i++) { switch (mutation.type) {
var d = diffs[i] case 'characterData':
if (d[0] === 0) { // EQUAL var diffs = diff(yxml.toString(), dom.nodeValue)
pos += d[1].length var pos = 0
} else if (d[0] === -1) { // DELETE for (var i = 0; i < diffs.length; i++) {
yxml.delete(pos, d[1].length) var d = diffs[i]
} else { // INSERT if (d[0] === 0) { // EQUAL
yxml.insert(pos, d[1]) pos += d[1].length
pos += d[1].length } else if (d[0] === -1) { // DELETE
} yxml.delete(pos, d[1].length)
} } else { // INSERT
break yxml.insert(pos, d[1])
case 'attributes': pos += d[1].length
let name = mutation.attributeName
// check if filter accepts attribute
if (this._domFilter(dom, [name]).length > 0 && this.constructor !== YXmlFragment) {
var val = dom.getAttribute(name)
if (yxml.getAttribute(name) !== val) {
if (val == null) {
yxml.removeAttribute(name)
} else {
yxml.setAttribute(name, val)
} }
} }
} break
break case 'attributes':
case 'childList': let name = mutation.attributeName
diffChildren.add(mutation.target) // check if filter accepts attribute
break if (this._domFilter(dom, [name]).length > 0 && yxml.constructor !== YXmlFragment) {
var val = dom.getAttribute(name)
if (yxml.getAttribute(name) !== val) {
if (val == null) {
yxml.removeAttribute(name)
} else {
yxml.setAttribute(name, val)
}
}
}
break
case 'childList':
diffChildren.add(mutation.target)
break
}
})
for (let dom of diffChildren) {
if (dom._yxml != null && dom._yxml !== false) {
applyChangesFromDom(dom)
}
} }
}) })
for (let dom of diffChildren) {
if (dom._yxml != null) {
applyChangesFromDom(dom)
}
}
}) })
}
this._domObserver = new MutationObserver(this._domObserverListener)
this._domObserver.observe(dom, {
childList: true,
attributes: true,
characterData: true,
subtree: true
}) })
} }
this._domObserver = new MutationObserver(this._domObserverListener)
this._domObserver.observe(dom, {
childList: true,
attributes: true,
characterData: true,
subtree: true
})
return dom return dom
} }
_logString () { _logString () {

View File

@@ -7,7 +7,7 @@ let relativeSelection = null
export let beforeTransactionSelectionFixer export let beforeTransactionSelectionFixer
if (typeof getSelection !== 'undefined') { if (typeof getSelection !== 'undefined') {
beforeTransactionSelectionFixer = function _beforeTransactionSelectionFixer (y, remote) { beforeTransactionSelectionFixer = function _beforeTransactionSelectionFixer (y, transaction, remote) {
if (!remote) { if (!remote) {
return return
} }
@@ -30,7 +30,7 @@ if (typeof getSelection !== 'undefined') {
beforeTransactionSelectionFixer = function _fakeBeforeTransactionSelectionFixer () {} beforeTransactionSelectionFixer = function _fakeBeforeTransactionSelectionFixer () {}
} }
export function afterTransactionSelectionFixer (y, remote) { export function afterTransactionSelectionFixer (y, transaction, remote) {
if (relativeSelection === null || !remote) { if (relativeSelection === null || !remote) {
return return
} }

View File

@@ -135,7 +135,33 @@ export function applyChangesFromDom (dom) {
} }
} }
export function reflectChangesOnDom (events) { export function reflectChangesOnDom (events, _document) {
// Make sure that no filtered attributes are applied to the structure
// if they were, delete them
/*
events.forEach(event => {
const target = event.target
if (event.attributesChanged === undefined) {
// event.target is Y.XmlText
return
}
const keys = this._domFilter(target.nodeName, Array.from(event.attributesChanged))
if (keys === null) {
target._delete()
} else {
const removeKeys = new Set() // is a copy of event.attributesChanged
event.attributesChanged.forEach(key => { removeKeys.add(key) })
keys.forEach(key => {
// remove all accepted keys from removeKeys
removeKeys.delete(key)
})
// remove the filtered attribute
removeKeys.forEach(key => {
target.removeAttribute(key)
})
}
})
*/
this._mutualExclude(() => { this._mutualExclude(() => {
events.forEach(event => { events.forEach(event => {
const yxml = event.target const yxml = event.target
@@ -157,9 +183,9 @@ export function reflectChangesOnDom (events) {
}) })
if (event.childListChanged) { if (event.childListChanged) {
// create fragment of undeleted nodes // create fragment of undeleted nodes
const fragment = document.createDocumentFragment() const fragment = _document.createDocumentFragment()
yxml.forEach(function (t) { yxml.forEach(function (t) {
fragment.append(t.getDom()) fragment.appendChild(t.getDom(_document))
}) })
// remove remainding nodes // remove remainding nodes
let lastChild = dom.lastChild let lastChild = dom.lastChild
@@ -168,7 +194,7 @@ export function reflectChangesOnDom (events) {
lastChild = dom.lastChild lastChild = dom.lastChild
} }
// insert fragment of undeleted nodes // insert fragment of undeleted nodes
dom.append(fragment) dom.appendChild(fragment)
} }
} }
/* TODO: smartscrolling /* TODO: smartscrolling

View File

@@ -17,7 +17,7 @@ export default class EventHandler {
removeAllEventListeners () { removeAllEventListeners () {
this.eventListeners = [] this.eventListeners = []
} }
callEventListeners (event) { callEventListeners (transaction, event) {
for (var i = 0; i < this.eventListeners.length; i++) { for (var i = 0; i < this.eventListeners.length; i++) {
try { try {
const f = this.eventListeners[i] const f = this.eventListeners[i]

View File

@@ -1,15 +1,16 @@
import ID from './ID.js' import ID from './ID.js'
class ReverseOperation { class ReverseOperation {
constructor (y) { constructor (y, transaction) {
const beforeState = y._transaction.beforeState this.created = new Date()
const beforeState = transaction.beforeState
this.toState = new ID(y.userID, y.ss.getState(y.userID) - 1) this.toState = new ID(y.userID, y.ss.getState(y.userID) - 1)
if (beforeState.has(y.userID)) { if (beforeState.has(y.userID)) {
this.fromState = new ID(y.userID, beforeState.get(y.userID)) this.fromState = new ID(y.userID, beforeState.get(y.userID))
} else { } else {
this.fromState = this.toState this.fromState = this.toState
} }
this.deletedStructs = y._transaction.deletedStructs this.deletedStructs = transaction.deletedStructs
} }
} }
@@ -23,8 +24,45 @@ function isStructInScope (y, struct, scope) {
return false return false
} }
function applyReverseOperation (y, scope, reverseBuffer) {
let performedUndo = false
y.transact(() => {
while (!performedUndo && reverseBuffer.length > 0) {
let undoOp = reverseBuffer.pop()
// make sure that it is possible to iterate {from}-{to}
y.os.getItemCleanStart(undoOp.fromState)
y.os.getItemCleanEnd(undoOp.toState)
y.os.iterate(undoOp.fromState, undoOp.toState, op => {
if (!op._deleted && isStructInScope(y, op, scope)) {
performedUndo = true
op._delete(y)
}
})
for (let op of undoOp.deletedStructs) {
if (
isStructInScope(y, op, scope) &&
op._parent !== y &&
!op._parent._deleted &&
(
op._parent._id.user !== y.userID ||
op._parent._id.clock < undoOp.fromState.clock ||
op._parent._id.clock > undoOp.fromState.clock
)
) {
performedUndo = true
op = op._copy(undoOp.deletedStructs)
op._integrate(y)
}
}
}
})
return performedUndo
}
export default class UndoManager { export default class UndoManager {
constructor (scope) { constructor (scope, options = {}) {
this.options = options
options.captureTimeout = options.captureTimeout == null ? 500 : options.captureTimeout
this._undoBuffer = [] this._undoBuffer = []
this._redoBuffer = [] this._redoBuffer = []
this._scope = scope this._scope = scope
@@ -32,11 +70,18 @@ export default class UndoManager {
this._redoing = false this._redoing = false
const y = scope._y const y = scope._y
this.y = y this.y = y
y.on('afterTransaction', (y, remote) => { y.on('afterTransaction', (y, transaction, remote) => {
if (!remote && (y._transaction.beforeState.has(y.userID) || y._transaction.deletedStructs.size > 0)) { if (!remote && transaction.changedParentTypes.has(scope)) {
let reverseOperation = new ReverseOperation(y) let reverseOperation = new ReverseOperation(y, transaction)
if (!this._undoing) { if (!this._undoing) {
this._undoBuffer.push(reverseOperation) let lastUndoOp = this._undoBuffer.length > 0 ? this._undoBuffer[this._undoBuffer.length - 1] : null
if (lastUndoOp !== null && reverseOperation.created - lastUndoOp.created <= options.captureTimeout) {
lastUndoOp.created = reverseOperation.created
lastUndoOp.toState = reverseOperation.toState
reverseOperation.deletedStructs.forEach(lastUndoOp.deletedStructs.add, lastUndoOp.deletedStructs)
} else {
this._undoBuffer.push(reverseOperation)
}
if (!this._redoing) { if (!this._redoing) {
this._redoBuffer = [] this._redoBuffer = []
} }
@@ -48,45 +93,14 @@ export default class UndoManager {
} }
undo () { undo () {
this._undoing = true this._undoing = true
this._applyReverseOperation(this._undoBuffer) const performedUndo = applyReverseOperation(this.y, this._scope, this._undoBuffer)
this._undoing = false this._undoing = false
return performedUndo
} }
redo () { redo () {
this._redoing = true this._redoing = true
this._applyReverseOperation(this._redoBuffer) const performedRedo = applyReverseOperation(this.y, this._scope, this._redoBuffer)
this._redoing = false this._redoing = false
} return performedRedo
_applyReverseOperation (reverseBuffer) {
this.y.transact(() => {
let performedUndo = false
while (!performedUndo && reverseBuffer.length > 0) {
let undoOp = reverseBuffer.pop()
// make sure that it is possible to iterate {from}-{to}
this.y.os.getItemCleanStart(undoOp.fromState)
this.y.os.getItemCleanEnd(undoOp.toState)
this.y.os.iterate(undoOp.fromState, undoOp.toState, op => {
if (!op._deleted && isStructInScope(this.y, op, this._scope)) {
performedUndo = true
op._delete(this.y)
}
})
for (let op of undoOp.deletedStructs) {
if (
isStructInScope(this.y, op, this._scope) &&
op._parent !== this.y &&
!op._parent._deleted &&
(
op._parent._id.user !== this.y.userID ||
op._parent._id.clock < undoOp.fromState.clock ||
op._parent._id.clock > undoOp.fromState.clock
)
) {
performedUndo = true
op = op._copy(undoOp.deletedStructs)
op._integrate(this.y)
}
}
}
})
} }
} }

View File

@@ -2,32 +2,27 @@
export default class YEvent { export default class YEvent {
constructor (target) { constructor (target) {
this.target = target this.target = target
this._path = null this.currentTarget = target
} }
get path () { get path () {
if (this._path !== null) { const path = []
return this._path let type = this.target
} else { const y = type._y
const path = [] while (type !== this.currentTarget && type !== y) {
let type = this.target let parent = type._parent
const y = type._y if (type._parentSub !== null) {
while (type._parent !== y) { path.unshift(type._parentSub)
let parent = type._parent } else {
if (type._parentSub !== null) { // parent is array-ish
path.push(type._parentSub) for (let [i, child] of parent) {
} else { if (child === type) {
// parent is array-ish path.unshift(i)
for (let [i, child] of parent) { break
if (child === type) {
path.push(i)
break
}
} }
} }
type = parent
} }
this._path = path type = parent
return path
} }
return path
} }
} }

View File

@@ -15,6 +15,7 @@ import YMap from './Type/YMap.js'
import YText from './Type/YText.js' import YText from './Type/YText.js'
import { YXmlFragment, YXmlElement, YXmlText } from './Type/y-xml/y-xml.js' import { YXmlFragment, YXmlElement, YXmlText } from './Type/y-xml/y-xml.js'
import BinaryDecoder from './Binary/Decoder.js' import BinaryDecoder from './Binary/Decoder.js'
import { getRelativePosition, fromRelativePosition } from './Util/relativePosition.js'
import debug from 'debug' import debug from 'debug'
import Transaction from './Transaction.js' import Transaction from './Transaction.js'
@@ -44,8 +45,8 @@ export default class Y extends NamedEventHandler {
transact (f, remote = false) { transact (f, remote = false) {
let initialCall = this._transaction === null let initialCall = this._transaction === null
if (initialCall) { if (initialCall) {
this.emit('beforeTransaction', this, remote)
this._transaction = new Transaction(this) this._transaction = new Transaction(this)
this.emit('beforeTransaction', this, this._transaction, remote)
} }
try { try {
f(this) f(this)
@@ -53,25 +54,31 @@ export default class Y extends NamedEventHandler {
console.error(e) console.error(e)
} }
if (initialCall) { if (initialCall) {
const transaction = this._transaction
this._transaction = null
// emit change events on changed types // emit change events on changed types
this._transaction.changedTypes.forEach(function (subs, type) { transaction.changedTypes.forEach(function (subs, type) {
if (!type._deleted) { if (!type._deleted) {
type._callObserver(subs, remote) type._callObserver(transaction, subs, remote)
} }
}) })
this._transaction.changedParentTypes.forEach(function (events, type) { transaction.changedParentTypes.forEach(function (events, type) {
if (!type._deleted) { if (!type._deleted) {
events = events.filter(event => events = events
!event.target._deleted .filter(event =>
) !event.target._deleted
)
events
.forEach(event => {
event.currentTarget = type
})
// we don't have to check for events.length // we don't have to check for events.length
// because there is no way events is empty.. // because there is no way events is empty..
type._deepEventHandler.callEventListeners(events) type._deepEventHandler.callEventListeners(transaction, events)
} }
}) })
// when all changes & events are processed, emit afterTransaction event // when all changes & events are processed, emit afterTransaction event
this.emit('afterTransaction', this, remote) this.emit('afterTransaction', this, transaction, remote)
this._transaction = null
} }
} }
// fake _start for root properties (y.set('name', type)) // fake _start for root properties (y.set('name', type))
@@ -163,7 +170,9 @@ Y.XmlText = YXmlText
Y.utils = { Y.utils = {
BinaryDecoder, BinaryDecoder,
UndoManager UndoManager,
getRelativePosition,
fromRelativePosition
} }
Y.debug = debug Y.debug = debug

View File

@@ -41,6 +41,8 @@ test('basic map tests', async function map0 (t) {
test('Basic get&set of Map property (converge via sync)', async function map1 (t) { test('Basic get&set of Map property (converge via sync)', async function map1 (t) {
let { users, map0 } = await initArrays(t, { users: 2 }) let { users, map0 } = await initArrays(t, { users: 2 })
map0.set('stuff', 'stuffy') map0.set('stuff', 'stuffy')
map0.set('undefined', undefined)
map0.set('null', null)
t.compare(map0.get('stuff'), 'stuffy') t.compare(map0.get('stuff'), 'stuffy')
await flushAll(t, users) await flushAll(t, users)
@@ -48,6 +50,8 @@ test('Basic get&set of Map property (converge via sync)', async function map1 (t
for (let user of users) { for (let user of users) {
var u = user.get('map', Y.Map) var u = user.get('map', Y.Map)
t.compare(u.get('stuff'), 'stuffy') t.compare(u.get('stuff'), 'stuffy')
t.assert(u.get('undefined') === undefined, 'undefined')
t.compare(u.get('null'), null, 'null')
} }
await compareUsers(t, users) await compareUsers(t, users)
}) })
@@ -172,31 +176,36 @@ test('observePath properties', async function map10 (t) {
}) })
*/ */
/* TODO: reimplement observeDeep
test('observe deep properties', async function map11 (t) { test('observe deep properties', async function map11 (t) {
let { users, map1, map2, map3 } = await initArrays(t, { users: 4 }) let { users, map1, map2, map3 } = await initArrays(t, { users: 4 })
var _map1 = map1.set('map', new Y.Map()) var _map1 = map1.set('map', new Y.Map())
var calls = 0 var calls = 0
var dmapid var dmapid
_map1.observe(function (event) { map1.observeDeep(function (events) {
calls++ events.forEach(event => {
t.compare(event.name, 'deepmap') calls++
dmapid = event.target.opContents.deepmap t.assert(event.keysChanged.has('deepmap'))
t.assert(event.path.length === 1)
t.assert(event.path[0] === 'map')
dmapid = event.target.get('deepmap')._id
})
}) })
await flushAll(t, users) await flushAll(t, users)
await flushAll(t, users)
var _map3 = map3.get('map') var _map3 = map3.get('map')
_map3.set('deepmap', new Y.Map()) _map3.set('deepmap', new Y.Map())
await flushAll(t, users) await flushAll(t, users)
var _map2 = map2.get('map') var _map2 = map2.get('map')
_map2.set('deepmap', new Y.Map()) _map2.set('deepmap', new Y.Map())
await flushAll(t, users) await flushAll(t, users)
await flushAll(t, users)
var dmap1 = _map1.get('deepmap') var dmap1 = _map1.get('deepmap')
var dmap2 = _map2.get('deepmap') var dmap2 = _map2.get('deepmap')
var dmap3 = _map3.get('deepmap') var dmap3 = _map3.get('deepmap')
t.assert(calls > 0) t.assert(calls > 0)
t.compare(dmap1._model, dmap2._model) t.assert(dmap1._id.equals(dmap2._id))
t.compare(dmap1._model, dmap3._model) t.assert(dmap1._id.equals(dmap3._id))
t.compare(dmap1._model, dmapid) t.assert(dmap1._id.equals(dmapid))
await compareUsers(t, users) await compareUsers(t, users)
}) })
@@ -204,8 +213,10 @@ test('observes using observeDeep', async function map12 (t) {
let { users, map0 } = await initArrays(t, { users: 2 }) let { users, map0 } = await initArrays(t, { users: 2 })
var pathes = [] var pathes = []
var calls = 0 var calls = 0
map0.observeDeep(function (event) { map0.observeDeep(function (events) {
pathes.push(event.path) events.forEach(event => {
pathes.push(event.path)
})
calls++ calls++
}) })
map0.set('map', new Y.Map()) map0.set('map', new Y.Map())
@@ -215,7 +226,6 @@ test('observes using observeDeep', async function map12 (t) {
t.compare(pathes, [[], ['map'], ['map', 'array']]) t.compare(pathes, [[], ['map'], ['map', 'array']])
await compareUsers(t, users) await compareUsers(t, users)
}) })
*/
/* TODO: Test events in Y.Map /* TODO: Test events in Y.Map
function compareEvent (t, is, should) { function compareEvent (t, is, should) {

View File

@@ -287,6 +287,22 @@ test('deep element insert', async function xml16 (t) {
await compareUsers(t, users) await compareUsers(t, users)
}) })
test('treeWalker', async function xml17 (t) {
var { users, xml0 } = await initArrays(t, { users: 3 })
let paragraph1 = new Y.XmlElement('p')
let paragraph2 = new Y.XmlElement('p')
let text1 = new Y.Text('init')
let text2 = new Y.Text('text')
paragraph1.insert(0, [text1, text2])
xml0.insert(0, [paragraph1, paragraph2, new Y.XmlElement('img')])
let allParagraphs = xml0.querySelectorAll('p')
t.assert(allParagraphs.length === 2, 'found exactly two paragraphs')
t.assert(allParagraphs[0] === paragraph1, 'querySelectorAll found paragraph1')
t.assert(allParagraphs[1] === paragraph2, 'querySelectorAll found paragraph2')
t.assert(xml0.querySelector('p') === paragraph1, 'querySelector found paragraph1')
await compareUsers(t, users)
})
// TODO: move elements // TODO: move elements
var xmlTransactions = [ var xmlTransactions = [
function attributeChange (t, user, chance) { function attributeChange (t, user, chance) {

View File

@@ -150,10 +150,10 @@ export async function initArrays (t, opts) {
connector: connOpts connector: connOpts
}) })
result.users.push(y) result.users.push(y)
result['array' + i] = y.get('array', Y.Array) result['array' + i] = y.define('array', Y.Array)
result['map' + i] = y.get('map', Y.Map) result['map' + i] = y.define('map', Y.Map)
result['xml' + i] = y.get('xml', Y.XmlElement) result['xml' + i] = y.define('xml', Y.XmlElement)
y.get('xml', Y.Xml).setDomFilter(function (d, attrs) { y.get('xml').setDomFilter(function (d, attrs) {
if (d.nodeName === 'HIDDEN') { if (d.nodeName === 'HIDDEN') {
return null return null
} else { } else {
@@ -187,7 +187,7 @@ export async function flushAll (t, users) {
if (users.length === 0) { if (users.length === 0) {
return return
} }
await wait(0) await wait(10)
if (users[0].connector.testRoom != null) { if (users[0].connector.testRoom != null) {
// use flushAll method specified in Test Connector // use flushAll method specified in Test Connector
await users[0].connector.testRoom.flushAll(users) await users[0].connector.testRoom.flushAll(users)

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

633
y.node.js
View File

@@ -1,7 +1,7 @@
/** /**
* 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 v13.0.0-24 * @version v13.0.0-32
* @license MIT * @license MIT
*/ */
@@ -687,7 +687,6 @@ if (!String.fromCodePoint) {
/*! http://mths.be/codepointat v0.2.0 by @mathias */ /*! http://mths.be/codepointat v0.2.0 by @mathias */
if (!String.prototype.codePointAt) { if (!String.prototype.codePointAt) {
(function() { (function() {
'use strict'; // needed to support `apply`/`call` with `undefined`/`null`
var defineProperty = (function() { var defineProperty = (function() {
// IE 8 only supports `Object.defineProperty` on DOM elements // IE 8 only supports `Object.defineProperty` on DOM elements
try { try {
@@ -1900,7 +1899,7 @@ class EventHandler {
removeAllEventListeners () { removeAllEventListeners () {
this.eventListeners = []; this.eventListeners = [];
} }
callEventListeners (event) { callEventListeners (transaction, event) {
for (var i = 0; i < this.eventListeners.length; i++) { for (var i = 0; i < this.eventListeners.length; i++) {
try { try {
const f = this.eventListeners[i]; const f = this.eventListeners[i];
@@ -1941,9 +1940,35 @@ class Type extends Item {
this._eventHandler = new EventHandler(); this._eventHandler = new EventHandler();
this._deepEventHandler = new EventHandler(); this._deepEventHandler = new EventHandler();
} }
_callEventHandler (event) { getPathTo (type) {
const changedParentTypes = this._y._transaction.changedParentTypes; if (type === this) {
this._eventHandler.callEventListeners(event); return []
}
const path = [];
const y = this._y;
while (type._parent !== this && this._parent !== y) {
let parent = type._parent;
if (type._parentSub !== null) {
path.push(type._parentSub);
} else {
// parent is array-ish
for (let [i, child] of parent) {
if (child === type) {
path.push(i);
break
}
}
}
type = parent;
}
if (this._parent !== this) {
throw new Error('The type is not a child of this node')
}
return path
}
_callEventHandler (transaction, event) {
const changedParentTypes = transaction.changedParentTypes;
this._eventHandler.callEventListeners(transaction, event);
let type = this; let type = this;
while (type !== this._y) { while (type !== this._y) {
let events = changedParentTypes.get(type); let events = changedParentTypes.get(type);
@@ -2066,7 +2091,13 @@ class ItemJSON extends Item {
this._content = new Array(len); this._content = new Array(len);
for (let i = 0; i < len; i++) { for (let i = 0; i < len; i++) {
const ctnt = decoder.readVarString(); const ctnt = decoder.readVarString();
this._content[i] = JSON.parse(ctnt); let parsed;
if (ctnt === 'undefined') {
parsed = undefined;
} else {
parsed = JSON.parse(ctnt);
}
this._content[i] = parsed;
} }
return missing return missing
} }
@@ -2075,13 +2106,20 @@ class ItemJSON extends Item {
let len = this._content.length; let len = this._content.length;
encoder.writeVarUint(len); encoder.writeVarUint(len);
for (let i = 0; i < len; i++) { for (let i = 0; i < len; i++) {
encoder.writeVarString(JSON.stringify(this._content[i])); let encoded;
let content = this._content[i];
if (content === undefined) {
encoded = 'undefined';
} else {
encoded = JSON.stringify(content);
}
encoder.writeVarString(encoded);
} }
} }
_logString () { _logString () {
const left = this._left !== null ? this._left._lastId : null; const left = this._left !== null ? this._left._lastId : null;
const origin = this._origin !== null ? this._origin._lastId : null; const origin = this._origin !== null ? this._origin._lastId : null;
return `ItemJSON(id:${logID(this._id)},content:${JSON.stringify(this._content)},left:${logID(left)},origin:${logID(origin)},right:${logID(this._right)},parent:${logID(this._parent)},parentSub:${logID(this._parentSub)})` return `ItemJSON(id:${logID(this._id)},content:${JSON.stringify(this._content)},left:${logID(left)},origin:${logID(origin)},right:${logID(this._right)},parent:${logID(this._parent)},parentSub:${this._parentSub})`
} }
_splitAt (y, diff) { _splitAt (y, diff) {
if (diff === 0) { if (diff === 0) {
@@ -2096,36 +2134,72 @@ class ItemJSON extends Item {
} }
} }
class ItemString extends Item {
constructor () {
super();
this._content = null;
}
_copy () {
let struct = super._copy();
struct._content = this._content;
return struct
}
get _length () {
return this._content.length
}
_fromBinary (y, decoder) {
let missing = super._fromBinary(y, decoder);
this._content = decoder.readVarString();
return missing
}
_toBinary (encoder) {
super._toBinary(encoder);
encoder.writeVarString(this._content);
}
_logString () {
const left = this._left !== null ? this._left._lastId : null;
const origin = this._origin !== null ? this._origin._lastId : null;
return `ItemJSON(id:${logID(this._id)},content:${JSON.stringify(this._content)},left:${logID(left)},origin:${logID(origin)},right:${logID(this._right)},parent:${logID(this._parent)},parentSub:${this._parentSub})`
}
_splitAt (y, diff) {
if (diff === 0) {
return this
} else if (diff >= this._length) {
return this._right
}
let item = new ItemString();
item._content = this._content.slice(diff);
this._content = this._content.slice(0, diff);
splitHelper(y, this, item, diff);
return item
}
}
class YEvent { class YEvent {
constructor (target) { constructor (target) {
this.target = target; this.target = target;
this._path = null; this.currentTarget = target;
} }
get path () { get path () {
if (this._path !== null) { const path = [];
return this._path let type = this.target;
} else { const y = type._y;
const path = []; while (type !== this.currentTarget && type !== y) {
let type = this.target; let parent = type._parent;
const y = type._y; if (type._parentSub !== null) {
while (type._parent !== y) { path.unshift(type._parentSub);
let parent = type._parent; } else {
if (type._parentSub !== null) { // parent is array-ish
path.push(type._parentSub); for (let [i, child] of parent) {
} else { if (child === type) {
// parent is array-ish path.unshift(i);
for (let [i, child] of parent) { break
if (child === type) {
path.push(i);
break
}
} }
} }
type = parent;
} }
this._path = path; type = parent;
return path
} }
return path
} }
} }
@@ -2137,15 +2211,19 @@ class YArrayEvent extends YEvent {
} }
class YArray extends Type { class YArray extends Type {
_callObserver (parentSubs, remote) { _callObserver (transaction, parentSubs, remote) {
this._callEventHandler(new YArrayEvent(this, remote)); this._callEventHandler(transaction, new YArrayEvent(this, remote));
} }
get (pos) { get (pos) {
let n = this._start; let n = this._start;
while (n !== null) { while (n !== null) {
if (!n._deleted) { if (!n._deleted) {
if (pos < n._length) { if (pos < n._length) {
return n._content[n._length - pos] if (n.constructor === ItemJSON || n.constructor === ItemString) {
return n._content[pos]
} else {
return n
}
} }
pos -= n._length; pos -= n._length;
} }
@@ -2338,7 +2416,7 @@ class YArray extends Type {
_logString () { _logString () {
const left = this._left !== null ? this._left._lastId : null; const left = this._left !== null ? this._left._lastId : null;
const origin = this._origin !== null ? this._origin._lastId : null; const origin = this._origin !== null ? this._origin._lastId : null;
return `YArray(id:${logID(this._id)},start:${logID(this._start)},left:${logID(left)},origin:${logID(origin)},right:${logID(this._right)},parent:${logID(this._parent)},parentSub:${logID(this._parentSub)})` return `YArray(id:${logID(this._id)},start:${logID(this._start)},left:${logID(left)},origin:${logID(origin)},right:${logID(this._right)},parent:${logID(this._parent)},parentSub:${this._parentSub})`
} }
} }
@@ -2351,8 +2429,8 @@ class YMapEvent extends YEvent {
} }
class YMap extends Type { class YMap extends Type {
_callObserver (parentSubs, remote) { _callObserver (transaction, parentSubs, remote) {
this._callEventHandler(new YMapEvent(this, parentSubs, remote)); this._callEventHandler(transaction, new YMapEvent(this, parentSubs, remote));
} }
toJSON () { toJSON () {
const map = {}; const map = {};
@@ -2447,48 +2525,7 @@ class YMap extends Type {
_logString () { _logString () {
const left = this._left !== null ? this._left._lastId : null; const left = this._left !== null ? this._left._lastId : null;
const origin = this._origin !== null ? this._origin._lastId : null; const origin = this._origin !== null ? this._origin._lastId : null;
return `YMap(id:${logID(this._id)},mapSize:${this._map.size},left:${logID(left)},origin:${logID(origin)},right:${logID(this._right)},parent:${logID(this._parent)},parentSub:${logID(this._parentSub)})` return `YMap(id:${logID(this._id)},mapSize:${this._map.size},left:${logID(left)},origin:${logID(origin)},right:${logID(this._right)},parent:${logID(this._parent)},parentSub:${this._parentSub})`
}
}
class ItemString extends Item {
constructor () {
super();
this._content = null;
}
_copy () {
let struct = super._copy();
struct._content = this._content;
return struct
}
get _length () {
return this._content.length
}
_fromBinary (y, decoder) {
let missing = super._fromBinary(y, decoder);
this._content = decoder.readVarString();
return missing
}
_toBinary (encoder) {
super._toBinary(encoder);
encoder.writeVarString(this._content);
}
_logString () {
const left = this._left !== null ? this._left._lastId : null;
const origin = this._origin !== null ? this._origin._lastId : null;
return `ItemJSON(id:${logID(this._id)},content:${JSON.stringify(this._content)},left:${logID(left)},origin:${logID(origin)},right:${logID(this._right)},parent:${logID(this._parent)},parentSub:${logID(this._parentSub)})`
}
_splitAt (y, diff) {
if (diff === 0) {
return this
} else if (diff >= this._length) {
return this._right
}
let item = new ItemString();
item._content = this._content.slice(diff);
this._content = this._content.slice(0, diff);
splitHelper(y, this, item, diff);
return item
} }
} }
@@ -2552,7 +2589,7 @@ class YText extends YArray {
_logString () { _logString () {
const left = this._left !== null ? this._left._lastId : null; const left = this._left !== null ? this._left._lastId : null;
const origin = this._origin !== null ? this._origin._lastId : null; const origin = this._origin !== null ? this._origin._lastId : null;
return `YText(id:${logID(this._id)},start:${logID(this._start)},left:${logID(left)},origin:${logID(origin)},right:${logID(this._right)},parent:${logID(this._parent)},parentSub:${logID(this._parentSub)})` return `YText(id:${logID(this._id)},start:${logID(this._start)},left:${logID(left)},origin:${logID(origin)},right:${logID(this._right)},parent:${logID(this._parent)},parentSub:${this._parentSub})`
} }
} }
@@ -2739,7 +2776,33 @@ function applyChangesFromDom (dom) {
} }
} }
function reflectChangesOnDom (events) { function reflectChangesOnDom (events, _document) {
// Make sure that no filtered attributes are applied to the structure
// if they were, delete them
/*
events.forEach(event => {
const target = event.target
if (event.attributesChanged === undefined) {
// event.target is Y.XmlText
return
}
const keys = this._domFilter(target.nodeName, Array.from(event.attributesChanged))
if (keys === null) {
target._delete()
} else {
const removeKeys = new Set() // is a copy of event.attributesChanged
event.attributesChanged.forEach(key => { removeKeys.add(key) })
keys.forEach(key => {
// remove all accepted keys from removeKeys
removeKeys.delete(key)
})
// remove the filtered attribute
removeKeys.forEach(key => {
target.removeAttribute(key)
})
}
})
*/
this._mutualExclude(() => { this._mutualExclude(() => {
events.forEach(event => { events.forEach(event => {
const yxml = event.target; const yxml = event.target;
@@ -2761,9 +2824,9 @@ function reflectChangesOnDom (events) {
}); });
if (event.childListChanged) { if (event.childListChanged) {
// create fragment of undeleted nodes // create fragment of undeleted nodes
const fragment = document.createDocumentFragment(); const fragment = _document.createDocumentFragment();
yxml.forEach(function (t) { yxml.forEach(function (t) {
fragment.append(t.getDom()); fragment.appendChild(t.getDom(_document));
}); });
// remove remainding nodes // remove remainding nodes
let lastChild = dom.lastChild; let lastChild = dom.lastChild;
@@ -2772,7 +2835,7 @@ function reflectChangesOnDom (events) {
lastChild = dom.lastChild; lastChild = dom.lastChild;
} }
// insert fragment of undeleted nodes // insert fragment of undeleted nodes
dom.append(fragment); dom.appendChild(fragment);
} }
} }
/* TODO: smartscrolling /* TODO: smartscrolling
@@ -2893,7 +2956,7 @@ let relativeSelection = null;
let beforeTransactionSelectionFixer; let beforeTransactionSelectionFixer;
if (typeof getSelection !== 'undefined') { if (typeof getSelection !== 'undefined') {
beforeTransactionSelectionFixer = function _beforeTransactionSelectionFixer (y, remote) { beforeTransactionSelectionFixer = function _beforeTransactionSelectionFixer (y, transaction, remote) {
if (!remote) { if (!remote) {
return return
} }
@@ -2916,7 +2979,7 @@ if (typeof getSelection !== 'undefined') {
beforeTransactionSelectionFixer = function _fakeBeforeTransactionSelectionFixer () {}; beforeTransactionSelectionFixer = function _fakeBeforeTransactionSelectionFixer () {};
} }
function afterTransactionSelectionFixer (y, remote) { function afterTransactionSelectionFixer (y, transaction, remote) {
if (relativeSelection === null || !remote) { if (relativeSelection === null || !remote) {
return return
} }
@@ -3715,7 +3778,7 @@ function merge_tuples (diffs, start, length) {
/* global MutationObserver */ /* global MutationObserver */
function domToYXml (parent, doms) { function domToYXml (parent, doms, _document) {
const types = []; const types = [];
doms.forEach(d => { doms.forEach(d => {
if (d._yxml != null && d._yxml !== false) { if (d._yxml != null && d._yxml !== false) {
@@ -3726,7 +3789,7 @@ function domToYXml (parent, doms) {
if (d.nodeType === d.TEXT_NODE) { if (d.nodeType === d.TEXT_NODE) {
type = new YXmlText(d); type = new YXmlText(d);
} else if (d.nodeType === d.ELEMENT_NODE) { } else if (d.nodeType === d.ELEMENT_NODE) {
type = new YXmlFragment._YXmlElement(d, parent._domFilter); type = new YXmlFragment._YXmlElement(d, parent._domFilter, _document);
} else { } else {
throw new Error('Unsupported node!') throw new Error('Unsupported node!')
} }
@@ -3739,6 +3802,54 @@ function domToYXml (parent, doms) {
return types return types
} }
class YXmlTreeWalker {
constructor (root, f) {
this._filter = f || (() => true);
this._root = root;
this._currentNode = root;
this._firstCall = true;
}
[Symbol.iterator] () {
return this
}
next () {
let n = this._currentNode;
if (this._firstCall) {
this._firstCall = false;
if (!n._deleted && this._filter(n)) {
return { value: n, done: false }
}
}
do {
if (!n._deleted && n.constructor === YXmlFragment._YXmlElement && n._start !== null) {
// walk down in the tree
n = n._start;
} else {
// walk right or up in the tree
while (n !== this._root) {
if (n._right !== null) {
n = n._right;
break
}
n = n._parent;
}
if (n === this._root) {
n = null;
}
}
if (n === this._root) {
break
}
} while (n !== null && (n._deleted || !this._filter(n)))
this._currentNode = n;
if (n === null) {
return { done: true }
} else {
return { value: n, done: false }
}
}
}
class YXmlFragment extends YArray { class YXmlFragment extends YArray {
constructor () { constructor () {
super(); super();
@@ -3756,11 +3867,40 @@ class YXmlFragment extends YArray {
} catch (e) { } catch (e) {
console.error(e); console.error(e);
} }
this._domObserver.takeRecords(); if (this._domObserver !== null) {
this._domObserver.takeRecords();
}
token = true; token = true;
} }
}; };
} }
createTreeWalker (filter) {
return new YXmlTreeWalker(this, filter)
}
/**
* Retrieve first element that matches *query*
* Similar to DOM's querySelector, but only accepts a subset of its queries
*
* Query support:
* - tagname
* TODO:
* - id
* - attribute
*/
querySelector (query) {
query = query.toUpperCase();
const iterator = new YXmlTreeWalker(this, element => element.nodeName === query);
const next = iterator.next();
if (next.done) {
return null
} else {
return next.value
}
}
querySelectorAll (query) {
query = query.toUpperCase();
return Array.from(new YXmlTreeWalker(this, element => element.nodeName === query))
}
enableSmartScrolling (scrollElement) { enableSmartScrolling (scrollElement) {
this._scrollElement = scrollElement; this._scrollElement = scrollElement;
this.forEach(xml => { this.forEach(xml => {
@@ -3773,8 +3913,8 @@ class YXmlFragment extends YArray {
xml.setDomFilter(f); xml.setDomFilter(f);
}); });
} }
_callObserver (parentSubs, remote) { _callObserver (transaction, parentSubs, remote) {
this._callEventHandler(new YXmlEvent(this, parentSubs, remote)); this._callEventHandler(transaction, new YXmlEvent(this, parentSubs, remote));
} }
toString () { toString () {
return this.map(xml => xml.toString()).join('') return this.map(xml => xml.toString()).join('')
@@ -3793,113 +3933,116 @@ class YXmlFragment extends YArray {
this._dom = null; this._dom = null;
} }
} }
insertDomElementsAfter (prev, doms) { insertDomElementsAfter (prev, doms, _document) {
const types = domToYXml(this, doms); const types = domToYXml(this, doms, _document);
this.insertAfter(prev, types); this.insertAfter(prev, types);
return types return types
} }
insertDomElements (pos, doms) { insertDomElements (pos, doms, _document) {
const types = domToYXml(this, doms); const types = domToYXml(this, doms, _document);
this.insert(pos, types); this.insert(pos, types);
return types return types
} }
getDom () { getDom () {
return this._dom return this._dom
} }
bindToDom (dom) { bindToDom (dom, _document) {
if (this._dom != null) { if (this._dom != null) {
this._unbindFromDom(); this._unbindFromDom();
} }
if (dom._yxml != null) { if (dom._yxml != null) {
dom._yxml._unbindFromDom(); dom._yxml._unbindFromDom();
} }
if (MutationObserver == null) {
throw new Error('Not able to bind to a DOM element, because MutationObserver is not available!')
}
dom.innerHTML = ''; dom.innerHTML = '';
this._dom = dom;
dom._yxml = this;
this.forEach(t => { this.forEach(t => {
dom.insertBefore(t.getDom(), null); dom.insertBefore(t.getDom(_document), null);
}); });
this._bindToDom(dom); this._bindToDom(dom, _document);
} }
// binds to a dom element // binds to a dom element
// Only call if dom and YXml are isomorph // Only call if dom and YXml are isomorph
_bindToDom (dom) { _bindToDom (dom, _document) {
if (this._parent === null || this._parent._dom != null || typeof MutationObserver === 'undefined') { _document = _document || document;
// only bind if parent did not already bind this._dom = dom;
dom._yxml = this;
// TODO: refine this..
if ((this.constructor !== YXmlFragment && this._parent !== this._y) || this._parent === null) {
// TODO: only top level YXmlFragment can bind. Also allow YXmlElements..
return return
} }
this._y.on('beforeTransaction', () => {
this._domObserverListener(this._domObserver.takeRecords());
});
this._y.on('beforeTransaction', beforeTransactionSelectionFixer); this._y.on('beforeTransaction', beforeTransactionSelectionFixer);
this._y.on('afterTransaction', afterTransactionSelectionFixer); this._y.on('afterTransaction', afterTransactionSelectionFixer);
// Apply Y.Xml events to dom // Apply Y.Xml events to dom
this.observeDeep(reflectChangesOnDom.bind(this)); this.observeDeep(events => {
reflectChangesOnDom.call(this, events, _document);
});
// Apply Dom changes on Y.Xml // Apply Dom changes on Y.Xml
this._domObserverListener = mutations => { if (typeof MutationObserver !== 'undefined') {
this._mutualExclude(() => { this._y.on('beforeTransaction', () => {
this._y.transact(() => { this._domObserverListener(this._domObserver.takeRecords());
let diffChildren = new Set(); });
mutations.forEach(mutation => { this._domObserverListener = mutations => {
const dom = mutation.target; this._mutualExclude(() => {
const yxml = dom._yxml; this._y.transact(() => {
if (yxml == null) { let diffChildren = new Set();
// dom element is filtered mutations.forEach(mutation => {
return const dom = mutation.target;
} const yxml = dom._yxml;
switch (mutation.type) { if (yxml == null) {
case 'characterData': // dom element is filtered
var diffs = diff_1(yxml.toString(), dom.nodeValue); return
var pos = 0; }
for (var i = 0; i < diffs.length; i++) { switch (mutation.type) {
var d = diffs[i]; case 'characterData':
if (d[0] === 0) { // EQUAL var diffs = diff_1(yxml.toString(), dom.nodeValue);
pos += d[1].length; var pos = 0;
} else if (d[0] === -1) { // DELETE for (var i = 0; i < diffs.length; i++) {
yxml.delete(pos, d[1].length); var d = diffs[i];
} else { // INSERT if (d[0] === 0) { // EQUAL
yxml.insert(pos, d[1]); pos += d[1].length;
pos += d[1].length; } else if (d[0] === -1) { // DELETE
} yxml.delete(pos, d[1].length);
} } else { // INSERT
break yxml.insert(pos, d[1]);
case 'attributes': pos += d[1].length;
let name = mutation.attributeName;
// check if filter accepts attribute
if (this._domFilter(dom, [name]).length > 0 && this.constructor !== YXmlFragment) {
var val = dom.getAttribute(name);
if (yxml.getAttribute(name) !== val) {
if (val == null) {
yxml.removeAttribute(name);
} else {
yxml.setAttribute(name, val);
} }
} }
} break
break case 'attributes':
case 'childList': let name = mutation.attributeName;
diffChildren.add(mutation.target); // check if filter accepts attribute
break if (this._domFilter(dom, [name]).length > 0 && yxml.constructor !== YXmlFragment) {
var val = dom.getAttribute(name);
if (yxml.getAttribute(name) !== val) {
if (val == null) {
yxml.removeAttribute(name);
} else {
yxml.setAttribute(name, val);
}
}
}
break
case 'childList':
diffChildren.add(mutation.target);
break
}
});
for (let dom of diffChildren) {
if (dom._yxml != null && dom._yxml !== false) {
applyChangesFromDom(dom);
}
} }
}); });
for (let dom of diffChildren) {
if (dom._yxml != null) {
applyChangesFromDom(dom);
}
}
}); });
};
this._domObserver = new MutationObserver(this._domObserverListener);
this._domObserver.observe(dom, {
childList: true,
attributes: true,
characterData: true,
subtree: true
}); });
}; }
this._domObserver = new MutationObserver(this._domObserverListener);
this._domObserver.observe(dom, {
childList: true,
attributes: true,
characterData: true,
subtree: true
});
return dom return dom
} }
_logString () { _logString () {
@@ -3911,7 +4054,7 @@ class YXmlFragment extends YArray {
// import diff from 'fast-diff' // import diff from 'fast-diff'
class YXmlElement extends YXmlFragment { class YXmlElement extends YXmlFragment {
constructor (arg1, arg2) { constructor (arg1, arg2, _document) {
super(); super();
this.nodeName = null; this.nodeName = null;
this._scrollElement = null; this._scrollElement = null;
@@ -3919,7 +4062,7 @@ class YXmlElement extends YXmlFragment {
this.nodeName = arg1.toUpperCase(); this.nodeName = arg1.toUpperCase();
} else if (arg1 != null && arg1.nodeType != null && arg1.nodeType === arg1.ELEMENT_NODE) { } else if (arg1 != null && arg1.nodeType != null && arg1.nodeType === arg1.ELEMENT_NODE) {
this.nodeName = arg1.nodeName; this.nodeName = arg1.nodeName;
this._setDom(arg1); this._setDom(arg1, _document);
} else { } else {
this.nodeName = 'UNDEFINED'; this.nodeName = 'UNDEFINED';
} }
@@ -3932,14 +4075,12 @@ class YXmlElement extends YXmlFragment {
struct.nodeName = this.nodeName; struct.nodeName = this.nodeName;
return struct return struct
} }
_setDom (dom) { _setDom (dom, _document) {
if (this._dom != null) { if (this._dom != null) {
throw new Error('Only call this method if you know what you are doing ;)') throw new Error('Only call this method if you know what you are doing ;)')
} else if (dom._yxml != null) { // TODO do i need to check this? - no.. but for dev purps.. } else if (dom._yxml != null) { // TODO do i need to check this? - no.. but for dev purps..
throw new Error('Already bound to an YXml type') throw new Error('Already bound to an YXml type')
} else { } else {
this._dom = dom;
dom._yxml = this;
// tag is already set in constructor // tag is already set in constructor
// set attributes // set attributes
let attrNames = []; let attrNames = [];
@@ -3952,8 +4093,8 @@ class YXmlElement extends YXmlFragment {
let attrValue = dom.getAttribute(attrName); let attrValue = dom.getAttribute(attrName);
this.setAttribute(attrName, attrValue); this.setAttribute(attrName, attrValue);
} }
this.insertDomElements(0, Array.prototype.slice.call(dom.childNodes)); this.insertDomElements(0, Array.prototype.slice.call(dom.childNodes), _document);
this._bindToDom(dom); this._bindToDom(dom, _document);
return dom return dom
} }
} }
@@ -4021,7 +4162,6 @@ class YXmlElement extends YXmlFragment {
let dom = this._dom; let dom = this._dom;
if (dom == null) { if (dom == null) {
dom = _document.createElement(this.nodeName); dom = _document.createElement(this.nodeName);
this._dom = dom;
dom._yxml = this; dom._yxml = this;
let attrs = this.getAttributes(); let attrs = this.getAttributes();
for (let key in attrs) { for (let key in attrs) {
@@ -4030,7 +4170,7 @@ class YXmlElement extends YXmlFragment {
this.forEach(yxml => { this.forEach(yxml => {
dom.appendChild(yxml.getDom(_document)); dom.appendChild(yxml.getDom(_document));
}); });
this._bindToDom(dom); this._bindToDom(dom, _document);
} }
return dom return dom
} }
@@ -4271,15 +4411,16 @@ class NamedEventHandler {
} }
class ReverseOperation { class ReverseOperation {
constructor (y) { constructor (y, transaction) {
const beforeState = y._transaction.beforeState; this.created = new Date();
const beforeState = transaction.beforeState;
this.toState = new ID(y.userID, y.ss.getState(y.userID) - 1); this.toState = new ID(y.userID, y.ss.getState(y.userID) - 1);
if (beforeState.has(y.userID)) { if (beforeState.has(y.userID)) {
this.fromState = new ID(y.userID, beforeState.get(y.userID)); this.fromState = new ID(y.userID, beforeState.get(y.userID));
} else { } else {
this.fromState = this.toState; this.fromState = this.toState;
} }
this.deletedStructs = y._transaction.deletedStructs; this.deletedStructs = transaction.deletedStructs;
} }
} }
@@ -4293,8 +4434,45 @@ function isStructInScope (y, struct, scope) {
return false return false
} }
function applyReverseOperation (y, scope, reverseBuffer) {
let performedUndo = false;
y.transact(() => {
while (!performedUndo && reverseBuffer.length > 0) {
let undoOp = reverseBuffer.pop();
// make sure that it is possible to iterate {from}-{to}
y.os.getItemCleanStart(undoOp.fromState);
y.os.getItemCleanEnd(undoOp.toState);
y.os.iterate(undoOp.fromState, undoOp.toState, op => {
if (!op._deleted && isStructInScope(y, op, scope)) {
performedUndo = true;
op._delete(y);
}
});
for (let op of undoOp.deletedStructs) {
if (
isStructInScope(y, op, scope) &&
op._parent !== y &&
!op._parent._deleted &&
(
op._parent._id.user !== y.userID ||
op._parent._id.clock < undoOp.fromState.clock ||
op._parent._id.clock > undoOp.fromState.clock
)
) {
performedUndo = true;
op = op._copy(undoOp.deletedStructs);
op._integrate(y);
}
}
}
});
return performedUndo
}
class UndoManager { class UndoManager {
constructor (scope) { constructor (scope, options = {}) {
this.options = options;
options.captureTimeout = options.captureTimeout == null ? 500 : options.captureTimeout;
this._undoBuffer = []; this._undoBuffer = [];
this._redoBuffer = []; this._redoBuffer = [];
this._scope = scope; this._scope = scope;
@@ -4302,11 +4480,18 @@ class UndoManager {
this._redoing = false; this._redoing = false;
const y = scope._y; const y = scope._y;
this.y = y; this.y = y;
y.on('afterTransaction', (y, remote) => { y.on('afterTransaction', (y, transaction, remote) => {
if (!remote && (y._transaction.beforeState.has(y.userID) || y._transaction.deletedStructs.size > 0)) { if (!remote && transaction.changedParentTypes.has(scope)) {
let reverseOperation = new ReverseOperation(y); let reverseOperation = new ReverseOperation(y, transaction);
if (!this._undoing) { if (!this._undoing) {
this._undoBuffer.push(reverseOperation); let lastUndoOp = this._undoBuffer.length > 0 ? this._undoBuffer[this._undoBuffer.length - 1] : null;
if (lastUndoOp !== null && reverseOperation.created - lastUndoOp.created <= options.captureTimeout) {
lastUndoOp.created = reverseOperation.created;
lastUndoOp.toState = reverseOperation.toState;
reverseOperation.deletedStructs.forEach(lastUndoOp.deletedStructs.add, lastUndoOp.deletedStructs);
} else {
this._undoBuffer.push(reverseOperation);
}
if (!this._redoing) { if (!this._redoing) {
this._redoBuffer = []; this._redoBuffer = [];
} }
@@ -4318,46 +4503,15 @@ class UndoManager {
} }
undo () { undo () {
this._undoing = true; this._undoing = true;
this._applyReverseOperation(this._undoBuffer); const performedUndo = applyReverseOperation(this.y, this._scope, this._undoBuffer);
this._undoing = false; this._undoing = false;
return performedUndo
} }
redo () { redo () {
this._redoing = true; this._redoing = true;
this._applyReverseOperation(this._redoBuffer); const performedRedo = applyReverseOperation(this.y, this._scope, this._redoBuffer);
this._redoing = false; this._redoing = false;
} return performedRedo
_applyReverseOperation (reverseBuffer) {
this.y.transact(() => {
let performedUndo = false;
while (!performedUndo && reverseBuffer.length > 0) {
let undoOp = reverseBuffer.pop();
// make sure that it is possible to iterate {from}-{to}
this.y.os.getItemCleanStart(undoOp.fromState);
this.y.os.getItemCleanEnd(undoOp.toState);
this.y.os.iterate(undoOp.fromState, undoOp.toState, op => {
if (!op._deleted && isStructInScope(this.y, op, this._scope)) {
performedUndo = true;
op._delete(this.y);
}
});
for (let op of undoOp.deletedStructs) {
if (
isStructInScope(this.y, op, this._scope) &&
op._parent !== this.y &&
!op._parent._deleted &&
(
op._parent._id.user !== this.y.userID ||
op._parent._id.clock < undoOp.fromState.clock ||
op._parent._id.clock > undoOp.fromState.clock
)
) {
performedUndo = true;
op = op._copy(undoOp.deletedStructs);
op._integrate(this.y);
}
}
}
});
} }
} }
@@ -4718,6 +4872,15 @@ function coerce(val) {
} }
}); });
var debug_1 = debug$1.coerce;
var debug_2 = debug$1.disable;
var debug_3 = debug$1.enable;
var debug_4 = debug$1.enabled;
var debug_5 = debug$1.humanize;
var debug_6 = debug$1.names;
var debug_7 = debug$1.skips;
var debug_8 = debug$1.formatters;
var browser = createCommonjsModule(function (module, exports) { var browser = createCommonjsModule(function (module, exports) {
/** /**
* This is the web browser implementation of `debug()`. * This is the web browser implementation of `debug()`.
@@ -4906,6 +5069,14 @@ function localstorage() {
} }
}); });
var browser_1 = browser.log;
var browser_2 = browser.formatArgs;
var browser_3 = browser.save;
var browser_4 = browser.load;
var browser_5 = browser.useColors;
var browser_6 = browser.storage;
var browser_7 = browser.colors;
class AbstractConnector { class AbstractConnector {
constructor (y, opts) { constructor (y, opts) {
this.y = y; this.y = y;
@@ -5267,8 +5438,8 @@ class Y$1 extends NamedEventHandler {
transact (f, remote = false) { transact (f, remote = false) {
let initialCall = this._transaction === null; let initialCall = this._transaction === null;
if (initialCall) { if (initialCall) {
this.emit('beforeTransaction', this, remote);
this._transaction = new Transaction(this); this._transaction = new Transaction(this);
this.emit('beforeTransaction', this, this._transaction, remote);
} }
try { try {
f(this); f(this);
@@ -5276,25 +5447,31 @@ class Y$1 extends NamedEventHandler {
console.error(e); console.error(e);
} }
if (initialCall) { if (initialCall) {
const transaction = this._transaction;
this._transaction = null;
// emit change events on changed types // emit change events on changed types
this._transaction.changedTypes.forEach(function (subs, type) { transaction.changedTypes.forEach(function (subs, type) {
if (!type._deleted) { if (!type._deleted) {
type._callObserver(subs, remote); type._callObserver(transaction, subs, remote);
} }
}); });
this._transaction.changedParentTypes.forEach(function (events, type) { transaction.changedParentTypes.forEach(function (events, type) {
if (!type._deleted) { if (!type._deleted) {
events = events.filter(event => events = events
!event.target._deleted .filter(event =>
); !event.target._deleted
);
events
.forEach(event => {
event.currentTarget = type;
});
// we don't have to check for events.length // we don't have to check for events.length
// because there is no way events is empty.. // because there is no way events is empty..
type._deepEventHandler.callEventListeners(events); type._deepEventHandler.callEventListeners(transaction, events);
} }
}); });
// when all changes & events are processed, emit afterTransaction event // when all changes & events are processed, emit afterTransaction event
this.emit('afterTransaction', this, remote); this.emit('afterTransaction', this, transaction, remote);
this._transaction = null;
} }
} }
// fake _start for root properties (y.set('name', type)) // fake _start for root properties (y.set('name', type))
@@ -5386,7 +5563,9 @@ Y$1.XmlText = YXmlText;
Y$1.utils = { Y$1.utils = {
BinaryDecoder, BinaryDecoder,
UndoManager UndoManager,
getRelativePosition,
fromRelativePosition
}; };
Y$1.debug = browser; Y$1.debug = browser;

File diff suppressed because one or more lines are too long

20068
y.test.js

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long