Skip to content

Commit 533471c

Browse files
authored
Merge pull request #61 from blocknotes/build/update-quill-to-2.0.3
Update Quill to version 2.0.3
2 parents 181aab7 + 3617aab commit 533471c

15 files changed

Lines changed: 345 additions & 22454 deletions

File tree

Gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ gem 'cuprite'
4848
gem 'rspec_junit_formatter'
4949
gem 'rspec-rails'
5050
gem 'simplecov', require: false
51+
gem 'super_diff'
5152

5253
# Linters
5354
gem 'fasterer'
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
import LoadingImage from "./blots/image.js";
2+
3+
class ImageUploader {
4+
constructor(quill, options) {
5+
this.quill = quill;
6+
this.options = options;
7+
this.range = null;
8+
this.placeholderDelta = null;
9+
10+
if (typeof this.options.upload !== "function")
11+
console.warn(
12+
"[Missing config] upload function that returns a promise is required"
13+
);
14+
15+
var toolbar = this.quill.getModule("toolbar");
16+
if (toolbar) {
17+
toolbar.addHandler("image", this.selectLocalImage.bind(this));
18+
}
19+
20+
this.handleDrop = this.handleDrop.bind(this);
21+
this.handlePaste = this.handlePaste.bind(this);
22+
23+
this.quill.root.addEventListener("drop", this.handleDrop, false);
24+
this.quill.root.addEventListener("paste", this.handlePaste, false);
25+
}
26+
27+
selectLocalImage() {
28+
this.quill.focus();
29+
this.range = this.quill.getSelection();
30+
this.fileHolder = document.createElement("input");
31+
this.fileHolder.setAttribute("type", "file");
32+
this.fileHolder.setAttribute("accept", "image/*");
33+
this.fileHolder.setAttribute("style", "visibility:hidden");
34+
35+
this.fileHolder.onchange = this.fileChanged.bind(this);
36+
37+
document.body.appendChild(this.fileHolder);
38+
39+
this.fileHolder.click();
40+
41+
window.requestAnimationFrame(() => {
42+
document.body.removeChild(this.fileHolder);
43+
});
44+
}
45+
46+
handleDrop(evt) {
47+
if (
48+
evt.dataTransfer &&
49+
evt.dataTransfer.files &&
50+
evt.dataTransfer.files.length
51+
) {
52+
evt.stopPropagation();
53+
evt.preventDefault();
54+
if (document.caretRangeFromPoint) {
55+
const selection = document.getSelection();
56+
const range = document.caretRangeFromPoint(evt.clientX, evt.clientY);
57+
if (selection && range) {
58+
selection.setBaseAndExtent(
59+
range.startContainer,
60+
range.startOffset,
61+
range.startContainer,
62+
range.startOffset
63+
);
64+
}
65+
} else {
66+
const selection = document.getSelection();
67+
const range = document.caretPositionFromPoint(evt.clientX, evt.clientY);
68+
if (selection && range) {
69+
selection.setBaseAndExtent(
70+
range.offsetNode,
71+
range.offset,
72+
range.offsetNode,
73+
range.offset
74+
);
75+
}
76+
}
77+
78+
this.quill.focus();
79+
this.range = this.quill.getSelection();
80+
let file = evt.dataTransfer.files[0];
81+
82+
setTimeout(() => {
83+
this.quill.focus();
84+
this.range = this.quill.getSelection();
85+
this.readAndUploadFile(file);
86+
}, 0);
87+
}
88+
}
89+
90+
handlePaste(evt) {
91+
let clipboard = evt.clipboardData || window.clipboardData;
92+
93+
// IE 11 is .files other browsers are .items
94+
if (clipboard && (clipboard.items || clipboard.files)) {
95+
let items = clipboard.items || clipboard.files;
96+
const IMAGE_MIME_REGEX = /^image\/(jpe?g|gif|png|svg|webp)$/i;
97+
98+
for (let i = 0; i < items.length; i++) {
99+
if (IMAGE_MIME_REGEX.test(items[i].type)) {
100+
let file = items[i].getAsFile ? items[i].getAsFile() : items[i];
101+
102+
if (file) {
103+
this.quill.focus();
104+
this.range = this.quill.getSelection();
105+
evt.preventDefault();
106+
setTimeout(() => {
107+
this.quill.focus();
108+
this.range = this.quill.getSelection();
109+
this.readAndUploadFile(file);
110+
}, 0);
111+
}
112+
}
113+
}
114+
}
115+
}
116+
117+
readAndUploadFile(file) {
118+
let isUploadReject = false;
119+
120+
const fileReader = new FileReader();
121+
122+
fileReader.addEventListener(
123+
"load",
124+
() => {
125+
if (!isUploadReject) {
126+
let base64ImageSrc = fileReader.result;
127+
this.insertBase64Image(base64ImageSrc);
128+
}
129+
},
130+
false
131+
);
132+
133+
if (file) {
134+
fileReader.readAsDataURL(file);
135+
}
136+
137+
this.options.upload(file).then(
138+
(imageUrl) => {
139+
this.insertToEditor(imageUrl);
140+
},
141+
(error) => {
142+
isUploadReject = true;
143+
this.removeBase64Image();
144+
console.warn(error);
145+
}
146+
);
147+
}
148+
149+
fileChanged() {
150+
const file = this.fileHolder.files[0];
151+
this.readAndUploadFile(file);
152+
}
153+
154+
insertBase64Image(url) {
155+
const range = this.range;
156+
157+
this.placeholderDelta = this.quill.insertEmbed(
158+
range.index,
159+
LoadingImage.blotName,
160+
`${url}`,
161+
"user"
162+
);
163+
}
164+
165+
insertToEditor(url) {
166+
const range = this.range;
167+
168+
const lengthToDelete = this.calculatePlaceholderInsertLength();
169+
170+
// Delete the placeholder image
171+
this.quill.deleteText(range.index, lengthToDelete, "user");
172+
// Insert the server saved image
173+
this.quill.insertEmbed(range.index, "image", `${url}`, "user");
174+
175+
range.index++;
176+
this.quill.setSelection(range, "user");
177+
}
178+
179+
// The length of the insert delta from insertBase64Image can vary depending on what part of the line the insert occurs
180+
calculatePlaceholderInsertLength() {
181+
return this.placeholderDelta.ops.reduce((accumulator, deltaOperation) => {
182+
if (deltaOperation.hasOwnProperty('insert'))
183+
accumulator++;
184+
185+
return accumulator;
186+
}, 0);
187+
}
188+
189+
removeBase64Image() {
190+
const range = this.range;
191+
const lengthToDelete = this.calculatePlaceholderInsertLength();
192+
193+
this.quill.deleteText(range.index, lengthToDelete, "user");
194+
}
195+
}
196+
197+
window.ImageUploader = ImageUploader;
198+
export default ImageUploader;

app/assets/javascripts/activeadmin/quill.imageUploader.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)