uploads 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.rdoc +3 -0
- data/Rakefile +38 -0
- data/app/assets/javascripts/anjlab/uploads.js +7 -0
- data/app/assets/javascripts/anjlab/uploads/button.js.coffee +80 -0
- data/app/assets/javascripts/anjlab/uploads/dnd.js.coffee +215 -0
- data/app/assets/javascripts/anjlab/uploads/handler.base.js.coffee +101 -0
- data/app/assets/javascripts/anjlab/uploads/handler.form.js.coffee +173 -0
- data/app/assets/javascripts/anjlab/uploads/handler.xhr.js.coffee +393 -0
- data/app/assets/javascripts/anjlab/uploads/uploader.js.coffee +487 -0
- data/app/assets/javascripts/anjlab/uploads/utils.js.coffee +116 -0
- data/lib/tasks/uploads_tasks.rake +4 -0
- data/lib/uploads.rb +10 -0
- data/lib/uploads/engine.rb +6 -0
- data/lib/uploads/version.rb +3 -0
- data/test/dummy/README.rdoc +261 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/assets/javascripts/application.js +16 -0
- data/test/dummy/app/assets/stylesheets/application.css +13 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/controllers/uploads_controller.rb +15 -0
- data/test/dummy/app/controllers/welcome_controller.rb +4 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/app/views/welcome/index.html.slim +53 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +59 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +37 -0
- data/test/dummy/config/environments/production.rb +67 -0
- data/test/dummy/config/environments/test.rb +37 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +15 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +59 -0
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/log/development.log +23847 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +25 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/dummy/tmp/cache/assets/C79/790/sprockets%2Fde97a756642791010533233b267b1afe +0 -0
- data/test/dummy/tmp/cache/assets/C99/300/sprockets%2F3bd289b286c074ad66220ad20181b135 +0 -0
- data/test/dummy/tmp/cache/assets/C9E/CA0/sprockets%2F1252e234767209f94d0a41a0bc19e33c +0 -0
- data/test/dummy/tmp/cache/assets/CA1/9E0/sprockets%2F4c40d59c233952767c150cb8234cab22 +0 -0
- data/test/dummy/tmp/cache/assets/CA5/AC0/sprockets%2Fcc77204615926b95033b4fa044d7c61b +0 -0
- data/test/dummy/tmp/cache/assets/CAA/860/sprockets%2F6b1d0c998769132cda15f7c5d1283300 +0 -0
- data/test/dummy/tmp/cache/assets/CCD/830/sprockets%2F962b2259b044ca05005c14fd83ba982a +0 -0
- data/test/dummy/tmp/cache/assets/CD2/7B0/sprockets%2Fe9a64cf98e26a4719e64d747d7912893 +0 -0
- data/test/dummy/tmp/cache/assets/CD8/370/sprockets%2F357970feca3ac29060c1e3861e2c0953 +0 -0
- data/test/dummy/tmp/cache/assets/CE4/A00/sprockets%2Fe589f936724000ba0e7f077a63b75d4e +0 -0
- data/test/dummy/tmp/cache/assets/CE7/D40/sprockets%2F888fd320a7c0125218313f1ef9d6d99d +0 -0
- data/test/dummy/tmp/cache/assets/CFD/650/sprockets%2F73de09397391979f4dcd67ce32a7816f +0 -0
- data/test/dummy/tmp/cache/assets/D07/710/sprockets%2F56976babbbfe475040b22885733ca41a +0 -0
- data/test/dummy/tmp/cache/assets/D0B/D70/sprockets%2F6c298d94014122a613afbebce90590f8 +0 -0
- data/test/dummy/tmp/cache/assets/D0F/6A0/sprockets%2F18414faf9678c2ab6d1d60891c6f06a1 +0 -0
- data/test/dummy/tmp/cache/assets/D15/C40/sprockets%2F5516d9e4e25aaefc72a78fc641962512 +0 -0
- data/test/dummy/tmp/cache/assets/D24/CD0/sprockets%2Fd0cd795c6f0b6074de96706e8bf70975 +0 -0
- data/test/dummy/tmp/cache/assets/D2E/980/sprockets%2Fded84d1bb4c31404691d106f4b8e120f +0 -0
- data/test/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
- data/test/dummy/tmp/cache/assets/D33/120/sprockets%2F70d8b84b628400ffbf410b17b845bae2 +0 -0
- data/test/dummy/tmp/cache/assets/D35/8C0/sprockets%2F20424272a569797e71dbae1bdb33c2fc +0 -0
- data/test/dummy/tmp/cache/assets/D3F/FE0/sprockets%2Fbfc6e8a285039670d3ca0399bd3e633b +0 -0
- data/test/dummy/tmp/cache/assets/D44/E50/sprockets%2F5767c31d95e72a26ffb3c0092dc3cf94 +0 -0
- data/test/dummy/tmp/cache/assets/D48/8B0/sprockets%2F795b331f7ff4a59f0604c531e71af9de +0 -0
- data/test/dummy/tmp/cache/assets/D4D/420/sprockets%2Fab27b88913d73cdabe65891eeb884444 +0 -0
- data/test/dummy/tmp/cache/assets/D4E/1B0/sprockets%2Ff7cbd26ba1d28d48de824f0e94586655 +0 -0
- data/test/dummy/tmp/cache/assets/D5A/2A0/sprockets%2Fc379b9f699c8f614cefea762d376d224 +0 -0
- data/test/dummy/tmp/cache/assets/D5A/EA0/sprockets%2Fd771ace226fc8215a3572e0aa35bb0d6 +0 -0
- data/test/dummy/tmp/cache/assets/D5D/330/sprockets%2Fce164ed2c0f3e91b338035376ad41bac +0 -0
- data/test/dummy/tmp/cache/assets/D6D/350/sprockets%2Fdac0344fd04f6fe00bce8983177635ea +0 -0
- data/test/dummy/tmp/cache/assets/D71/6E0/sprockets%2F07996d57b5cfca8a92b02613fdaae735 +0 -0
- data/test/dummy/tmp/cache/assets/D7C/210/sprockets%2Ff98c0ec43e4406aa3cec29837d9f618e +0 -0
- data/test/dummy/tmp/cache/assets/D7E/AF0/sprockets%2F1d4ffb9a3d536682ff7d185668dc2d4b +0 -0
- data/test/dummy/tmp/cache/assets/D96/9A0/sprockets%2Ff1caf4ea19ee66a13841eaf6f40a6433 +0 -0
- data/test/dummy/tmp/cache/assets/D97/760/sprockets%2Fd1c9a476956a0ccae81fa57dad53135a +0 -0
- data/test/dummy/tmp/cache/assets/DBB/F20/sprockets%2F03e6ee44546ae1ea1bfd3e0801b7ca7a +0 -0
- data/test/dummy/tmp/cache/assets/DC3/810/sprockets%2Fc86e769b165de4f9d49745ab9fee7b5f +0 -0
- data/test/dummy/tmp/cache/assets/DC5/3C0/sprockets%2Fc21f1ad7fdce18f5b7a5a581b1351fd6 +0 -0
- data/test/dummy/tmp/cache/assets/DCA/4A0/sprockets%2Fbdfe27f0efa249de171cbfb2970661c2 +0 -0
- data/test/dummy/tmp/cache/assets/DD3/AF0/sprockets%2Fb090f4fd52cbc2e5ccc815aa8999f2f3 +0 -0
- data/test/dummy/tmp/cache/assets/DDC/400/sprockets%2Fcffd775d018f68ce5dba1ee0d951a994 +0 -0
- data/test/dummy/tmp/cache/assets/DE7/920/sprockets%2Feea789b2f9e87ceaffa7757e32fd1192 +0 -0
- data/test/dummy/tmp/cache/assets/E02/FE0/sprockets%2F68a4afe37ab83b2d9fa1cf5e084fec81 +0 -0
- data/test/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
- data/test/dummy/tmp/cache/assets/E2B/C80/sprockets%2Ff6f179d2c6207aabbc3cec5c260eee9e +0 -0
- data/test/dummy/tmp/cache/assets/E47/5F0/sprockets%2Fef5f1e3bcbc9baa50e54a15ba0e5ca91 +0 -0
- data/test/test_helper.rb +15 -0
- data/test/uploads_test.rb +7 -0
- metadata +276 -0
@@ -0,0 +1,487 @@
|
|
1
|
+
utils = @AnjLab.Uploads.Utils
|
2
|
+
|
3
|
+
class @AnjLab.Uploads.Uploader
|
4
|
+
|
5
|
+
constructor: (element, options)->
|
6
|
+
@$element = $(element)
|
7
|
+
@options = $.extend(true, {}, $.fn.uploaderDefaults, @$element.data(), options)
|
8
|
+
# number of files being uploaded
|
9
|
+
@filesInProgress = []
|
10
|
+
@storedFileIds = []
|
11
|
+
@autoRetries = []
|
12
|
+
@retryTimeouts = []
|
13
|
+
@preventRetries = []
|
14
|
+
|
15
|
+
@paramsStore = @createParamsStore()
|
16
|
+
@endpointStore = @createEndpointStore()
|
17
|
+
|
18
|
+
@handler = @createUploadHandler()
|
19
|
+
@dnd = @createDragAndDrop()
|
20
|
+
|
21
|
+
if @options.button
|
22
|
+
@button = new AnjLab.Uploads.Button(@options.button, {
|
23
|
+
multiple: @options.multiple && utils.isXhrUploadSupported()
|
24
|
+
acceptFiles: @options.validation.acceptFiles
|
25
|
+
onChange: (input)=>
|
26
|
+
@onInputChange(input)
|
27
|
+
hoverClass: @options.classes.buttonHover
|
28
|
+
focusClass: @options.classes.buttonFocus
|
29
|
+
})
|
30
|
+
|
31
|
+
log: (str, level) ->
|
32
|
+
if @options.debug && (!level || level == 'info')
|
33
|
+
utils.log('[FineUploader] ' + str)
|
34
|
+
else if level && level != 'info'
|
35
|
+
utils.log('[FineUploader] ' + str, level)
|
36
|
+
true
|
37
|
+
|
38
|
+
setParams: (params, fileId) ->
|
39
|
+
if fileId?
|
40
|
+
@paramsStore.setParams(params, fileId)
|
41
|
+
else
|
42
|
+
@options.request.params = params
|
43
|
+
|
44
|
+
setEndpoint: (endpoint, fileId) ->
|
45
|
+
if fileId?
|
46
|
+
@endpointStore.setEndpoint(endpoint, fileId)
|
47
|
+
else
|
48
|
+
@options.request.endpoint = endpoint
|
49
|
+
|
50
|
+
getInProgress: -> @filesInProgress.length
|
51
|
+
|
52
|
+
createDragAndDrop: ->
|
53
|
+
new AnjLab.Uploads.DragAndDrop(@options.dropzones,
|
54
|
+
{
|
55
|
+
multiple: @options.multiple
|
56
|
+
classes:
|
57
|
+
dropActive: @options.classes.dropActive
|
58
|
+
bodyDragover: @options.classes.bodyDragover
|
59
|
+
callbacks:
|
60
|
+
dropProcessing: (isProcessing, files) =>
|
61
|
+
if files
|
62
|
+
@addFiles(files)
|
63
|
+
error: (code, filename) =>
|
64
|
+
@error(code, filename)
|
65
|
+
log: (message, level) =>
|
66
|
+
@log(message, level)
|
67
|
+
})
|
68
|
+
|
69
|
+
uploadStoredFiles: ->
|
70
|
+
while @storedFileIds.length
|
71
|
+
idToUpload = @storedFileIds.shift()
|
72
|
+
@filesInProgress.push(idToUpload)
|
73
|
+
@handler.upload(idToUpload)
|
74
|
+
|
75
|
+
clearStoredFiles: -> @storedFileIds = []
|
76
|
+
|
77
|
+
retry: (id) ->
|
78
|
+
if @onBeforeManualRetry(id)
|
79
|
+
@handler.retry(id)
|
80
|
+
true
|
81
|
+
else
|
82
|
+
false
|
83
|
+
|
84
|
+
cancel: (fileId) ->
|
85
|
+
@handler.cancel(fileId)
|
86
|
+
|
87
|
+
reset: ->
|
88
|
+
@log("Resetting uploader...")
|
89
|
+
@handler.reset()
|
90
|
+
@filesInProgress = []
|
91
|
+
@storedFileIds = []
|
92
|
+
@autoRetries = []
|
93
|
+
@retryTimeouts = []
|
94
|
+
@preventRetries = []
|
95
|
+
@button.reset()
|
96
|
+
@paramsStore.reset()
|
97
|
+
@endpointStore.reset()
|
98
|
+
|
99
|
+
|
100
|
+
addFiles: (filesOrInputs)->
|
101
|
+
verifiedFilesOrInputs = []
|
102
|
+
|
103
|
+
return if !filesOrInputs
|
104
|
+
|
105
|
+
if !window.FileList || !(filesOrInputs instanceof FileList)
|
106
|
+
filesOrInputs = [].concat(filesOrInputs)
|
107
|
+
|
108
|
+
for fileOrInput in filesOrInputs
|
109
|
+
if utils.isFileOrInput(fileOrInput)
|
110
|
+
verifiedFilesOrInputs.push(fileOrInput);
|
111
|
+
else
|
112
|
+
@log("#{fileOrInput} is not a File or INPUT element! Ignoring!", 'warn')
|
113
|
+
@log("Processing #{verifiedFilesOrInputs.length} files or inputs...")
|
114
|
+
@uploadFileList(verifiedFilesOrInputs)
|
115
|
+
|
116
|
+
getUuid: (fileId) -> @handler.getUuid(fileId)
|
117
|
+
getResumableFilesData: -> @handler.getResumableFilesData()
|
118
|
+
getSize: (fileId) -> @handler.getSize(fileId)
|
119
|
+
getFile: (fileId) -> @handler.getFile(fileId)
|
120
|
+
|
121
|
+
createParamsStore: ->
|
122
|
+
paramsStore = {}
|
123
|
+
self = this
|
124
|
+
|
125
|
+
{
|
126
|
+
setParams: (params, fileId)->
|
127
|
+
paramsStore[fileId] = $.extend(true, {}, params)
|
128
|
+
getParams: (fileId) ->
|
129
|
+
if fileId? && paramsStore[fileId]
|
130
|
+
$.extend(true, {}, paramsStore[fileId])
|
131
|
+
else
|
132
|
+
$.extend(true, {}, self.options.request.params)
|
133
|
+
remove: (fileId) -> delete paramsStore[fileId]
|
134
|
+
reset: -> paramsStore = {}
|
135
|
+
}
|
136
|
+
|
137
|
+
createEndpointStore: () ->
|
138
|
+
endpointStore = {}
|
139
|
+
self = this
|
140
|
+
|
141
|
+
{
|
142
|
+
setEndpoint: (endpoint, fileId) ->
|
143
|
+
endpointStore[fileId] = endpoint
|
144
|
+
|
145
|
+
getEndpoint: (fileId) ->
|
146
|
+
if fileId? && endpointStore[fileId]
|
147
|
+
return endpointStore[fileId]
|
148
|
+
|
149
|
+
self.options.request.endpoint;
|
150
|
+
|
151
|
+
remove: (fileId) -> delete endpointStore[fileId]
|
152
|
+
|
153
|
+
reset: -> endpointStore = {}
|
154
|
+
}
|
155
|
+
|
156
|
+
preventLeaveInProgress: ->
|
157
|
+
$(window).on 'beforeunload', (e)=>
|
158
|
+
return if !@filesInProgress.length
|
159
|
+
|
160
|
+
e.returnValue = @options.messages.onLeave
|
161
|
+
onSubmit: (id, fileName) ->
|
162
|
+
if @options.autoUpload
|
163
|
+
@filesInProgress.push(id)
|
164
|
+
|
165
|
+
onProgress: (id, fileName, loaded, total) -> false
|
166
|
+
|
167
|
+
onComplete: (id, fileName, result, xhr) ->
|
168
|
+
@removeFromFilesInProgress(id)
|
169
|
+
@maybeParseAndSendUploadError(id, fileName, result, xhr)
|
170
|
+
|
171
|
+
onCancel: (id, fileName) ->
|
172
|
+
@removeFromFilesInProgress(id)
|
173
|
+
|
174
|
+
clearTimeout(@retryTimeouts[id])
|
175
|
+
|
176
|
+
storedFileIndex = $.inArray(id, @storedFileIds)
|
177
|
+
if !@options.autoUpload && storedFileIndex >= 0
|
178
|
+
@storedFileIds.splice(storedFileIndex, 1)
|
179
|
+
|
180
|
+
removeFromFilesInProgress: (id) ->
|
181
|
+
index = $.inArray(id, @filesInProgress)
|
182
|
+
if index >= 0
|
183
|
+
@filesInProgress.splice(index, 1)
|
184
|
+
|
185
|
+
onUpload: (id, fileName) -> null
|
186
|
+
|
187
|
+
onInputChange: (input) ->
|
188
|
+
if utils.isXhrUploadSupported()
|
189
|
+
@addFiles(input.files)
|
190
|
+
else
|
191
|
+
@addFiles(input)
|
192
|
+
@button.reset()
|
193
|
+
|
194
|
+
onBeforeAutoRetry: (id, fileName) ->
|
195
|
+
@log("Waiting #{@options.retry.autoAttemptDelay} seconds before retrying #{fileName}...")
|
196
|
+
|
197
|
+
onAutoRetry: (id, fileName, responseJSON) ->
|
198
|
+
@log("Retrying #{fileName}...")
|
199
|
+
@autoRetries[id]++
|
200
|
+
@handler.retry(id)
|
201
|
+
|
202
|
+
shouldAutoRetry: (id, fileName, responseJSON) ->
|
203
|
+
if !@preventRetries[id] && @options.retry.enableAuto
|
204
|
+
if !@autoRetries[id]?
|
205
|
+
@autoRetries[id] = 0
|
206
|
+
|
207
|
+
return @autoRetries[id] < @options.retry.maxAutoAttempts
|
208
|
+
|
209
|
+
false
|
210
|
+
|
211
|
+
uploadFile: (fileContainer) ->
|
212
|
+
id = @handler.add(fileContainer)
|
213
|
+
fileName = @handler.getName(id)
|
214
|
+
|
215
|
+
if @options.callbacks.onSubmit(id, fileName) != false
|
216
|
+
@onSubmit(id, fileName)
|
217
|
+
if @options.autoUpload
|
218
|
+
@handler.upload(id)
|
219
|
+
else
|
220
|
+
@storeFileForLater(id)
|
221
|
+
|
222
|
+
storeFileForLater: (id) -> @storedFileIds.push(id)
|
223
|
+
|
224
|
+
parseFileSize: (file) ->
|
225
|
+
size = null
|
226
|
+
# fix missing properties in Safari 4 and firefox 11.0a2
|
227
|
+
size = file.fileSize ? file.size if !file.value
|
228
|
+
size
|
229
|
+
|
230
|
+
formatSize: (bytes) ->
|
231
|
+
i = -1;
|
232
|
+
while true
|
233
|
+
bytes = bytes / 1024
|
234
|
+
i++
|
235
|
+
break if bytes <= 99
|
236
|
+
|
237
|
+
Math.max(bytes, 0.1).toFixed(1) + @options.text.sizeSymbols[i]
|
238
|
+
|
239
|
+
parseFileName: (file) ->
|
240
|
+
if file.value
|
241
|
+
# it is a file input
|
242
|
+
# get input value and remove path to normalize
|
243
|
+
file.value.replace(/.*(\/|\\)/, "")
|
244
|
+
else
|
245
|
+
# fix missing properties in Safari 4 and firefox 11.0a2
|
246
|
+
file.fileName ? file.name
|
247
|
+
|
248
|
+
getValidationDescriptor: (file) ->
|
249
|
+
fileDescriptor = {name: @parseFileName(file)}
|
250
|
+
size = @parseFileSize(file)
|
251
|
+
|
252
|
+
fileDescriptor.size = size if size
|
253
|
+
|
254
|
+
fileDescriptor
|
255
|
+
|
256
|
+
getValidationDescriptors: (files) ->
|
257
|
+
@getValidationDescriptor(file) for file in files
|
258
|
+
|
259
|
+
isAllowedExtension: (fileName) ->
|
260
|
+
allowed = @options.validation.allowedExtensions
|
261
|
+
|
262
|
+
return true if !allowed.length
|
263
|
+
|
264
|
+
for allowedExt in allowed
|
265
|
+
extRegex = new RegExp('\\.' + allowedExt + "$", 'i')
|
266
|
+
|
267
|
+
return true if fileName.match(extRegex)?
|
268
|
+
|
269
|
+
false
|
270
|
+
|
271
|
+
validateFile: (file) ->
|
272
|
+
validationDescriptor = @getValidationDescriptor(file)
|
273
|
+
name = validationDescriptor.name
|
274
|
+
size = validationDescriptor.size
|
275
|
+
|
276
|
+
return false if @options.callbacks.onValidate(validationDescriptor) == false
|
277
|
+
|
278
|
+
if !@isAllowedExtension(name)
|
279
|
+
@error('typeError', name)
|
280
|
+
false
|
281
|
+
else if size == 0
|
282
|
+
@error('emptyError', name)
|
283
|
+
false
|
284
|
+
else if size && @options.validation.sizeLimit && size > @options.validation.sizeLimit
|
285
|
+
@error('sizeError', name)
|
286
|
+
false
|
287
|
+
else if (size && size < @options.validation.minSizeLimit)
|
288
|
+
@error('minSizeError', name)
|
289
|
+
false
|
290
|
+
else
|
291
|
+
true
|
292
|
+
|
293
|
+
error: (code, fileName) ->
|
294
|
+
message = @options.messages[code]
|
295
|
+
r = (name, replacement) -> message = message.replace(name, replacement)
|
296
|
+
|
297
|
+
extensions = @options.validation.allowedExtensions.join(', ').toLowerCase()
|
298
|
+
|
299
|
+
r('{file}', @options.formatFileName(fileName))
|
300
|
+
r('{extensions}', extensions)
|
301
|
+
r('{sizeLimit}', @formatSize(@options.validation.sizeLimit))
|
302
|
+
r('{minSizeLimit}', @formatSize(@options.validation.minSizeLimit))
|
303
|
+
|
304
|
+
@options.callbacks.onError(null, fileName, message)
|
305
|
+
|
306
|
+
message
|
307
|
+
|
308
|
+
# return false if we should not attempt the requested retry
|
309
|
+
onBeforeManualRetry: (id) ->
|
310
|
+
if @preventRetries[id]
|
311
|
+
@log("Retries are forbidden for id #{id}", 'warn')
|
312
|
+
false
|
313
|
+
else if @handler.isValid(id)
|
314
|
+
fileName = @handler.getName(id);
|
315
|
+
|
316
|
+
return false if @options.callbacks.onManualRetry(id, fileName) == false
|
317
|
+
|
318
|
+
@log("Retrying upload for '#{fileName}' (id: #{id})...")
|
319
|
+
@filesInProgress.push(id)
|
320
|
+
true
|
321
|
+
else
|
322
|
+
@log("'#{id}' is not a valid file ID", 'error')
|
323
|
+
false
|
324
|
+
|
325
|
+
maybeParseAndSendUploadError: (id, fileName, response, xhr) ->
|
326
|
+
# assuming no one will actually set the response code to something other than 200 and still set 'success' to true
|
327
|
+
if !response.success
|
328
|
+
if xhr && xhr.status != 200 && !response.error
|
329
|
+
@options.callbacks.onError(id, fileName, "XHR returned response code #{xhr.status}")
|
330
|
+
else
|
331
|
+
errorReason = if response.error then response.error else "Upload failure reason unknown"
|
332
|
+
@options.callbacks.onError(id, fileName, errorReason)
|
333
|
+
|
334
|
+
uploadFileList: (files) ->
|
335
|
+
validationDescriptors = @getValidationDescriptors(files)
|
336
|
+
batchInvalid = @options.callbacks.onValidateBatch(validationDescriptors) == false
|
337
|
+
if !batchInvalid
|
338
|
+
if files.length > 0
|
339
|
+
for file in files
|
340
|
+
if @validateFile(file)
|
341
|
+
@uploadFile(file)
|
342
|
+
else
|
343
|
+
return if @options.validation.stopOnFirstInvalidFile
|
344
|
+
else
|
345
|
+
@error('noFilesError', "")
|
346
|
+
|
347
|
+
|
348
|
+
createUploadHandler: ->
|
349
|
+
AnjLab.Uploads.UploadHandler.create({
|
350
|
+
debug: @options.debug
|
351
|
+
forceMultipart: @options.request.forceMultipart
|
352
|
+
maxConnections: @options.maxConnections
|
353
|
+
customHeaders: @options.request.customHeaders
|
354
|
+
inputName: @options.request.inputName
|
355
|
+
uuidParamName: @options.request.uuidName
|
356
|
+
totalFileSizeParamName: @options.request.totalFileSizeName
|
357
|
+
demoMode: @options.demoMode
|
358
|
+
paramsStore: @paramsStore
|
359
|
+
endpointStore: @endpointStore
|
360
|
+
chunking: @options.chunking
|
361
|
+
resume: @options.resume
|
362
|
+
log: (str, level) => @log(str, level)
|
363
|
+
|
364
|
+
onProgress: (id, fileName, loaded, total) =>
|
365
|
+
@onProgress(id, fileName, loaded, total)
|
366
|
+
@options.callbacks.onProgress(id, fileName, loaded, total)
|
367
|
+
|
368
|
+
onComplete: (id, fileName, result, xhr) =>
|
369
|
+
@onComplete(id, fileName, result, xhr)
|
370
|
+
@options.callbacks.onComplete(id, fileName, result)
|
371
|
+
|
372
|
+
onCancel: (id, fileName) =>
|
373
|
+
@onCancel(id, fileName)
|
374
|
+
@options.callbacks.onCancel(id, fileName)
|
375
|
+
|
376
|
+
onUpload: (id, fileName) =>
|
377
|
+
@onUpload(id, fileName)
|
378
|
+
@options.callbacks.onUpload(id, fileName)
|
379
|
+
|
380
|
+
onUploadChunk: (id, fileName, chunkData) =>
|
381
|
+
@options.callbacks.onUploadChunk(id, fileName, chunkData)
|
382
|
+
|
383
|
+
onResume: (id, fileName, chunkData) =>
|
384
|
+
@options.callbacks.onResume(id, fileName, chunkData)
|
385
|
+
|
386
|
+
onAutoRetry: (id, fileName, responseJSON, xhr) =>
|
387
|
+
@preventRetries[id] = responseJSON[@options.retry.preventRetryResponseProperty]
|
388
|
+
|
389
|
+
if @shouldAutoRetry(id, fileName, responseJSON)
|
390
|
+
@maybeParseAndSendUploadError(id, fileName, responseJSON, xhr)
|
391
|
+
@options.callbacks.onAutoRetry(id, fileName, self._autoRetries[id] + 1)
|
392
|
+
@onBeforeAutoRetry(id, fileName)
|
393
|
+
|
394
|
+
@retryTimeouts[id] = setTimeout( =>
|
395
|
+
@onAutoRetry(id, fileName, responseJSON)
|
396
|
+
, @options.retry.autoAttemptDelay * 1000
|
397
|
+
)
|
398
|
+
|
399
|
+
true
|
400
|
+
else
|
401
|
+
false
|
402
|
+
})
|
403
|
+
|
404
|
+
|
405
|
+
|
406
|
+
$.fn.uploaderDefaults =
|
407
|
+
debug: false
|
408
|
+
button: null
|
409
|
+
multiple: true
|
410
|
+
maxConnections: 3
|
411
|
+
disableCancelForFormUploads: false
|
412
|
+
autoUpload: true
|
413
|
+
request:
|
414
|
+
endpoint: '/uploads'
|
415
|
+
params: {}
|
416
|
+
customHeaders: {}
|
417
|
+
forceMultipart: true
|
418
|
+
inputName: 'qqfile'
|
419
|
+
uuidName: 'qquuid'
|
420
|
+
totalFileSizeName: 'qqtotalfilesize'
|
421
|
+
validation:
|
422
|
+
allowedExtensions: []
|
423
|
+
sizeLimit: 0
|
424
|
+
minSizeLimit: 0
|
425
|
+
stopOnFirstInvalidFile: true
|
426
|
+
callbacks:
|
427
|
+
onSubmit: (id, fileName) -> null
|
428
|
+
onComplete: (id, fileName, responseJSON) -> null
|
429
|
+
onCancel: (id, fileName) -> null
|
430
|
+
onUpload: (id, fileName) -> null
|
431
|
+
onUploadChunk: (id, fileName, chunkData) -> null
|
432
|
+
onResume: (id, fileName, chunkData) -> null
|
433
|
+
onProgress: (id, fileName, loaded, total) -> null
|
434
|
+
onError: (id, fileName, reason) -> null
|
435
|
+
onAutoRetry: (id, fileName, attemptNumber) -> null
|
436
|
+
onManualRetry: (id, fileName) -> false
|
437
|
+
onValidateBatch: (fileData) -> null
|
438
|
+
onValidate: (fileData) -> null
|
439
|
+
messages:
|
440
|
+
typeError: "{file} has an invalid extension. Valid extension(s): {extensions}."
|
441
|
+
sizeError: "{file} is too large, maximum file size is {sizeLimit}."
|
442
|
+
minSizeError: "{file} is too small, minimum file size is {minSizeLimit}."
|
443
|
+
emptyError: "{file} is empty, please select files again without it."
|
444
|
+
noFilesError: "No files to upload.",
|
445
|
+
onLeave: "The files are being uploaded, if you leave now the upload will be cancelled."
|
446
|
+
retry:
|
447
|
+
enableAuto: false
|
448
|
+
maxAutoAttempts: 3
|
449
|
+
autoAttemptDelay: 5
|
450
|
+
preventRetryResponseProperty: 'preventRetry'
|
451
|
+
classes:
|
452
|
+
buttonHover: 'qq-upload-button-hover'
|
453
|
+
buttonFocus: 'qq-upload-button-focus'
|
454
|
+
bodyDragover: 'qq-upload-dragging'
|
455
|
+
chunking:
|
456
|
+
enabled: false
|
457
|
+
partSize: 2000000
|
458
|
+
paramNames:
|
459
|
+
partIndex: 'qqpartindex'
|
460
|
+
partByteOffset: 'qqpartbyteoffset'
|
461
|
+
chunkSize: 'qqchunksize'
|
462
|
+
totalFileSize: 'qqtotalfilesize'
|
463
|
+
totalParts: 'qqtotalparts'
|
464
|
+
filename: 'qqfilename'
|
465
|
+
resume:
|
466
|
+
enabled: false
|
467
|
+
id: null
|
468
|
+
cookiesExpireIn: 7 #days
|
469
|
+
paramNames:
|
470
|
+
resuming: "qqresume"
|
471
|
+
text:
|
472
|
+
sizeSymbols: ['kB', 'MB', 'GB', 'TB', 'PB', 'EB']
|
473
|
+
|
474
|
+
formatFileName: (fileName)->
|
475
|
+
if fileName.length > 33
|
476
|
+
fileName = fileName.slice(0, 19) + '...' + fileName.slice(-14)
|
477
|
+
fileName
|
478
|
+
|
479
|
+
$.fn.uploader = (option) ->
|
480
|
+
this.each ->
|
481
|
+
$this = $(this)
|
482
|
+
data = $this.data('uploader')
|
483
|
+
if !data
|
484
|
+
options = $.extend(true, {}, typeof option == 'object' && option)
|
485
|
+
$this.data('uploader', (data = new AnjLab.Uploads.Uploader(this, options)))
|
486
|
+
if (typeof option == 'string')
|
487
|
+
data[option]()
|