uploads 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +3 -0
  3. data/Rakefile +38 -0
  4. data/app/assets/javascripts/anjlab/uploads.js +7 -0
  5. data/app/assets/javascripts/anjlab/uploads/button.js.coffee +80 -0
  6. data/app/assets/javascripts/anjlab/uploads/dnd.js.coffee +215 -0
  7. data/app/assets/javascripts/anjlab/uploads/handler.base.js.coffee +101 -0
  8. data/app/assets/javascripts/anjlab/uploads/handler.form.js.coffee +173 -0
  9. data/app/assets/javascripts/anjlab/uploads/handler.xhr.js.coffee +393 -0
  10. data/app/assets/javascripts/anjlab/uploads/uploader.js.coffee +487 -0
  11. data/app/assets/javascripts/anjlab/uploads/utils.js.coffee +116 -0
  12. data/lib/tasks/uploads_tasks.rake +4 -0
  13. data/lib/uploads.rb +10 -0
  14. data/lib/uploads/engine.rb +6 -0
  15. data/lib/uploads/version.rb +3 -0
  16. data/test/dummy/README.rdoc +261 -0
  17. data/test/dummy/Rakefile +7 -0
  18. data/test/dummy/app/assets/javascripts/application.js +16 -0
  19. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  20. data/test/dummy/app/controllers/application_controller.rb +3 -0
  21. data/test/dummy/app/controllers/uploads_controller.rb +15 -0
  22. data/test/dummy/app/controllers/welcome_controller.rb +4 -0
  23. data/test/dummy/app/helpers/application_helper.rb +2 -0
  24. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  25. data/test/dummy/app/views/welcome/index.html.slim +53 -0
  26. data/test/dummy/config.ru +4 -0
  27. data/test/dummy/config/application.rb +59 -0
  28. data/test/dummy/config/boot.rb +10 -0
  29. data/test/dummy/config/database.yml +25 -0
  30. data/test/dummy/config/environment.rb +5 -0
  31. data/test/dummy/config/environments/development.rb +37 -0
  32. data/test/dummy/config/environments/production.rb +67 -0
  33. data/test/dummy/config/environments/test.rb +37 -0
  34. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  35. data/test/dummy/config/initializers/inflections.rb +15 -0
  36. data/test/dummy/config/initializers/mime_types.rb +5 -0
  37. data/test/dummy/config/initializers/secret_token.rb +7 -0
  38. data/test/dummy/config/initializers/session_store.rb +8 -0
  39. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  40. data/test/dummy/config/locales/en.yml +5 -0
  41. data/test/dummy/config/routes.rb +59 -0
  42. data/test/dummy/db/development.sqlite3 +0 -0
  43. data/test/dummy/log/development.log +23847 -0
  44. data/test/dummy/public/404.html +26 -0
  45. data/test/dummy/public/422.html +26 -0
  46. data/test/dummy/public/500.html +25 -0
  47. data/test/dummy/public/favicon.ico +0 -0
  48. data/test/dummy/script/rails +6 -0
  49. data/test/dummy/tmp/cache/assets/C79/790/sprockets%2Fde97a756642791010533233b267b1afe +0 -0
  50. data/test/dummy/tmp/cache/assets/C99/300/sprockets%2F3bd289b286c074ad66220ad20181b135 +0 -0
  51. data/test/dummy/tmp/cache/assets/C9E/CA0/sprockets%2F1252e234767209f94d0a41a0bc19e33c +0 -0
  52. data/test/dummy/tmp/cache/assets/CA1/9E0/sprockets%2F4c40d59c233952767c150cb8234cab22 +0 -0
  53. data/test/dummy/tmp/cache/assets/CA5/AC0/sprockets%2Fcc77204615926b95033b4fa044d7c61b +0 -0
  54. data/test/dummy/tmp/cache/assets/CAA/860/sprockets%2F6b1d0c998769132cda15f7c5d1283300 +0 -0
  55. data/test/dummy/tmp/cache/assets/CCD/830/sprockets%2F962b2259b044ca05005c14fd83ba982a +0 -0
  56. data/test/dummy/tmp/cache/assets/CD2/7B0/sprockets%2Fe9a64cf98e26a4719e64d747d7912893 +0 -0
  57. data/test/dummy/tmp/cache/assets/CD8/370/sprockets%2F357970feca3ac29060c1e3861e2c0953 +0 -0
  58. data/test/dummy/tmp/cache/assets/CE4/A00/sprockets%2Fe589f936724000ba0e7f077a63b75d4e +0 -0
  59. data/test/dummy/tmp/cache/assets/CE7/D40/sprockets%2F888fd320a7c0125218313f1ef9d6d99d +0 -0
  60. data/test/dummy/tmp/cache/assets/CFD/650/sprockets%2F73de09397391979f4dcd67ce32a7816f +0 -0
  61. data/test/dummy/tmp/cache/assets/D07/710/sprockets%2F56976babbbfe475040b22885733ca41a +0 -0
  62. data/test/dummy/tmp/cache/assets/D0B/D70/sprockets%2F6c298d94014122a613afbebce90590f8 +0 -0
  63. data/test/dummy/tmp/cache/assets/D0F/6A0/sprockets%2F18414faf9678c2ab6d1d60891c6f06a1 +0 -0
  64. data/test/dummy/tmp/cache/assets/D15/C40/sprockets%2F5516d9e4e25aaefc72a78fc641962512 +0 -0
  65. data/test/dummy/tmp/cache/assets/D24/CD0/sprockets%2Fd0cd795c6f0b6074de96706e8bf70975 +0 -0
  66. data/test/dummy/tmp/cache/assets/D2E/980/sprockets%2Fded84d1bb4c31404691d106f4b8e120f +0 -0
  67. data/test/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
  68. data/test/dummy/tmp/cache/assets/D33/120/sprockets%2F70d8b84b628400ffbf410b17b845bae2 +0 -0
  69. data/test/dummy/tmp/cache/assets/D35/8C0/sprockets%2F20424272a569797e71dbae1bdb33c2fc +0 -0
  70. data/test/dummy/tmp/cache/assets/D3F/FE0/sprockets%2Fbfc6e8a285039670d3ca0399bd3e633b +0 -0
  71. data/test/dummy/tmp/cache/assets/D44/E50/sprockets%2F5767c31d95e72a26ffb3c0092dc3cf94 +0 -0
  72. data/test/dummy/tmp/cache/assets/D48/8B0/sprockets%2F795b331f7ff4a59f0604c531e71af9de +0 -0
  73. data/test/dummy/tmp/cache/assets/D4D/420/sprockets%2Fab27b88913d73cdabe65891eeb884444 +0 -0
  74. data/test/dummy/tmp/cache/assets/D4E/1B0/sprockets%2Ff7cbd26ba1d28d48de824f0e94586655 +0 -0
  75. data/test/dummy/tmp/cache/assets/D5A/2A0/sprockets%2Fc379b9f699c8f614cefea762d376d224 +0 -0
  76. data/test/dummy/tmp/cache/assets/D5A/EA0/sprockets%2Fd771ace226fc8215a3572e0aa35bb0d6 +0 -0
  77. data/test/dummy/tmp/cache/assets/D5D/330/sprockets%2Fce164ed2c0f3e91b338035376ad41bac +0 -0
  78. data/test/dummy/tmp/cache/assets/D6D/350/sprockets%2Fdac0344fd04f6fe00bce8983177635ea +0 -0
  79. data/test/dummy/tmp/cache/assets/D71/6E0/sprockets%2F07996d57b5cfca8a92b02613fdaae735 +0 -0
  80. data/test/dummy/tmp/cache/assets/D7C/210/sprockets%2Ff98c0ec43e4406aa3cec29837d9f618e +0 -0
  81. data/test/dummy/tmp/cache/assets/D7E/AF0/sprockets%2F1d4ffb9a3d536682ff7d185668dc2d4b +0 -0
  82. data/test/dummy/tmp/cache/assets/D96/9A0/sprockets%2Ff1caf4ea19ee66a13841eaf6f40a6433 +0 -0
  83. data/test/dummy/tmp/cache/assets/D97/760/sprockets%2Fd1c9a476956a0ccae81fa57dad53135a +0 -0
  84. data/test/dummy/tmp/cache/assets/DBB/F20/sprockets%2F03e6ee44546ae1ea1bfd3e0801b7ca7a +0 -0
  85. data/test/dummy/tmp/cache/assets/DC3/810/sprockets%2Fc86e769b165de4f9d49745ab9fee7b5f +0 -0
  86. data/test/dummy/tmp/cache/assets/DC5/3C0/sprockets%2Fc21f1ad7fdce18f5b7a5a581b1351fd6 +0 -0
  87. data/test/dummy/tmp/cache/assets/DCA/4A0/sprockets%2Fbdfe27f0efa249de171cbfb2970661c2 +0 -0
  88. data/test/dummy/tmp/cache/assets/DD3/AF0/sprockets%2Fb090f4fd52cbc2e5ccc815aa8999f2f3 +0 -0
  89. data/test/dummy/tmp/cache/assets/DDC/400/sprockets%2Fcffd775d018f68ce5dba1ee0d951a994 +0 -0
  90. data/test/dummy/tmp/cache/assets/DE7/920/sprockets%2Feea789b2f9e87ceaffa7757e32fd1192 +0 -0
  91. data/test/dummy/tmp/cache/assets/E02/FE0/sprockets%2F68a4afe37ab83b2d9fa1cf5e084fec81 +0 -0
  92. data/test/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
  93. data/test/dummy/tmp/cache/assets/E2B/C80/sprockets%2Ff6f179d2c6207aabbc3cec5c260eee9e +0 -0
  94. data/test/dummy/tmp/cache/assets/E47/5F0/sprockets%2Fef5f1e3bcbc9baa50e54a15ba0e5ca91 +0 -0
  95. data/test/test_helper.rb +15 -0
  96. data/test/uploads_test.rb +7 -0
  97. 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]()