-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathshare-codemirror.js
121 lines (104 loc) · 3.38 KB
/
share-codemirror.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
(function () {
'use strict';
/**
* @param cm - CodeMirror instance
* @param ctx - Share context
*/
function shareCodeMirror(cm, ctx) {
if (!ctx.provides.text) throw new Error('Cannot attach to non-text document');
var suppress = false;
var text = ctx.get() || ''; // Due to a bug in share - get() returns undefined for empty docs.
cm.setValue(text);
check();
// *** remote -> local changes
ctx.onInsert = function (pos, text) {
suppress = true;
cm.replaceRange(text, cm.posFromIndex(pos));
suppress = false;
check();
};
ctx.onRemove = function (pos, length) {
suppress = true;
var from = cm.posFromIndex(pos);
var to = cm.posFromIndex(pos + length);
cm.replaceRange('', from, to);
suppress = false;
check();
};
// *** local -> remote changes
cm.on('change', onLocalChange);
function onLocalChange(cm, change) {
if (suppress) return;
applyToShareJS(cm, change);
check();
}
cm.detachShareJsDoc = function () {
ctx.onRemove = null;
ctx.onInsert = null;
cm.off('change', onLocalChange);
}
// Convert a CodeMirror change into an op understood by share.js
function applyToShareJS(cm, change) {
// CodeMirror changes give a text replacement.
// I tuned this operation a little bit, for speed.
var startPos = 0; // Get character position from # of chars in each line.
var i = 0; // i goes through all lines.
while (i < change.from.line) {
startPos += cm.lineInfo(i).text.length + 1; // Add 1 for '\n'
i++;
}
startPos += change.from.ch;
if (change.to.line == change.from.line && change.to.ch == change.from.ch) {
// nothing was removed.
} else {
// delete.removed contains an array of removed lines as strings, so this adds
// all the lengths. Later change.removed.length - 1 is added for the \n-chars
// (-1 because the linebreak on the last line won't get deleted)
var delLen = 0;
for (var rm = 0; rm < change.removed.length; rm++) {
delLen += change.removed[rm].length;
}
delLen += change.removed.length - 1;
ctx.remove(startPos, delLen);
}
if (change.text) {
ctx.insert(startPos, change.text.join('\n'));
}
if (change.next) {
applyToShareJS(cm, change.next);
}
}
function check() {
setTimeout(function () {
var cmText = cm.getValue();
var otText = ctx.get() || '';
if (cmText != otText) {
console.error("Text does not match!");
console.error("cm: " + cmText);
console.error("ot: " + otText);
// Replace the editor text with the ctx snapshot.
cm.setValue(ctx.get() || '');
}
}, 0);
}
return ctx;
}
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
// Node.js
module.exports = shareCodeMirror;
module.exports.scriptsDir = __dirname;
} else {
if (typeof define === 'function' && define.amd) {
// AMD
define([], function () {
return shareCodeMirror;
});
} else {
// Browser, no AMD
window.sharejs.Doc.prototype.attachCodeMirror = function (cm, ctx) {
if (!ctx) ctx = this.createContext();
shareCodeMirror(cm, ctx);
};
}
}
})();