fine_uploader 3.1.1 → 3.4.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,161 +1,143 @@
1
- /**
2
- * Class for uploading files using form and iframe
3
- * @inherits qq.UploadHandlerAbstract
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
- qq.extend(qq.UploadHandlerForm.prototype, {
15
- add: function(fileInput){
16
- fileInput.setAttribute('name', this._options.inputName);
17
- var id = qq.getUniqueId();
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
- // remove file input from DOM
22
- if (fileInput.parentNode){
23
- qq(fileInput).remove();
18
+ function detachLoadEvent(id) {
19
+ if (detachLoadEvents[id] !== undefined) {
20
+ detachLoadEvents[id]();
21
+ delete detachLoadEvents[id];
24
22
  }
23
+ }
25
24
 
26
- return id;
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
- if (!input){
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
- var fileName = this.getName(id);
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
- var iframe = this._createIframe(id);
67
- var form = this._createForm(iframe, this._options.paramsStore.getParams(id));
68
- form.appendChild(input);
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
- var self = this;
71
- this._attachLoadEvent(iframe, function(){
72
- self.log('iframe loaded');
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
- var response = self._getIframeContentJSON(iframe);
50
+ if (uuid && onloadCallbacks[uuid]) {
51
+ clearTimeout(postMessageCallbackTimers[id]);
52
+ delete postMessageCallbackTimers[id];
75
53
 
76
- // timeout added to fix busy state in FF3.6
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
- if (!response.success) {
84
- if (self._options.onAutoRetry(id, fileName, response)) {
85
- return;
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
- this.log('Sending upload request for ' + id);
93
- form.submit();
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
- try {
111
- // fixing Opera 10.53
112
- if (iframe.contentDocument &&
113
- iframe.contentDocument.body &&
114
- iframe.contentDocument.body.innerHTML == "false"){
115
- // In Opera event is fired second time
116
- // when body.innerHTML changed from false
117
- // to server response approx. after 1 sec
118
- // when we upload file with iframe
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
- callback();
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
- _getIframeContentJSON: function(iframe){
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 ? iframe.contentDocument: iframe.contentWindow.document,
138
- response;
118
+ var doc = iframe.contentDocument || iframe.contentWindow.document,
119
+ innerHTML = doc.body.innerHTML;
139
120
 
140
- var innerHTML = doc.body.innerHTML;
141
- this.log("converting iframe's innerHTML to JSON");
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
- response = eval("(" + innerHTML + ")");
127
+
128
+ response = qq.parseJson(innerHTML);
148
129
  } catch(error){
149
- this.log('Error when attempting to parse form upload response (' + error + ")", 'error');
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
- _createIframe: function(id){
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
- _createForm: function(iframe, params){
179
- // We can't use the following code in IE6
180
- // var form = document.createElement('form');
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
- url = this._options.endpoint;
164
+ endpoint = options.endpointStore.getEndpoint(id),
165
+ url = endpoint;
166
+
167
+ params[options.uuidParamName] = uuids[id];
187
168
 
188
- if (!this._options.paramsInBody) {
189
- url = qq.obj2url(params, this._options.endpoint);
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
+ };