fine_uploader 2.1.1 → 3.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,202 @@
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);
13
+
14
+ qq.extend(qq.UploadHandlerForm.prototype, {
15
+ add: function(fileInput){
16
+ fileInput.setAttribute('name', this._options.inputName);
17
+ var id = qq.getUniqueId();
18
+
19
+ this._inputs[id] = fileInput;
20
+
21
+ // remove file input from DOM
22
+ if (fileInput.parentNode){
23
+ qq(fileInput).remove();
24
+ }
25
+
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];
59
+
60
+ if (!input){
61
+ throw new Error('file with passed id was not added, or already uploaded or cancelled');
62
+ }
63
+
64
+ var fileName = this.getName(id);
65
+
66
+ var iframe = this._createIframe(id);
67
+ var form = this._createForm(iframe, this._options.paramsStore.getParams(id));
68
+ form.appendChild(input);
69
+
70
+ var self = this;
71
+ this._attachLoadEvent(iframe, function(){
72
+ self.log('iframe loaded');
73
+
74
+ var response = self._getIframeContentJSON(iframe);
75
+
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);
82
+
83
+ if (!response.success) {
84
+ if (self._options.onAutoRetry(id, fileName, response)) {
85
+ return;
86
+ }
87
+ }
88
+ self._options.onComplete(id, fileName, response);
89
+ self._dequeue(id);
90
+ });
91
+
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
+ }
109
+
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
119
+ return;
120
+ }
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
+
127
+ callback();
128
+ });
129
+ },
130
+ /**
131
+ * Returns json object received by iframe from server.
132
+ */
133
+ _getIframeContentJSON: function(iframe){
134
+ //IE may throw an "access is denied" error when attempting to access contentDocument on the iframe in some cases
135
+ try {
136
+ // iframe.contentWindow.document - for IE<7
137
+ var doc = iframe.contentDocument ? iframe.contentDocument: iframe.contentWindow.document,
138
+ response;
139
+
140
+ var innerHTML = doc.body.innerHTML;
141
+ this.log("converting iframe's innerHTML to JSON");
142
+ this.log("innerHTML = " + innerHTML);
143
+ //plain text response may be wrapped in <pre> tag
144
+ if (innerHTML && innerHTML.match(/^<pre/i)) {
145
+ innerHTML = doc.body.firstChild.firstChild.nodeValue;
146
+ }
147
+ response = eval("(" + innerHTML + ")");
148
+ } catch(error){
149
+ this.log('Error when attempting to parse form upload response (' + error + ")", 'error');
150
+ response = {success: false};
151
+ }
152
+
153
+ return response;
154
+ },
155
+ /**
156
+ * Creates iframe with unique name
157
+ */
158
+ _createIframe: function(id){
159
+ // We can't use following code as the name attribute
160
+ // won't be properly registered in IE6, and new window
161
+ // on form submit will open
162
+ // var iframe = document.createElement('iframe');
163
+ // iframe.setAttribute('name', id);
164
+
165
+ var iframe = qq.toElement('<iframe src="javascript:false;" name="' + id + '" />');
166
+ // src="javascript:false;" removes ie6 prompt on https
167
+
168
+ iframe.setAttribute('id', id);
169
+
170
+ iframe.style.display = 'none';
171
+ document.body.appendChild(iframe);
172
+
173
+ return iframe;
174
+ },
175
+ /**
176
+ * Creates form, that will be submitted to iframe
177
+ */
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",
185
+ form = qq.toElement('<form method="' + protocol + '" enctype="multipart/form-data"></form>'),
186
+ url = this._options.endpoint;
187
+
188
+ if (!this._options.paramsInBody) {
189
+ url = qq.obj2url(params, this._options.endpoint);
190
+ }
191
+ else {
192
+ qq.obj2Inputs(params, form);
193
+ }
194
+
195
+ form.setAttribute('action', url);
196
+ form.setAttribute('target', iframe.name);
197
+ form.style.display = 'none';
198
+ document.body.appendChild(form);
199
+
200
+ return form;
201
+ }
202
+ });
@@ -0,0 +1,168 @@
1
+ /**
2
+ * Class for uploading files using xhr
3
+ * @inherits qq.UploadHandlerAbstract
4
+ */
5
+ qq.UploadHandlerXhr = function(o){
6
+ qq.UploadHandlerAbstract.apply(this, arguments);
7
+
8
+ this._files = [];
9
+ this._xhrs = [];
10
+
11
+ // current loaded size in bytes for each file
12
+ this._loaded = [];
13
+ };
14
+
15
+ // @inherits qq.UploadHandlerAbstract
16
+ qq.extend(qq.UploadHandlerXhr.prototype, qq.UploadHandlerAbstract.prototype)
17
+
18
+ qq.extend(qq.UploadHandlerXhr.prototype, {
19
+ /**
20
+ * Adds file to the queue
21
+ * Returns id to use with upload, cancel
22
+ **/
23
+ add: function(file){
24
+ if (!(file instanceof File)){
25
+ throw new Error('Passed obj in not a File (in qq.UploadHandlerXhr)');
26
+ }
27
+
28
+ return this._files.push(file) - 1;
29
+ },
30
+ getName: function(id){
31
+ var file = this._files[id];
32
+ // fix missing name in Safari 4
33
+ //NOTE: fixed missing name firefox 11.0a2 file.fileName is actually undefined
34
+ return (file.fileName !== null && file.fileName !== undefined) ? file.fileName : file.name;
35
+ },
36
+ getSize: function(id){
37
+ var file = this._files[id];
38
+ return file.fileSize != null ? file.fileSize : file.size;
39
+ },
40
+ /**
41
+ * Returns uploaded bytes for file identified by id
42
+ */
43
+ getLoaded: function(id){
44
+ return this._loaded[id] || 0;
45
+ },
46
+ isValid: function(id) {
47
+ return this._files[id] !== undefined;
48
+ },
49
+ reset: function() {
50
+ qq.UploadHandlerAbstract.prototype.reset.apply(this, arguments);
51
+ this._files = [];
52
+ this._xhrs = [];
53
+ this._loaded = [];
54
+ },
55
+ /**
56
+ * Sends the file identified by id to the server
57
+ */
58
+ _upload: function(id){
59
+ var file = this._files[id],
60
+ name = this.getName(id),
61
+ size = this.getSize(id),
62
+ self = this,
63
+ url = this._options.endpoint,
64
+ protocol = this._options.demoMode ? "GET" : "POST",
65
+ xhr, formData, paramName, key, params;
66
+
67
+ this._options.onUpload(id, this.getName(id), true);
68
+
69
+ this._loaded[id] = 0;
70
+
71
+ xhr = this._xhrs[id] = new XMLHttpRequest();
72
+
73
+ xhr.upload.onprogress = function(e){
74
+ if (e.lengthComputable){
75
+ self._loaded[id] = e.loaded;
76
+ self._options.onProgress(id, name, e.loaded, e.total);
77
+ }
78
+ };
79
+
80
+ xhr.onreadystatechange = function(){
81
+ if (xhr.readyState === 4){
82
+ self._onComplete(id, xhr);
83
+ }
84
+ };
85
+
86
+ params = this._options.paramsStore.getParams(id);
87
+
88
+ //build query string
89
+ if (!this._options.paramsInBody) {
90
+ params[this._options.inputName] = name;
91
+ url = qq.obj2url(params, this._options.endpoint);
92
+ }
93
+
94
+ xhr.open(protocol, url, true);
95
+ xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
96
+ xhr.setRequestHeader("X-File-Name", encodeURIComponent(name));
97
+ xhr.setRequestHeader("Cache-Control", "no-cache");
98
+ if (this._options.forceMultipart || this._options.paramsInBody) {
99
+ formData = new FormData();
100
+
101
+ if (this._options.paramsInBody) {
102
+ qq.obj2FormData(params, formData);
103
+ }
104
+
105
+ formData.append(this._options.inputName, file);
106
+ file = formData;
107
+ } else {
108
+ xhr.setRequestHeader("Content-Type", "application/octet-stream");
109
+ //NOTE: return mime type in xhr works on chrome 16.0.9 firefox 11.0a2
110
+ xhr.setRequestHeader("X-Mime-Type", file.type);
111
+ }
112
+
113
+ for (key in this._options.customHeaders){
114
+ if (this._options.customHeaders.hasOwnProperty(key)) {
115
+ xhr.setRequestHeader(key, this._options.customHeaders[key]);
116
+ }
117
+ }
118
+
119
+ this.log('Sending upload request for ' + id);
120
+ xhr.send(file);
121
+ },
122
+ _onComplete: function(id, xhr){
123
+ "use strict";
124
+ // the request was aborted/cancelled
125
+ if (!this._files[id]) { return; }
126
+
127
+ var name = this.getName(id);
128
+ var size = this.getSize(id);
129
+ var response; //the parsed JSON response from the server, or the empty object if parsing failed.
130
+
131
+ this._options.onProgress(id, name, size, size);
132
+
133
+ this.log("xhr - server response received for " + id);
134
+ this.log("responseText = " + xhr.responseText);
135
+
136
+ try {
137
+ if (typeof JSON.parse === "function") {
138
+ response = JSON.parse(xhr.responseText);
139
+ } else {
140
+ response = eval("(" + xhr.responseText + ")");
141
+ }
142
+ } catch(error){
143
+ this.log('Error when attempting to parse xhr response text (' + error + ')', 'error');
144
+ response = {};
145
+ }
146
+
147
+ if (xhr.status !== 200 || !response.success){
148
+ if (this._options.onAutoRetry(id, name, response, xhr)) {
149
+ return;
150
+ }
151
+ }
152
+
153
+ this._options.onComplete(id, name, response, xhr);
154
+
155
+ this._xhrs[id] = null;
156
+ this._dequeue(id);
157
+ },
158
+ _cancel: function(id){
159
+ this._options.onCancel(id, this.getName(id));
160
+
161
+ this._files[id] = null;
162
+
163
+ if (this._xhrs[id]){
164
+ this._xhrs[id].abort();
165
+ this._xhrs[id] = null;
166
+ }
167
+ }
168
+ });
@@ -0,0 +1,10 @@
1
+ /**
2
+ * http://github.com/Valums-File-Uploader/file-uploader
3
+ *
4
+ * Multiple file upload component with progress-bar, drag-and-drop, support for all modern browsers.
5
+ *
6
+ * Original version: 1.0 © 2010 Andrew Valums ( andrew(at)valums.com )
7
+ * Current Maintainer (2.0+): © 2012, Ray Nicholus ( fineuploader(at)garstasio.com )
8
+ *
9
+ * Licensed under MIT license, GNU GPL 2 or later, GNU LGPL 2 or later, see license.txt.
10
+ */
@@ -0,0 +1,168 @@
1
+ /*globals jQuery, qq*/
2
+ (function($) {
3
+ "use strict";
4
+ var uploader, $el, init, dataStore, pluginOption, pluginOptions, addCallbacks, transformVariables, isValidCommand,
5
+ delegateCommand;
6
+
7
+ pluginOptions = ['uploaderType'];
8
+
9
+ init = function (options) {
10
+ if (options) {
11
+ var xformedOpts = transformVariables(options);
12
+ addCallbacks(xformedOpts);
13
+
14
+ if (pluginOption('uploaderType') === 'basic') {
15
+ uploader(new qq.FineUploaderBasic(xformedOpts));
16
+ }
17
+ else {
18
+ uploader(new qq.FineUploader(xformedOpts));
19
+ }
20
+ }
21
+
22
+ return $el;
23
+ };
24
+
25
+ dataStore = function(key, val) {
26
+ var data = $el.data('fineuploader');
27
+
28
+ if (val) {
29
+ if (data === undefined) {
30
+ data = {};
31
+ }
32
+ data[key] = val;
33
+ $el.data('fineuploader', data);
34
+ }
35
+ else {
36
+ if (data === undefined) {
37
+ return null;
38
+ }
39
+ return data[key];
40
+ }
41
+ };
42
+
43
+ //the underlying Fine Uploader instance is stored in jQuery's data stored, associated with the element
44
+ // tied to this instance of the plug-in
45
+ uploader = function(instanceToStore) {
46
+ return dataStore('uploader', instanceToStore);
47
+ };
48
+
49
+ pluginOption = function(option, optionVal) {
50
+ return dataStore(option, optionVal);
51
+ };
52
+
53
+ //implement all callbacks defined in Fine Uploader as functions that trigger appropriately names events and
54
+ // return the result of executing the bound handler back to Fine Uploader
55
+ addCallbacks = function(transformedOpts) {
56
+ var callbacks = transformedOpts.callbacks = {};
57
+
58
+ $.each(new qq.FineUploaderBasic()._options.callbacks, function(prop, func) {
59
+ var name, $callbackEl;
60
+
61
+ name = /^on(\w+)/.exec(prop)[1];
62
+ name = name.substring(0, 1).toLowerCase() + name.substring(1);
63
+ $callbackEl = $el;
64
+
65
+ callbacks[prop] = function() {
66
+ var args = Array.prototype.slice.call(arguments);
67
+ return $callbackEl.triggerHandler(name, args);
68
+ };
69
+ });
70
+ };
71
+
72
+ //transform jQuery objects into HTMLElements, and pass along all other option properties
73
+ transformVariables = function(source, dest) {
74
+ var xformed, arrayVals;
75
+
76
+ if (dest === undefined) {
77
+ if (source.uploaderType !== 'basic') {
78
+ xformed = { element : $el[0] };
79
+ }
80
+ else {
81
+ xformed = {};
82
+ }
83
+ }
84
+ else {
85
+ xformed = dest;
86
+ }
87
+
88
+ $.each(source, function(prop, val) {
89
+ if ($.inArray(prop, pluginOptions) >= 0) {
90
+ pluginOption(prop, val);
91
+ }
92
+ else if (val instanceof $) {
93
+ xformed[prop] = val[0];
94
+ }
95
+ else if ($.isPlainObject(val)) {
96
+ xformed[prop] = {};
97
+ transformVariables(val, xformed[prop]);
98
+ }
99
+ else if ($.isArray(val)) {
100
+ arrayVals = [];
101
+ $.each(val, function(idx, arrayVal) {
102
+ if (arrayVal instanceof $) {
103
+ $.merge(arrayVals, arrayVal);
104
+ }
105
+ else {
106
+ arrayVals.push(arrayVal);
107
+ }
108
+ });
109
+ xformed[prop] = arrayVals;
110
+ }
111
+ else {
112
+ xformed[prop] = val;
113
+ }
114
+ });
115
+
116
+ if (dest === undefined) {
117
+ return xformed;
118
+ }
119
+ };
120
+
121
+ isValidCommand = function(command) {
122
+ return $.type(command) === "string" &&
123
+ !command.match(/^_/) && //enforce private methods convention
124
+ uploader()[command] !== undefined;
125
+ };
126
+
127
+ //assuming we have already verified that this is a valid command, call the associated function in the underlying
128
+ // Fine Uploader instance (passing along the arguments from the caller) and return the result of the call back to the caller
129
+ delegateCommand = function(command) {
130
+ var xformedArgs = [], origArgs = Array.prototype.slice.call(arguments, 1);
131
+
132
+ transformVariables(origArgs, xformedArgs);
133
+
134
+ return uploader()[command].apply(uploader(), xformedArgs);
135
+ };
136
+
137
+ $.fn.fineUploader = function(optionsOrCommand) {
138
+ var self = this, selfArgs = arguments, retVals = [];
139
+
140
+ this.each(function(index, el) {
141
+ $el = $(el);
142
+
143
+ if (uploader() && isValidCommand(optionsOrCommand)) {
144
+ retVals.push(delegateCommand.apply(self, selfArgs));
145
+
146
+ if (self.length === 1) {
147
+ return false;
148
+ }
149
+ }
150
+ else if (typeof optionsOrCommand === 'object' || !optionsOrCommand) {
151
+ init.apply(self, selfArgs);
152
+ }
153
+ else {
154
+ $.error('Method ' + optionsOrCommand + ' does not exist on jQuery.fineUploader');
155
+ }
156
+ });
157
+
158
+ if (retVals.length === 1) {
159
+ return retVals[0];
160
+ }
161
+ else if (retVals.length > 1) {
162
+ return retVals;
163
+ }
164
+
165
+ return this;
166
+ };
167
+
168
+ }(jQuery));