braid-text 0.2.64 → 0.2.66

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.
@@ -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 apply_patches_and_update_selection, diff, first_time, i, j, render_delay, scroll, t, timer, ting, toggle_editor, update_markdown, update_markdown_later;
24
-
25
- t = function() {
26
- return document.getElementById('the editor');
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(t(), patches);
32
- state.source = t().value;
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
- on_ack: () => t().style.caretColor = ''
45
- });
46
-
47
- window.statebus_ready || (window.statebus_ready = []);
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
- first_time = true;
65
+ window.onresize = update_layout
272
66
 
273
- toggle_editor = function() {
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
- // Support #hashtag scrolling into view
287
- ting = null;
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
- scroll = function() {
290
- // We only scroll to the ting once -- if it's fresh
291
- if (ting || location.hash.length === 0) {
292
- return;
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
- ting = document.getElementById(location.hash.substr(1));
295
- return ting && ting.scrollIntoView();
296
- };
87
+ }
297
88
 
298
- for (i = j = 0; j <= 50; i = ++j) {
299
- setTimeout(scroll, i / 5.0 * 1000);
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
- </script>
94
+ function update_markdown() {
95
+ markdown_container.innerHTML = marked(the_editor.value, { sanitize: false })
96
+ }
303
97
 
304
- <link rel="stylesheet" href="https://invisible.college/css/github-markdown.css">
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "braid-text",
3
- "version": "0.2.64",
3
+ "version": "0.2.66",
4
4
  "description": "Library for collaborative text over http using braid.",
5
5
  "author": "Braid Working Group",
6
6
  "repository": "braid-org/braid-text",
package/server-demo.js CHANGED
@@ -13,21 +13,16 @@ var server = require("http").createServer(async (req, res) => {
13
13
  braid_text.free_cors(res)
14
14
  if (req.method === 'OPTIONS') return res.end()
15
15
 
16
- if (req.url.endsWith("?editor")) {
16
+ var q = req.url.split('?').slice(-1)[0]
17
+ if (q === 'editor' || q === 'markdown-editor') {
17
18
  res.writeHead(200, { "Content-Type": "text/html", "Cache-Control": "no-cache" })
18
- require("fs").createReadStream("./editor.html").pipe(res)
19
+ require("fs").createReadStream(`./${q}.html`).pipe(res)
19
20
  return
20
21
  }
21
22
 
22
- if (req.url.endsWith("?markdown-editor")) {
23
- res.writeHead(200, { "Content-Type": "text/html", "Cache-Control": "no-cache" })
24
- require("fs").createReadStream("./markdown-editor.html").pipe(res)
25
- return
26
- }
27
-
28
- if (req.url == '/simpleton-client.js') {
23
+ if (req.url === '/simpleton-client.js' || req.url === '/web-utils.js') {
29
24
  res.writeHead(200, { "Content-Type": "text/javascript", "Cache-Control": "no-cache" })
30
- require("fs").createReadStream("./simpleton-client.js").pipe(res)
25
+ require("fs").createReadStream("." + req.url).pipe(res)
31
26
  return
32
27
  }
33
28
 
package/test/test.html CHANGED
@@ -96,6 +96,69 @@ async function runTest(testName, testFunction, expectedResult) {
96
96
  }
97
97
  }
98
98
 
99
+ runTest(
100
+ "test updating meta data",
101
+ async () => {
102
+ var key = 'test-' + Math.random().toString(36).slice(2)
103
+
104
+ var r1 = await braid_fetch(`/eval`, {
105
+ method: 'PUT',
106
+ body: `void (async () => {
107
+ var resource = await braid_text.get_resource('/${key}')
108
+ resource.update_meta({ test_meta_info: 42 })
109
+ res.end(JSON.stringify(resource.meta))
110
+ })()`
111
+ })
112
+
113
+ return (await r1.text())
114
+ },
115
+ '{"test_meta_info":42}'
116
+ )
117
+
118
+ runTest(
119
+ "test updating meta data when do db_folder set",
120
+ async () => {
121
+ var key = 'test-' + Math.random().toString(36).slice(2)
122
+
123
+ var r1 = await braid_fetch(`/eval`, {
124
+ method: 'PUT',
125
+ body: `void (async () => {
126
+ var resource = await braid_text2.get_resource('/${key}')
127
+ resource.update_meta({ test_meta_info: 42 })
128
+ res.end(JSON.stringify(resource.meta))
129
+ })()`
130
+ })
131
+
132
+ return (await r1.text())
133
+ },
134
+ '{"test_meta_info":42}'
135
+ )
136
+
137
+ runTest(
138
+ "test loading a meta file from disk",
139
+ async () => {
140
+ var key = 'test-' + Math.random().toString(36).slice(2)
141
+
142
+ var r1 = await braid_fetch(`/eval`, {
143
+ method: 'PUT',
144
+ body: `void (async () => {
145
+ var resource = await braid_text.get_resource('/${key}')
146
+ resource.update_meta({ test_meta_info: 42 })
147
+
148
+ await new Promise(done => setTimeout(done, 200))
149
+
150
+ delete braid_text.cache['/${key}']
151
+
152
+ var resource = await braid_text.get_resource('/${key}')
153
+ res.end(JSON.stringify(resource.meta))
154
+ })()`
155
+ })
156
+
157
+ return (await r1.text())
158
+ },
159
+ '{"test_meta_info":42}'
160
+ )
161
+
99
162
  runTest(
100
163
  "test selection-sharing-prototype PUT and GET",
101
164
  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
+ }