braid-text 0.2.63 → 0.2.65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/editor.html +12 -70
- package/index.js +20 -2
- package/markdown-editor.html +76 -287
- package/package.json +1 -1
- package/server-demo.js +5 -12
- package/test/test.html +29 -0
- package/web-utils.js +75 -0
package/editor.html
CHANGED
|
@@ -1,81 +1,23 @@
|
|
|
1
|
-
<body style="
|
|
2
|
-
<textarea
|
|
3
|
-
id="texty"
|
|
4
|
-
style="width: 100%; height: 100%; box-sizing: border-box"
|
|
5
|
-
></textarea>
|
|
1
|
+
<body style="margin: 0px; padding: 0px; box-sizing: border-box">
|
|
2
|
+
<textarea id="the_editor" style="width: 100%; height: 100%;"></textarea>
|
|
6
3
|
</body>
|
|
7
4
|
<script src="https://braid.org/code/myers-diff1.js"></script>
|
|
8
5
|
<script src="https://unpkg.com/braid-http@~1.3/braid-http-client.js"></script>
|
|
6
|
+
<script src="/web-utils.js"></script>
|
|
9
7
|
<script src="/simpleton-client.js"></script>
|
|
10
8
|
<script>
|
|
9
|
+
|
|
11
10
|
let simpleton = simpleton_client(location.pathname, {
|
|
12
|
-
on_patches: (patches) => apply_patches_and_update_selection(
|
|
13
|
-
get_patches: (prev_state) => diff(prev_state,
|
|
14
|
-
get_state: () =>
|
|
15
|
-
on_error: (e) =>
|
|
16
|
-
|
|
17
|
-
texty.style.background = '#fee'
|
|
18
|
-
texty.style.border = '4px solid red'
|
|
19
|
-
},
|
|
20
|
-
on_ack: () => texty.style.caretColor = ''
|
|
11
|
+
on_patches: (patches) => apply_patches_and_update_selection(the_editor, patches),
|
|
12
|
+
get_patches: (prev_state) => diff(prev_state, the_editor.value),
|
|
13
|
+
get_state: () => the_editor.value,
|
|
14
|
+
on_error: (e) => set_error_state(the_editor),
|
|
15
|
+
on_ack: () => set_acked_state(the_editor)
|
|
21
16
|
});
|
|
22
17
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
simpleton.changed();
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function diff(before, after) {
|
|
30
|
-
let diff = diff_main(before, after);
|
|
31
|
-
let patches = [];
|
|
32
|
-
let offset = 0;
|
|
33
|
-
for (let d of diff) {
|
|
34
|
-
let p = null;
|
|
35
|
-
if (d[0] == 1) p = { range: [offset, offset], content: d[1] };
|
|
36
|
-
else if (d[0] == -1) {
|
|
37
|
-
p = { range: [offset, offset + d[1].length], content: "" };
|
|
38
|
-
offset += d[1].length;
|
|
39
|
-
} else offset += d[1].length;
|
|
40
|
-
if (p) {
|
|
41
|
-
p.unit = "text";
|
|
42
|
-
patches.push(p);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
return patches;
|
|
18
|
+
the_editor.oninput = (e) => {
|
|
19
|
+
set_acked_state(the_editor, false)
|
|
20
|
+
simpleton.changed()
|
|
46
21
|
}
|
|
47
22
|
|
|
48
|
-
function apply_patches_and_update_selection(textarea, patches) {
|
|
49
|
-
let offset = 0;
|
|
50
|
-
for (let p of patches) {
|
|
51
|
-
p.range[0] += offset;
|
|
52
|
-
p.range[1] += offset;
|
|
53
|
-
offset -= p.range[1] - p.range[0];
|
|
54
|
-
offset += p.content.length;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
let original = textarea.value;
|
|
58
|
-
let sel = [textarea.selectionStart, textarea.selectionEnd];
|
|
59
|
-
|
|
60
|
-
for (var p of patches) {
|
|
61
|
-
let range = p.range;
|
|
62
|
-
|
|
63
|
-
for (let i = 0; i < sel.length; i++)
|
|
64
|
-
if (sel[i] > range[0])
|
|
65
|
-
if (sel[i] > range[1]) sel[i] -= range[1] - range[0];
|
|
66
|
-
else sel[i] = range[0];
|
|
67
|
-
|
|
68
|
-
for (let i = 0; i < sel.length; i++)
|
|
69
|
-
if (sel[i] > range[0]) sel[i] += p.content.length;
|
|
70
|
-
|
|
71
|
-
original =
|
|
72
|
-
original.substring(0, range[0]) +
|
|
73
|
-
p.content +
|
|
74
|
-
original.substring(range[1]);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
textarea.value = original;
|
|
78
|
-
textarea.selectionStart = sel[0];
|
|
79
|
-
textarea.selectionEnd = sel[1];
|
|
80
|
-
}
|
|
81
23
|
</script>
|
package/index.js
CHANGED
|
@@ -433,6 +433,26 @@ braid_text.put = async (key, options) => {
|
|
|
433
433
|
})
|
|
434
434
|
}
|
|
435
435
|
|
|
436
|
+
let resource = (typeof key == 'string') ? await get_resource(key) : key
|
|
437
|
+
|
|
438
|
+
if (options.transfer_encoding === 'dt') {
|
|
439
|
+
var start_i = 1 + resource.doc.getLocalVersion().reduce((a, b) => Math.max(a, b), -1)
|
|
440
|
+
|
|
441
|
+
resource.doc.mergeBytes(body)
|
|
442
|
+
|
|
443
|
+
var end_i = resource.doc.getLocalVersion().reduce((a, b) => Math.max(a, b), -1)
|
|
444
|
+
for (var i = start_i; i <= end_i; i++) {
|
|
445
|
+
let v = resource.doc.localToRemoteVersion([i])[0]
|
|
446
|
+
if (!resource.actor_seqs[v[0]]) resource.actor_seqs[v[0]] = new braid_text.RangeSet()
|
|
447
|
+
resource.actor_seqs[v[0]].add_range(v[1], v[1])
|
|
448
|
+
}
|
|
449
|
+
resource.val = resource.doc.get()
|
|
450
|
+
resource.version = resource.doc.getRemoteVersion().map(x => x.join("-")).sort()
|
|
451
|
+
|
|
452
|
+
await resource.db_delta(body)
|
|
453
|
+
return { change_count: end_i - start_i + 1 }
|
|
454
|
+
}
|
|
455
|
+
|
|
436
456
|
if (version) validate_version_array(version)
|
|
437
457
|
|
|
438
458
|
// translate a single parent of "root" to the empty array (same meaning)
|
|
@@ -445,8 +465,6 @@ braid_text.put = async (key, options) => {
|
|
|
445
465
|
if (body != null && (typeof body !== 'string')) throw new Error(`body must be a string`)
|
|
446
466
|
if (patches) validate_patches(patches)
|
|
447
467
|
|
|
448
|
-
let resource = (typeof key == 'string') ? await get_resource(key) : key
|
|
449
|
-
|
|
450
468
|
if (options_parents) {
|
|
451
469
|
// make sure we have all these parents
|
|
452
470
|
for (let p of options_parents) {
|
package/markdown-editor.html
CHANGED
|
@@ -1,309 +1,98 @@
|
|
|
1
|
-
<html lang="en">
|
|
2
|
-
<script type="statebus">
|
|
3
|
-
dom.BODY = -> DIV(WIKI())
|
|
4
|
-
</script>
|
|
5
1
|
<meta name="viewport" content="width=device-width,initial-scale=.62"/>
|
|
6
|
-
|
|
7
|
-
<script src="https://stateb.us/client6.js" server="none"></script>
|
|
8
2
|
<script src="https://invisible.college/js/marked.min.js"></script>
|
|
9
|
-
|
|
10
3
|
<script src="https://braid.org/code/myers-diff1.js"></script>
|
|
11
|
-
<script>
|
|
12
|
-
window.statebus_fetch = window.fetch
|
|
13
|
-
window.fetch = window.og_fetch
|
|
14
|
-
</script>
|
|
15
4
|
<script src="https://unpkg.com/braid-http@~1.3/braid-http-client.js"></script>
|
|
16
|
-
<script>
|
|
17
|
-
window.fetch = window.statebus_fetch
|
|
18
|
-
</script>
|
|
5
|
+
<script src="/web-utils.js"></script>
|
|
19
6
|
<script src="/simpleton-client.js"></script>
|
|
20
|
-
|
|
7
|
+
<body>
|
|
8
|
+
<div id="markdown_container" style="max-width: 750px; padding: 10px 60px"></div>
|
|
9
|
+
<div id="bottom_spacer" style="height: 50vh; display: none;"></div>
|
|
10
|
+
<textarea
|
|
11
|
+
id="the_editor"
|
|
12
|
+
style="
|
|
13
|
+
position: fixed;
|
|
14
|
+
bottom: 0;
|
|
15
|
+
right: 0;
|
|
16
|
+
width: 45vw;
|
|
17
|
+
height: 100%;
|
|
18
|
+
font-size: 15px;
|
|
19
|
+
font-family: helvetica, arial, avenir, lucida grande;
|
|
20
|
+
hyphens: none;
|
|
21
|
+
"></textarea>
|
|
22
|
+
<div
|
|
23
|
+
id="edit-button"
|
|
24
|
+
style="
|
|
25
|
+
position: fixed;
|
|
26
|
+
bottom: 0;
|
|
27
|
+
right: 0;
|
|
28
|
+
padding: 30px;
|
|
29
|
+
cursor: pointer;
|
|
30
|
+
text-decoration: none;
|
|
31
|
+
background-color: rgba(250, 250, 250, .5);
|
|
32
|
+
" onclick="toggle_editor()">edit</div>
|
|
33
|
+
</body>
|
|
21
34
|
<script>
|
|
22
35
|
|
|
23
|
-
var
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
};
|
|
36
|
+
var editing = false
|
|
37
|
+
var first_time = true
|
|
38
|
+
var render_timer = null
|
|
39
|
+
var render_delay = 100
|
|
28
40
|
|
|
29
41
|
var simpleton = simpleton_client(location.pathname, {
|
|
30
42
|
on_patches: (patches) => {
|
|
31
|
-
apply_patches_and_update_selection(
|
|
32
|
-
|
|
33
|
-
update_markdown_later();
|
|
34
|
-
},
|
|
35
|
-
get_patches: (prev_state) => diff(prev_state, t().value),
|
|
36
|
-
get_state: () => t().value,
|
|
37
|
-
|
|
38
|
-
on_error: (e) => {
|
|
39
|
-
t().disabled = true
|
|
40
|
-
t().style.background = '#fee'
|
|
41
|
-
t().style.border = '4px solid red'
|
|
43
|
+
apply_patches_and_update_selection(the_editor, patches)
|
|
44
|
+
update_markdown_later()
|
|
42
45
|
},
|
|
46
|
+
get_patches: (prev_state) => diff(prev_state, the_editor.value),
|
|
47
|
+
get_state: () => the_editor.value,
|
|
48
|
+
on_error: (e) => set_error_state(the_editor),
|
|
49
|
+
on_ack: () => set_acked_state(the_editor)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
the_editor.oninput = (e) => {
|
|
53
|
+
set_acked_state(the_editor, false)
|
|
54
|
+
simpleton.changed()
|
|
55
|
+
update_markdown_later()
|
|
56
|
+
}
|
|
43
57
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
window.statebus_ready.push(function() {
|
|
50
|
-
state.vert = true;
|
|
51
|
-
state.editing = false;
|
|
52
|
-
state.source = '';
|
|
53
|
-
// Toggle the editor with keyboard or edit button
|
|
54
|
-
document.body.onkeydown = function(e) {
|
|
55
|
-
if (e.keyCode === 27) { // Escape key
|
|
56
|
-
e.stopPropagation();
|
|
57
|
-
toggle_editor();
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
// Switch to vertical layout when you resize
|
|
61
|
-
window.onresize = function() {
|
|
62
|
-
return state.vert = window.innerWidth < 1200;
|
|
63
|
-
};
|
|
64
|
-
return onresize();
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
// Diffing and Patching Utilities
|
|
68
|
-
diff = function(before, after) {
|
|
69
|
-
var d, diff2, j, len, offset, p, patches;
|
|
70
|
-
diff2 = diff_main(before, after);
|
|
71
|
-
// Now we just need to reformat the output from diff_main into some
|
|
72
|
-
// nice json objects
|
|
73
|
-
patches = [];
|
|
74
|
-
offset = 0;
|
|
75
|
-
for (j = 0, len = diff2.length; j < len; j++) {
|
|
76
|
-
d = diff2[j];
|
|
77
|
-
p = null;
|
|
78
|
-
if (d[0] === 1) {
|
|
79
|
-
p = {
|
|
80
|
-
range: [offset, offset],
|
|
81
|
-
content: d[1]
|
|
82
|
-
};
|
|
83
|
-
} else if (d[0] === -1) {
|
|
84
|
-
p = {
|
|
85
|
-
range: [offset, offset + d[1].length],
|
|
86
|
-
content: ''
|
|
87
|
-
};
|
|
88
|
-
offset += d[1].length;
|
|
89
|
-
} else {
|
|
90
|
-
offset += d[1].length;
|
|
91
|
-
}
|
|
92
|
-
if (p) {
|
|
93
|
-
patches.push(p);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
return patches;
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
apply_patches_and_update_selection = function(textarea, patches) {
|
|
100
|
-
var i, j, k, l, len, len1, m, offset, original, p, range, ref, ref1, sel;
|
|
101
|
-
// convert from absolute to relative coordinates
|
|
102
|
-
offset = 0;
|
|
103
|
-
for (j = 0, len = patches.length; j < len; j++) {
|
|
104
|
-
p = patches[j];
|
|
105
|
-
p.range[0] += offset;
|
|
106
|
-
p.range[1] += offset;
|
|
107
|
-
offset -= p.range[1] - p.range[0];
|
|
108
|
-
offset += p.content.length;
|
|
109
|
-
}
|
|
110
|
-
original = textarea.value;
|
|
111
|
-
sel = [
|
|
112
|
-
textarea.selectionStart,
|
|
113
|
-
textarea.selectionEnd // Current cursor & selection
|
|
114
|
-
];
|
|
115
|
-
for (k = 0, len1 = patches.length; k < len1; k++) {
|
|
116
|
-
p = patches[k];
|
|
117
|
-
range = p.range;
|
|
118
|
-
// Update the cursor locations
|
|
119
|
-
for (i = l = 0, ref = sel.length; (0 <= ref ? l < ref : l > ref); i = 0 <= ref ? ++l : --l) {
|
|
120
|
-
if (sel[i] > range[0]) {
|
|
121
|
-
if (sel[i] > range[1]) {
|
|
122
|
-
sel[i] -= range[1] - range[0];
|
|
123
|
-
} else {
|
|
124
|
-
sel[i] = range[0];
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
for (i = m = 0, ref1 = sel.length; (0 <= ref1 ? m < ref1 : m > ref1); i = 0 <= ref1 ? ++m : --m) {
|
|
129
|
-
if (sel[i] > range[0]) {
|
|
130
|
-
sel[i] += p.content.length;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
// Update the text with the new value
|
|
134
|
-
original = original.substring(0, range[0]) + p.content + original.substring(range[1]);
|
|
135
|
-
}
|
|
136
|
-
textarea.value = original;
|
|
137
|
-
textarea.selectionStart = sel[0];
|
|
138
|
-
return textarea.selectionEnd = sel[1];
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
// Render everything
|
|
142
|
-
dom.WIKI = function() {
|
|
143
|
-
var o;
|
|
144
|
-
// output
|
|
145
|
-
return DIV({}, DIV({
|
|
146
|
-
className: 'pad',
|
|
147
|
-
maxWidth: 750,
|
|
148
|
-
width: state.editing && !state.vert ? '55vw' : void 0
|
|
149
|
-
}, (function() {
|
|
150
|
-
var j, len, ref, results;
|
|
151
|
-
ref = state.outputs || [];
|
|
152
|
-
results = [];
|
|
153
|
-
for (j = 0, len = ref.length; j < len; j++) {
|
|
154
|
-
o = ref[j];
|
|
155
|
-
results.push(DIV({
|
|
156
|
-
dangerouslySetInnerHTML: {
|
|
157
|
-
__html: o
|
|
158
|
-
}
|
|
159
|
-
}));
|
|
160
|
-
}
|
|
161
|
-
return results;
|
|
162
|
-
// bottom pad
|
|
163
|
-
})()), DIV({
|
|
164
|
-
height: '50vh',
|
|
165
|
-
display: !state.editing || !state.vert ? 'none' : void 0
|
|
166
|
-
}), TEXTAREA({
|
|
167
|
-
position: 'fixed',
|
|
168
|
-
hyphens: 'none',
|
|
169
|
-
bottom: 0,
|
|
170
|
-
right: 0,
|
|
171
|
-
width: state.vert ? '100%' : '45vw',
|
|
172
|
-
height: state.vert ? '50vh' : '100%',
|
|
173
|
-
display: !state.editing ? 'none' : void 0,
|
|
174
|
-
fontSize: 15,
|
|
175
|
-
fontFamily: 'helvetica, arial, avenir, lucida grande',
|
|
176
|
-
id: 'the editor',
|
|
177
|
-
onChange: function(e) {
|
|
178
|
-
if (!e.target.value && e.target.value !== '') {
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
// Bail on edits that try to wipe us out
|
|
182
|
-
state.source = e.target.value;
|
|
183
|
-
e.target.style.caretColor = 'red'
|
|
184
|
-
simpleton.changed();
|
|
185
|
-
return update_markdown_later();
|
|
186
|
-
},
|
|
187
|
-
defaultValue: state.source
|
|
188
|
-
}), DIV({
|
|
189
|
-
position: 'fixed',
|
|
190
|
-
bottom: 0,
|
|
191
|
-
right: 0,
|
|
192
|
-
padding: 30,
|
|
193
|
-
cursor: 'pointer',
|
|
194
|
-
textDecoration: 'none',
|
|
195
|
-
backgroundColor: 'rgba(250, 250, 250, .5)',
|
|
196
|
-
onClick: toggle_editor
|
|
197
|
-
}, 'edit'));
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
// Render markdown after a delay
|
|
201
|
-
timer = null;
|
|
202
|
-
|
|
203
|
-
render_delay = 100;
|
|
204
|
-
|
|
205
|
-
update_markdown_later = function() {
|
|
206
|
-
if (timer) {
|
|
207
|
-
clearTimeout(timer);
|
|
208
|
-
}
|
|
209
|
-
return timer = setTimeout(update_markdown, render_delay);
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
update_markdown = function() {
|
|
213
|
-
var e, i, j, len, parse_markdown, ref, results, s, sources;
|
|
214
|
-
parse_markdown = function() {
|
|
215
|
-
var match, matches;
|
|
216
|
-
matches = (function() {
|
|
217
|
-
var results;
|
|
218
|
-
results = [];
|
|
219
|
-
while (match = /\n\|{3,}([^\n]*)\n/g.exec(state.source)) {
|
|
220
|
-
results.push(match[1]);
|
|
221
|
-
}
|
|
222
|
-
return results;
|
|
223
|
-
})();
|
|
224
|
-
return matches;
|
|
225
|
-
};
|
|
226
|
-
try {
|
|
227
|
-
if (!state.source) {
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
sources = state.source.split(/\n\|{3,}[^\n]*\n/g);
|
|
231
|
-
timer = null;
|
|
232
|
-
if (!state.sources || sources.length !== state.sources.length) {
|
|
233
|
-
state.sources = sources.splice();
|
|
234
|
-
state.outputs = (function() {
|
|
235
|
-
var j, len, results;
|
|
236
|
-
results = [];
|
|
237
|
-
for (j = 0, len = sources.length; j < len; j++) {
|
|
238
|
-
s = sources[j];
|
|
239
|
-
results.push(marked(s, {
|
|
240
|
-
sanitize: false
|
|
241
|
-
}));
|
|
242
|
-
}
|
|
243
|
-
return results;
|
|
244
|
-
})();
|
|
245
|
-
return document.body.className = 'nopad';
|
|
246
|
-
} else {
|
|
247
|
-
ref = state.sources;
|
|
248
|
-
// But most of the time we just redo one section
|
|
249
|
-
results = [];
|
|
250
|
-
for (i = j = 0, len = ref.length; j < len; i = ++j) {
|
|
251
|
-
s = ref[i];
|
|
252
|
-
if (s !== sources[i]) {
|
|
253
|
-
state.sources[i] = sources[i];
|
|
254
|
-
results.push(state.outputs[i] = marked(s, {
|
|
255
|
-
sanitize: false
|
|
256
|
-
}));
|
|
257
|
-
} else {
|
|
258
|
-
results.push(void 0);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
return results;
|
|
262
|
-
}
|
|
263
|
-
} catch (error) {
|
|
264
|
-
e = error;
|
|
265
|
-
return console.error('parse failure with', e);
|
|
58
|
+
document.body.onkeydown = (e) => {
|
|
59
|
+
if (e.keyCode === 27) { // Escape key
|
|
60
|
+
e.stopPropagation()
|
|
61
|
+
toggle_editor()
|
|
266
62
|
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
update_markdown();
|
|
63
|
+
}
|
|
270
64
|
|
|
271
|
-
|
|
65
|
+
window.onresize = update_layout
|
|
272
66
|
|
|
273
|
-
|
|
274
|
-
state.editing = !state.editing;
|
|
275
|
-
if (state.editing) {
|
|
276
|
-
t().focus();
|
|
277
|
-
}
|
|
278
|
-
if (state.editing && first_time) {
|
|
279
|
-
first_time = false;
|
|
280
|
-
t().setSelectionRange(0, 0);
|
|
281
|
-
t().scrollTop = 0;
|
|
282
|
-
}
|
|
283
|
-
return update_markdown();
|
|
284
|
-
};
|
|
67
|
+
update_layout()
|
|
285
68
|
|
|
286
|
-
|
|
287
|
-
|
|
69
|
+
function update_layout() {
|
|
70
|
+
var vert = window.innerWidth < 1200
|
|
71
|
+
markdown_container.style.width = editing && !vert ? '55vw' : ''
|
|
72
|
+
bottom_spacer.style.display = !editing || !vert ? 'none' : ''
|
|
73
|
+
the_editor.style.width = vert ? '100%' : '45vw'
|
|
74
|
+
the_editor.style.height = vert ? '50vh' : '100%'
|
|
75
|
+
the_editor.style.display = !editing ? 'none' : ''
|
|
76
|
+
}
|
|
288
77
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
78
|
+
function toggle_editor() {
|
|
79
|
+
editing = !editing
|
|
80
|
+
update_layout()
|
|
81
|
+
if (editing) the_editor.focus()
|
|
82
|
+
if (editing && first_time) {
|
|
83
|
+
first_time = false
|
|
84
|
+
the_editor.setSelectionRange(0, 0)
|
|
85
|
+
the_editor.scrollTop = 0
|
|
293
86
|
}
|
|
294
|
-
|
|
295
|
-
return ting && ting.scrollIntoView();
|
|
296
|
-
};
|
|
87
|
+
}
|
|
297
88
|
|
|
298
|
-
|
|
299
|
-
|
|
89
|
+
function update_markdown_later() {
|
|
90
|
+
if (render_timer) clearTimeout(render_timer)
|
|
91
|
+
render_timer = setTimeout(update_markdown, render_delay)
|
|
300
92
|
}
|
|
301
93
|
|
|
302
|
-
|
|
94
|
+
function update_markdown() {
|
|
95
|
+
markdown_container.innerHTML = marked(the_editor.value, { sanitize: false })
|
|
96
|
+
}
|
|
303
97
|
|
|
304
|
-
|
|
305
|
-
<style>
|
|
306
|
-
body{-ms-hyphens: auto;-webkit-hyphens: auto;hyphens: auto;}
|
|
307
|
-
h1,h2,h3,h4 {text-align: left; -ms-hyphens: none; -webkit-hyphens: none; hyphens: none;}
|
|
308
|
-
note {position: absolute; left: 720px; width: 270px; background-color: #F8F3B7; padding: 10px; box-shadow: -2px 2px 2px #ccc; border-radius: 2px; text-align: left;}
|
|
309
|
-
</style>
|
|
98
|
+
</script>
|
package/package.json
CHANGED
package/server-demo.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
1
|
var port = process.argv[2] || 8888
|
|
3
|
-
|
|
4
2
|
var braid_text = require("./index.js")
|
|
5
3
|
|
|
6
4
|
// TODO: set a custom database folder
|
|
@@ -15,21 +13,16 @@ var server = require("http").createServer(async (req, res) => {
|
|
|
15
13
|
braid_text.free_cors(res)
|
|
16
14
|
if (req.method === 'OPTIONS') return res.end()
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
require("fs").createReadStream("./editor.html").pipe(res)
|
|
21
|
-
return
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
if (req.url.endsWith("?markdown-editor")) {
|
|
16
|
+
var q = req.url.split('?').slice(-1)[0]
|
|
17
|
+
if (q === 'editor' || q === 'markdown-editor') {
|
|
25
18
|
res.writeHead(200, { "Content-Type": "text/html", "Cache-Control": "no-cache" })
|
|
26
|
-
require("fs").createReadStream(
|
|
19
|
+
require("fs").createReadStream(`./${q}.html`).pipe(res)
|
|
27
20
|
return
|
|
28
21
|
}
|
|
29
22
|
|
|
30
|
-
if (req.url
|
|
23
|
+
if (req.url === '/simpleton-client.js' || req.url === '/web-utils.js') {
|
|
31
24
|
res.writeHead(200, { "Content-Type": "text/javascript", "Cache-Control": "no-cache" })
|
|
32
|
-
require("fs").createReadStream("
|
|
25
|
+
require("fs").createReadStream("." + req.url).pipe(res)
|
|
33
26
|
return
|
|
34
27
|
}
|
|
35
28
|
|
package/test/test.html
CHANGED
|
@@ -732,6 +732,35 @@ runTest(
|
|
|
732
732
|
'xy'
|
|
733
733
|
)
|
|
734
734
|
|
|
735
|
+
runTest(
|
|
736
|
+
"test put with transfer-encoding: dt",
|
|
737
|
+
async () => {
|
|
738
|
+
var key = 'test-' + Math.random().toString(36).slice(2)
|
|
739
|
+
var doc = new Doc('hi')
|
|
740
|
+
doc.ins(0, 'xy')
|
|
741
|
+
|
|
742
|
+
var bytes = doc.toBytes()
|
|
743
|
+
|
|
744
|
+
var r1 = await braid_fetch(`/eval`, {
|
|
745
|
+
method: 'PUT',
|
|
746
|
+
body: `void (async () => {
|
|
747
|
+
var key = '/${key}'
|
|
748
|
+
|
|
749
|
+
var {change_count} = await braid_text.put(key, {
|
|
750
|
+
body: new Uint8Array([${'' + bytes}]),
|
|
751
|
+
transfer_encoding: "dt"
|
|
752
|
+
})
|
|
753
|
+
var {body, version} = await braid_text.get(key, {})
|
|
754
|
+
|
|
755
|
+
res.end('' + change_count + " " + body + " " + version)
|
|
756
|
+
})()`
|
|
757
|
+
})
|
|
758
|
+
|
|
759
|
+
return await r1.text()
|
|
760
|
+
},
|
|
761
|
+
'2 xy hi-1'
|
|
762
|
+
)
|
|
763
|
+
|
|
735
764
|
runTest(
|
|
736
765
|
"test transfer-encoding dt (with parents)",
|
|
737
766
|
async () => {
|
package/web-utils.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
|
|
2
|
+
function set_acked_state(textarea, binary = true) {
|
|
3
|
+
if (!binary) {
|
|
4
|
+
textarea.old_caretColor = textarea.style.caretColor
|
|
5
|
+
|
|
6
|
+
textarea.style.caretColor = 'red'
|
|
7
|
+
} else {
|
|
8
|
+
textarea.style.caretColor = textarea.old_caretColor
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function set_error_state(textarea, binary = true) {
|
|
13
|
+
if (binary) {
|
|
14
|
+
textarea.old_disabled = textarea.disabled
|
|
15
|
+
textarea.old_background = textarea.style.background
|
|
16
|
+
textarea.old_border = textarea.style.border
|
|
17
|
+
|
|
18
|
+
textarea.disabled = true
|
|
19
|
+
textarea.style.background = '#fee'
|
|
20
|
+
textarea.style.border = '4px solid red'
|
|
21
|
+
} else {
|
|
22
|
+
textarea.disabled = textarea.old_disabled
|
|
23
|
+
textarea.style.background = textarea.old_background
|
|
24
|
+
textarea.style.border = textarea.old_border
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function diff(before, after) {
|
|
29
|
+
let diff = diff_main(before, after)
|
|
30
|
+
let patches = []
|
|
31
|
+
let offset = 0
|
|
32
|
+
for (let d of diff) {
|
|
33
|
+
let p = null
|
|
34
|
+
if (d[0] === 1) p = { range: [offset, offset], content: d[1] }
|
|
35
|
+
else if (d[0] === -1) {
|
|
36
|
+
p = { range: [offset, offset + d[1].length], content: "" }
|
|
37
|
+
offset += d[1].length
|
|
38
|
+
} else offset += d[1].length
|
|
39
|
+
if (p) {
|
|
40
|
+
p.unit = "text"
|
|
41
|
+
patches.push(p)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return patches
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function apply_patches_and_update_selection(textarea, patches) {
|
|
48
|
+
let offset = 0
|
|
49
|
+
for (let p of patches) {
|
|
50
|
+
p.range[0] += offset
|
|
51
|
+
p.range[1] += offset
|
|
52
|
+
offset -= p.range[1] - p.range[0]
|
|
53
|
+
offset += p.content.length
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let original = textarea.value
|
|
57
|
+
let sel = [textarea.selectionStart, textarea.selectionEnd]
|
|
58
|
+
|
|
59
|
+
for (var p of patches) {
|
|
60
|
+
let range = p.range
|
|
61
|
+
|
|
62
|
+
for (let i = 0; i < sel.length; i++)
|
|
63
|
+
if (sel[i] > range[0])
|
|
64
|
+
if (sel[i] > range[1]) sel[i] -= range[1] - range[0]
|
|
65
|
+
else sel[i] = range[0]
|
|
66
|
+
|
|
67
|
+
for (let i = 0; i < sel.length; i++) if (sel[i] > range[0]) sel[i] += p.content.length
|
|
68
|
+
|
|
69
|
+
original = original.substring(0, range[0]) + p.content + original.substring(range[1])
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
textarea.value = original
|
|
73
|
+
textarea.selectionStart = sel[0]
|
|
74
|
+
textarea.selectionEnd = sel[1]
|
|
75
|
+
}
|