@widgetic/editor 3.10.55 → 3.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/control.js +1 -1
- package/lib/controls.js +1 -1
- package/lib/core.js +1 -1
- package/lib/dev/control.js +8 -8
- package/lib/dev/controls.js +123 -99
- package/lib/dev/core.js +20 -20
- package/lib/dev/editor.js +5812 -666
- package/lib/editor.js +26 -4
- package/package.json +4 -3
- package/src/core/services/uploads.js +76 -18
- package/src/editor/content/browser/dropzone.html +94 -8
- package/src/editor/content/browser/file.html +2 -0
- package/src/editor/content/browser/upload.html +216 -195
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@widgetic/editor",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.11.1",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"start": "webpack --config webpack.config.js --watch --progress",
|
|
6
6
|
"build": "webpack --config webpack.config.js",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"svelte": "^1.22.5",
|
|
59
59
|
"svelte-loader": "^1.3.0",
|
|
60
60
|
"uglify-js": "^2.8.7",
|
|
61
|
-
"uglifyjs-webpack-plugin": "^
|
|
61
|
+
"uglifyjs-webpack-plugin": "^1.3.0",
|
|
62
62
|
"virtual-dom": "^2.1.1",
|
|
63
63
|
"webpack": "^3.0.0",
|
|
64
64
|
"webpack-bundle-analyzer": "^2.3.0",
|
|
@@ -67,7 +67,8 @@
|
|
|
67
67
|
"dependencies": {
|
|
68
68
|
"http-server": "^0.11.1",
|
|
69
69
|
"json-logic-js": "^1.2.2",
|
|
70
|
-
"soundcloud": "^3.3.2"
|
|
70
|
+
"soundcloud": "^3.3.2",
|
|
71
|
+
"tus-js-client": "^4.1.0"
|
|
71
72
|
},
|
|
72
73
|
"volta": {
|
|
73
74
|
"node": "12.22.2"
|
|
@@ -24,8 +24,10 @@ File.defaults = {
|
|
|
24
24
|
};
|
|
25
25
|
File.guid = 0;
|
|
26
26
|
|
|
27
|
-
const parseType = type =>
|
|
28
|
-
|
|
27
|
+
const parseType = type => type.indexOf('/') > 0 ? type.slice(0, type.indexOf('/')) : type;
|
|
28
|
+
|
|
29
|
+
// const root = "http://127.0.0.1:8091"; // for local development
|
|
30
|
+
const root = ''; // (empty) for running on https://widgetic.com/upload
|
|
29
31
|
|
|
30
32
|
export default class UploadService extends EventsMixin(Service) {
|
|
31
33
|
constructor(user, token) {
|
|
@@ -41,6 +43,9 @@ export default class UploadService extends EventsMixin(Service) {
|
|
|
41
43
|
// max file size in MB
|
|
42
44
|
this.maxFilesize = user.premium ? 250 : 25; // MB limit / file for premium / free plan
|
|
43
45
|
|
|
46
|
+
// chunk size in MB
|
|
47
|
+
this.chunkSize = 90; // MB to go over Cloudflare's 100MB limit
|
|
48
|
+
|
|
44
49
|
// max quota in bytes
|
|
45
50
|
this.quota = (user.premium ? 10000 : 250) * 1000 * 1000; // total quota for premium / free plan
|
|
46
51
|
|
|
@@ -54,8 +59,16 @@ export default class UploadService extends EventsMixin(Service) {
|
|
|
54
59
|
return this.maxFilesize;
|
|
55
60
|
}
|
|
56
61
|
|
|
62
|
+
getChunkSize() {
|
|
63
|
+
return this.chunkSize;
|
|
64
|
+
}
|
|
65
|
+
|
|
57
66
|
getUploadURL() {
|
|
58
|
-
|
|
67
|
+
let url = root + '/upload?user=' + this.user;
|
|
68
|
+
|
|
69
|
+
// console.log("getUploadURL:", url);
|
|
70
|
+
|
|
71
|
+
return url;
|
|
59
72
|
}
|
|
60
73
|
|
|
61
74
|
getAuthHeaders() {
|
|
@@ -63,10 +76,15 @@ export default class UploadService extends EventsMixin(Service) {
|
|
|
63
76
|
}
|
|
64
77
|
|
|
65
78
|
getFiles(type) {
|
|
79
|
+
// console.log("getting files:", this.files);
|
|
66
80
|
type = parseType(type);
|
|
81
|
+
|
|
82
|
+
// if files for this type were already fetched, return them
|
|
67
83
|
if (this.refreshed[type])
|
|
68
84
|
return Promise.resolve(this.files.filter(f => f.type === type));
|
|
69
85
|
|
|
86
|
+
// otherwise fetch them
|
|
87
|
+
// console.log("fetching files for type:", type);
|
|
70
88
|
return this._fetch(type)
|
|
71
89
|
.then(files => {
|
|
72
90
|
// console.log('fetched files:', files)
|
|
@@ -80,8 +98,13 @@ export default class UploadService extends EventsMixin(Service) {
|
|
|
80
98
|
return this.files.filter(file => file.id === id)[0];
|
|
81
99
|
}
|
|
82
100
|
|
|
101
|
+
getUser() {
|
|
102
|
+
return this.user;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// when the upload starts: create a new File instance and push it to the files array
|
|
83
106
|
upload(uploadFile) {
|
|
84
|
-
|
|
107
|
+
console.log("Uploads Service onUploadStart:", uploadFile);
|
|
85
108
|
|
|
86
109
|
// create a new File instance from the uploadFile
|
|
87
110
|
let file = new File({
|
|
@@ -104,29 +127,37 @@ export default class UploadService extends EventsMixin(Service) {
|
|
|
104
127
|
this._broadcast.update();
|
|
105
128
|
}
|
|
106
129
|
|
|
130
|
+
// when the upload is complete: update the file in the files array with the data from the server
|
|
107
131
|
uploadComplete(uploadFile, instanceId) {
|
|
108
|
-
// console.log("uploadComplete
|
|
132
|
+
// console.log("Uploads Service uploadComplete:", uploadFile);
|
|
109
133
|
|
|
134
|
+
// if the upload failed, show an error
|
|
110
135
|
if (uploadFile.status === 'error') return;
|
|
111
136
|
|
|
112
|
-
// read
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
137
|
+
// read the response string from the request
|
|
138
|
+
let responseText = uploadFile.xhr && uploadFile.xhr.response ? uploadFile.xhr.response : "";
|
|
139
|
+
if(!responseText) {
|
|
140
|
+
console.error("Error parsing uploadComplete response:", error);
|
|
141
|
+
return this.setError(uploadFile, 'Error uploading file!');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// read server file from the response
|
|
145
|
+
const response = JSON.parse(responseText);
|
|
117
146
|
let serverFile = response.files.data[0];
|
|
118
147
|
// console.log("uploadComplete file on the server:", serverFile);
|
|
119
148
|
|
|
120
|
-
// read quota from response
|
|
149
|
+
// read quota from response to update the used quota in dropzone
|
|
121
150
|
// this.quota = response.quota.max;
|
|
122
151
|
this.used = response.quota.used;
|
|
123
152
|
|
|
153
|
+
// console.log("Upload Service uploadComplete update file to complete:", uploadFile, serverFile);
|
|
154
|
+
|
|
124
155
|
// update the file in the files array with the data from the server
|
|
125
156
|
this._update(uploadFile, f => {
|
|
126
157
|
f.state = 'complete';
|
|
127
158
|
f.id = serverFile.id;
|
|
128
159
|
f.path = serverFile.path;
|
|
129
|
-
f.url = response.hostUrl +
|
|
160
|
+
f.url = response.hostUrl + serverFile.path;
|
|
130
161
|
f.lastModified = serverFile.lastModified;
|
|
131
162
|
});
|
|
132
163
|
|
|
@@ -135,10 +166,15 @@ export default class UploadService extends EventsMixin(Service) {
|
|
|
135
166
|
}
|
|
136
167
|
|
|
137
168
|
accept(file, done) {
|
|
169
|
+
// console.log("Editor accept file:", file, done);
|
|
170
|
+
|
|
171
|
+
// if with the file size the quota is not exceeded, accept the file
|
|
138
172
|
// this.used was increased by adding file.size in the `upload` method
|
|
139
173
|
if (this.used < this.quota) {
|
|
140
174
|
return done();
|
|
141
175
|
}
|
|
176
|
+
|
|
177
|
+
// if the file exceeds the quota show an error
|
|
142
178
|
done(
|
|
143
179
|
`The file exceeds the quota by ${prettyBytes(this.used - this.quota)}.`
|
|
144
180
|
);
|
|
@@ -155,29 +191,49 @@ export default class UploadService extends EventsMixin(Service) {
|
|
|
155
191
|
}
|
|
156
192
|
|
|
157
193
|
setThumbnail(file, thumbnail) {
|
|
194
|
+
console.log("Uploads Service setThumbnail:", file, thumbnail);
|
|
158
195
|
this._update(file, f => f.thumbnail = thumbnail);
|
|
159
196
|
}
|
|
160
197
|
|
|
161
198
|
setError(file, error) {
|
|
162
199
|
this.used -= file.size;
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
f.size = 0;
|
|
166
|
-
});
|
|
200
|
+
|
|
201
|
+
this._update(file, f => { f.error = error; f.size = 0; });
|
|
167
202
|
}
|
|
168
203
|
|
|
169
204
|
setUploadProgress(file, percentage, size) {
|
|
170
205
|
this._update(file, f => f.progress = percentage);
|
|
206
|
+
// console.log("Uploads Service setUploadProgress:", percentage, file.state);
|
|
171
207
|
}
|
|
172
208
|
|
|
173
209
|
setUploadState(file, state) {
|
|
210
|
+
// console.log("Uploads Service setUploadState:", state);
|
|
174
211
|
this._update(file, f => f.state = state);
|
|
175
212
|
}
|
|
176
213
|
|
|
177
214
|
_update(file, transform) {
|
|
215
|
+
|
|
216
|
+
// console.log("Uploads Service _update file:", transform);
|
|
217
|
+
|
|
178
218
|
// apply the transform function to the file in the array
|
|
179
219
|
this.files = this.files.map(f => {
|
|
180
|
-
|
|
220
|
+
let fl = f.uploadFile ? f.uploadFile : f;
|
|
221
|
+
|
|
222
|
+
let equal = fl.name === file.name &&
|
|
223
|
+
fl.size === file.size &&
|
|
224
|
+
fl.lastModified === file.lastModified;
|
|
225
|
+
|
|
226
|
+
// console.log("Uploads Service _update compare:", equal, file, fl);
|
|
227
|
+
|
|
228
|
+
// if the file is the one to update, apply the transform function
|
|
229
|
+
// if (f.uploadFile === file)
|
|
230
|
+
if(equal)
|
|
231
|
+
{
|
|
232
|
+
// console.log("Uploads Service _update file found:", file, fl);
|
|
233
|
+
transform(f);
|
|
234
|
+
// console.log("Uploads Service _update file found:", f);
|
|
235
|
+
}
|
|
236
|
+
|
|
181
237
|
return f;
|
|
182
238
|
});
|
|
183
239
|
|
|
@@ -185,10 +241,12 @@ export default class UploadService extends EventsMixin(Service) {
|
|
|
185
241
|
this._broadcast.update();
|
|
186
242
|
}
|
|
187
243
|
|
|
244
|
+
// fetch files from the server for the user
|
|
188
245
|
_fetch(type) {
|
|
246
|
+
// console.log("fetching files for type:", type);
|
|
189
247
|
return jQuery
|
|
190
248
|
.ajax({
|
|
191
|
-
url: `/upload/?user=${this.user}&type=${type}`,
|
|
249
|
+
url: root + `/upload/?user=${this.user}&type=${type}`,
|
|
192
250
|
headers: this.getAuthHeaders(),
|
|
193
251
|
})
|
|
194
252
|
.then(response => {
|
|
@@ -73,6 +73,9 @@ import Dropzone from 'dropzone';
|
|
|
73
73
|
import Service from './service.html';
|
|
74
74
|
import prettyBytes from 'pretty-bytes';
|
|
75
75
|
|
|
76
|
+
// Third party libraries imports
|
|
77
|
+
import * as tus from 'tus-js-client';
|
|
78
|
+
|
|
76
79
|
export default {
|
|
77
80
|
components: { Service },
|
|
78
81
|
data() {
|
|
@@ -82,7 +85,8 @@ export default {
|
|
|
82
85
|
quota: 0,
|
|
83
86
|
uploading: { total: 0, now: 0 },
|
|
84
87
|
dragging: false,
|
|
85
|
-
maxFilesize: 10,
|
|
88
|
+
maxFilesize: 10, // in MB default
|
|
89
|
+
chunkSize: 0, // 0 by default, so no chunking
|
|
86
90
|
instanceId: 0,
|
|
87
91
|
};
|
|
88
92
|
},
|
|
@@ -92,22 +96,31 @@ export default {
|
|
|
92
96
|
},
|
|
93
97
|
oncreate() {
|
|
94
98
|
this.Uploads = this.refs.Uploads.instance();
|
|
99
|
+
|
|
100
|
+
// read the quota and used space from the Uploads service
|
|
95
101
|
this.set({ quota: this.Uploads.quota, used: this.Uploads.used });
|
|
96
102
|
|
|
103
|
+
// check if should process uploads with XHR depending on the chunkSize
|
|
104
|
+
const autoProcessQueue = !this.get('chunkSize');
|
|
105
|
+
// console.log("Dropzone oncreate autoProcessQueue:", autoProcessQueue);
|
|
106
|
+
|
|
107
|
+
// Initialize Dropzone component
|
|
97
108
|
this.dropzone = new Dropzone(this.refs.dropzone, {
|
|
98
|
-
acceptedFiles: this.get('filetypes'),
|
|
109
|
+
acceptedFiles: this.get('filetypes') || 'application/offset+octet',
|
|
99
110
|
url: this.Uploads.getUploadURL(),
|
|
100
111
|
headers: this.Uploads.getAuthHeaders(),
|
|
101
112
|
accept: this.Uploads.accept.bind(this.Uploads),
|
|
102
113
|
uploadMultiple: false,
|
|
103
114
|
maxFilesize: this.get('maxFilesize'),
|
|
115
|
+
// Disable the default XHR upload handler to use TUS instead
|
|
116
|
+
autoProcessQueue: autoProcessQueue,
|
|
104
117
|
});
|
|
105
118
|
|
|
106
119
|
const events = {
|
|
107
120
|
dragenter: this._onDragEnter.bind(this),
|
|
108
121
|
dragleave: this._onDragLeave.bind(this),
|
|
109
122
|
drop: this._onDragLeave.bind(this),
|
|
110
|
-
addedfile: this.
|
|
123
|
+
addedfile: this._onUploadStart.bind(this),
|
|
111
124
|
thumbnail: this._onThumbnail.bind(this),
|
|
112
125
|
error: this._onError.bind(this),
|
|
113
126
|
uploadprogress: this._onUploadProgress.bind(this),
|
|
@@ -116,7 +129,7 @@ export default {
|
|
|
116
129
|
success: this._updateFileState.bind(this, 'success'),
|
|
117
130
|
complete: this._updateFileState.bind(this, 'complete'),
|
|
118
131
|
canceled: this._updateFileState.bind(this, 'canceled'),
|
|
119
|
-
complete: this.
|
|
132
|
+
complete: this._onUploadComplete.bind(this),
|
|
120
133
|
totaluploadprogress: this._onTotalUploadProgress.bind(this),
|
|
121
134
|
queuecomplete: this._onQueueComplete.bind(this),
|
|
122
135
|
};
|
|
@@ -135,25 +148,94 @@ export default {
|
|
|
135
148
|
this.set({ dragging: false });
|
|
136
149
|
this.fire('dragging', false);
|
|
137
150
|
},
|
|
138
|
-
|
|
139
|
-
|
|
151
|
+
_onUploadStart(file) {
|
|
152
|
+
console.log("Dropzone _onUploadStart file:", file);
|
|
153
|
+
|
|
154
|
+
// get the chunk size from the component
|
|
155
|
+
const chunkSize = this.get('chunkSize');
|
|
156
|
+
console.log("Dropzone _onUploadStart chunkSize:", chunkSize);
|
|
157
|
+
|
|
158
|
+
/* Initialize TUS upload */
|
|
159
|
+
if(chunkSize) {
|
|
160
|
+
const upload = new tus.Upload(file, {
|
|
161
|
+
endpoint: this.Uploads.getUploadURL(),
|
|
162
|
+
headers: this.Uploads.getAuthHeaders(),
|
|
163
|
+
retryDelays: [0, 3000, 5000, 10000, 20000],
|
|
164
|
+
chunkSize: chunkSize * 1024 * 1024, // in MB
|
|
165
|
+
metadata: {
|
|
166
|
+
filename: file.name,
|
|
167
|
+
filetype: file.type,
|
|
168
|
+
},
|
|
169
|
+
onError: (error) => {
|
|
170
|
+
// console.error("Failed because: " + error);
|
|
171
|
+
this._onError(file, error);
|
|
172
|
+
},
|
|
173
|
+
onProgress: (bytesUploaded, bytesTotal) => {
|
|
174
|
+
const percentage = (bytesUploaded / bytesTotal * 100);//.toFixed(2);
|
|
175
|
+
// console.log("Dropzone TUS Client Progress:", percentage);
|
|
176
|
+
this._onUploadProgress(file, percentage, bytesUploaded);
|
|
177
|
+
},
|
|
178
|
+
onSuccess: (payload) => {
|
|
179
|
+
// console.log("Dropzone TUS Client onSuccess:", upload);
|
|
180
|
+
|
|
181
|
+
// if the response is not empty, then the upload to widgetic server finished
|
|
182
|
+
if(upload._req._xhr.response == "") {
|
|
183
|
+
|
|
184
|
+
console.log("Dropzone TUS Client Upload Complete to server for file:", upload);
|
|
185
|
+
|
|
186
|
+
// update the file thumbnail to processing state
|
|
187
|
+
this._updateFileState('sending', file);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
else {
|
|
191
|
+
console.log("Dropzone TUS Client Upload Complete to B2 with response:", upload._req._xhr.response);
|
|
192
|
+
|
|
193
|
+
// create uploadFile object
|
|
194
|
+
let uploadFile = upload.file;
|
|
195
|
+
|
|
196
|
+
// add the request to the file object for later use in Uploads.uploadComplete method
|
|
197
|
+
uploadFile.xhr = upload._req._xhr;
|
|
198
|
+
|
|
199
|
+
// call upload complete method from here that calls the Uploads.uploadComplete method
|
|
200
|
+
this._onUploadComplete(uploadFile);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// Start the TUS upload
|
|
206
|
+
upload.start();
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// fire new event in the editor
|
|
210
|
+
this.fire('new', file);
|
|
211
|
+
|
|
212
|
+
// call uploadStart method on Uploads service
|
|
140
213
|
this.Uploads.upload(file);
|
|
214
|
+
|
|
215
|
+
// change the file state to uploading
|
|
216
|
+
this._updateFileState('sending', file);
|
|
141
217
|
},
|
|
142
218
|
_onThumbnail(file, thumbnail) {
|
|
143
219
|
this.Uploads.setThumbnail(file, thumbnail);
|
|
144
220
|
},
|
|
145
221
|
_onError(file, error) {
|
|
146
|
-
console.error(error);
|
|
222
|
+
// console.error(error);
|
|
223
|
+
|
|
224
|
+
// Fire an error event in the editor
|
|
147
225
|
this.fire('error', { message: error });
|
|
226
|
+
|
|
227
|
+
// Set the error on the file object in the Uploads service
|
|
148
228
|
this.Uploads.setError(file, error);
|
|
149
229
|
},
|
|
150
230
|
_onUploadProgress(file, percentage, size) {
|
|
151
231
|
this.Uploads.setUploadProgress(file, percentage, size);
|
|
152
232
|
},
|
|
153
|
-
|
|
233
|
+
_onUploadComplete(file) {
|
|
234
|
+
// console.log("Dropzone Component cploadComplete for file:", file);
|
|
154
235
|
this.Uploads.uploadComplete(file, this.get('instanceId'));
|
|
155
236
|
},
|
|
156
237
|
_updateFileState(state, file) {
|
|
238
|
+
// console.log("Dropzone Component _updateFileState:", state);
|
|
157
239
|
this.Uploads.setUploadState(file, state);
|
|
158
240
|
},
|
|
159
241
|
_onTotalUploadProgress() {
|
|
@@ -168,6 +250,10 @@ export default {
|
|
|
168
250
|
this.dropzone.removeAllFiles();
|
|
169
251
|
},
|
|
170
252
|
_onUploadsUpdate() {
|
|
253
|
+
|
|
254
|
+
// console.log("Dropzone _onUploadsUpdate:", this.Uploads.quota, this.Uploads.used);
|
|
255
|
+
|
|
256
|
+
// Update the quota and used space so it updates in the dropzone UI
|
|
171
257
|
this.set({
|
|
172
258
|
quota: this.Uploads.quota,
|
|
173
259
|
used: this.Uploads.used,
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
{{#if file.thumbnail}}
|
|
13
13
|
<div class="browser-image-prew_img" style="background-image: url('{{ file.thumbnail }}')" width="112" height="71"/>
|
|
14
14
|
{{/if}}
|
|
15
|
+
|
|
15
16
|
<div class="progress-bar">
|
|
16
17
|
{{#if file.progress === 100 && file.state === 'sending'}}
|
|
17
18
|
<div style="width: 100%">Processing...</div>
|
|
@@ -19,6 +20,7 @@
|
|
|
19
20
|
<div class="progress-bar_fill" style="width: {{ file.progress }}%"></div>
|
|
20
21
|
{{/if}}
|
|
21
22
|
</div>
|
|
23
|
+
|
|
22
24
|
</div>
|
|
23
25
|
<div class="browser-entry-info" on:click="togglePlay()">{{ file.name }}</div>
|
|
24
26
|
</div>
|