-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
71bb896
commit 7c7b56f
Showing
4 changed files
with
96 additions
and
65 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
lib/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,34 @@ | ||
# gulf-contenteditable | ||
Convenient [gulf](http://github.com/marcelklehr/gulf#readme) wrapper for contenteditable elements | ||
# gulf-editor-contenteditable | ||
[Gulf](http://github.com/marcelklehr/gulf#readme) bindings for contenteditable elements | ||
|
||
## Install | ||
|
||
``` | ||
npm install gulf-contenteditable | ||
npm install --save gulf gulf-editor-contenteditable dom-ot | ||
``` | ||
|
||
## Usage | ||
|
||
``` | ||
var bindEditor = require('gulf-contenteditable') | ||
const domOT = require('dom-ot') | ||
const ContenteditableDocument = require('gulf-editor-contenteditable') | ||
var editable = document.querySelecor('#doc[contenteditable]') | ||
var doc = bindEditor(editable) | ||
var doc = new ContenteditableDocument({ | ||
storageAdapter: new gulf.MemoryAdapter | ||
, ottype: domOT | ||
, editorInstance: editable | ||
}) | ||
``` | ||
|
||
## API | ||
### bindEditor(editable:DOMElement, [storageAdapter]) | ||
* `editable` -- a contenteditable Element to be wired up with gulf | ||
### class ContenteditableDocument({editorInstance:HTMLElement,...}) extends gulf.EditableDocument | ||
* `contenteditable` -- a contenteditable Element to be wired up with gulf | ||
* `storageAdapter` -- a gulf storage adapter (optional; defaults to the in-memory Adapter) | ||
* *returns* the `gulf.EditableDocument` (see [the gulf docs](http://github.com/marcelklehr/gulf#readme)) | ||
* `ottype` -- the OT type, with this binding you'll want `dom-ot` | ||
|
||
|
||
## Legal | ||
(c) 2015 by Marcel Klehr | ||
|
||
GNU Lesser General Public License | ||
GNU Lesser General Public License |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,34 @@ | ||
{ | ||
"name": "gulf-contenteditable", | ||
"version": "4.0.1", | ||
"name": "gulf-editor-contenteditable", | ||
"version": "5.0.0", | ||
"description": "Convenient gulf wrapper for contenteditable elements (uses dom-ot)", | ||
"main": "index.js", | ||
"main": "lib/index.js", | ||
"dependencies": { | ||
"mutation-summary": "^0.0.0" | ||
"mutation-summary": "^0.0.0", | ||
"babel-runtime": "^6.11.6", | ||
"core-js": "2.x" | ||
}, | ||
"peerDependencies": { | ||
"gulf": "4.x", | ||
"gulf": "5.x", | ||
"dom-ot": "2.x" | ||
}, | ||
"scripts": { | ||
"build": "babel src --out-dir lib" | ||
}, | ||
"babel": { | ||
"presets": [ | ||
"es2015" | ||
], | ||
"plugins": ["transform-runtime"] | ||
}, | ||
"devDependencies": { | ||
"babel-cli": "6.x", | ||
"babel-plugin-transform-runtime": "^6.15.0", | ||
"babel-preset-es2015": "6.x" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/marcelklehr/gulf-contenteditable.git" | ||
"url": "git+https://github.com/marcelklehr/gulf-editor-contenteditable.git" | ||
}, | ||
"keywords": [ | ||
"operational transformation", | ||
|
@@ -22,7 +38,7 @@ | |
"author": "Marcel Klehr <[email protected]>", | ||
"license": "LGPL-3.0", | ||
"bugs": { | ||
"url": "https://github.com/marcelklehr/gulf-contenteditable/issues" | ||
"url": "https://github.com/marcelklehr/gulf-editor-contenteditable/issues" | ||
}, | ||
"homepage": "https://github.com/marcelklehr/gulf-contenteditable#readme" | ||
"homepage": "https://github.com/marcelklehr/gulf-editor-contenteditable#readme" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
/** | ||
* gulf-contenteditable | ||
* Copyright (C) 2015 Marcel Klehr <[email protected]> | ||
* gulf-editor-contenteditable | ||
* Copyright (C) 2015-2016 Marcel Klehr <[email protected]> | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU Lesser General Public License as published by | ||
|
@@ -18,70 +18,79 @@ | |
var gulf = require('gulf') | ||
, domOT = require('dom-ot') | ||
, MutationSummary = require('mutation-summary') | ||
|
||
module.exports = function(contenteditable) { | ||
var doc = new gulf.EditableDocument(new gulf.MemoryAdapter, domOT) | ||
doc.rootNode = contenteditable | ||
|
||
doc._setContents = function(newcontent, cb) { | ||
observer.disconnect() | ||
contenteditable.innerHTML = '' | ||
class ContenteditableDocument extends gulf.EditableDocument { | ||
constructor(opts) { | ||
super(opts) | ||
if (!opts.editorInstance) throw new Error('No contenteditable HTMLElement passed') | ||
this.rootNode = opts.editorInstance | ||
|
||
this.mutationSummary = new MutationSummary({ | ||
rootNode: this.rootNode, // (defaults to window.document) | ||
oldPreviousSibling: true, | ||
queries: [ | ||
{ all: true} | ||
], | ||
callback: (summaries) => this.onLocalChange(summaries) | ||
}) | ||
} | ||
|
||
close() { | ||
super.close() | ||
this.mutationSummary.disconnect() | ||
} | ||
|
||
_onLocalChange(summaries) { | ||
var ops = domOT.adapters.mutationSummary.import(summaries[0], this.rootNode) | ||
ops = ops.filter(function(op) { | ||
// filter out changes to the root node | ||
if(op.path) return !!op.path.length | ||
else return true | ||
}) | ||
if(!ops.length) return | ||
|
||
this.submitChange(ops) | ||
ops.forEach(function(op) { | ||
op.apply(this.rootNode, /*index:*/true, /*dry:*/true) | ||
}) | ||
} | ||
|
||
_setContent(newcontent) { | ||
this.mutationSummary.disconnect() | ||
|
||
this.rootNode.innerHTML = '' | ||
for(var i=0; i<newcontent.childNodes.length; i++) { | ||
contenteditable.appendChild(newcontent.childNodes[i].cloneNode(/*deep:*/true)) | ||
this.rootNode.appendChild(newcontent.childNodes[i].cloneNode(/*deep:*/true)) | ||
} | ||
|
||
domOT.adapters.mutationSummary.createIndex(contenteditable) | ||
observer.reconnect() | ||
cb() | ||
this.mutationSummary.reconnect() | ||
|
||
return Promise.resolve() | ||
} | ||
|
||
doc._change = function(changes, cb) { | ||
_onChange(changes) { | ||
observer.disconnect() | ||
console.log('_change', changes) | ||
|
||
var ops = domOT.unpackOps(changes) | ||
retainSelection(doc.rootNode, ops, function() { | ||
ops.forEach(function(op) { | ||
op.apply(contenteditable, /*index:*/true) | ||
retainSelection(this.rootNode, ops, () => { | ||
ops.forEach((op) => { | ||
op.apply(this.rootNode, /*index:*/true) | ||
}) | ||
}) | ||
|
||
observer.reconnect() | ||
cb() | ||
} | ||
|
||
doc._collectChanges = function(cb) { | ||
// changes are automatically collected by MutationSummary | ||
cb() | ||
return Promise.resolve() | ||
} | ||
|
||
var observer = new MutationSummary({ | ||
rootNode: contenteditable, // (defaults to window.document) | ||
oldPreviousSibling: true, | ||
queries: [ | ||
{ all: true} | ||
], | ||
callback: onChange | ||
}) | ||
doc.mutationSummary = observer | ||
|
||
function onChange(summaries) { | ||
var ops = domOT.adapters.mutationSummary.import(summaries[0], contenteditable) | ||
ops = ops.filter(function(op) { | ||
// filter out changes to the root node | ||
if(op.path) return !!op.path.length | ||
else return true | ||
}) | ||
if(!ops.length) return | ||
console.log(ops) | ||
doc.update(ops) | ||
ops.forEach(function(op) { | ||
op.apply(contenteditable, /*index:*/true, /*dry:*/true) | ||
}) | ||
_onBeforeChange() { | ||
return Promise.resolve() | ||
} | ||
|
||
return doc | ||
} | ||
|
||
module.export = ContenteditableDocument | ||
|
||
function retainSelection(rootNode, ops, fn) { | ||
var selection = rootNode.ownerDocument.defaultView.getSelection() | ||
, ranges = [] | ||
|
@@ -97,4 +106,4 @@ function retainSelection(rootNode, ops, fn) { | |
if(r.endContainer) range.setEnd(r.endContainer, r.endOffset) | ||
selection.addRange(range) | ||
}) | ||
} | ||
} |