fine_uploader 3.1.1 → 3.4.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.
- data/LICENSE.txt +1 -3
- data/Vendorfile +16 -5
- data/lib/fine_uploader/version.rb +1 -1
- data/vendor/assets/javascripts/fine_uploader/ajax.requester.js +184 -0
- data/vendor/assets/javascripts/fine_uploader/button.js +62 -58
- data/vendor/assets/javascripts/fine_uploader/deletefile.ajax.requester.js +44 -0
- data/vendor/assets/javascripts/fine_uploader/dnd.js +2 -0
- data/vendor/assets/javascripts/fine_uploader/handler.base.js +164 -92
- data/vendor/assets/javascripts/fine_uploader/handler.form.js +220 -133
- data/vendor/assets/javascripts/fine_uploader/handler.xhr.js +594 -132
- data/vendor/assets/javascripts/fine_uploader/header.js +3 -4
- data/vendor/assets/javascripts/fine_uploader/iframe.xss.response.js +6 -0
- data/vendor/assets/javascripts/fine_uploader/jquery-plugin.js +13 -4
- data/vendor/assets/javascripts/fine_uploader/paste.js +49 -0
- data/vendor/assets/javascripts/fine_uploader/promise.js +45 -0
- data/vendor/assets/javascripts/fine_uploader/uploader.basic.js +547 -175
- data/vendor/assets/javascripts/fine_uploader/uploader.js +197 -42
- data/vendor/assets/javascripts/fine_uploader/util.js +117 -12
- data/vendor/assets/javascripts/fine_uploader/window.receive.message.js +32 -0
- data/vendor/assets/javascripts/fine_uploader.js +17 -0
- data/vendor/assets/stylesheets/fine_uploader.css.scss +6 -7
- metadata +9 -2
@@ -1,161 +1,143 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
qq.UploadHandlerForm = function(o){
|
6
|
-
qq.UploadHandlerAbstract.apply(this, arguments);
|
7
|
-
|
8
|
-
this._inputs = {};
|
9
|
-
this._detach_load_events = {};
|
10
|
-
};
|
11
|
-
// @inherits qq.UploadHandlerAbstract
|
12
|
-
qq.extend(qq.UploadHandlerForm.prototype, qq.UploadHandlerAbstract.prototype);
|
1
|
+
/*globals qq, document, setTimeout*/
|
2
|
+
/*globals clearTimeout*/
|
3
|
+
qq.UploadHandlerForm = function(o, uploadCompleteCallback, logCallback) {
|
4
|
+
"use strict";
|
13
5
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
6
|
+
var options = o,
|
7
|
+
inputs = [],
|
8
|
+
uuids = [],
|
9
|
+
detachLoadEvents = {},
|
10
|
+
postMessageCallbackTimers = {},
|
11
|
+
uploadComplete = uploadCompleteCallback,
|
12
|
+
log = logCallback,
|
13
|
+
corsMessageReceiver = new qq.WindowReceiveMessage({log: log}),
|
14
|
+
onloadCallbacks = {},
|
15
|
+
api;
|
18
16
|
|
19
|
-
this._inputs[id] = fileInput;
|
20
17
|
|
21
|
-
|
22
|
-
if (
|
23
|
-
|
18
|
+
function detachLoadEvent(id) {
|
19
|
+
if (detachLoadEvents[id] !== undefined) {
|
20
|
+
detachLoadEvents[id]();
|
21
|
+
delete detachLoadEvents[id];
|
24
22
|
}
|
23
|
+
}
|
25
24
|
|
26
|
-
|
27
|
-
|
28
|
-
getName: function(id){
|
29
|
-
// get input value and remove path to normalize
|
30
|
-
return this._inputs[id].value.replace(/.*(\/|\\)/, "");
|
31
|
-
},
|
32
|
-
isValid: function(id) {
|
33
|
-
return this._inputs[id] !== undefined;
|
34
|
-
},
|
35
|
-
reset: function() {
|
36
|
-
qq.UploadHandlerAbstract.prototype.reset.apply(this, arguments);
|
37
|
-
this._inputs = {};
|
38
|
-
this._detach_load_events = {};
|
39
|
-
},
|
40
|
-
_cancel: function(id){
|
41
|
-
this._options.onCancel(id, this.getName(id));
|
42
|
-
|
43
|
-
delete this._inputs[id];
|
44
|
-
delete this._detach_load_events[id];
|
45
|
-
|
46
|
-
var iframe = document.getElementById(id);
|
47
|
-
if (iframe){
|
48
|
-
// to cancel request set src to something else
|
49
|
-
// we use src="javascript:false;" because it doesn't
|
50
|
-
// trigger ie6 prompt on https
|
51
|
-
iframe.setAttribute('src', 'javascript:false;');
|
52
|
-
|
53
|
-
qq(iframe).remove();
|
54
|
-
}
|
55
|
-
},
|
56
|
-
_upload: function(id){
|
57
|
-
this._options.onUpload(id, this.getName(id), false);
|
58
|
-
var input = this._inputs[id];
|
25
|
+
function registerPostMessageCallback(iframe, callback) {
|
26
|
+
var id = iframe.id;
|
59
27
|
|
60
|
-
|
61
|
-
throw new Error('file with passed id was not added, or already uploaded or cancelled');
|
62
|
-
}
|
28
|
+
onloadCallbacks[uuids[id]] = callback;
|
63
29
|
|
64
|
-
|
30
|
+
detachLoadEvents[id] = qq(iframe).attach('load', function() {
|
31
|
+
if (inputs[id]) {
|
32
|
+
log("Received iframe load event for CORS upload request (file id " + id + ")");
|
65
33
|
|
66
|
-
|
67
|
-
|
68
|
-
|
34
|
+
postMessageCallbackTimers[id] = setTimeout(function() {
|
35
|
+
var errorMessage = "No valid message received from loaded iframe for file id " + id;
|
36
|
+
log(errorMessage, "error");
|
37
|
+
callback({
|
38
|
+
error: errorMessage
|
39
|
+
});
|
40
|
+
}, 1000);
|
41
|
+
}
|
42
|
+
});
|
69
43
|
|
70
|
-
|
71
|
-
|
72
|
-
|
44
|
+
corsMessageReceiver.receiveMessage(id, function(message) {
|
45
|
+
log("Received the following window message: '" + message + "'");
|
46
|
+
var response = qq.parseJson(message),
|
47
|
+
uuid = response.uuid,
|
48
|
+
onloadCallback;
|
73
49
|
|
74
|
-
|
50
|
+
if (uuid && onloadCallbacks[uuid]) {
|
51
|
+
clearTimeout(postMessageCallbackTimers[id]);
|
52
|
+
delete postMessageCallbackTimers[id];
|
75
53
|
|
76
|
-
|
77
|
-
setTimeout(function(){
|
78
|
-
self._detach_load_events[id]();
|
79
|
-
delete self._detach_load_events[id];
|
80
|
-
qq(iframe).remove();
|
81
|
-
}, 1);
|
54
|
+
detachLoadEvent(id);
|
82
55
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
56
|
+
onloadCallback = onloadCallbacks[uuid];
|
57
|
+
|
58
|
+
delete onloadCallbacks[uuid];
|
59
|
+
corsMessageReceiver.stopReceivingMessages(id);
|
60
|
+
onloadCallback(response);
|
61
|
+
}
|
62
|
+
else if (!uuid) {
|
63
|
+
log("'" + message + "' does not contain a UUID - ignoring.");
|
87
64
|
}
|
88
|
-
self._options.onComplete(id, fileName, response);
|
89
|
-
self._dequeue(id);
|
90
65
|
});
|
66
|
+
}
|
91
67
|
|
92
|
-
|
93
|
-
|
94
|
-
qq(form).remove();
|
95
|
-
|
96
|
-
return id;
|
97
|
-
},
|
98
|
-
_attachLoadEvent: function(iframe, callback){
|
99
|
-
var self = this;
|
100
|
-
this._detach_load_events[iframe.id] = qq(iframe).attach('load', function(){
|
101
|
-
self.log('Received response for ' + iframe.id);
|
102
|
-
|
103
|
-
// when we remove iframe from dom
|
104
|
-
// the request stops, but in IE load
|
105
|
-
// event fires
|
106
|
-
if (!iframe.parentNode){
|
107
|
-
return;
|
108
|
-
}
|
68
|
+
function attachLoadEvent(iframe, callback) {
|
69
|
+
/*jslint eqeq: true*/
|
109
70
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
71
|
+
if (options.cors.expected) {
|
72
|
+
registerPostMessageCallback(iframe, callback);
|
73
|
+
}
|
74
|
+
else {
|
75
|
+
detachLoadEvents[iframe.id] = qq(iframe).attach('load', function(){
|
76
|
+
log('Received response for ' + iframe.id);
|
77
|
+
|
78
|
+
// when we remove iframe from dom
|
79
|
+
// the request stops, but in IE load
|
80
|
+
// event fires
|
81
|
+
if (!iframe.parentNode){
|
119
82
|
return;
|
120
83
|
}
|
121
|
-
}
|
122
|
-
catch (error) {
|
123
|
-
//IE may throw an "access is denied" error when attempting to access contentDocument on the iframe in some cases
|
124
|
-
self.log('Error when attempting to access iframe during handling of upload response (' + error + ")", 'error');
|
125
|
-
}
|
126
84
|
|
127
|
-
|
128
|
-
|
129
|
-
|
85
|
+
try {
|
86
|
+
// fixing Opera 10.53
|
87
|
+
if (iframe.contentDocument &&
|
88
|
+
iframe.contentDocument.body &&
|
89
|
+
iframe.contentDocument.body.innerHTML == "false"){
|
90
|
+
// In Opera event is fired second time
|
91
|
+
// when body.innerHTML changed from false
|
92
|
+
// to server response approx. after 1 sec
|
93
|
+
// when we upload file with iframe
|
94
|
+
return;
|
95
|
+
}
|
96
|
+
}
|
97
|
+
catch (error) {
|
98
|
+
//IE may throw an "access is denied" error when attempting to access contentDocument on the iframe in some cases
|
99
|
+
log('Error when attempting to access iframe during handling of upload response (' + error + ")", 'error');
|
100
|
+
}
|
101
|
+
|
102
|
+
callback();
|
103
|
+
});
|
104
|
+
}
|
105
|
+
}
|
106
|
+
|
130
107
|
/**
|
131
108
|
* Returns json object received by iframe from server.
|
132
109
|
*/
|
133
|
-
|
110
|
+
function getIframeContentJson(iframe) {
|
111
|
+
/*jshint evil: true*/
|
112
|
+
|
113
|
+
var response;
|
114
|
+
|
134
115
|
//IE may throw an "access is denied" error when attempting to access contentDocument on the iframe in some cases
|
135
116
|
try {
|
136
117
|
// iframe.contentWindow.document - for IE<7
|
137
|
-
var doc = iframe.contentDocument
|
138
|
-
|
118
|
+
var doc = iframe.contentDocument || iframe.contentWindow.document,
|
119
|
+
innerHTML = doc.body.innerHTML;
|
139
120
|
|
140
|
-
|
141
|
-
|
142
|
-
this.log("innerHTML = " + innerHTML);
|
121
|
+
log("converting iframe's innerHTML to JSON");
|
122
|
+
log("innerHTML = " + innerHTML);
|
143
123
|
//plain text response may be wrapped in <pre> tag
|
144
124
|
if (innerHTML && innerHTML.match(/^<pre/i)) {
|
145
125
|
innerHTML = doc.body.firstChild.firstChild.nodeValue;
|
146
126
|
}
|
147
|
-
|
127
|
+
|
128
|
+
response = qq.parseJson(innerHTML);
|
148
129
|
} catch(error){
|
149
|
-
|
130
|
+
log('Error when attempting to parse form upload response (' + error + ")", 'error');
|
150
131
|
response = {success: false};
|
151
132
|
}
|
152
133
|
|
153
134
|
return response;
|
154
|
-
}
|
135
|
+
}
|
136
|
+
|
155
137
|
/**
|
156
138
|
* Creates iframe with unique name
|
157
139
|
*/
|
158
|
-
|
140
|
+
function createIframe(id){
|
159
141
|
// We can't use following code as the name attribute
|
160
142
|
// won't be properly registered in IE6, and new window
|
161
143
|
// on form submit will open
|
@@ -163,7 +145,6 @@ qq.extend(qq.UploadHandlerForm.prototype, {
|
|
163
145
|
// iframe.setAttribute('name', id);
|
164
146
|
|
165
147
|
var iframe = qq.toElement('<iframe src="javascript:false;" name="' + id + '" />');
|
166
|
-
// src="javascript:false;" removes ie6 prompt on https
|
167
148
|
|
168
149
|
iframe.setAttribute('id', id);
|
169
150
|
|
@@ -171,22 +152,22 @@ qq.extend(qq.UploadHandlerForm.prototype, {
|
|
171
152
|
document.body.appendChild(iframe);
|
172
153
|
|
173
154
|
return iframe;
|
174
|
-
}
|
155
|
+
}
|
156
|
+
|
175
157
|
/**
|
176
158
|
* Creates form, that will be submitted to iframe
|
177
159
|
*/
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
// form.setAttribute('method', 'post');
|
182
|
-
// form.setAttribute('enctype', 'multipart/form-data');
|
183
|
-
// Because in this case file won't be attached to request
|
184
|
-
var protocol = this._options.demoMode ? "GET" : "POST",
|
160
|
+
function createForm(id, iframe){
|
161
|
+
var params = options.paramsStore.getParams(id),
|
162
|
+
protocol = options.demoMode ? "GET" : "POST",
|
185
163
|
form = qq.toElement('<form method="' + protocol + '" enctype="multipart/form-data"></form>'),
|
186
|
-
|
164
|
+
endpoint = options.endpointStore.getEndpoint(id),
|
165
|
+
url = endpoint;
|
166
|
+
|
167
|
+
params[options.uuidParamName] = uuids[id];
|
187
168
|
|
188
|
-
if (!
|
189
|
-
url = qq.obj2url(params,
|
169
|
+
if (!options.paramsInBody) {
|
170
|
+
url = qq.obj2url(params, endpoint);
|
190
171
|
}
|
191
172
|
else {
|
192
173
|
qq.obj2Inputs(params, form);
|
@@ -199,4 +180,110 @@ qq.extend(qq.UploadHandlerForm.prototype, {
|
|
199
180
|
|
200
181
|
return form;
|
201
182
|
}
|
202
|
-
|
183
|
+
|
184
|
+
|
185
|
+
api = {
|
186
|
+
add: function(fileInput) {
|
187
|
+
fileInput.setAttribute('name', options.inputName);
|
188
|
+
|
189
|
+
var id = inputs.push(fileInput) - 1;
|
190
|
+
uuids[id] = qq.getUniqueId();
|
191
|
+
|
192
|
+
// remove file input from DOM
|
193
|
+
if (fileInput.parentNode){
|
194
|
+
qq(fileInput).remove();
|
195
|
+
}
|
196
|
+
|
197
|
+
return id;
|
198
|
+
},
|
199
|
+
getName: function(id) {
|
200
|
+
/*jslint regexp: true*/
|
201
|
+
|
202
|
+
if (api.isValid(id)) {
|
203
|
+
// get input value and remove path to normalize
|
204
|
+
return inputs[id].value.replace(/.*(\/|\\)/, "");
|
205
|
+
}
|
206
|
+
else {
|
207
|
+
log(id + " is not a valid item ID.", "error");
|
208
|
+
}
|
209
|
+
},
|
210
|
+
isValid: function(id) {
|
211
|
+
return inputs[id] !== undefined;
|
212
|
+
},
|
213
|
+
reset: function() {
|
214
|
+
inputs = [];
|
215
|
+
uuids = [];
|
216
|
+
detachLoadEvents = {};
|
217
|
+
},
|
218
|
+
getUuid: function(id) {
|
219
|
+
return uuids[id];
|
220
|
+
},
|
221
|
+
cancel: function(id) {
|
222
|
+
options.onCancel(id, this.getName(id));
|
223
|
+
|
224
|
+
delete inputs[id];
|
225
|
+
delete uuids[id];
|
226
|
+
delete detachLoadEvents[id];
|
227
|
+
|
228
|
+
if (options.cors.expected) {
|
229
|
+
clearTimeout(postMessageCallbackTimers[id]);
|
230
|
+
delete postMessageCallbackTimers[id];
|
231
|
+
corsMessageReceiver.stopReceivingMessages(id);
|
232
|
+
}
|
233
|
+
|
234
|
+
var iframe = document.getElementById(id);
|
235
|
+
if (iframe) {
|
236
|
+
// to cancel request set src to something else
|
237
|
+
// we use src="javascript:false;" because it doesn't
|
238
|
+
// trigger ie6 prompt on https
|
239
|
+
iframe.setAttribute('src', 'java' + String.fromCharCode(115) + 'cript:false;'); //deal with "JSLint: javascript URL" warning, which apparently cannot be turned off
|
240
|
+
|
241
|
+
qq(iframe).remove();
|
242
|
+
}
|
243
|
+
},
|
244
|
+
upload: function(id){
|
245
|
+
var input = inputs[id],
|
246
|
+
fileName = api.getName(id),
|
247
|
+
iframe = createIframe(id),
|
248
|
+
form;
|
249
|
+
|
250
|
+
if (!input){
|
251
|
+
throw new Error('file with passed id was not added, or already uploaded or cancelled');
|
252
|
+
}
|
253
|
+
|
254
|
+
options.onUpload(id, this.getName(id));
|
255
|
+
|
256
|
+
form = createForm(id, iframe);
|
257
|
+
form.appendChild(input);
|
258
|
+
|
259
|
+
attachLoadEvent(iframe, function(responseFromMessage){
|
260
|
+
log('iframe loaded');
|
261
|
+
|
262
|
+
var response = responseFromMessage ? responseFromMessage : getIframeContentJson(iframe);
|
263
|
+
|
264
|
+
detachLoadEvent(id);
|
265
|
+
|
266
|
+
//we can't remove an iframe if the iframe doesn't belong to the same domain
|
267
|
+
if (!options.cors.expected) {
|
268
|
+
qq(iframe).remove();
|
269
|
+
}
|
270
|
+
|
271
|
+
if (!response.success) {
|
272
|
+
if (options.onAutoRetry(id, fileName, response)) {
|
273
|
+
return;
|
274
|
+
}
|
275
|
+
}
|
276
|
+
options.onComplete(id, fileName, response);
|
277
|
+
uploadComplete(id);
|
278
|
+
});
|
279
|
+
|
280
|
+
log('Sending upload request for ' + id);
|
281
|
+
form.submit();
|
282
|
+
qq(form).remove();
|
283
|
+
|
284
|
+
return id;
|
285
|
+
}
|
286
|
+
};
|
287
|
+
|
288
|
+
return api;
|
289
|
+
};
|