marksmith 0.0.13 → 0.0.15
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.
- checksums.yaml +4 -4
- data/README.md +32 -20
- data/app/assets/config/marksmith_manifest.js +1 -0
- data/app/assets/javascripts/list_continuation_controller-full.esm.js +592 -0
- data/app/assets/javascripts/list_continuation_controller-no-stimulus.esm.js +102 -0
- data/app/assets/javascripts/marksmith_controller-full.esm.js +2939 -0
- data/app/assets/javascripts/marksmith_controller-no-stimulus.esm.js +2449 -0
- data/app/assets/stylesheets/marksmith.css +1 -1
- data/app/components/marksmith/markdown_field/edit_component.html.erb +8 -0
- data/app/components/marksmith/markdown_field/edit_component.rb +4 -0
- data/app/components/marksmith/markdown_field/show_component.html.erb +3 -0
- data/app/components/marksmith/markdown_field/show_component.rb +4 -0
- data/lib/marksmith/engine.rb +25 -0
- data/lib/marksmith/fields/markdown_field.rb +15 -0
- data/lib/marksmith/version.rb +1 -1
- metadata +11 -2
@@ -0,0 +1,2449 @@
|
|
1
|
+
/*!
|
2
|
+
Marksmith 0.0.15
|
3
|
+
*/
|
4
|
+
var MarksmithController = (function (stimulus) {
|
5
|
+
'use strict';
|
6
|
+
|
7
|
+
var __classPrivateFieldGet = (undefined && undefined.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
9
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
10
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
11
|
+
};
|
12
|
+
var _MarkdownHeaderButtonElement_instances, _MarkdownHeaderButtonElement_setLevelStyle;
|
13
|
+
const buttonSelectors = [
|
14
|
+
'[data-md-button]',
|
15
|
+
'md-header',
|
16
|
+
'md-bold',
|
17
|
+
'md-italic',
|
18
|
+
'md-quote',
|
19
|
+
'md-code',
|
20
|
+
'md-link',
|
21
|
+
'md-image',
|
22
|
+
'md-unordered-list',
|
23
|
+
'md-ordered-list',
|
24
|
+
'md-task-list',
|
25
|
+
'md-mention',
|
26
|
+
'md-ref',
|
27
|
+
'md-strikethrough'
|
28
|
+
];
|
29
|
+
function getButtons(toolbar) {
|
30
|
+
const els = [];
|
31
|
+
for (const button of toolbar.querySelectorAll(buttonSelectors.join(', '))) {
|
32
|
+
if (button.hidden || (button.offsetWidth <= 0 && button.offsetHeight <= 0))
|
33
|
+
continue;
|
34
|
+
if (button.closest('markdown-toolbar') === toolbar)
|
35
|
+
els.push(button);
|
36
|
+
}
|
37
|
+
return els;
|
38
|
+
}
|
39
|
+
function keydown(fn) {
|
40
|
+
return function (event) {
|
41
|
+
if (event.key === ' ' || event.key === 'Enter') {
|
42
|
+
fn(event);
|
43
|
+
}
|
44
|
+
};
|
45
|
+
}
|
46
|
+
const styles = new WeakMap();
|
47
|
+
const manualStyles = {
|
48
|
+
'header-1': { prefix: '# ' },
|
49
|
+
'header-2': { prefix: '## ' },
|
50
|
+
'header-3': { prefix: '### ' },
|
51
|
+
'header-4': { prefix: '#### ' },
|
52
|
+
'header-5': { prefix: '##### ' },
|
53
|
+
'header-6': { prefix: '###### ' },
|
54
|
+
bold: { prefix: '**', suffix: '**', trimFirst: true },
|
55
|
+
italic: { prefix: '_', suffix: '_', trimFirst: true },
|
56
|
+
quote: { prefix: '> ', multiline: true, surroundWithNewlines: true },
|
57
|
+
code: {
|
58
|
+
prefix: '`',
|
59
|
+
suffix: '`',
|
60
|
+
blockPrefix: '```',
|
61
|
+
blockSuffix: '```'
|
62
|
+
},
|
63
|
+
link: { prefix: '[', suffix: '](url)', replaceNext: 'url', scanFor: 'https?://' },
|
64
|
+
image: { prefix: '', replaceNext: 'url', scanFor: 'https?://' },
|
65
|
+
'unordered-list': {
|
66
|
+
prefix: '- ',
|
67
|
+
multiline: true,
|
68
|
+
unorderedList: true
|
69
|
+
},
|
70
|
+
'ordered-list': {
|
71
|
+
prefix: '1. ',
|
72
|
+
multiline: true,
|
73
|
+
orderedList: true
|
74
|
+
},
|
75
|
+
'task-list': { prefix: '- [ ] ', multiline: true, surroundWithNewlines: true },
|
76
|
+
mention: { prefix: '@', prefixSpace: true },
|
77
|
+
ref: { prefix: '#', prefixSpace: true },
|
78
|
+
strikethrough: { prefix: '~~', suffix: '~~', trimFirst: true }
|
79
|
+
};
|
80
|
+
class MarkdownButtonElement extends HTMLElement {
|
81
|
+
constructor() {
|
82
|
+
super();
|
83
|
+
const apply = (event) => {
|
84
|
+
const style = styles.get(this);
|
85
|
+
if (!style)
|
86
|
+
return;
|
87
|
+
event.preventDefault();
|
88
|
+
applyStyle(this, style);
|
89
|
+
};
|
90
|
+
this.addEventListener('keydown', keydown(apply));
|
91
|
+
this.addEventListener('click', apply);
|
92
|
+
}
|
93
|
+
connectedCallback() {
|
94
|
+
if (!this.hasAttribute('role')) {
|
95
|
+
this.setAttribute('role', 'button');
|
96
|
+
}
|
97
|
+
}
|
98
|
+
click() {
|
99
|
+
const style = styles.get(this);
|
100
|
+
if (!style)
|
101
|
+
return;
|
102
|
+
applyStyle(this, style);
|
103
|
+
}
|
104
|
+
}
|
105
|
+
class MarkdownHeaderButtonElement extends MarkdownButtonElement {
|
106
|
+
constructor() {
|
107
|
+
super(...arguments);
|
108
|
+
_MarkdownHeaderButtonElement_instances.add(this);
|
109
|
+
}
|
110
|
+
connectedCallback() {
|
111
|
+
const level = parseInt(this.getAttribute('level') || '3', 10);
|
112
|
+
__classPrivateFieldGet(this, _MarkdownHeaderButtonElement_instances, "m", _MarkdownHeaderButtonElement_setLevelStyle).call(this, level);
|
113
|
+
}
|
114
|
+
static get observedAttributes() {
|
115
|
+
return ['level'];
|
116
|
+
}
|
117
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
118
|
+
if (name !== 'level')
|
119
|
+
return;
|
120
|
+
const level = parseInt(newValue || '3', 10);
|
121
|
+
__classPrivateFieldGet(this, _MarkdownHeaderButtonElement_instances, "m", _MarkdownHeaderButtonElement_setLevelStyle).call(this, level);
|
122
|
+
}
|
123
|
+
}
|
124
|
+
_MarkdownHeaderButtonElement_instances = new WeakSet(), _MarkdownHeaderButtonElement_setLevelStyle = function _MarkdownHeaderButtonElement_setLevelStyle(level) {
|
125
|
+
if (level < 1 || level > 6) {
|
126
|
+
return;
|
127
|
+
}
|
128
|
+
const prefix = `${'#'.repeat(level)} `;
|
129
|
+
styles.set(this, {
|
130
|
+
prefix
|
131
|
+
});
|
132
|
+
};
|
133
|
+
if (!window.customElements.get('md-header')) {
|
134
|
+
window.MarkdownHeaderButtonElement = MarkdownHeaderButtonElement;
|
135
|
+
window.customElements.define('md-header', MarkdownHeaderButtonElement);
|
136
|
+
}
|
137
|
+
class MarkdownBoldButtonElement extends MarkdownButtonElement {
|
138
|
+
connectedCallback() {
|
139
|
+
styles.set(this, { prefix: '**', suffix: '**', trimFirst: true });
|
140
|
+
}
|
141
|
+
}
|
142
|
+
if (!window.customElements.get('md-bold')) {
|
143
|
+
window.MarkdownBoldButtonElement = MarkdownBoldButtonElement;
|
144
|
+
window.customElements.define('md-bold', MarkdownBoldButtonElement);
|
145
|
+
}
|
146
|
+
class MarkdownItalicButtonElement extends MarkdownButtonElement {
|
147
|
+
connectedCallback() {
|
148
|
+
styles.set(this, { prefix: '_', suffix: '_', trimFirst: true });
|
149
|
+
}
|
150
|
+
}
|
151
|
+
if (!window.customElements.get('md-italic')) {
|
152
|
+
window.MarkdownItalicButtonElement = MarkdownItalicButtonElement;
|
153
|
+
window.customElements.define('md-italic', MarkdownItalicButtonElement);
|
154
|
+
}
|
155
|
+
class MarkdownQuoteButtonElement extends MarkdownButtonElement {
|
156
|
+
connectedCallback() {
|
157
|
+
styles.set(this, { prefix: '> ', multiline: true, surroundWithNewlines: true });
|
158
|
+
}
|
159
|
+
}
|
160
|
+
if (!window.customElements.get('md-quote')) {
|
161
|
+
window.MarkdownQuoteButtonElement = MarkdownQuoteButtonElement;
|
162
|
+
window.customElements.define('md-quote', MarkdownQuoteButtonElement);
|
163
|
+
}
|
164
|
+
class MarkdownCodeButtonElement extends MarkdownButtonElement {
|
165
|
+
connectedCallback() {
|
166
|
+
styles.set(this, { prefix: '`', suffix: '`', blockPrefix: '```', blockSuffix: '```' });
|
167
|
+
}
|
168
|
+
}
|
169
|
+
if (!window.customElements.get('md-code')) {
|
170
|
+
window.MarkdownCodeButtonElement = MarkdownCodeButtonElement;
|
171
|
+
window.customElements.define('md-code', MarkdownCodeButtonElement);
|
172
|
+
}
|
173
|
+
class MarkdownLinkButtonElement extends MarkdownButtonElement {
|
174
|
+
connectedCallback() {
|
175
|
+
styles.set(this, { prefix: '[', suffix: '](url)', replaceNext: 'url', scanFor: 'https?://' });
|
176
|
+
}
|
177
|
+
}
|
178
|
+
if (!window.customElements.get('md-link')) {
|
179
|
+
window.MarkdownLinkButtonElement = MarkdownLinkButtonElement;
|
180
|
+
window.customElements.define('md-link', MarkdownLinkButtonElement);
|
181
|
+
}
|
182
|
+
class MarkdownImageButtonElement extends MarkdownButtonElement {
|
183
|
+
connectedCallback() {
|
184
|
+
styles.set(this, { prefix: '', replaceNext: 'url', scanFor: 'https?://' });
|
185
|
+
}
|
186
|
+
}
|
187
|
+
if (!window.customElements.get('md-image')) {
|
188
|
+
window.MarkdownImageButtonElement = MarkdownImageButtonElement;
|
189
|
+
window.customElements.define('md-image', MarkdownImageButtonElement);
|
190
|
+
}
|
191
|
+
class MarkdownUnorderedListButtonElement extends MarkdownButtonElement {
|
192
|
+
connectedCallback() {
|
193
|
+
styles.set(this, { prefix: '- ', multiline: true, unorderedList: true });
|
194
|
+
}
|
195
|
+
}
|
196
|
+
if (!window.customElements.get('md-unordered-list')) {
|
197
|
+
window.MarkdownUnorderedListButtonElement = MarkdownUnorderedListButtonElement;
|
198
|
+
window.customElements.define('md-unordered-list', MarkdownUnorderedListButtonElement);
|
199
|
+
}
|
200
|
+
class MarkdownOrderedListButtonElement extends MarkdownButtonElement {
|
201
|
+
connectedCallback() {
|
202
|
+
styles.set(this, { prefix: '1. ', multiline: true, orderedList: true });
|
203
|
+
}
|
204
|
+
}
|
205
|
+
if (!window.customElements.get('md-ordered-list')) {
|
206
|
+
window.MarkdownOrderedListButtonElement = MarkdownOrderedListButtonElement;
|
207
|
+
window.customElements.define('md-ordered-list', MarkdownOrderedListButtonElement);
|
208
|
+
}
|
209
|
+
class MarkdownTaskListButtonElement extends MarkdownButtonElement {
|
210
|
+
connectedCallback() {
|
211
|
+
styles.set(this, { prefix: '- [ ] ', multiline: true, surroundWithNewlines: true });
|
212
|
+
}
|
213
|
+
}
|
214
|
+
if (!window.customElements.get('md-task-list')) {
|
215
|
+
window.MarkdownTaskListButtonElement = MarkdownTaskListButtonElement;
|
216
|
+
window.customElements.define('md-task-list', MarkdownTaskListButtonElement);
|
217
|
+
}
|
218
|
+
class MarkdownMentionButtonElement extends MarkdownButtonElement {
|
219
|
+
connectedCallback() {
|
220
|
+
styles.set(this, { prefix: '@', prefixSpace: true });
|
221
|
+
}
|
222
|
+
}
|
223
|
+
if (!window.customElements.get('md-mention')) {
|
224
|
+
window.MarkdownMentionButtonElement = MarkdownMentionButtonElement;
|
225
|
+
window.customElements.define('md-mention', MarkdownMentionButtonElement);
|
226
|
+
}
|
227
|
+
class MarkdownRefButtonElement extends MarkdownButtonElement {
|
228
|
+
connectedCallback() {
|
229
|
+
styles.set(this, { prefix: '#', prefixSpace: true });
|
230
|
+
}
|
231
|
+
}
|
232
|
+
if (!window.customElements.get('md-ref')) {
|
233
|
+
window.MarkdownRefButtonElement = MarkdownRefButtonElement;
|
234
|
+
window.customElements.define('md-ref', MarkdownRefButtonElement);
|
235
|
+
}
|
236
|
+
class MarkdownStrikethroughButtonElement extends MarkdownButtonElement {
|
237
|
+
connectedCallback() {
|
238
|
+
styles.set(this, { prefix: '~~', suffix: '~~', trimFirst: true });
|
239
|
+
}
|
240
|
+
}
|
241
|
+
if (!window.customElements.get('md-strikethrough')) {
|
242
|
+
window.MarkdownStrikethroughButtonElement = MarkdownStrikethroughButtonElement;
|
243
|
+
window.customElements.define('md-strikethrough', MarkdownStrikethroughButtonElement);
|
244
|
+
}
|
245
|
+
function applyFromToolbar(event) {
|
246
|
+
const { target, currentTarget } = event;
|
247
|
+
if (!(target instanceof Element))
|
248
|
+
return;
|
249
|
+
const mdButton = target.closest('[data-md-button]');
|
250
|
+
if (!mdButton || mdButton.closest('markdown-toolbar') !== currentTarget)
|
251
|
+
return;
|
252
|
+
const mdButtonStyle = mdButton.getAttribute('data-md-button');
|
253
|
+
const style = manualStyles[mdButtonStyle];
|
254
|
+
if (!style)
|
255
|
+
return;
|
256
|
+
event.preventDefault();
|
257
|
+
applyStyle(target, style);
|
258
|
+
}
|
259
|
+
function setFocusManagement(toolbar) {
|
260
|
+
toolbar.addEventListener('keydown', focusKeydown);
|
261
|
+
toolbar.setAttribute('tabindex', '0');
|
262
|
+
toolbar.addEventListener('focus', onToolbarFocus, { once: true });
|
263
|
+
}
|
264
|
+
function unsetFocusManagement(toolbar) {
|
265
|
+
toolbar.removeEventListener('keydown', focusKeydown);
|
266
|
+
toolbar.removeAttribute('tabindex');
|
267
|
+
toolbar.removeEventListener('focus', onToolbarFocus);
|
268
|
+
}
|
269
|
+
class MarkdownToolbarElement extends HTMLElement {
|
270
|
+
connectedCallback() {
|
271
|
+
if (!this.hasAttribute('role')) {
|
272
|
+
this.setAttribute('role', 'toolbar');
|
273
|
+
}
|
274
|
+
if (!this.hasAttribute('data-no-focus')) {
|
275
|
+
setFocusManagement(this);
|
276
|
+
}
|
277
|
+
this.addEventListener('keydown', keydown(applyFromToolbar));
|
278
|
+
this.addEventListener('click', applyFromToolbar);
|
279
|
+
}
|
280
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
281
|
+
if (name !== 'data-no-focus')
|
282
|
+
return;
|
283
|
+
if (newValue === null) {
|
284
|
+
setFocusManagement(this);
|
285
|
+
}
|
286
|
+
else {
|
287
|
+
unsetFocusManagement(this);
|
288
|
+
}
|
289
|
+
}
|
290
|
+
disconnectedCallback() {
|
291
|
+
unsetFocusManagement(this);
|
292
|
+
}
|
293
|
+
get field() {
|
294
|
+
const id = this.getAttribute('for');
|
295
|
+
if (!id)
|
296
|
+
return null;
|
297
|
+
const root = 'getRootNode' in this ? this.getRootNode() : document;
|
298
|
+
let field;
|
299
|
+
if (root instanceof Document || root instanceof ShadowRoot) {
|
300
|
+
field = root.getElementById(id);
|
301
|
+
}
|
302
|
+
return field instanceof HTMLTextAreaElement ? field : null;
|
303
|
+
}
|
304
|
+
}
|
305
|
+
MarkdownToolbarElement.observedAttributes = ['data-no-focus'];
|
306
|
+
function onToolbarFocus({ target }) {
|
307
|
+
if (!(target instanceof Element))
|
308
|
+
return;
|
309
|
+
target.removeAttribute('tabindex');
|
310
|
+
let tabindex = '0';
|
311
|
+
for (const button of getButtons(target)) {
|
312
|
+
button.setAttribute('tabindex', tabindex);
|
313
|
+
if (tabindex === '0') {
|
314
|
+
button.focus();
|
315
|
+
tabindex = '-1';
|
316
|
+
}
|
317
|
+
}
|
318
|
+
}
|
319
|
+
function focusKeydown(event) {
|
320
|
+
const key = event.key;
|
321
|
+
if (key !== 'ArrowRight' && key !== 'ArrowLeft' && key !== 'Home' && key !== 'End')
|
322
|
+
return;
|
323
|
+
const toolbar = event.currentTarget;
|
324
|
+
if (!(toolbar instanceof HTMLElement))
|
325
|
+
return;
|
326
|
+
const buttons = getButtons(toolbar);
|
327
|
+
const index = buttons.indexOf(event.target);
|
328
|
+
const length = buttons.length;
|
329
|
+
if (index === -1)
|
330
|
+
return;
|
331
|
+
let n = 0;
|
332
|
+
if (key === 'ArrowLeft')
|
333
|
+
n = index - 1;
|
334
|
+
if (key === 'ArrowRight')
|
335
|
+
n = index + 1;
|
336
|
+
if (key === 'End')
|
337
|
+
n = length - 1;
|
338
|
+
if (n < 0)
|
339
|
+
n = length - 1;
|
340
|
+
if (n > length - 1)
|
341
|
+
n = 0;
|
342
|
+
for (let i = 0; i < length; i += 1) {
|
343
|
+
buttons[i].setAttribute('tabindex', i === n ? '0' : '-1');
|
344
|
+
}
|
345
|
+
event.preventDefault();
|
346
|
+
buttons[n].focus();
|
347
|
+
}
|
348
|
+
if (!window.customElements.get('markdown-toolbar')) {
|
349
|
+
window.MarkdownToolbarElement = MarkdownToolbarElement;
|
350
|
+
window.customElements.define('markdown-toolbar', MarkdownToolbarElement);
|
351
|
+
}
|
352
|
+
function isMultipleLines(string) {
|
353
|
+
return string.trim().split('\n').length > 1;
|
354
|
+
}
|
355
|
+
function repeat(string, n) {
|
356
|
+
return Array(n + 1).join(string);
|
357
|
+
}
|
358
|
+
function wordSelectionStart(text, i) {
|
359
|
+
let index = i;
|
360
|
+
while (text[index] && text[index - 1] != null && !text[index - 1].match(/\s/)) {
|
361
|
+
index--;
|
362
|
+
}
|
363
|
+
return index;
|
364
|
+
}
|
365
|
+
function wordSelectionEnd(text, i, multiline) {
|
366
|
+
let index = i;
|
367
|
+
const breakpoint = multiline ? /\n/ : /\s/;
|
368
|
+
while (text[index] && !text[index].match(breakpoint)) {
|
369
|
+
index++;
|
370
|
+
}
|
371
|
+
return index;
|
372
|
+
}
|
373
|
+
let canInsertText = null;
|
374
|
+
function insertText$1(textarea, { text, selectionStart, selectionEnd }) {
|
375
|
+
const originalSelectionStart = textarea.selectionStart;
|
376
|
+
const before = textarea.value.slice(0, originalSelectionStart);
|
377
|
+
const after = textarea.value.slice(textarea.selectionEnd);
|
378
|
+
if (canInsertText === null || canInsertText === true) {
|
379
|
+
textarea.contentEditable = 'true';
|
380
|
+
try {
|
381
|
+
canInsertText = document.execCommand('insertText', false, text);
|
382
|
+
}
|
383
|
+
catch (error) {
|
384
|
+
canInsertText = false;
|
385
|
+
}
|
386
|
+
textarea.contentEditable = 'false';
|
387
|
+
}
|
388
|
+
if (canInsertText && !textarea.value.slice(0, textarea.selectionStart).endsWith(text)) {
|
389
|
+
canInsertText = false;
|
390
|
+
}
|
391
|
+
if (!canInsertText) {
|
392
|
+
try {
|
393
|
+
document.execCommand('ms-beginUndoUnit');
|
394
|
+
}
|
395
|
+
catch (e) {
|
396
|
+
}
|
397
|
+
textarea.value = before + text + after;
|
398
|
+
try {
|
399
|
+
document.execCommand('ms-endUndoUnit');
|
400
|
+
}
|
401
|
+
catch (e) {
|
402
|
+
}
|
403
|
+
textarea.dispatchEvent(new CustomEvent('input', { bubbles: true, cancelable: true }));
|
404
|
+
}
|
405
|
+
if (selectionStart != null && selectionEnd != null) {
|
406
|
+
textarea.setSelectionRange(selectionStart, selectionEnd);
|
407
|
+
}
|
408
|
+
else {
|
409
|
+
textarea.setSelectionRange(originalSelectionStart, textarea.selectionEnd);
|
410
|
+
}
|
411
|
+
}
|
412
|
+
function styleSelectedText(textarea, styleArgs) {
|
413
|
+
const text = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
|
414
|
+
let result;
|
415
|
+
if (styleArgs.orderedList || styleArgs.unorderedList) {
|
416
|
+
result = listStyle(textarea, styleArgs);
|
417
|
+
}
|
418
|
+
else if (styleArgs.multiline && isMultipleLines(text)) {
|
419
|
+
result = multilineStyle(textarea, styleArgs);
|
420
|
+
}
|
421
|
+
else {
|
422
|
+
result = blockStyle(textarea, styleArgs);
|
423
|
+
}
|
424
|
+
insertText$1(textarea, result);
|
425
|
+
}
|
426
|
+
function expandSelectionToLine(textarea) {
|
427
|
+
const lines = textarea.value.split('\n');
|
428
|
+
let counter = 0;
|
429
|
+
for (let index = 0; index < lines.length; index++) {
|
430
|
+
const lineLength = lines[index].length + 1;
|
431
|
+
if (textarea.selectionStart >= counter && textarea.selectionStart < counter + lineLength) {
|
432
|
+
textarea.selectionStart = counter;
|
433
|
+
}
|
434
|
+
if (textarea.selectionEnd >= counter && textarea.selectionEnd < counter + lineLength) {
|
435
|
+
textarea.selectionEnd = counter + lineLength - 1;
|
436
|
+
}
|
437
|
+
counter += lineLength;
|
438
|
+
}
|
439
|
+
}
|
440
|
+
function expandSelectedText(textarea, prefixToUse, suffixToUse, multiline = false) {
|
441
|
+
if (textarea.selectionStart === textarea.selectionEnd) {
|
442
|
+
textarea.selectionStart = wordSelectionStart(textarea.value, textarea.selectionStart);
|
443
|
+
textarea.selectionEnd = wordSelectionEnd(textarea.value, textarea.selectionEnd, multiline);
|
444
|
+
}
|
445
|
+
else {
|
446
|
+
const expandedSelectionStart = textarea.selectionStart - prefixToUse.length;
|
447
|
+
const expandedSelectionEnd = textarea.selectionEnd + suffixToUse.length;
|
448
|
+
const beginsWithPrefix = textarea.value.slice(expandedSelectionStart, textarea.selectionStart) === prefixToUse;
|
449
|
+
const endsWithSuffix = textarea.value.slice(textarea.selectionEnd, expandedSelectionEnd) === suffixToUse;
|
450
|
+
if (beginsWithPrefix && endsWithSuffix) {
|
451
|
+
textarea.selectionStart = expandedSelectionStart;
|
452
|
+
textarea.selectionEnd = expandedSelectionEnd;
|
453
|
+
}
|
454
|
+
}
|
455
|
+
return textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
|
456
|
+
}
|
457
|
+
function newlinesToSurroundSelectedText(textarea) {
|
458
|
+
const beforeSelection = textarea.value.slice(0, textarea.selectionStart);
|
459
|
+
const afterSelection = textarea.value.slice(textarea.selectionEnd);
|
460
|
+
const breaksBefore = beforeSelection.match(/\n*$/);
|
461
|
+
const breaksAfter = afterSelection.match(/^\n*/);
|
462
|
+
const newlinesBeforeSelection = breaksBefore ? breaksBefore[0].length : 0;
|
463
|
+
const newlinesAfterSelection = breaksAfter ? breaksAfter[0].length : 0;
|
464
|
+
let newlinesToAppend;
|
465
|
+
let newlinesToPrepend;
|
466
|
+
if (beforeSelection.match(/\S/) && newlinesBeforeSelection < 2) {
|
467
|
+
newlinesToAppend = repeat('\n', 2 - newlinesBeforeSelection);
|
468
|
+
}
|
469
|
+
if (afterSelection.match(/\S/) && newlinesAfterSelection < 2) {
|
470
|
+
newlinesToPrepend = repeat('\n', 2 - newlinesAfterSelection);
|
471
|
+
}
|
472
|
+
if (newlinesToAppend == null) {
|
473
|
+
newlinesToAppend = '';
|
474
|
+
}
|
475
|
+
if (newlinesToPrepend == null) {
|
476
|
+
newlinesToPrepend = '';
|
477
|
+
}
|
478
|
+
return { newlinesToAppend, newlinesToPrepend };
|
479
|
+
}
|
480
|
+
function blockStyle(textarea, arg) {
|
481
|
+
let newlinesToAppend;
|
482
|
+
let newlinesToPrepend;
|
483
|
+
const { prefix, suffix, blockPrefix, blockSuffix, replaceNext, prefixSpace, scanFor, surroundWithNewlines } = arg;
|
484
|
+
const originalSelectionStart = textarea.selectionStart;
|
485
|
+
const originalSelectionEnd = textarea.selectionEnd;
|
486
|
+
let selectedText = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
|
487
|
+
let prefixToUse = isMultipleLines(selectedText) && blockPrefix.length > 0 ? `${blockPrefix}\n` : prefix;
|
488
|
+
let suffixToUse = isMultipleLines(selectedText) && blockSuffix.length > 0 ? `\n${blockSuffix}` : suffix;
|
489
|
+
if (prefixSpace) {
|
490
|
+
const beforeSelection = textarea.value[textarea.selectionStart - 1];
|
491
|
+
if (textarea.selectionStart !== 0 && beforeSelection != null && !beforeSelection.match(/\s/)) {
|
492
|
+
prefixToUse = ` ${prefixToUse}`;
|
493
|
+
}
|
494
|
+
}
|
495
|
+
selectedText = expandSelectedText(textarea, prefixToUse, suffixToUse, arg.multiline);
|
496
|
+
let selectionStart = textarea.selectionStart;
|
497
|
+
let selectionEnd = textarea.selectionEnd;
|
498
|
+
const hasReplaceNext = replaceNext.length > 0 && suffixToUse.indexOf(replaceNext) > -1 && selectedText.length > 0;
|
499
|
+
if (surroundWithNewlines) {
|
500
|
+
const ref = newlinesToSurroundSelectedText(textarea);
|
501
|
+
newlinesToAppend = ref.newlinesToAppend;
|
502
|
+
newlinesToPrepend = ref.newlinesToPrepend;
|
503
|
+
prefixToUse = newlinesToAppend + prefix;
|
504
|
+
suffixToUse += newlinesToPrepend;
|
505
|
+
}
|
506
|
+
if (selectedText.startsWith(prefixToUse) && selectedText.endsWith(suffixToUse)) {
|
507
|
+
const replacementText = selectedText.slice(prefixToUse.length, selectedText.length - suffixToUse.length);
|
508
|
+
if (originalSelectionStart === originalSelectionEnd) {
|
509
|
+
let position = originalSelectionStart - prefixToUse.length;
|
510
|
+
position = Math.max(position, selectionStart);
|
511
|
+
position = Math.min(position, selectionStart + replacementText.length);
|
512
|
+
selectionStart = selectionEnd = position;
|
513
|
+
}
|
514
|
+
else {
|
515
|
+
selectionEnd = selectionStart + replacementText.length;
|
516
|
+
}
|
517
|
+
return { text: replacementText, selectionStart, selectionEnd };
|
518
|
+
}
|
519
|
+
else if (!hasReplaceNext) {
|
520
|
+
let replacementText = prefixToUse + selectedText + suffixToUse;
|
521
|
+
selectionStart = originalSelectionStart + prefixToUse.length;
|
522
|
+
selectionEnd = originalSelectionEnd + prefixToUse.length;
|
523
|
+
const whitespaceEdges = selectedText.match(/^\s*|\s*$/g);
|
524
|
+
if (arg.trimFirst && whitespaceEdges) {
|
525
|
+
const leadingWhitespace = whitespaceEdges[0] || '';
|
526
|
+
const trailingWhitespace = whitespaceEdges[1] || '';
|
527
|
+
replacementText = leadingWhitespace + prefixToUse + selectedText.trim() + suffixToUse + trailingWhitespace;
|
528
|
+
selectionStart += leadingWhitespace.length;
|
529
|
+
selectionEnd -= trailingWhitespace.length;
|
530
|
+
}
|
531
|
+
return { text: replacementText, selectionStart, selectionEnd };
|
532
|
+
}
|
533
|
+
else if (scanFor.length > 0 && selectedText.match(scanFor)) {
|
534
|
+
suffixToUse = suffixToUse.replace(replaceNext, selectedText);
|
535
|
+
const replacementText = prefixToUse + suffixToUse;
|
536
|
+
selectionStart = selectionEnd = selectionStart + prefixToUse.length;
|
537
|
+
return { text: replacementText, selectionStart, selectionEnd };
|
538
|
+
}
|
539
|
+
else {
|
540
|
+
const replacementText = prefixToUse + selectedText + suffixToUse;
|
541
|
+
selectionStart = selectionStart + prefixToUse.length + selectedText.length + suffixToUse.indexOf(replaceNext);
|
542
|
+
selectionEnd = selectionStart + replaceNext.length;
|
543
|
+
return { text: replacementText, selectionStart, selectionEnd };
|
544
|
+
}
|
545
|
+
}
|
546
|
+
function multilineStyle(textarea, arg) {
|
547
|
+
const { prefix, suffix, surroundWithNewlines } = arg;
|
548
|
+
let text = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
|
549
|
+
let selectionStart = textarea.selectionStart;
|
550
|
+
let selectionEnd = textarea.selectionEnd;
|
551
|
+
const lines = text.split('\n');
|
552
|
+
const undoStyle = lines.every(line => line.startsWith(prefix) && line.endsWith(suffix));
|
553
|
+
if (undoStyle) {
|
554
|
+
text = lines.map(line => line.slice(prefix.length, line.length - suffix.length)).join('\n');
|
555
|
+
selectionEnd = selectionStart + text.length;
|
556
|
+
}
|
557
|
+
else {
|
558
|
+
text = lines.map(line => prefix + line + suffix).join('\n');
|
559
|
+
if (surroundWithNewlines) {
|
560
|
+
const { newlinesToAppend, newlinesToPrepend } = newlinesToSurroundSelectedText(textarea);
|
561
|
+
selectionStart += newlinesToAppend.length;
|
562
|
+
selectionEnd = selectionStart + text.length;
|
563
|
+
text = newlinesToAppend + text + newlinesToPrepend;
|
564
|
+
}
|
565
|
+
}
|
566
|
+
return { text, selectionStart, selectionEnd };
|
567
|
+
}
|
568
|
+
function undoOrderedListStyle(text) {
|
569
|
+
const lines = text.split('\n');
|
570
|
+
const orderedListRegex = /^\d+\.\s+/;
|
571
|
+
const shouldUndoOrderedList = lines.every(line => orderedListRegex.test(line));
|
572
|
+
let result = lines;
|
573
|
+
if (shouldUndoOrderedList) {
|
574
|
+
result = lines.map(line => line.replace(orderedListRegex, ''));
|
575
|
+
}
|
576
|
+
return {
|
577
|
+
text: result.join('\n'),
|
578
|
+
processed: shouldUndoOrderedList
|
579
|
+
};
|
580
|
+
}
|
581
|
+
function undoUnorderedListStyle(text) {
|
582
|
+
const lines = text.split('\n');
|
583
|
+
const unorderedListPrefix = '- ';
|
584
|
+
const shouldUndoUnorderedList = lines.every(line => line.startsWith(unorderedListPrefix));
|
585
|
+
let result = lines;
|
586
|
+
if (shouldUndoUnorderedList) {
|
587
|
+
result = lines.map(line => line.slice(unorderedListPrefix.length, line.length));
|
588
|
+
}
|
589
|
+
return {
|
590
|
+
text: result.join('\n'),
|
591
|
+
processed: shouldUndoUnorderedList
|
592
|
+
};
|
593
|
+
}
|
594
|
+
function makePrefix(index, unorderedList) {
|
595
|
+
if (unorderedList) {
|
596
|
+
return '- ';
|
597
|
+
}
|
598
|
+
else {
|
599
|
+
return `${index + 1}. `;
|
600
|
+
}
|
601
|
+
}
|
602
|
+
function clearExistingListStyle(style, selectedText) {
|
603
|
+
let undoResultOpositeList;
|
604
|
+
let undoResult;
|
605
|
+
let pristineText;
|
606
|
+
if (style.orderedList) {
|
607
|
+
undoResult = undoOrderedListStyle(selectedText);
|
608
|
+
undoResultOpositeList = undoUnorderedListStyle(undoResult.text);
|
609
|
+
pristineText = undoResultOpositeList.text;
|
610
|
+
}
|
611
|
+
else {
|
612
|
+
undoResult = undoUnorderedListStyle(selectedText);
|
613
|
+
undoResultOpositeList = undoOrderedListStyle(undoResult.text);
|
614
|
+
pristineText = undoResultOpositeList.text;
|
615
|
+
}
|
616
|
+
return [undoResult, undoResultOpositeList, pristineText];
|
617
|
+
}
|
618
|
+
function listStyle(textarea, style) {
|
619
|
+
const noInitialSelection = textarea.selectionStart === textarea.selectionEnd;
|
620
|
+
let selectionStart = textarea.selectionStart;
|
621
|
+
let selectionEnd = textarea.selectionEnd;
|
622
|
+
expandSelectionToLine(textarea);
|
623
|
+
const selectedText = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
|
624
|
+
const [undoResult, undoResultOpositeList, pristineText] = clearExistingListStyle(style, selectedText);
|
625
|
+
const prefixedLines = pristineText.split('\n').map((value, index) => {
|
626
|
+
return `${makePrefix(index, style.unorderedList)}${value}`;
|
627
|
+
});
|
628
|
+
const totalPrefixLength = prefixedLines.reduce((previousValue, _currentValue, currentIndex) => {
|
629
|
+
return previousValue + makePrefix(currentIndex, style.unorderedList).length;
|
630
|
+
}, 0);
|
631
|
+
const totalPrefixLengthOpositeList = prefixedLines.reduce((previousValue, _currentValue, currentIndex) => {
|
632
|
+
return previousValue + makePrefix(currentIndex, !style.unorderedList).length;
|
633
|
+
}, 0);
|
634
|
+
if (undoResult.processed) {
|
635
|
+
if (noInitialSelection) {
|
636
|
+
selectionStart = Math.max(selectionStart - makePrefix(0, style.unorderedList).length, 0);
|
637
|
+
selectionEnd = selectionStart;
|
638
|
+
}
|
639
|
+
else {
|
640
|
+
selectionStart = textarea.selectionStart;
|
641
|
+
selectionEnd = textarea.selectionEnd - totalPrefixLength;
|
642
|
+
}
|
643
|
+
return { text: pristineText, selectionStart, selectionEnd };
|
644
|
+
}
|
645
|
+
const { newlinesToAppend, newlinesToPrepend } = newlinesToSurroundSelectedText(textarea);
|
646
|
+
const text = newlinesToAppend + prefixedLines.join('\n') + newlinesToPrepend;
|
647
|
+
if (noInitialSelection) {
|
648
|
+
selectionStart = Math.max(selectionStart + makePrefix(0, style.unorderedList).length + newlinesToAppend.length, 0);
|
649
|
+
selectionEnd = selectionStart;
|
650
|
+
}
|
651
|
+
else {
|
652
|
+
if (undoResultOpositeList.processed) {
|
653
|
+
selectionStart = Math.max(textarea.selectionStart + newlinesToAppend.length, 0);
|
654
|
+
selectionEnd = textarea.selectionEnd + newlinesToAppend.length + totalPrefixLength - totalPrefixLengthOpositeList;
|
655
|
+
}
|
656
|
+
else {
|
657
|
+
selectionStart = Math.max(textarea.selectionStart + newlinesToAppend.length, 0);
|
658
|
+
selectionEnd = textarea.selectionEnd + newlinesToAppend.length + totalPrefixLength;
|
659
|
+
}
|
660
|
+
}
|
661
|
+
return { text, selectionStart, selectionEnd };
|
662
|
+
}
|
663
|
+
function applyStyle(button, stylesToApply) {
|
664
|
+
const toolbar = button.closest('markdown-toolbar');
|
665
|
+
if (!(toolbar instanceof MarkdownToolbarElement))
|
666
|
+
return;
|
667
|
+
const defaults = {
|
668
|
+
prefix: '',
|
669
|
+
suffix: '',
|
670
|
+
blockPrefix: '',
|
671
|
+
blockSuffix: '',
|
672
|
+
multiline: false,
|
673
|
+
replaceNext: '',
|
674
|
+
prefixSpace: false,
|
675
|
+
scanFor: '',
|
676
|
+
surroundWithNewlines: false,
|
677
|
+
orderedList: false,
|
678
|
+
unorderedList: false,
|
679
|
+
trimFirst: false
|
680
|
+
};
|
681
|
+
const style = Object.assign(Object.assign({}, defaults), stylesToApply);
|
682
|
+
const field = toolbar.field;
|
683
|
+
if (field) {
|
684
|
+
field.focus();
|
685
|
+
styleSelectedText(field, style);
|
686
|
+
}
|
687
|
+
}
|
688
|
+
|
689
|
+
var sparkMd5 = {
|
690
|
+
exports: {}
|
691
|
+
};
|
692
|
+
|
693
|
+
(function(module, exports) {
|
694
|
+
(function(factory) {
|
695
|
+
{
|
696
|
+
module.exports = factory();
|
697
|
+
}
|
698
|
+
})((function(undefined$1) {
|
699
|
+
var hex_chr = [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" ];
|
700
|
+
function md5cycle(x, k) {
|
701
|
+
var a = x[0], b = x[1], c = x[2], d = x[3];
|
702
|
+
a += (b & c | ~b & d) + k[0] - 680876936 | 0;
|
703
|
+
a = (a << 7 | a >>> 25) + b | 0;
|
704
|
+
d += (a & b | ~a & c) + k[1] - 389564586 | 0;
|
705
|
+
d = (d << 12 | d >>> 20) + a | 0;
|
706
|
+
c += (d & a | ~d & b) + k[2] + 606105819 | 0;
|
707
|
+
c = (c << 17 | c >>> 15) + d | 0;
|
708
|
+
b += (c & d | ~c & a) + k[3] - 1044525330 | 0;
|
709
|
+
b = (b << 22 | b >>> 10) + c | 0;
|
710
|
+
a += (b & c | ~b & d) + k[4] - 176418897 | 0;
|
711
|
+
a = (a << 7 | a >>> 25) + b | 0;
|
712
|
+
d += (a & b | ~a & c) + k[5] + 1200080426 | 0;
|
713
|
+
d = (d << 12 | d >>> 20) + a | 0;
|
714
|
+
c += (d & a | ~d & b) + k[6] - 1473231341 | 0;
|
715
|
+
c = (c << 17 | c >>> 15) + d | 0;
|
716
|
+
b += (c & d | ~c & a) + k[7] - 45705983 | 0;
|
717
|
+
b = (b << 22 | b >>> 10) + c | 0;
|
718
|
+
a += (b & c | ~b & d) + k[8] + 1770035416 | 0;
|
719
|
+
a = (a << 7 | a >>> 25) + b | 0;
|
720
|
+
d += (a & b | ~a & c) + k[9] - 1958414417 | 0;
|
721
|
+
d = (d << 12 | d >>> 20) + a | 0;
|
722
|
+
c += (d & a | ~d & b) + k[10] - 42063 | 0;
|
723
|
+
c = (c << 17 | c >>> 15) + d | 0;
|
724
|
+
b += (c & d | ~c & a) + k[11] - 1990404162 | 0;
|
725
|
+
b = (b << 22 | b >>> 10) + c | 0;
|
726
|
+
a += (b & c | ~b & d) + k[12] + 1804603682 | 0;
|
727
|
+
a = (a << 7 | a >>> 25) + b | 0;
|
728
|
+
d += (a & b | ~a & c) + k[13] - 40341101 | 0;
|
729
|
+
d = (d << 12 | d >>> 20) + a | 0;
|
730
|
+
c += (d & a | ~d & b) + k[14] - 1502002290 | 0;
|
731
|
+
c = (c << 17 | c >>> 15) + d | 0;
|
732
|
+
b += (c & d | ~c & a) + k[15] + 1236535329 | 0;
|
733
|
+
b = (b << 22 | b >>> 10) + c | 0;
|
734
|
+
a += (b & d | c & ~d) + k[1] - 165796510 | 0;
|
735
|
+
a = (a << 5 | a >>> 27) + b | 0;
|
736
|
+
d += (a & c | b & ~c) + k[6] - 1069501632 | 0;
|
737
|
+
d = (d << 9 | d >>> 23) + a | 0;
|
738
|
+
c += (d & b | a & ~b) + k[11] + 643717713 | 0;
|
739
|
+
c = (c << 14 | c >>> 18) + d | 0;
|
740
|
+
b += (c & a | d & ~a) + k[0] - 373897302 | 0;
|
741
|
+
b = (b << 20 | b >>> 12) + c | 0;
|
742
|
+
a += (b & d | c & ~d) + k[5] - 701558691 | 0;
|
743
|
+
a = (a << 5 | a >>> 27) + b | 0;
|
744
|
+
d += (a & c | b & ~c) + k[10] + 38016083 | 0;
|
745
|
+
d = (d << 9 | d >>> 23) + a | 0;
|
746
|
+
c += (d & b | a & ~b) + k[15] - 660478335 | 0;
|
747
|
+
c = (c << 14 | c >>> 18) + d | 0;
|
748
|
+
b += (c & a | d & ~a) + k[4] - 405537848 | 0;
|
749
|
+
b = (b << 20 | b >>> 12) + c | 0;
|
750
|
+
a += (b & d | c & ~d) + k[9] + 568446438 | 0;
|
751
|
+
a = (a << 5 | a >>> 27) + b | 0;
|
752
|
+
d += (a & c | b & ~c) + k[14] - 1019803690 | 0;
|
753
|
+
d = (d << 9 | d >>> 23) + a | 0;
|
754
|
+
c += (d & b | a & ~b) + k[3] - 187363961 | 0;
|
755
|
+
c = (c << 14 | c >>> 18) + d | 0;
|
756
|
+
b += (c & a | d & ~a) + k[8] + 1163531501 | 0;
|
757
|
+
b = (b << 20 | b >>> 12) + c | 0;
|
758
|
+
a += (b & d | c & ~d) + k[13] - 1444681467 | 0;
|
759
|
+
a = (a << 5 | a >>> 27) + b | 0;
|
760
|
+
d += (a & c | b & ~c) + k[2] - 51403784 | 0;
|
761
|
+
d = (d << 9 | d >>> 23) + a | 0;
|
762
|
+
c += (d & b | a & ~b) + k[7] + 1735328473 | 0;
|
763
|
+
c = (c << 14 | c >>> 18) + d | 0;
|
764
|
+
b += (c & a | d & ~a) + k[12] - 1926607734 | 0;
|
765
|
+
b = (b << 20 | b >>> 12) + c | 0;
|
766
|
+
a += (b ^ c ^ d) + k[5] - 378558 | 0;
|
767
|
+
a = (a << 4 | a >>> 28) + b | 0;
|
768
|
+
d += (a ^ b ^ c) + k[8] - 2022574463 | 0;
|
769
|
+
d = (d << 11 | d >>> 21) + a | 0;
|
770
|
+
c += (d ^ a ^ b) + k[11] + 1839030562 | 0;
|
771
|
+
c = (c << 16 | c >>> 16) + d | 0;
|
772
|
+
b += (c ^ d ^ a) + k[14] - 35309556 | 0;
|
773
|
+
b = (b << 23 | b >>> 9) + c | 0;
|
774
|
+
a += (b ^ c ^ d) + k[1] - 1530992060 | 0;
|
775
|
+
a = (a << 4 | a >>> 28) + b | 0;
|
776
|
+
d += (a ^ b ^ c) + k[4] + 1272893353 | 0;
|
777
|
+
d = (d << 11 | d >>> 21) + a | 0;
|
778
|
+
c += (d ^ a ^ b) + k[7] - 155497632 | 0;
|
779
|
+
c = (c << 16 | c >>> 16) + d | 0;
|
780
|
+
b += (c ^ d ^ a) + k[10] - 1094730640 | 0;
|
781
|
+
b = (b << 23 | b >>> 9) + c | 0;
|
782
|
+
a += (b ^ c ^ d) + k[13] + 681279174 | 0;
|
783
|
+
a = (a << 4 | a >>> 28) + b | 0;
|
784
|
+
d += (a ^ b ^ c) + k[0] - 358537222 | 0;
|
785
|
+
d = (d << 11 | d >>> 21) + a | 0;
|
786
|
+
c += (d ^ a ^ b) + k[3] - 722521979 | 0;
|
787
|
+
c = (c << 16 | c >>> 16) + d | 0;
|
788
|
+
b += (c ^ d ^ a) + k[6] + 76029189 | 0;
|
789
|
+
b = (b << 23 | b >>> 9) + c | 0;
|
790
|
+
a += (b ^ c ^ d) + k[9] - 640364487 | 0;
|
791
|
+
a = (a << 4 | a >>> 28) + b | 0;
|
792
|
+
d += (a ^ b ^ c) + k[12] - 421815835 | 0;
|
793
|
+
d = (d << 11 | d >>> 21) + a | 0;
|
794
|
+
c += (d ^ a ^ b) + k[15] + 530742520 | 0;
|
795
|
+
c = (c << 16 | c >>> 16) + d | 0;
|
796
|
+
b += (c ^ d ^ a) + k[2] - 995338651 | 0;
|
797
|
+
b = (b << 23 | b >>> 9) + c | 0;
|
798
|
+
a += (c ^ (b | ~d)) + k[0] - 198630844 | 0;
|
799
|
+
a = (a << 6 | a >>> 26) + b | 0;
|
800
|
+
d += (b ^ (a | ~c)) + k[7] + 1126891415 | 0;
|
801
|
+
d = (d << 10 | d >>> 22) + a | 0;
|
802
|
+
c += (a ^ (d | ~b)) + k[14] - 1416354905 | 0;
|
803
|
+
c = (c << 15 | c >>> 17) + d | 0;
|
804
|
+
b += (d ^ (c | ~a)) + k[5] - 57434055 | 0;
|
805
|
+
b = (b << 21 | b >>> 11) + c | 0;
|
806
|
+
a += (c ^ (b | ~d)) + k[12] + 1700485571 | 0;
|
807
|
+
a = (a << 6 | a >>> 26) + b | 0;
|
808
|
+
d += (b ^ (a | ~c)) + k[3] - 1894986606 | 0;
|
809
|
+
d = (d << 10 | d >>> 22) + a | 0;
|
810
|
+
c += (a ^ (d | ~b)) + k[10] - 1051523 | 0;
|
811
|
+
c = (c << 15 | c >>> 17) + d | 0;
|
812
|
+
b += (d ^ (c | ~a)) + k[1] - 2054922799 | 0;
|
813
|
+
b = (b << 21 | b >>> 11) + c | 0;
|
814
|
+
a += (c ^ (b | ~d)) + k[8] + 1873313359 | 0;
|
815
|
+
a = (a << 6 | a >>> 26) + b | 0;
|
816
|
+
d += (b ^ (a | ~c)) + k[15] - 30611744 | 0;
|
817
|
+
d = (d << 10 | d >>> 22) + a | 0;
|
818
|
+
c += (a ^ (d | ~b)) + k[6] - 1560198380 | 0;
|
819
|
+
c = (c << 15 | c >>> 17) + d | 0;
|
820
|
+
b += (d ^ (c | ~a)) + k[13] + 1309151649 | 0;
|
821
|
+
b = (b << 21 | b >>> 11) + c | 0;
|
822
|
+
a += (c ^ (b | ~d)) + k[4] - 145523070 | 0;
|
823
|
+
a = (a << 6 | a >>> 26) + b | 0;
|
824
|
+
d += (b ^ (a | ~c)) + k[11] - 1120210379 | 0;
|
825
|
+
d = (d << 10 | d >>> 22) + a | 0;
|
826
|
+
c += (a ^ (d | ~b)) + k[2] + 718787259 | 0;
|
827
|
+
c = (c << 15 | c >>> 17) + d | 0;
|
828
|
+
b += (d ^ (c | ~a)) + k[9] - 343485551 | 0;
|
829
|
+
b = (b << 21 | b >>> 11) + c | 0;
|
830
|
+
x[0] = a + x[0] | 0;
|
831
|
+
x[1] = b + x[1] | 0;
|
832
|
+
x[2] = c + x[2] | 0;
|
833
|
+
x[3] = d + x[3] | 0;
|
834
|
+
}
|
835
|
+
function md5blk(s) {
|
836
|
+
var md5blks = [], i;
|
837
|
+
for (i = 0; i < 64; i += 4) {
|
838
|
+
md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24);
|
839
|
+
}
|
840
|
+
return md5blks;
|
841
|
+
}
|
842
|
+
function md5blk_array(a) {
|
843
|
+
var md5blks = [], i;
|
844
|
+
for (i = 0; i < 64; i += 4) {
|
845
|
+
md5blks[i >> 2] = a[i] + (a[i + 1] << 8) + (a[i + 2] << 16) + (a[i + 3] << 24);
|
846
|
+
}
|
847
|
+
return md5blks;
|
848
|
+
}
|
849
|
+
function md51(s) {
|
850
|
+
var n = s.length, state = [ 1732584193, -271733879, -1732584194, 271733878 ], i, length, tail, tmp, lo, hi;
|
851
|
+
for (i = 64; i <= n; i += 64) {
|
852
|
+
md5cycle(state, md5blk(s.substring(i - 64, i)));
|
853
|
+
}
|
854
|
+
s = s.substring(i - 64);
|
855
|
+
length = s.length;
|
856
|
+
tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
|
857
|
+
for (i = 0; i < length; i += 1) {
|
858
|
+
tail[i >> 2] |= s.charCodeAt(i) << (i % 4 << 3);
|
859
|
+
}
|
860
|
+
tail[i >> 2] |= 128 << (i % 4 << 3);
|
861
|
+
if (i > 55) {
|
862
|
+
md5cycle(state, tail);
|
863
|
+
for (i = 0; i < 16; i += 1) {
|
864
|
+
tail[i] = 0;
|
865
|
+
}
|
866
|
+
}
|
867
|
+
tmp = n * 8;
|
868
|
+
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
|
869
|
+
lo = parseInt(tmp[2], 16);
|
870
|
+
hi = parseInt(tmp[1], 16) || 0;
|
871
|
+
tail[14] = lo;
|
872
|
+
tail[15] = hi;
|
873
|
+
md5cycle(state, tail);
|
874
|
+
return state;
|
875
|
+
}
|
876
|
+
function md51_array(a) {
|
877
|
+
var n = a.length, state = [ 1732584193, -271733879, -1732584194, 271733878 ], i, length, tail, tmp, lo, hi;
|
878
|
+
for (i = 64; i <= n; i += 64) {
|
879
|
+
md5cycle(state, md5blk_array(a.subarray(i - 64, i)));
|
880
|
+
}
|
881
|
+
a = i - 64 < n ? a.subarray(i - 64) : new Uint8Array(0);
|
882
|
+
length = a.length;
|
883
|
+
tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
|
884
|
+
for (i = 0; i < length; i += 1) {
|
885
|
+
tail[i >> 2] |= a[i] << (i % 4 << 3);
|
886
|
+
}
|
887
|
+
tail[i >> 2] |= 128 << (i % 4 << 3);
|
888
|
+
if (i > 55) {
|
889
|
+
md5cycle(state, tail);
|
890
|
+
for (i = 0; i < 16; i += 1) {
|
891
|
+
tail[i] = 0;
|
892
|
+
}
|
893
|
+
}
|
894
|
+
tmp = n * 8;
|
895
|
+
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
|
896
|
+
lo = parseInt(tmp[2], 16);
|
897
|
+
hi = parseInt(tmp[1], 16) || 0;
|
898
|
+
tail[14] = lo;
|
899
|
+
tail[15] = hi;
|
900
|
+
md5cycle(state, tail);
|
901
|
+
return state;
|
902
|
+
}
|
903
|
+
function rhex(n) {
|
904
|
+
var s = "", j;
|
905
|
+
for (j = 0; j < 4; j += 1) {
|
906
|
+
s += hex_chr[n >> j * 8 + 4 & 15] + hex_chr[n >> j * 8 & 15];
|
907
|
+
}
|
908
|
+
return s;
|
909
|
+
}
|
910
|
+
function hex(x) {
|
911
|
+
var i;
|
912
|
+
for (i = 0; i < x.length; i += 1) {
|
913
|
+
x[i] = rhex(x[i]);
|
914
|
+
}
|
915
|
+
return x.join("");
|
916
|
+
}
|
917
|
+
if (hex(md51("hello")) !== "5d41402abc4b2a76b9719d911017c592") ;
|
918
|
+
if (typeof ArrayBuffer !== "undefined" && !ArrayBuffer.prototype.slice) {
|
919
|
+
(function() {
|
920
|
+
function clamp(val, length) {
|
921
|
+
val = val | 0 || 0;
|
922
|
+
if (val < 0) {
|
923
|
+
return Math.max(val + length, 0);
|
924
|
+
}
|
925
|
+
return Math.min(val, length);
|
926
|
+
}
|
927
|
+
ArrayBuffer.prototype.slice = function(from, to) {
|
928
|
+
var length = this.byteLength, begin = clamp(from, length), end = length, num, target, targetArray, sourceArray;
|
929
|
+
if (to !== undefined$1) {
|
930
|
+
end = clamp(to, length);
|
931
|
+
}
|
932
|
+
if (begin > end) {
|
933
|
+
return new ArrayBuffer(0);
|
934
|
+
}
|
935
|
+
num = end - begin;
|
936
|
+
target = new ArrayBuffer(num);
|
937
|
+
targetArray = new Uint8Array(target);
|
938
|
+
sourceArray = new Uint8Array(this, begin, num);
|
939
|
+
targetArray.set(sourceArray);
|
940
|
+
return target;
|
941
|
+
};
|
942
|
+
})();
|
943
|
+
}
|
944
|
+
function toUtf8(str) {
|
945
|
+
if (/[\u0080-\uFFFF]/.test(str)) {
|
946
|
+
str = unescape(encodeURIComponent(str));
|
947
|
+
}
|
948
|
+
return str;
|
949
|
+
}
|
950
|
+
function utf8Str2ArrayBuffer(str, returnUInt8Array) {
|
951
|
+
var length = str.length, buff = new ArrayBuffer(length), arr = new Uint8Array(buff), i;
|
952
|
+
for (i = 0; i < length; i += 1) {
|
953
|
+
arr[i] = str.charCodeAt(i);
|
954
|
+
}
|
955
|
+
return returnUInt8Array ? arr : buff;
|
956
|
+
}
|
957
|
+
function arrayBuffer2Utf8Str(buff) {
|
958
|
+
return String.fromCharCode.apply(null, new Uint8Array(buff));
|
959
|
+
}
|
960
|
+
function concatenateArrayBuffers(first, second, returnUInt8Array) {
|
961
|
+
var result = new Uint8Array(first.byteLength + second.byteLength);
|
962
|
+
result.set(new Uint8Array(first));
|
963
|
+
result.set(new Uint8Array(second), first.byteLength);
|
964
|
+
return result ;
|
965
|
+
}
|
966
|
+
function hexToBinaryString(hex) {
|
967
|
+
var bytes = [], length = hex.length, x;
|
968
|
+
for (x = 0; x < length - 1; x += 2) {
|
969
|
+
bytes.push(parseInt(hex.substr(x, 2), 16));
|
970
|
+
}
|
971
|
+
return String.fromCharCode.apply(String, bytes);
|
972
|
+
}
|
973
|
+
function SparkMD5() {
|
974
|
+
this.reset();
|
975
|
+
}
|
976
|
+
SparkMD5.prototype.append = function(str) {
|
977
|
+
this.appendBinary(toUtf8(str));
|
978
|
+
return this;
|
979
|
+
};
|
980
|
+
SparkMD5.prototype.appendBinary = function(contents) {
|
981
|
+
this._buff += contents;
|
982
|
+
this._length += contents.length;
|
983
|
+
var length = this._buff.length, i;
|
984
|
+
for (i = 64; i <= length; i += 64) {
|
985
|
+
md5cycle(this._hash, md5blk(this._buff.substring(i - 64, i)));
|
986
|
+
}
|
987
|
+
this._buff = this._buff.substring(i - 64);
|
988
|
+
return this;
|
989
|
+
};
|
990
|
+
SparkMD5.prototype.end = function(raw) {
|
991
|
+
var buff = this._buff, length = buff.length, i, tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], ret;
|
992
|
+
for (i = 0; i < length; i += 1) {
|
993
|
+
tail[i >> 2] |= buff.charCodeAt(i) << (i % 4 << 3);
|
994
|
+
}
|
995
|
+
this._finish(tail, length);
|
996
|
+
ret = hex(this._hash);
|
997
|
+
if (raw) {
|
998
|
+
ret = hexToBinaryString(ret);
|
999
|
+
}
|
1000
|
+
this.reset();
|
1001
|
+
return ret;
|
1002
|
+
};
|
1003
|
+
SparkMD5.prototype.reset = function() {
|
1004
|
+
this._buff = "";
|
1005
|
+
this._length = 0;
|
1006
|
+
this._hash = [ 1732584193, -271733879, -1732584194, 271733878 ];
|
1007
|
+
return this;
|
1008
|
+
};
|
1009
|
+
SparkMD5.prototype.getState = function() {
|
1010
|
+
return {
|
1011
|
+
buff: this._buff,
|
1012
|
+
length: this._length,
|
1013
|
+
hash: this._hash.slice()
|
1014
|
+
};
|
1015
|
+
};
|
1016
|
+
SparkMD5.prototype.setState = function(state) {
|
1017
|
+
this._buff = state.buff;
|
1018
|
+
this._length = state.length;
|
1019
|
+
this._hash = state.hash;
|
1020
|
+
return this;
|
1021
|
+
};
|
1022
|
+
SparkMD5.prototype.destroy = function() {
|
1023
|
+
delete this._hash;
|
1024
|
+
delete this._buff;
|
1025
|
+
delete this._length;
|
1026
|
+
};
|
1027
|
+
SparkMD5.prototype._finish = function(tail, length) {
|
1028
|
+
var i = length, tmp, lo, hi;
|
1029
|
+
tail[i >> 2] |= 128 << (i % 4 << 3);
|
1030
|
+
if (i > 55) {
|
1031
|
+
md5cycle(this._hash, tail);
|
1032
|
+
for (i = 0; i < 16; i += 1) {
|
1033
|
+
tail[i] = 0;
|
1034
|
+
}
|
1035
|
+
}
|
1036
|
+
tmp = this._length * 8;
|
1037
|
+
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
|
1038
|
+
lo = parseInt(tmp[2], 16);
|
1039
|
+
hi = parseInt(tmp[1], 16) || 0;
|
1040
|
+
tail[14] = lo;
|
1041
|
+
tail[15] = hi;
|
1042
|
+
md5cycle(this._hash, tail);
|
1043
|
+
};
|
1044
|
+
SparkMD5.hash = function(str, raw) {
|
1045
|
+
return SparkMD5.hashBinary(toUtf8(str), raw);
|
1046
|
+
};
|
1047
|
+
SparkMD5.hashBinary = function(content, raw) {
|
1048
|
+
var hash = md51(content), ret = hex(hash);
|
1049
|
+
return raw ? hexToBinaryString(ret) : ret;
|
1050
|
+
};
|
1051
|
+
SparkMD5.ArrayBuffer = function() {
|
1052
|
+
this.reset();
|
1053
|
+
};
|
1054
|
+
SparkMD5.ArrayBuffer.prototype.append = function(arr) {
|
1055
|
+
var buff = concatenateArrayBuffers(this._buff.buffer, arr), length = buff.length, i;
|
1056
|
+
this._length += arr.byteLength;
|
1057
|
+
for (i = 64; i <= length; i += 64) {
|
1058
|
+
md5cycle(this._hash, md5blk_array(buff.subarray(i - 64, i)));
|
1059
|
+
}
|
1060
|
+
this._buff = i - 64 < length ? new Uint8Array(buff.buffer.slice(i - 64)) : new Uint8Array(0);
|
1061
|
+
return this;
|
1062
|
+
};
|
1063
|
+
SparkMD5.ArrayBuffer.prototype.end = function(raw) {
|
1064
|
+
var buff = this._buff, length = buff.length, tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], i, ret;
|
1065
|
+
for (i = 0; i < length; i += 1) {
|
1066
|
+
tail[i >> 2] |= buff[i] << (i % 4 << 3);
|
1067
|
+
}
|
1068
|
+
this._finish(tail, length);
|
1069
|
+
ret = hex(this._hash);
|
1070
|
+
if (raw) {
|
1071
|
+
ret = hexToBinaryString(ret);
|
1072
|
+
}
|
1073
|
+
this.reset();
|
1074
|
+
return ret;
|
1075
|
+
};
|
1076
|
+
SparkMD5.ArrayBuffer.prototype.reset = function() {
|
1077
|
+
this._buff = new Uint8Array(0);
|
1078
|
+
this._length = 0;
|
1079
|
+
this._hash = [ 1732584193, -271733879, -1732584194, 271733878 ];
|
1080
|
+
return this;
|
1081
|
+
};
|
1082
|
+
SparkMD5.ArrayBuffer.prototype.getState = function() {
|
1083
|
+
var state = SparkMD5.prototype.getState.call(this);
|
1084
|
+
state.buff = arrayBuffer2Utf8Str(state.buff);
|
1085
|
+
return state;
|
1086
|
+
};
|
1087
|
+
SparkMD5.ArrayBuffer.prototype.setState = function(state) {
|
1088
|
+
state.buff = utf8Str2ArrayBuffer(state.buff, true);
|
1089
|
+
return SparkMD5.prototype.setState.call(this, state);
|
1090
|
+
};
|
1091
|
+
SparkMD5.ArrayBuffer.prototype.destroy = SparkMD5.prototype.destroy;
|
1092
|
+
SparkMD5.ArrayBuffer.prototype._finish = SparkMD5.prototype._finish;
|
1093
|
+
SparkMD5.ArrayBuffer.hash = function(arr, raw) {
|
1094
|
+
var hash = md51_array(new Uint8Array(arr)), ret = hex(hash);
|
1095
|
+
return raw ? hexToBinaryString(ret) : ret;
|
1096
|
+
};
|
1097
|
+
return SparkMD5;
|
1098
|
+
}));
|
1099
|
+
})(sparkMd5);
|
1100
|
+
|
1101
|
+
var SparkMD5 = sparkMd5.exports;
|
1102
|
+
|
1103
|
+
const fileSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
|
1104
|
+
|
1105
|
+
class FileChecksum {
|
1106
|
+
static create(file, callback) {
|
1107
|
+
const instance = new FileChecksum(file);
|
1108
|
+
instance.create(callback);
|
1109
|
+
}
|
1110
|
+
constructor(file) {
|
1111
|
+
this.file = file;
|
1112
|
+
this.chunkSize = 2097152;
|
1113
|
+
this.chunkCount = Math.ceil(this.file.size / this.chunkSize);
|
1114
|
+
this.chunkIndex = 0;
|
1115
|
+
}
|
1116
|
+
create(callback) {
|
1117
|
+
this.callback = callback;
|
1118
|
+
this.md5Buffer = new SparkMD5.ArrayBuffer;
|
1119
|
+
this.fileReader = new FileReader;
|
1120
|
+
this.fileReader.addEventListener("load", (event => this.fileReaderDidLoad(event)));
|
1121
|
+
this.fileReader.addEventListener("error", (event => this.fileReaderDidError(event)));
|
1122
|
+
this.readNextChunk();
|
1123
|
+
}
|
1124
|
+
fileReaderDidLoad(event) {
|
1125
|
+
this.md5Buffer.append(event.target.result);
|
1126
|
+
if (!this.readNextChunk()) {
|
1127
|
+
const binaryDigest = this.md5Buffer.end(true);
|
1128
|
+
const base64digest = btoa(binaryDigest);
|
1129
|
+
this.callback(null, base64digest);
|
1130
|
+
}
|
1131
|
+
}
|
1132
|
+
fileReaderDidError(event) {
|
1133
|
+
this.callback(`Error reading ${this.file.name}`);
|
1134
|
+
}
|
1135
|
+
readNextChunk() {
|
1136
|
+
if (this.chunkIndex < this.chunkCount || this.chunkIndex == 0 && this.chunkCount == 0) {
|
1137
|
+
const start = this.chunkIndex * this.chunkSize;
|
1138
|
+
const end = Math.min(start + this.chunkSize, this.file.size);
|
1139
|
+
const bytes = fileSlice.call(this.file, start, end);
|
1140
|
+
this.fileReader.readAsArrayBuffer(bytes);
|
1141
|
+
this.chunkIndex++;
|
1142
|
+
return true;
|
1143
|
+
} else {
|
1144
|
+
return false;
|
1145
|
+
}
|
1146
|
+
}
|
1147
|
+
}
|
1148
|
+
|
1149
|
+
function getMetaValue(name) {
|
1150
|
+
const element = findElement(document.head, `meta[name="${name}"]`);
|
1151
|
+
if (element) {
|
1152
|
+
return element.getAttribute("content");
|
1153
|
+
}
|
1154
|
+
}
|
1155
|
+
|
1156
|
+
function findElements(root, selector) {
|
1157
|
+
if (typeof root == "string") {
|
1158
|
+
selector = root;
|
1159
|
+
root = document;
|
1160
|
+
}
|
1161
|
+
const elements = root.querySelectorAll(selector);
|
1162
|
+
return toArray(elements);
|
1163
|
+
}
|
1164
|
+
|
1165
|
+
function findElement(root, selector) {
|
1166
|
+
if (typeof root == "string") {
|
1167
|
+
selector = root;
|
1168
|
+
root = document;
|
1169
|
+
}
|
1170
|
+
return root.querySelector(selector);
|
1171
|
+
}
|
1172
|
+
|
1173
|
+
function dispatchEvent(element, type, eventInit = {}) {
|
1174
|
+
const {disabled: disabled} = element;
|
1175
|
+
const {bubbles: bubbles, cancelable: cancelable, detail: detail} = eventInit;
|
1176
|
+
const event = document.createEvent("Event");
|
1177
|
+
event.initEvent(type, bubbles || true, cancelable || true);
|
1178
|
+
event.detail = detail || {};
|
1179
|
+
try {
|
1180
|
+
element.disabled = false;
|
1181
|
+
element.dispatchEvent(event);
|
1182
|
+
} finally {
|
1183
|
+
element.disabled = disabled;
|
1184
|
+
}
|
1185
|
+
return event;
|
1186
|
+
}
|
1187
|
+
|
1188
|
+
function toArray(value) {
|
1189
|
+
if (Array.isArray(value)) {
|
1190
|
+
return value;
|
1191
|
+
} else if (Array.from) {
|
1192
|
+
return Array.from(value);
|
1193
|
+
} else {
|
1194
|
+
return [].slice.call(value);
|
1195
|
+
}
|
1196
|
+
}
|
1197
|
+
|
1198
|
+
class BlobRecord {
|
1199
|
+
constructor(file, checksum, url, customHeaders = {}) {
|
1200
|
+
this.file = file;
|
1201
|
+
this.attributes = {
|
1202
|
+
filename: file.name,
|
1203
|
+
content_type: file.type || "application/octet-stream",
|
1204
|
+
byte_size: file.size,
|
1205
|
+
checksum: checksum
|
1206
|
+
};
|
1207
|
+
this.xhr = new XMLHttpRequest;
|
1208
|
+
this.xhr.open("POST", url, true);
|
1209
|
+
this.xhr.responseType = "json";
|
1210
|
+
this.xhr.setRequestHeader("Content-Type", "application/json");
|
1211
|
+
this.xhr.setRequestHeader("Accept", "application/json");
|
1212
|
+
this.xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
|
1213
|
+
Object.keys(customHeaders).forEach((headerKey => {
|
1214
|
+
this.xhr.setRequestHeader(headerKey, customHeaders[headerKey]);
|
1215
|
+
}));
|
1216
|
+
const csrfToken = getMetaValue("csrf-token");
|
1217
|
+
if (csrfToken != undefined) {
|
1218
|
+
this.xhr.setRequestHeader("X-CSRF-Token", csrfToken);
|
1219
|
+
}
|
1220
|
+
this.xhr.addEventListener("load", (event => this.requestDidLoad(event)));
|
1221
|
+
this.xhr.addEventListener("error", (event => this.requestDidError(event)));
|
1222
|
+
}
|
1223
|
+
get status() {
|
1224
|
+
return this.xhr.status;
|
1225
|
+
}
|
1226
|
+
get response() {
|
1227
|
+
const {responseType: responseType, response: response} = this.xhr;
|
1228
|
+
if (responseType == "json") {
|
1229
|
+
return response;
|
1230
|
+
} else {
|
1231
|
+
return JSON.parse(response);
|
1232
|
+
}
|
1233
|
+
}
|
1234
|
+
create(callback) {
|
1235
|
+
this.callback = callback;
|
1236
|
+
this.xhr.send(JSON.stringify({
|
1237
|
+
blob: this.attributes
|
1238
|
+
}));
|
1239
|
+
}
|
1240
|
+
requestDidLoad(event) {
|
1241
|
+
if (this.status >= 200 && this.status < 300) {
|
1242
|
+
const {response: response} = this;
|
1243
|
+
const {direct_upload: direct_upload} = response;
|
1244
|
+
delete response.direct_upload;
|
1245
|
+
this.attributes = response;
|
1246
|
+
this.directUploadData = direct_upload;
|
1247
|
+
this.callback(null, this.toJSON());
|
1248
|
+
} else {
|
1249
|
+
this.requestDidError(event);
|
1250
|
+
}
|
1251
|
+
}
|
1252
|
+
requestDidError(event) {
|
1253
|
+
this.callback(`Error creating Blob for "${this.file.name}". Status: ${this.status}`);
|
1254
|
+
}
|
1255
|
+
toJSON() {
|
1256
|
+
const result = {};
|
1257
|
+
for (const key in this.attributes) {
|
1258
|
+
result[key] = this.attributes[key];
|
1259
|
+
}
|
1260
|
+
return result;
|
1261
|
+
}
|
1262
|
+
}
|
1263
|
+
|
1264
|
+
class BlobUpload {
|
1265
|
+
constructor(blob) {
|
1266
|
+
this.blob = blob;
|
1267
|
+
this.file = blob.file;
|
1268
|
+
const {url: url, headers: headers} = blob.directUploadData;
|
1269
|
+
this.xhr = new XMLHttpRequest;
|
1270
|
+
this.xhr.open("PUT", url, true);
|
1271
|
+
this.xhr.responseType = "text";
|
1272
|
+
for (const key in headers) {
|
1273
|
+
this.xhr.setRequestHeader(key, headers[key]);
|
1274
|
+
}
|
1275
|
+
this.xhr.addEventListener("load", (event => this.requestDidLoad(event)));
|
1276
|
+
this.xhr.addEventListener("error", (event => this.requestDidError(event)));
|
1277
|
+
}
|
1278
|
+
create(callback) {
|
1279
|
+
this.callback = callback;
|
1280
|
+
this.xhr.send(this.file.slice());
|
1281
|
+
}
|
1282
|
+
requestDidLoad(event) {
|
1283
|
+
const {status: status, response: response} = this.xhr;
|
1284
|
+
if (status >= 200 && status < 300) {
|
1285
|
+
this.callback(null, response);
|
1286
|
+
} else {
|
1287
|
+
this.requestDidError(event);
|
1288
|
+
}
|
1289
|
+
}
|
1290
|
+
requestDidError(event) {
|
1291
|
+
this.callback(`Error storing "${this.file.name}". Status: ${this.xhr.status}`);
|
1292
|
+
}
|
1293
|
+
}
|
1294
|
+
|
1295
|
+
let id = 0;
|
1296
|
+
|
1297
|
+
class DirectUpload {
|
1298
|
+
constructor(file, url, delegate, customHeaders = {}) {
|
1299
|
+
this.id = ++id;
|
1300
|
+
this.file = file;
|
1301
|
+
this.url = url;
|
1302
|
+
this.delegate = delegate;
|
1303
|
+
this.customHeaders = customHeaders;
|
1304
|
+
}
|
1305
|
+
create(callback) {
|
1306
|
+
FileChecksum.create(this.file, ((error, checksum) => {
|
1307
|
+
if (error) {
|
1308
|
+
callback(error);
|
1309
|
+
return;
|
1310
|
+
}
|
1311
|
+
const blob = new BlobRecord(this.file, checksum, this.url, this.customHeaders);
|
1312
|
+
notify(this.delegate, "directUploadWillCreateBlobWithXHR", blob.xhr);
|
1313
|
+
blob.create((error => {
|
1314
|
+
if (error) {
|
1315
|
+
callback(error);
|
1316
|
+
} else {
|
1317
|
+
const upload = new BlobUpload(blob);
|
1318
|
+
notify(this.delegate, "directUploadWillStoreFileWithXHR", upload.xhr);
|
1319
|
+
upload.create((error => {
|
1320
|
+
if (error) {
|
1321
|
+
callback(error);
|
1322
|
+
} else {
|
1323
|
+
callback(null, blob.toJSON());
|
1324
|
+
}
|
1325
|
+
}));
|
1326
|
+
}
|
1327
|
+
}));
|
1328
|
+
}));
|
1329
|
+
}
|
1330
|
+
}
|
1331
|
+
|
1332
|
+
function notify(object, methodName, ...messages) {
|
1333
|
+
if (object && typeof object[methodName] == "function") {
|
1334
|
+
return object[methodName](...messages);
|
1335
|
+
}
|
1336
|
+
}
|
1337
|
+
|
1338
|
+
class DirectUploadController {
|
1339
|
+
constructor(input, file) {
|
1340
|
+
this.input = input;
|
1341
|
+
this.file = file;
|
1342
|
+
this.directUpload = new DirectUpload(this.file, this.url, this);
|
1343
|
+
this.dispatch("initialize");
|
1344
|
+
}
|
1345
|
+
start(callback) {
|
1346
|
+
const hiddenInput = document.createElement("input");
|
1347
|
+
hiddenInput.type = "hidden";
|
1348
|
+
hiddenInput.name = this.input.name;
|
1349
|
+
this.input.insertAdjacentElement("beforebegin", hiddenInput);
|
1350
|
+
this.dispatch("start");
|
1351
|
+
this.directUpload.create(((error, attributes) => {
|
1352
|
+
if (error) {
|
1353
|
+
hiddenInput.parentNode.removeChild(hiddenInput);
|
1354
|
+
this.dispatchError(error);
|
1355
|
+
} else {
|
1356
|
+
hiddenInput.value = attributes.signed_id;
|
1357
|
+
}
|
1358
|
+
this.dispatch("end");
|
1359
|
+
callback(error);
|
1360
|
+
}));
|
1361
|
+
}
|
1362
|
+
uploadRequestDidProgress(event) {
|
1363
|
+
const progress = event.loaded / event.total * 100;
|
1364
|
+
if (progress) {
|
1365
|
+
this.dispatch("progress", {
|
1366
|
+
progress: progress
|
1367
|
+
});
|
1368
|
+
}
|
1369
|
+
}
|
1370
|
+
get url() {
|
1371
|
+
return this.input.getAttribute("data-direct-upload-url");
|
1372
|
+
}
|
1373
|
+
dispatch(name, detail = {}) {
|
1374
|
+
detail.file = this.file;
|
1375
|
+
detail.id = this.directUpload.id;
|
1376
|
+
return dispatchEvent(this.input, `direct-upload:${name}`, {
|
1377
|
+
detail: detail
|
1378
|
+
});
|
1379
|
+
}
|
1380
|
+
dispatchError(error) {
|
1381
|
+
const event = this.dispatch("error", {
|
1382
|
+
error: error
|
1383
|
+
});
|
1384
|
+
if (!event.defaultPrevented) {
|
1385
|
+
alert(error);
|
1386
|
+
}
|
1387
|
+
}
|
1388
|
+
directUploadWillCreateBlobWithXHR(xhr) {
|
1389
|
+
this.dispatch("before-blob-request", {
|
1390
|
+
xhr: xhr
|
1391
|
+
});
|
1392
|
+
}
|
1393
|
+
directUploadWillStoreFileWithXHR(xhr) {
|
1394
|
+
this.dispatch("before-storage-request", {
|
1395
|
+
xhr: xhr
|
1396
|
+
});
|
1397
|
+
xhr.upload.addEventListener("progress", (event => this.uploadRequestDidProgress(event)));
|
1398
|
+
}
|
1399
|
+
}
|
1400
|
+
|
1401
|
+
const inputSelector = "input[type=file][data-direct-upload-url]:not([disabled])";
|
1402
|
+
|
1403
|
+
class DirectUploadsController {
|
1404
|
+
constructor(form) {
|
1405
|
+
this.form = form;
|
1406
|
+
this.inputs = findElements(form, inputSelector).filter((input => input.files.length));
|
1407
|
+
}
|
1408
|
+
start(callback) {
|
1409
|
+
const controllers = this.createDirectUploadControllers();
|
1410
|
+
const startNextController = () => {
|
1411
|
+
const controller = controllers.shift();
|
1412
|
+
if (controller) {
|
1413
|
+
controller.start((error => {
|
1414
|
+
if (error) {
|
1415
|
+
callback(error);
|
1416
|
+
this.dispatch("end");
|
1417
|
+
} else {
|
1418
|
+
startNextController();
|
1419
|
+
}
|
1420
|
+
}));
|
1421
|
+
} else {
|
1422
|
+
callback();
|
1423
|
+
this.dispatch("end");
|
1424
|
+
}
|
1425
|
+
};
|
1426
|
+
this.dispatch("start");
|
1427
|
+
startNextController();
|
1428
|
+
}
|
1429
|
+
createDirectUploadControllers() {
|
1430
|
+
const controllers = [];
|
1431
|
+
this.inputs.forEach((input => {
|
1432
|
+
toArray(input.files).forEach((file => {
|
1433
|
+
const controller = new DirectUploadController(input, file);
|
1434
|
+
controllers.push(controller);
|
1435
|
+
}));
|
1436
|
+
}));
|
1437
|
+
return controllers;
|
1438
|
+
}
|
1439
|
+
dispatch(name, detail = {}) {
|
1440
|
+
return dispatchEvent(this.form, `direct-uploads:${name}`, {
|
1441
|
+
detail: detail
|
1442
|
+
});
|
1443
|
+
}
|
1444
|
+
}
|
1445
|
+
|
1446
|
+
const processingAttribute = "data-direct-uploads-processing";
|
1447
|
+
|
1448
|
+
const submitButtonsByForm = new WeakMap;
|
1449
|
+
|
1450
|
+
let started = false;
|
1451
|
+
|
1452
|
+
function start() {
|
1453
|
+
if (!started) {
|
1454
|
+
started = true;
|
1455
|
+
document.addEventListener("click", didClick, true);
|
1456
|
+
document.addEventListener("submit", didSubmitForm, true);
|
1457
|
+
document.addEventListener("ajax:before", didSubmitRemoteElement);
|
1458
|
+
}
|
1459
|
+
}
|
1460
|
+
|
1461
|
+
function didClick(event) {
|
1462
|
+
const button = event.target.closest("button, input");
|
1463
|
+
if (button && button.type === "submit" && button.form) {
|
1464
|
+
submitButtonsByForm.set(button.form, button);
|
1465
|
+
}
|
1466
|
+
}
|
1467
|
+
|
1468
|
+
function didSubmitForm(event) {
|
1469
|
+
handleFormSubmissionEvent(event);
|
1470
|
+
}
|
1471
|
+
|
1472
|
+
function didSubmitRemoteElement(event) {
|
1473
|
+
if (event.target.tagName == "FORM") {
|
1474
|
+
handleFormSubmissionEvent(event);
|
1475
|
+
}
|
1476
|
+
}
|
1477
|
+
|
1478
|
+
function handleFormSubmissionEvent(event) {
|
1479
|
+
const form = event.target;
|
1480
|
+
if (form.hasAttribute(processingAttribute)) {
|
1481
|
+
event.preventDefault();
|
1482
|
+
return;
|
1483
|
+
}
|
1484
|
+
const controller = new DirectUploadsController(form);
|
1485
|
+
const {inputs: inputs} = controller;
|
1486
|
+
if (inputs.length) {
|
1487
|
+
event.preventDefault();
|
1488
|
+
form.setAttribute(processingAttribute, "");
|
1489
|
+
inputs.forEach(disable);
|
1490
|
+
controller.start((error => {
|
1491
|
+
form.removeAttribute(processingAttribute);
|
1492
|
+
if (error) {
|
1493
|
+
inputs.forEach(enable);
|
1494
|
+
} else {
|
1495
|
+
submitForm(form);
|
1496
|
+
}
|
1497
|
+
}));
|
1498
|
+
}
|
1499
|
+
}
|
1500
|
+
|
1501
|
+
function submitForm(form) {
|
1502
|
+
let button = submitButtonsByForm.get(form) || findElement(form, "input[type=submit], button[type=submit]");
|
1503
|
+
if (button) {
|
1504
|
+
const {disabled: disabled} = button;
|
1505
|
+
button.disabled = false;
|
1506
|
+
button.focus();
|
1507
|
+
button.click();
|
1508
|
+
button.disabled = disabled;
|
1509
|
+
} else {
|
1510
|
+
button = document.createElement("input");
|
1511
|
+
button.type = "submit";
|
1512
|
+
button.style.display = "none";
|
1513
|
+
form.appendChild(button);
|
1514
|
+
button.click();
|
1515
|
+
form.removeChild(button);
|
1516
|
+
}
|
1517
|
+
submitButtonsByForm.delete(form);
|
1518
|
+
}
|
1519
|
+
|
1520
|
+
function disable(input) {
|
1521
|
+
input.disabled = true;
|
1522
|
+
}
|
1523
|
+
|
1524
|
+
function enable(input) {
|
1525
|
+
input.disabled = false;
|
1526
|
+
}
|
1527
|
+
|
1528
|
+
function autostart() {
|
1529
|
+
if (window.ActiveStorage) {
|
1530
|
+
start();
|
1531
|
+
}
|
1532
|
+
}
|
1533
|
+
|
1534
|
+
setTimeout(autostart, 1);
|
1535
|
+
|
1536
|
+
class FetchResponse {
|
1537
|
+
constructor (response) {
|
1538
|
+
this.response = response;
|
1539
|
+
}
|
1540
|
+
|
1541
|
+
get statusCode () {
|
1542
|
+
return this.response.status
|
1543
|
+
}
|
1544
|
+
|
1545
|
+
get redirected () {
|
1546
|
+
return this.response.redirected
|
1547
|
+
}
|
1548
|
+
|
1549
|
+
get ok () {
|
1550
|
+
return this.response.ok
|
1551
|
+
}
|
1552
|
+
|
1553
|
+
get unauthenticated () {
|
1554
|
+
return this.statusCode === 401
|
1555
|
+
}
|
1556
|
+
|
1557
|
+
get unprocessableEntity () {
|
1558
|
+
return this.statusCode === 422
|
1559
|
+
}
|
1560
|
+
|
1561
|
+
get authenticationURL () {
|
1562
|
+
return this.response.headers.get('WWW-Authenticate')
|
1563
|
+
}
|
1564
|
+
|
1565
|
+
get contentType () {
|
1566
|
+
const contentType = this.response.headers.get('Content-Type') || '';
|
1567
|
+
|
1568
|
+
return contentType.replace(/;.*$/, '')
|
1569
|
+
}
|
1570
|
+
|
1571
|
+
get headers () {
|
1572
|
+
return this.response.headers
|
1573
|
+
}
|
1574
|
+
|
1575
|
+
get html () {
|
1576
|
+
if (this.contentType.match(/^(application|text)\/(html|xhtml\+xml)$/)) {
|
1577
|
+
return this.text
|
1578
|
+
}
|
1579
|
+
|
1580
|
+
return Promise.reject(new Error(`Expected an HTML response but got "${this.contentType}" instead`))
|
1581
|
+
}
|
1582
|
+
|
1583
|
+
get json () {
|
1584
|
+
if (this.contentType.match(/^application\/.*json$/)) {
|
1585
|
+
return this.responseJson || (this.responseJson = this.response.json())
|
1586
|
+
}
|
1587
|
+
|
1588
|
+
return Promise.reject(new Error(`Expected a JSON response but got "${this.contentType}" instead`))
|
1589
|
+
}
|
1590
|
+
|
1591
|
+
get text () {
|
1592
|
+
return this.responseText || (this.responseText = this.response.text())
|
1593
|
+
}
|
1594
|
+
|
1595
|
+
get isTurboStream () {
|
1596
|
+
return this.contentType.match(/^text\/vnd\.turbo-stream\.html/)
|
1597
|
+
}
|
1598
|
+
|
1599
|
+
get isScript () {
|
1600
|
+
return this.contentType.match(/\b(?:java|ecma)script\b/)
|
1601
|
+
}
|
1602
|
+
|
1603
|
+
async renderTurboStream () {
|
1604
|
+
if (this.isTurboStream) {
|
1605
|
+
if (window.Turbo) {
|
1606
|
+
await window.Turbo.renderStreamMessage(await this.text);
|
1607
|
+
} else {
|
1608
|
+
console.warn('You must set `window.Turbo = Turbo` to automatically process Turbo Stream events with request.js');
|
1609
|
+
}
|
1610
|
+
} else {
|
1611
|
+
return Promise.reject(new Error(`Expected a Turbo Stream response but got "${this.contentType}" instead`))
|
1612
|
+
}
|
1613
|
+
}
|
1614
|
+
|
1615
|
+
async activeScript () {
|
1616
|
+
if (this.isScript) {
|
1617
|
+
const script = document.createElement('script');
|
1618
|
+
const metaTag = document.querySelector('meta[name=csp-nonce]');
|
1619
|
+
const nonce = metaTag && metaTag.content;
|
1620
|
+
if (nonce) { script.setAttribute('nonce', nonce); }
|
1621
|
+
script.innerHTML = await this.text;
|
1622
|
+
document.body.appendChild(script);
|
1623
|
+
} else {
|
1624
|
+
return Promise.reject(new Error(`Expected a Script response but got "${this.contentType}" instead`))
|
1625
|
+
}
|
1626
|
+
}
|
1627
|
+
}
|
1628
|
+
|
1629
|
+
class RequestInterceptor {
|
1630
|
+
static register (interceptor) {
|
1631
|
+
this.interceptor = interceptor;
|
1632
|
+
}
|
1633
|
+
|
1634
|
+
static get () {
|
1635
|
+
return this.interceptor
|
1636
|
+
}
|
1637
|
+
|
1638
|
+
static reset () {
|
1639
|
+
this.interceptor = undefined;
|
1640
|
+
}
|
1641
|
+
}
|
1642
|
+
|
1643
|
+
function getCookie (name) {
|
1644
|
+
const cookies = document.cookie ? document.cookie.split('; ') : [];
|
1645
|
+
const prefix = `${encodeURIComponent(name)}=`;
|
1646
|
+
const cookie = cookies.find(cookie => cookie.startsWith(prefix));
|
1647
|
+
|
1648
|
+
if (cookie) {
|
1649
|
+
const value = cookie.split('=').slice(1).join('=');
|
1650
|
+
|
1651
|
+
if (value) {
|
1652
|
+
return decodeURIComponent(value)
|
1653
|
+
}
|
1654
|
+
}
|
1655
|
+
}
|
1656
|
+
|
1657
|
+
function compact (object) {
|
1658
|
+
const result = {};
|
1659
|
+
|
1660
|
+
for (const key in object) {
|
1661
|
+
const value = object[key];
|
1662
|
+
if (value !== undefined) {
|
1663
|
+
result[key] = value;
|
1664
|
+
}
|
1665
|
+
}
|
1666
|
+
|
1667
|
+
return result
|
1668
|
+
}
|
1669
|
+
|
1670
|
+
function metaContent (name) {
|
1671
|
+
const element = document.head.querySelector(`meta[name="${name}"]`);
|
1672
|
+
return element && element.content
|
1673
|
+
}
|
1674
|
+
|
1675
|
+
function stringEntriesFromFormData (formData) {
|
1676
|
+
return [...formData].reduce((entries, [name, value]) => {
|
1677
|
+
return entries.concat(typeof value === 'string' ? [[name, value]] : [])
|
1678
|
+
}, [])
|
1679
|
+
}
|
1680
|
+
|
1681
|
+
function mergeEntries (searchParams, entries) {
|
1682
|
+
for (const [name, value] of entries) {
|
1683
|
+
if (value instanceof window.File) continue
|
1684
|
+
|
1685
|
+
if (searchParams.has(name) && !name.includes('[]')) {
|
1686
|
+
searchParams.delete(name);
|
1687
|
+
searchParams.set(name, value);
|
1688
|
+
} else {
|
1689
|
+
searchParams.append(name, value);
|
1690
|
+
}
|
1691
|
+
}
|
1692
|
+
}
|
1693
|
+
|
1694
|
+
class FetchRequest {
|
1695
|
+
constructor (method, url, options = {}) {
|
1696
|
+
this.method = method;
|
1697
|
+
this.options = options;
|
1698
|
+
this.originalUrl = url.toString();
|
1699
|
+
}
|
1700
|
+
|
1701
|
+
async perform () {
|
1702
|
+
try {
|
1703
|
+
const requestInterceptor = RequestInterceptor.get();
|
1704
|
+
if (requestInterceptor) {
|
1705
|
+
await requestInterceptor(this);
|
1706
|
+
}
|
1707
|
+
} catch (error) {
|
1708
|
+
console.error(error);
|
1709
|
+
}
|
1710
|
+
|
1711
|
+
const fetch = (this.responseKind === 'turbo-stream' && window.Turbo)
|
1712
|
+
? window.Turbo.fetch
|
1713
|
+
: window.fetch;
|
1714
|
+
|
1715
|
+
const response = new FetchResponse(await fetch(this.url, this.fetchOptions));
|
1716
|
+
|
1717
|
+
if (response.unauthenticated && response.authenticationURL) {
|
1718
|
+
return Promise.reject(window.location.href = response.authenticationURL)
|
1719
|
+
}
|
1720
|
+
|
1721
|
+
if (response.isScript) {
|
1722
|
+
await response.activeScript();
|
1723
|
+
}
|
1724
|
+
|
1725
|
+
const responseStatusIsTurboStreamable = response.ok || response.unprocessableEntity;
|
1726
|
+
|
1727
|
+
if (responseStatusIsTurboStreamable && response.isTurboStream) {
|
1728
|
+
await response.renderTurboStream();
|
1729
|
+
}
|
1730
|
+
|
1731
|
+
return response
|
1732
|
+
}
|
1733
|
+
|
1734
|
+
addHeader (key, value) {
|
1735
|
+
const headers = this.additionalHeaders;
|
1736
|
+
headers[key] = value;
|
1737
|
+
this.options.headers = headers;
|
1738
|
+
}
|
1739
|
+
|
1740
|
+
sameHostname () {
|
1741
|
+
if (!this.originalUrl.startsWith('http:')) {
|
1742
|
+
return true
|
1743
|
+
}
|
1744
|
+
|
1745
|
+
try {
|
1746
|
+
return new URL(this.originalUrl).hostname === window.location.hostname
|
1747
|
+
} catch (_) {
|
1748
|
+
return true
|
1749
|
+
}
|
1750
|
+
}
|
1751
|
+
|
1752
|
+
get fetchOptions () {
|
1753
|
+
return {
|
1754
|
+
method: this.method.toUpperCase(),
|
1755
|
+
headers: this.headers,
|
1756
|
+
body: this.formattedBody,
|
1757
|
+
signal: this.signal,
|
1758
|
+
credentials: this.credentials,
|
1759
|
+
redirect: this.redirect
|
1760
|
+
}
|
1761
|
+
}
|
1762
|
+
|
1763
|
+
get headers () {
|
1764
|
+
const baseHeaders = {
|
1765
|
+
'X-Requested-With': 'XMLHttpRequest',
|
1766
|
+
'Content-Type': this.contentType,
|
1767
|
+
Accept: this.accept
|
1768
|
+
};
|
1769
|
+
|
1770
|
+
if (this.sameHostname()) {
|
1771
|
+
baseHeaders['X-CSRF-Token'] = this.csrfToken;
|
1772
|
+
}
|
1773
|
+
|
1774
|
+
return compact(
|
1775
|
+
Object.assign(baseHeaders, this.additionalHeaders)
|
1776
|
+
)
|
1777
|
+
}
|
1778
|
+
|
1779
|
+
get csrfToken () {
|
1780
|
+
return getCookie(metaContent('csrf-param')) || metaContent('csrf-token')
|
1781
|
+
}
|
1782
|
+
|
1783
|
+
get contentType () {
|
1784
|
+
if (this.options.contentType) {
|
1785
|
+
return this.options.contentType
|
1786
|
+
} else if (this.body == null || this.body instanceof window.FormData) {
|
1787
|
+
return undefined
|
1788
|
+
} else if (this.body instanceof window.File) {
|
1789
|
+
return this.body.type
|
1790
|
+
}
|
1791
|
+
|
1792
|
+
return 'application/json'
|
1793
|
+
}
|
1794
|
+
|
1795
|
+
get accept () {
|
1796
|
+
switch (this.responseKind) {
|
1797
|
+
case 'html':
|
1798
|
+
return 'text/html, application/xhtml+xml'
|
1799
|
+
case 'turbo-stream':
|
1800
|
+
return 'text/vnd.turbo-stream.html, text/html, application/xhtml+xml'
|
1801
|
+
case 'json':
|
1802
|
+
return 'application/json, application/vnd.api+json'
|
1803
|
+
case 'script':
|
1804
|
+
return 'text/javascript, application/javascript'
|
1805
|
+
default:
|
1806
|
+
return '*/*'
|
1807
|
+
}
|
1808
|
+
}
|
1809
|
+
|
1810
|
+
get body () {
|
1811
|
+
return this.options.body
|
1812
|
+
}
|
1813
|
+
|
1814
|
+
get query () {
|
1815
|
+
const originalQuery = (this.originalUrl.split('?')[1] || '').split('#')[0];
|
1816
|
+
const params = new URLSearchParams(originalQuery);
|
1817
|
+
|
1818
|
+
let requestQuery = this.options.query;
|
1819
|
+
if (requestQuery instanceof window.FormData) {
|
1820
|
+
requestQuery = stringEntriesFromFormData(requestQuery);
|
1821
|
+
} else if (requestQuery instanceof window.URLSearchParams) {
|
1822
|
+
requestQuery = requestQuery.entries();
|
1823
|
+
} else {
|
1824
|
+
requestQuery = Object.entries(requestQuery || {});
|
1825
|
+
}
|
1826
|
+
|
1827
|
+
mergeEntries(params, requestQuery);
|
1828
|
+
|
1829
|
+
const query = params.toString();
|
1830
|
+
return (query.length > 0 ? `?${query}` : '')
|
1831
|
+
}
|
1832
|
+
|
1833
|
+
get url () {
|
1834
|
+
return (this.originalUrl.split('?')[0]).split('#')[0] + this.query
|
1835
|
+
}
|
1836
|
+
|
1837
|
+
get responseKind () {
|
1838
|
+
return this.options.responseKind || 'html'
|
1839
|
+
}
|
1840
|
+
|
1841
|
+
get signal () {
|
1842
|
+
return this.options.signal
|
1843
|
+
}
|
1844
|
+
|
1845
|
+
get redirect () {
|
1846
|
+
return this.options.redirect || 'follow'
|
1847
|
+
}
|
1848
|
+
|
1849
|
+
get credentials () {
|
1850
|
+
return this.options.credentials || 'same-origin'
|
1851
|
+
}
|
1852
|
+
|
1853
|
+
get additionalHeaders () {
|
1854
|
+
return this.options.headers || {}
|
1855
|
+
}
|
1856
|
+
|
1857
|
+
get formattedBody () {
|
1858
|
+
const bodyIsAString = Object.prototype.toString.call(this.body) === '[object String]';
|
1859
|
+
const contentTypeIsJson = this.headers['Content-Type'] === 'application/json';
|
1860
|
+
|
1861
|
+
if (contentTypeIsJson && !bodyIsAString) {
|
1862
|
+
return JSON.stringify(this.body)
|
1863
|
+
}
|
1864
|
+
|
1865
|
+
return this.body
|
1866
|
+
}
|
1867
|
+
}
|
1868
|
+
|
1869
|
+
async function post (url, options) {
|
1870
|
+
const request = new FetchRequest('post', url, options);
|
1871
|
+
return request.perform()
|
1872
|
+
}
|
1873
|
+
|
1874
|
+
function insertText(textarea, text) {
|
1875
|
+
var _a, _b, _c;
|
1876
|
+
const before = textarea.value.slice(0, (_a = textarea.selectionStart) !== null && _a !== undefined ? _a : undefined);
|
1877
|
+
const after = textarea.value.slice((_b = textarea.selectionEnd) !== null && _b !== undefined ? _b : undefined);
|
1878
|
+
let canInsertText = true;
|
1879
|
+
textarea.contentEditable = 'true';
|
1880
|
+
try {
|
1881
|
+
canInsertText = document.execCommand('insertText', false, text);
|
1882
|
+
}
|
1883
|
+
catch (error) {
|
1884
|
+
canInsertText = false;
|
1885
|
+
}
|
1886
|
+
textarea.contentEditable = 'false';
|
1887
|
+
if (canInsertText && !textarea.value.slice(0, (_c = textarea.selectionStart) !== null && _c !== undefined ? _c : undefined).endsWith(text)) {
|
1888
|
+
canInsertText = false;
|
1889
|
+
}
|
1890
|
+
if (!canInsertText) {
|
1891
|
+
try {
|
1892
|
+
document.execCommand('ms-beginUndoUnit');
|
1893
|
+
}
|
1894
|
+
catch (e) {
|
1895
|
+
}
|
1896
|
+
textarea.value = before + text + after;
|
1897
|
+
try {
|
1898
|
+
document.execCommand('ms-endUndoUnit');
|
1899
|
+
}
|
1900
|
+
catch (e) {
|
1901
|
+
}
|
1902
|
+
textarea.dispatchEvent(new CustomEvent('change', { bubbles: true, cancelable: true }));
|
1903
|
+
}
|
1904
|
+
}
|
1905
|
+
|
1906
|
+
const skipFormattingMap = new WeakMap();
|
1907
|
+
function setSkipFormattingFlag(event) {
|
1908
|
+
const { currentTarget: el } = event;
|
1909
|
+
const isSkipFormattingKeys = event.code === 'KeyV' && (event.ctrlKey || event.metaKey) && event.shiftKey;
|
1910
|
+
if (isSkipFormattingKeys || (isSkipFormattingKeys && event.altKey)) {
|
1911
|
+
skipFormattingMap.set(el, true);
|
1912
|
+
}
|
1913
|
+
}
|
1914
|
+
function unsetSkipFormattedFlag(event) {
|
1915
|
+
const { currentTarget: el } = event;
|
1916
|
+
skipFormattingMap.delete(el);
|
1917
|
+
}
|
1918
|
+
function shouldSkipFormatting(el) {
|
1919
|
+
var _a;
|
1920
|
+
const shouldSkipFormattingState = (_a = skipFormattingMap.get(el)) !== null && _a !== undefined ? _a : false;
|
1921
|
+
return shouldSkipFormattingState;
|
1922
|
+
}
|
1923
|
+
function installAround(el, installCallbacks, optionConfig) {
|
1924
|
+
el.addEventListener('keydown', setSkipFormattingFlag);
|
1925
|
+
for (const installCallback of installCallbacks) {
|
1926
|
+
installCallback(el, optionConfig);
|
1927
|
+
}
|
1928
|
+
el.addEventListener('paste', unsetSkipFormattedFlag);
|
1929
|
+
}
|
1930
|
+
function uninstall$5(el) {
|
1931
|
+
el.removeEventListener('keydown', setSkipFormattingFlag);
|
1932
|
+
el.removeEventListener('paste', unsetSkipFormattedFlag);
|
1933
|
+
}
|
1934
|
+
|
1935
|
+
function install$4(el) {
|
1936
|
+
el.addEventListener('paste', onPaste$4);
|
1937
|
+
}
|
1938
|
+
function uninstall$4(el) {
|
1939
|
+
el.removeEventListener('paste', onPaste$4);
|
1940
|
+
}
|
1941
|
+
function onPaste$4(event) {
|
1942
|
+
const transfer = event.clipboardData;
|
1943
|
+
const { currentTarget: el } = event;
|
1944
|
+
if (shouldSkipFormatting(el))
|
1945
|
+
return;
|
1946
|
+
if (!transfer || !hasHTML(transfer))
|
1947
|
+
return;
|
1948
|
+
const field = event.currentTarget;
|
1949
|
+
if (!(field instanceof HTMLTextAreaElement))
|
1950
|
+
return;
|
1951
|
+
if (isWithinUserMention(field)) {
|
1952
|
+
return;
|
1953
|
+
}
|
1954
|
+
let plaintext = transfer.getData('text/plain');
|
1955
|
+
const textHTML = transfer.getData('text/html');
|
1956
|
+
const textHTMLClean = textHTML.replace(/\u00A0/g, ' ').replace(/\uC2A0/g, ' ');
|
1957
|
+
if (!textHTML)
|
1958
|
+
return;
|
1959
|
+
plaintext = plaintext.trim();
|
1960
|
+
if (!plaintext)
|
1961
|
+
return;
|
1962
|
+
const parser = new DOMParser();
|
1963
|
+
const doc = parser.parseFromString(textHTMLClean, 'text/html');
|
1964
|
+
const walker = doc.createTreeWalker(doc.body, NodeFilter.SHOW_ALL, node => node.parentNode && isLink(node.parentNode) ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT);
|
1965
|
+
const markdown = convertToMarkdown(plaintext, walker);
|
1966
|
+
if (markdown === plaintext)
|
1967
|
+
return;
|
1968
|
+
event.stopPropagation();
|
1969
|
+
event.preventDefault();
|
1970
|
+
insertText(field, markdown);
|
1971
|
+
}
|
1972
|
+
function convertToMarkdown(plaintext, walker) {
|
1973
|
+
let currentNode = walker.firstChild();
|
1974
|
+
let markdown = plaintext;
|
1975
|
+
let markdownIgnoreBeforeIndex = 0;
|
1976
|
+
let index = 0;
|
1977
|
+
const NODE_LIMIT = 10000;
|
1978
|
+
while (currentNode && index < NODE_LIMIT) {
|
1979
|
+
index++;
|
1980
|
+
const text = isLink(currentNode)
|
1981
|
+
? (currentNode.textContent || '').replace(/[\t\n\r ]+/g, ' ')
|
1982
|
+
: (currentNode === null || currentNode === undefined ? undefined : currentNode.wholeText) || '';
|
1983
|
+
if (isEmptyString(text)) {
|
1984
|
+
currentNode = walker.nextNode();
|
1985
|
+
continue;
|
1986
|
+
}
|
1987
|
+
if (!isLink(currentNode)) {
|
1988
|
+
markdownIgnoreBeforeIndex += text.replace(/[\t\n\r ]+/g, ' ').trimStart().length;
|
1989
|
+
currentNode = walker.nextNode();
|
1990
|
+
continue;
|
1991
|
+
}
|
1992
|
+
const markdownFoundIndex = markdown.indexOf(text, markdownIgnoreBeforeIndex);
|
1993
|
+
if (markdownFoundIndex >= 0) {
|
1994
|
+
const markdownLink = linkify$2(currentNode, text);
|
1995
|
+
markdown = markdown.slice(0, markdownFoundIndex) + markdownLink + markdown.slice(markdownFoundIndex + text.length);
|
1996
|
+
markdownIgnoreBeforeIndex = markdownFoundIndex + markdownLink.length;
|
1997
|
+
}
|
1998
|
+
currentNode = walker.nextNode();
|
1999
|
+
}
|
2000
|
+
return index === NODE_LIMIT ? plaintext : markdown;
|
2001
|
+
}
|
2002
|
+
function isWithinUserMention(textarea) {
|
2003
|
+
const selectionStart = textarea.selectionStart || 0;
|
2004
|
+
if (selectionStart === 0) {
|
2005
|
+
return false;
|
2006
|
+
}
|
2007
|
+
const previousChar = textarea.value.substring(selectionStart - 1, selectionStart);
|
2008
|
+
return previousChar === '@';
|
2009
|
+
}
|
2010
|
+
function isEmptyString(text) {
|
2011
|
+
return !text || (text === null || text === undefined ? undefined : text.trim().length) === 0;
|
2012
|
+
}
|
2013
|
+
function isLink(node) {
|
2014
|
+
var _a;
|
2015
|
+
return ((_a = node.tagName) === null || _a === undefined ? undefined : _a.toLowerCase()) === 'a' && node.hasAttribute('href');
|
2016
|
+
}
|
2017
|
+
function hasHTML(transfer) {
|
2018
|
+
return transfer.types.includes('text/html');
|
2019
|
+
}
|
2020
|
+
function linkify$2(element, label) {
|
2021
|
+
const url = element.href || '';
|
2022
|
+
let markdown = '';
|
2023
|
+
if (isUserMention(element) || isTeamMention(element)) {
|
2024
|
+
markdown = label;
|
2025
|
+
}
|
2026
|
+
else if (isSpecialLink(element) || areEqualLinks(url, label)) {
|
2027
|
+
markdown = url;
|
2028
|
+
}
|
2029
|
+
else {
|
2030
|
+
markdown = `[${label}](${url})`;
|
2031
|
+
}
|
2032
|
+
return markdown;
|
2033
|
+
}
|
2034
|
+
function isSpecialLink(link) {
|
2035
|
+
return (link.className.indexOf('commit-link') >= 0 ||
|
2036
|
+
(!!link.getAttribute('data-hovercard-type') && link.getAttribute('data-hovercard-type') !== 'user'));
|
2037
|
+
}
|
2038
|
+
function areEqualLinks(link1, link2) {
|
2039
|
+
link1 = link1.slice(-1) === '/' ? link1.slice(0, -1) : link1;
|
2040
|
+
link2 = link2.slice(-1) === '/' ? link2.slice(0, -1) : link2;
|
2041
|
+
return link1.toLowerCase() === link2.toLowerCase();
|
2042
|
+
}
|
2043
|
+
function isUserMention(link) {
|
2044
|
+
var _a;
|
2045
|
+
return ((_a = link.textContent) === null || _a === undefined ? undefined : _a.slice(0, 1)) === '@' && link.getAttribute('data-hovercard-type') === 'user';
|
2046
|
+
}
|
2047
|
+
function isTeamMention(link) {
|
2048
|
+
var _a;
|
2049
|
+
return ((_a = link.textContent) === null || _a === undefined ? undefined : _a.slice(0, 1)) === '@' && link.getAttribute('data-hovercard-type') === 'team';
|
2050
|
+
}
|
2051
|
+
|
2052
|
+
function install$3(el) {
|
2053
|
+
el.addEventListener('dragover', onDragover$1);
|
2054
|
+
el.addEventListener('drop', onDrop$1);
|
2055
|
+
el.addEventListener('paste', onPaste$3);
|
2056
|
+
}
|
2057
|
+
function uninstall$3(el) {
|
2058
|
+
el.removeEventListener('dragover', onDragover$1);
|
2059
|
+
el.removeEventListener('drop', onDrop$1);
|
2060
|
+
el.removeEventListener('paste', onPaste$3);
|
2061
|
+
}
|
2062
|
+
function onDrop$1(event) {
|
2063
|
+
const transfer = event.dataTransfer;
|
2064
|
+
if (!transfer)
|
2065
|
+
return;
|
2066
|
+
if (hasFile$1(transfer))
|
2067
|
+
return;
|
2068
|
+
if (!hasLink(transfer))
|
2069
|
+
return;
|
2070
|
+
const links = extractLinks(transfer);
|
2071
|
+
if (!links.some(isImageLink))
|
2072
|
+
return;
|
2073
|
+
event.stopPropagation();
|
2074
|
+
event.preventDefault();
|
2075
|
+
const field = event.currentTarget;
|
2076
|
+
if (!(field instanceof HTMLTextAreaElement))
|
2077
|
+
return;
|
2078
|
+
insertText(field, links.map(linkify$1).join(''));
|
2079
|
+
}
|
2080
|
+
function onDragover$1(event) {
|
2081
|
+
const transfer = event.dataTransfer;
|
2082
|
+
if (transfer)
|
2083
|
+
transfer.dropEffect = 'link';
|
2084
|
+
}
|
2085
|
+
function onPaste$3(event) {
|
2086
|
+
const { currentTarget: el } = event;
|
2087
|
+
if (shouldSkipFormatting(el))
|
2088
|
+
return;
|
2089
|
+
const transfer = event.clipboardData;
|
2090
|
+
if (!transfer || !hasLink(transfer))
|
2091
|
+
return;
|
2092
|
+
const links = extractLinks(transfer);
|
2093
|
+
if (!links.some(isImageLink))
|
2094
|
+
return;
|
2095
|
+
event.stopPropagation();
|
2096
|
+
event.preventDefault();
|
2097
|
+
const field = event.currentTarget;
|
2098
|
+
if (!(field instanceof HTMLTextAreaElement))
|
2099
|
+
return;
|
2100
|
+
insertText(field, links.map(linkify$1).join(''));
|
2101
|
+
}
|
2102
|
+
function linkify$1(link) {
|
2103
|
+
return isImageLink(link) ? `\n\n` : link;
|
2104
|
+
}
|
2105
|
+
function hasFile$1(transfer) {
|
2106
|
+
return Array.from(transfer.types).indexOf('Files') >= 0;
|
2107
|
+
}
|
2108
|
+
function hasLink(transfer) {
|
2109
|
+
return Array.from(transfer.types).indexOf('text/uri-list') >= 0;
|
2110
|
+
}
|
2111
|
+
function extractLinks(transfer) {
|
2112
|
+
return (transfer.getData('text/uri-list') || '').split('\r\n');
|
2113
|
+
}
|
2114
|
+
const IMAGE_RE = /\.(gif|png|jpe?g)$/i;
|
2115
|
+
function isImageLink(url) {
|
2116
|
+
return IMAGE_RE.test(url);
|
2117
|
+
}
|
2118
|
+
|
2119
|
+
const pasteLinkAsPlainTextOverSelectedTextMap = new WeakMap();
|
2120
|
+
function install$2(el, optionConfig) {
|
2121
|
+
var _a;
|
2122
|
+
pasteLinkAsPlainTextOverSelectedTextMap.set(el, ((_a = optionConfig === null || optionConfig === undefined ? undefined : optionConfig.defaultPlainTextPaste) === null || _a === undefined ? undefined : _a.urlLinks) === true);
|
2123
|
+
el.addEventListener('paste', onPaste$2);
|
2124
|
+
}
|
2125
|
+
function uninstall$2(el) {
|
2126
|
+
el.removeEventListener('paste', onPaste$2);
|
2127
|
+
}
|
2128
|
+
function onPaste$2(event) {
|
2129
|
+
var _a;
|
2130
|
+
const { currentTarget: el } = event;
|
2131
|
+
const element = el;
|
2132
|
+
const shouldPasteAsPlainText = (_a = pasteLinkAsPlainTextOverSelectedTextMap.get(element)) !== null && _a !== undefined ? _a : false;
|
2133
|
+
const shouldSkipDefaultBehavior = shouldSkipFormatting(element);
|
2134
|
+
if ((!shouldPasteAsPlainText && shouldSkipDefaultBehavior) ||
|
2135
|
+
(shouldPasteAsPlainText && !shouldSkipDefaultBehavior)) {
|
2136
|
+
return;
|
2137
|
+
}
|
2138
|
+
const transfer = event.clipboardData;
|
2139
|
+
if (!transfer || !hasPlainText(transfer))
|
2140
|
+
return;
|
2141
|
+
const field = event.currentTarget;
|
2142
|
+
if (!(field instanceof HTMLTextAreaElement))
|
2143
|
+
return;
|
2144
|
+
const text = transfer.getData('text/plain');
|
2145
|
+
if (!text)
|
2146
|
+
return;
|
2147
|
+
if (!isURL(text))
|
2148
|
+
return;
|
2149
|
+
if (isWithinLink(field))
|
2150
|
+
return;
|
2151
|
+
const selectedText = field.value.substring(field.selectionStart, field.selectionEnd);
|
2152
|
+
if (!selectedText.length)
|
2153
|
+
return;
|
2154
|
+
if (isURL(selectedText.trim()))
|
2155
|
+
return;
|
2156
|
+
event.stopPropagation();
|
2157
|
+
event.preventDefault();
|
2158
|
+
insertText(field, linkify(selectedText, text.trim()));
|
2159
|
+
}
|
2160
|
+
function hasPlainText(transfer) {
|
2161
|
+
return Array.from(transfer.types).includes('text/plain');
|
2162
|
+
}
|
2163
|
+
function isWithinLink(textarea) {
|
2164
|
+
const selectionStart = textarea.selectionStart || 0;
|
2165
|
+
if (selectionStart > 1) {
|
2166
|
+
const previousChars = textarea.value.substring(selectionStart - 2, selectionStart);
|
2167
|
+
return previousChars === '](';
|
2168
|
+
}
|
2169
|
+
else {
|
2170
|
+
return false;
|
2171
|
+
}
|
2172
|
+
}
|
2173
|
+
function linkify(selectedText, text) {
|
2174
|
+
return `[${selectedText}](${text})`;
|
2175
|
+
}
|
2176
|
+
function isURL(url) {
|
2177
|
+
try {
|
2178
|
+
const parsedURL = new URL(url);
|
2179
|
+
return removeTrailingSlash(parsedURL.href).trim() === removeTrailingSlash(url).trim();
|
2180
|
+
}
|
2181
|
+
catch (_a) {
|
2182
|
+
return false;
|
2183
|
+
}
|
2184
|
+
}
|
2185
|
+
function removeTrailingSlash(url) {
|
2186
|
+
return url.endsWith('/') ? url.slice(0, url.length - 1) : url;
|
2187
|
+
}
|
2188
|
+
|
2189
|
+
function install$1(el) {
|
2190
|
+
el.addEventListener('dragover', onDragover);
|
2191
|
+
el.addEventListener('drop', onDrop);
|
2192
|
+
el.addEventListener('paste', onPaste$1);
|
2193
|
+
}
|
2194
|
+
function uninstall$1(el) {
|
2195
|
+
el.removeEventListener('dragover', onDragover);
|
2196
|
+
el.removeEventListener('drop', onDrop);
|
2197
|
+
el.removeEventListener('paste', onPaste$1);
|
2198
|
+
}
|
2199
|
+
function onDrop(event) {
|
2200
|
+
const transfer = event.dataTransfer;
|
2201
|
+
if (!transfer)
|
2202
|
+
return;
|
2203
|
+
if (hasFile(transfer))
|
2204
|
+
return;
|
2205
|
+
const textToPaste = generateText(transfer);
|
2206
|
+
if (!textToPaste)
|
2207
|
+
return;
|
2208
|
+
event.stopPropagation();
|
2209
|
+
event.preventDefault();
|
2210
|
+
const field = event.currentTarget;
|
2211
|
+
if (field instanceof HTMLTextAreaElement) {
|
2212
|
+
insertText(field, textToPaste);
|
2213
|
+
}
|
2214
|
+
}
|
2215
|
+
function onDragover(event) {
|
2216
|
+
const transfer = event.dataTransfer;
|
2217
|
+
if (transfer)
|
2218
|
+
transfer.dropEffect = 'copy';
|
2219
|
+
}
|
2220
|
+
function onPaste$1(event) {
|
2221
|
+
const { currentTarget: el } = event;
|
2222
|
+
if (shouldSkipFormatting(el))
|
2223
|
+
return;
|
2224
|
+
if (!event.clipboardData)
|
2225
|
+
return;
|
2226
|
+
const textToPaste = generateText(event.clipboardData);
|
2227
|
+
if (!textToPaste)
|
2228
|
+
return;
|
2229
|
+
event.stopPropagation();
|
2230
|
+
event.preventDefault();
|
2231
|
+
const field = event.currentTarget;
|
2232
|
+
if (field instanceof HTMLTextAreaElement) {
|
2233
|
+
insertText(field, textToPaste);
|
2234
|
+
}
|
2235
|
+
}
|
2236
|
+
function hasFile(transfer) {
|
2237
|
+
return Array.from(transfer.types).indexOf('Files') >= 0;
|
2238
|
+
}
|
2239
|
+
function columnText(column) {
|
2240
|
+
const noBreakSpace = '\u00A0';
|
2241
|
+
const text = (column.textContent || '').trim().replace(/\|/g, '\\|').replace(/\n/g, ' ');
|
2242
|
+
return text || noBreakSpace;
|
2243
|
+
}
|
2244
|
+
function tableHeaders(row) {
|
2245
|
+
return Array.from(row.querySelectorAll('td, th')).map(columnText);
|
2246
|
+
}
|
2247
|
+
function tableMarkdown(node) {
|
2248
|
+
const rows = Array.from(node.querySelectorAll('tr'));
|
2249
|
+
const firstRow = rows.shift();
|
2250
|
+
if (!firstRow)
|
2251
|
+
return '';
|
2252
|
+
const headers = tableHeaders(firstRow);
|
2253
|
+
const spacers = headers.map(() => '--');
|
2254
|
+
const header = `${headers.join(' | ')}\n${spacers.join(' | ')}\n`;
|
2255
|
+
const body = rows
|
2256
|
+
.map(row => {
|
2257
|
+
return Array.from(row.querySelectorAll('td')).map(columnText).join(' | ');
|
2258
|
+
})
|
2259
|
+
.join('\n');
|
2260
|
+
return `\n${header}${body}\n\n`;
|
2261
|
+
}
|
2262
|
+
function generateText(transfer) {
|
2263
|
+
if (Array.from(transfer.types).indexOf('text/html') === -1)
|
2264
|
+
return;
|
2265
|
+
const html = transfer.getData('text/html');
|
2266
|
+
if (!/<table/i.test(html))
|
2267
|
+
return;
|
2268
|
+
const start = html.substring(0, html.indexOf('<table'));
|
2269
|
+
const tableCloseIndex = html.lastIndexOf('</table>');
|
2270
|
+
if (!start || !tableCloseIndex)
|
2271
|
+
return;
|
2272
|
+
const end = html.substring(tableCloseIndex + 8);
|
2273
|
+
const parser = new DOMParser();
|
2274
|
+
const parsedDocument = parser.parseFromString(html, 'text/html');
|
2275
|
+
let table = parsedDocument.querySelector('table');
|
2276
|
+
table = !table || table.closest('[data-paste-markdown-skip]') ? null : table;
|
2277
|
+
if (!table)
|
2278
|
+
return;
|
2279
|
+
const formattedTable = tableMarkdown(table);
|
2280
|
+
if (!formattedTable)
|
2281
|
+
return;
|
2282
|
+
return [start, formattedTable, end].join('').replace(/<meta.*?>/, '');
|
2283
|
+
}
|
2284
|
+
|
2285
|
+
function install(el) {
|
2286
|
+
el.addEventListener('paste', onPaste);
|
2287
|
+
}
|
2288
|
+
function uninstall(el) {
|
2289
|
+
el.removeEventListener('paste', onPaste);
|
2290
|
+
}
|
2291
|
+
function onPaste(event) {
|
2292
|
+
const { currentTarget: el } = event;
|
2293
|
+
if (shouldSkipFormatting(el))
|
2294
|
+
return;
|
2295
|
+
const transfer = event.clipboardData;
|
2296
|
+
if (!transfer || !hasMarkdown(transfer))
|
2297
|
+
return;
|
2298
|
+
const field = event.currentTarget;
|
2299
|
+
if (!(field instanceof HTMLTextAreaElement))
|
2300
|
+
return;
|
2301
|
+
const text = transfer.getData('text/x-gfm');
|
2302
|
+
if (!text)
|
2303
|
+
return;
|
2304
|
+
event.stopPropagation();
|
2305
|
+
event.preventDefault();
|
2306
|
+
insertText(field, text);
|
2307
|
+
}
|
2308
|
+
function hasMarkdown(transfer) {
|
2309
|
+
return Array.from(transfer.types).indexOf('text/x-gfm') >= 0;
|
2310
|
+
}
|
2311
|
+
|
2312
|
+
function subscribe(el, optionConfig) {
|
2313
|
+
installAround(el, [install$1, install$3, install$2, install, install$4], optionConfig);
|
2314
|
+
return {
|
2315
|
+
unsubscribe: () => {
|
2316
|
+
uninstall$5(el);
|
2317
|
+
uninstall$1(el);
|
2318
|
+
uninstall$4(el);
|
2319
|
+
uninstall$3(el);
|
2320
|
+
uninstall$2(el);
|
2321
|
+
uninstall(el);
|
2322
|
+
},
|
2323
|
+
};
|
2324
|
+
}
|
2325
|
+
|
2326
|
+
/* eslint-disable camelcase */
|
2327
|
+
|
2328
|
+
// upload code from Jeremy Smith's blog post
|
2329
|
+
// https://hybrd.co/posts/github-issue-style-file-uploader-using-stimulus-and-active-storage
|
2330
|
+
|
2331
|
+
// Connects to data-controller="marksmith"
|
2332
|
+
class marksmith_controller extends stimulus.Controller {
|
2333
|
+
static values = {
|
2334
|
+
attachUrl: String,
|
2335
|
+
previewUrl: String,
|
2336
|
+
extraPreviewParams: { type: Object, default: {} },
|
2337
|
+
fieldId: String,
|
2338
|
+
}
|
2339
|
+
|
2340
|
+
static targets = ['fieldContainer', 'fieldElement', 'previewElement', 'writeTabButton', 'previewTabButton', 'toolbar']
|
2341
|
+
|
2342
|
+
connect() {
|
2343
|
+
subscribe(this.fieldContainerTarget, { defaultPlainTextPaste: { urlLinks: true } });
|
2344
|
+
}
|
2345
|
+
|
2346
|
+
switchToWrite(event) {
|
2347
|
+
event.preventDefault();
|
2348
|
+
|
2349
|
+
// toggle buttons
|
2350
|
+
this.writeTabButtonTarget.classList.add('ms:hidden');
|
2351
|
+
this.previewTabButtonTarget.classList.remove('ms:hidden');
|
2352
|
+
|
2353
|
+
// toggle write/preview buttons
|
2354
|
+
this.fieldContainerTarget.classList.remove('ms:hidden');
|
2355
|
+
this.previewElementTarget.classList.add('ms:hidden');
|
2356
|
+
|
2357
|
+
// toggle the toolbar back
|
2358
|
+
this.toolbarTarget.classList.remove('ms:hidden');
|
2359
|
+
}
|
2360
|
+
|
2361
|
+
switchToPreview(event) {
|
2362
|
+
event.preventDefault();
|
2363
|
+
|
2364
|
+
post(this.previewUrlValue, {
|
2365
|
+
body: {
|
2366
|
+
body: this.fieldElementTarget.value,
|
2367
|
+
element_id: this.previewElementTarget.id,
|
2368
|
+
extra_params: this.extraPreviewParamsValue,
|
2369
|
+
},
|
2370
|
+
responseKind: 'turbo-stream',
|
2371
|
+
});
|
2372
|
+
|
2373
|
+
// set the min height to the field element height
|
2374
|
+
this.previewElementTarget.style.minHeight = `${this.fieldElementTarget.offsetHeight}px`;
|
2375
|
+
|
2376
|
+
// toggle buttons
|
2377
|
+
this.writeTabButtonTarget.classList.remove('ms:hidden');
|
2378
|
+
this.previewTabButtonTarget.classList.add('ms:hidden');
|
2379
|
+
|
2380
|
+
// toggle elements
|
2381
|
+
this.fieldContainerTarget.classList.add('ms:hidden');
|
2382
|
+
this.previewElementTarget.classList.remove('ms:hidden');
|
2383
|
+
|
2384
|
+
// toggle the toolbar
|
2385
|
+
this.toolbarTarget.classList.add('ms:hidden');
|
2386
|
+
}
|
2387
|
+
|
2388
|
+
dropUpload(event) {
|
2389
|
+
event.preventDefault();
|
2390
|
+
this.uploadFiles(event.dataTransfer.files);
|
2391
|
+
}
|
2392
|
+
|
2393
|
+
pasteUpload(event) {
|
2394
|
+
if (!event.clipboardData.files.length) return
|
2395
|
+
|
2396
|
+
event.preventDefault();
|
2397
|
+
this.uploadFiles(event.clipboardData.files);
|
2398
|
+
}
|
2399
|
+
|
2400
|
+
buttonUpload(event) {
|
2401
|
+
event.preventDefault();
|
2402
|
+
// Create a hidden file input and trigger it
|
2403
|
+
const fileInput = document.createElement('input');
|
2404
|
+
fileInput.type = 'file';
|
2405
|
+
fileInput.multiple = true;
|
2406
|
+
fileInput.accept = 'image/*,.pdf,.doc,.docx,.txt';
|
2407
|
+
|
2408
|
+
fileInput.addEventListener('change', (e) => {
|
2409
|
+
this.uploadFiles(e.target.files);
|
2410
|
+
});
|
2411
|
+
|
2412
|
+
fileInput.click();
|
2413
|
+
}
|
2414
|
+
|
2415
|
+
uploadFiles(files) {
|
2416
|
+
Array.from(files).forEach((file) => this.uploadFile(file));
|
2417
|
+
}
|
2418
|
+
|
2419
|
+
uploadFile(file) {
|
2420
|
+
const upload = new DirectUpload(file, this.attachUrlValue);
|
2421
|
+
|
2422
|
+
upload.create((error, blob) => {
|
2423
|
+
if (error) {
|
2424
|
+
console.log('Error', error);
|
2425
|
+
} else {
|
2426
|
+
const text = this.markdownLink(blob);
|
2427
|
+
const start = this.fieldElementTarget.selectionStart;
|
2428
|
+
const end = this.fieldElementTarget.selectionEnd;
|
2429
|
+
this.fieldElementTarget.setRangeText(text, start, end);
|
2430
|
+
}
|
2431
|
+
});
|
2432
|
+
}
|
2433
|
+
|
2434
|
+
markdownLink(blob) {
|
2435
|
+
const { filename } = blob;
|
2436
|
+
const url = `/rails/active_storage/blobs/${blob.signed_id}/${filename}`;
|
2437
|
+
const prefix = (this.isImage(blob.content_type) ? '!' : '');
|
2438
|
+
|
2439
|
+
return `${prefix}[${filename}](${url})\n`
|
2440
|
+
}
|
2441
|
+
|
2442
|
+
isImage(contentType) {
|
2443
|
+
return ['image/jpeg', 'image/gif', 'image/png'].includes(contentType)
|
2444
|
+
}
|
2445
|
+
}
|
2446
|
+
|
2447
|
+
return marksmith_controller;
|
2448
|
+
|
2449
|
+
})(FakeStimulus);
|