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
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2013 YOURNAME
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
begin
|
3
|
+
require 'bundler/setup'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'rdoc/task'
|
9
|
+
rescue LoadError
|
10
|
+
require 'rdoc/rdoc'
|
11
|
+
require 'rake/rdoctask'
|
12
|
+
RDoc::Task = Rake::RDocTask
|
13
|
+
end
|
14
|
+
|
15
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
16
|
+
rdoc.rdoc_dir = 'rdoc'
|
17
|
+
rdoc.title = 'Uploads'
|
18
|
+
rdoc.options << '--line-numbers'
|
19
|
+
rdoc.rdoc_files.include('README.rdoc')
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
Bundler::GemHelper.install_tasks
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
|
30
|
+
Rake::TestTask.new(:test) do |t|
|
31
|
+
t.libs << 'lib'
|
32
|
+
t.libs << 'test'
|
33
|
+
t.pattern = 'test/**/*_test.rb'
|
34
|
+
t.verbose = false
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
task :default => :test
|
@@ -0,0 +1,80 @@
|
|
1
|
+
class @AnjLab.Uploads.Button
|
2
|
+
|
3
|
+
constructor: (element, options) ->
|
4
|
+
@options = $.extend(true, {}, $.fn.uploaderButtonDefaults, options)
|
5
|
+
@$element = $(@options.element ? element)
|
6
|
+
|
7
|
+
# make button suitable container for input
|
8
|
+
@$element.css
|
9
|
+
position: 'relative'
|
10
|
+
overflow: 'hidden'
|
11
|
+
# Make sure browse button is in the right side
|
12
|
+
# in Internet Explorer
|
13
|
+
direction: 'ltr'
|
14
|
+
|
15
|
+
@input = @createInput()
|
16
|
+
|
17
|
+
getInput: -> @input
|
18
|
+
|
19
|
+
# cleans/recreates the file input
|
20
|
+
reset: ->
|
21
|
+
if @input.parentNode
|
22
|
+
$(@input).remove()
|
23
|
+
|
24
|
+
@$element.removeClass(@options.focusClass)
|
25
|
+
@input = @createInput()
|
26
|
+
|
27
|
+
createInput: ->
|
28
|
+
input = document.createElement("input")
|
29
|
+
|
30
|
+
if @options.multiple
|
31
|
+
input.setAttribute("multiple", "multiple")
|
32
|
+
|
33
|
+
if @options.acceptFiles
|
34
|
+
input.setAttribute("accept", @options.acceptFiles)
|
35
|
+
|
36
|
+
input.setAttribute("type", "file")
|
37
|
+
input.setAttribute("name", @options.name)
|
38
|
+
|
39
|
+
$(input).css
|
40
|
+
position: 'absolute'
|
41
|
+
# in Opera only 'browse' button
|
42
|
+
# is clickable and it is located at
|
43
|
+
# the right side of the input
|
44
|
+
right: 0
|
45
|
+
top: 0
|
46
|
+
fontFamily: 'Arial'
|
47
|
+
# 4 persons reported this, the max values that worked for them were 243, 236, 236, 118
|
48
|
+
fontSize: '118px'
|
49
|
+
margin: 0
|
50
|
+
padding: 0
|
51
|
+
cursor: 'pointer'
|
52
|
+
opacity: 0
|
53
|
+
|
54
|
+
@$element.append(input)
|
55
|
+
|
56
|
+
$(input).on
|
57
|
+
change: => @options.onChange(input)
|
58
|
+
mouseover: => @$element.addClass(@options.hoverClass)
|
59
|
+
mouseout: => @$element.removeClass(@options.hoverClass)
|
60
|
+
focus: => @$element.addClass(@options.focusClass)
|
61
|
+
blur: => @$element.removeClass(@options.focusClass)
|
62
|
+
|
63
|
+
# IE and Opera, unfortunately have 2 tab stops on file input
|
64
|
+
# which is unacceptable in our case, disable keyboard access
|
65
|
+
if window.attachEvent
|
66
|
+
# it is IE or Opera
|
67
|
+
input.setAttribute('tabIndex', '-1')
|
68
|
+
|
69
|
+
input
|
70
|
+
|
71
|
+
$.fn.uploaderButtonDefaults =
|
72
|
+
element: null
|
73
|
+
# if set to true adds multiple attribute to file input
|
74
|
+
multiple: false,
|
75
|
+
acceptFiles: null,
|
76
|
+
# name attribute of file input
|
77
|
+
name: 'file',
|
78
|
+
onChange: (input) -> null,
|
79
|
+
hoverClass: 'qq-upload-button-hover',
|
80
|
+
focusClass: 'qq-upload-button-focus'
|
@@ -0,0 +1,215 @@
|
|
1
|
+
utils = @AnjLab.Uploads.Utils
|
2
|
+
|
3
|
+
class @AnjLab.Uploads.DragAndDrop
|
4
|
+
|
5
|
+
constructor: (zones, options)->
|
6
|
+
@droppedFiles = []
|
7
|
+
@droppedEntriesCount = 0
|
8
|
+
@droppedEntriesParsedCount = 0
|
9
|
+
@options = options
|
10
|
+
@setupDragDrop(zones)
|
11
|
+
|
12
|
+
maybeUploadDroppedFiles: ->
|
13
|
+
if @droppedEntriesCount == @droppedEntriesParsedCount && !@dirPending
|
14
|
+
@options.callbacks.log("Grabbed #{@droppedFiles.length} files after tree traversal.")
|
15
|
+
@zones.each -> $(this).data('dropZone').dropDisabled(false)
|
16
|
+
@options.callbacks.dropProcessing(false, @droppedFiles)
|
17
|
+
|
18
|
+
addDroppedFile: (file)->
|
19
|
+
@droppedFiles.push(file)
|
20
|
+
@droppedEntriesParsedCount += 1
|
21
|
+
@maybeUploadDroppedFiles()
|
22
|
+
|
23
|
+
traverseFileTree: (entry)->
|
24
|
+
# var dirReader, i;
|
25
|
+
dirPending = false
|
26
|
+
@droppedEntriesCount+=1
|
27
|
+
|
28
|
+
if entry.isFile
|
29
|
+
entry.file (file) => @addDroppedFile(file)
|
30
|
+
else if entry.isDirectory
|
31
|
+
dirPending = true
|
32
|
+
dirReader = entry.createReader();
|
33
|
+
dirReader.readEntries (entries) =>
|
34
|
+
@droppedEntriesParsedCount+=1;
|
35
|
+
for e in entries
|
36
|
+
@traverseFileTree(e)
|
37
|
+
|
38
|
+
dirPending = false;
|
39
|
+
|
40
|
+
if !entries.length
|
41
|
+
@maybeUploadDroppedFiles()
|
42
|
+
|
43
|
+
isFileDrag: (dragEvent) ->
|
44
|
+
for own key, val of dragEvent.dataTransfer.types
|
45
|
+
return true if val == 'Files'
|
46
|
+
|
47
|
+
false
|
48
|
+
|
49
|
+
handleDataTransfer: (dataTransfer) ->
|
50
|
+
@options.callbacks.dropProcessing(true)
|
51
|
+
@zones.each -> $(this).data('dropZone').dropDisabled(true)
|
52
|
+
|
53
|
+
if dataTransfer.files.length > 1 && !@options.multiple
|
54
|
+
@options.callbacks.dropProcessing(false)
|
55
|
+
@options.callbacks.error('tooManyFilesError', "")
|
56
|
+
@zones.each -> $(this).data('dropZone').dropDisabled(false)
|
57
|
+
else
|
58
|
+
@droppedFiles = [];
|
59
|
+
@droppedEntriesCount = 0
|
60
|
+
@droppedEntriesParsedCount = 0
|
61
|
+
|
62
|
+
if utils.isFolderDropSupported(dataTransfer)
|
63
|
+
items = dataTransfer.items
|
64
|
+
|
65
|
+
i = 0
|
66
|
+
for item in items
|
67
|
+
entry = item.webkitGetAsEntry()
|
68
|
+
if entry
|
69
|
+
# due to a bug in Chrome's File System API impl - #149735
|
70
|
+
if entry.isFile
|
71
|
+
@droppedFiles.push(items[i].getAsFile());
|
72
|
+
if (i == items.length - 1)
|
73
|
+
@maybeUploadDroppedFiles()
|
74
|
+
else
|
75
|
+
@traverseFileTree(entry)
|
76
|
+
i += 1
|
77
|
+
else
|
78
|
+
@options.callbacks.dropProcessing(false, dataTransfer.files)
|
79
|
+
@zones.each -> $(this).data('dropZone').dropDisabled(false)
|
80
|
+
|
81
|
+
setupDragDrop: (zones)->
|
82
|
+
dnd = this
|
83
|
+
@zones = $(zones).each ->
|
84
|
+
$zone = $(this)
|
85
|
+
zoneOpts = $.extend(true, {}, {
|
86
|
+
onEnter: (e) =>
|
87
|
+
$zone.toggleClass(dnd.options.classes.dropActive, true)
|
88
|
+
e.stopPropagation()
|
89
|
+
|
90
|
+
onLeaveNotDescendants: (e) =>
|
91
|
+
$zone.toggleClass(dnd.options.classes.dropActive, false)
|
92
|
+
|
93
|
+
onDrop: (e) =>
|
94
|
+
$zone.toggleClass(dnd.options.classes.dropActive, false)
|
95
|
+
|
96
|
+
dnd.handleDataTransfer(e.originalEvent.dataTransfer)
|
97
|
+
}, dnd.options)
|
98
|
+
|
99
|
+
$zone.data('dropZone', new AnjLab.Uploads.UploadDropZone(this, zoneOpts))
|
100
|
+
|
101
|
+
$(document).on 'drop', (e) -> e.preventDefault()
|
102
|
+
|
103
|
+
_dropOutsideDisabled = false
|
104
|
+
_collection = $()
|
105
|
+
|
106
|
+
class @AnjLab.Uploads.UploadDropZone
|
107
|
+
|
108
|
+
constructor: (element, options)->
|
109
|
+
@$element = $(element)
|
110
|
+
|
111
|
+
defaultOptions =
|
112
|
+
onEnter: (e) -> null
|
113
|
+
onLeave: (e) -> null
|
114
|
+
# is not fired when leaving element by hovering descendants
|
115
|
+
onLeaveNotDescendants: (e) -> null,
|
116
|
+
onDrop: (e) -> null
|
117
|
+
|
118
|
+
@options = $.extend(true, {}, defaultOptions, options)
|
119
|
+
|
120
|
+
@disableDropOutside()
|
121
|
+
@attachEvents()
|
122
|
+
|
123
|
+
docDragEnter:(e)->
|
124
|
+
if (_collection.size() == 0)
|
125
|
+
$('body').toggleClass(@options.classes.bodyDragover, true)
|
126
|
+
_collection = _collection.add(e.target)
|
127
|
+
|
128
|
+
docDragLeave:(e)->
|
129
|
+
#timeout is needed because Firefox 3.6 fires the dragleave event on
|
130
|
+
# the previous element before firing dragenter on the next one
|
131
|
+
setTimeout( =>
|
132
|
+
# remove 'left' element from the collection
|
133
|
+
_collection = _collection.not(e.target)
|
134
|
+
|
135
|
+
# if collection is empty we have left the original element
|
136
|
+
# (dragleave has fired on all 'entered' elements)
|
137
|
+
if _collection.size() == 0
|
138
|
+
$('body').toggleClass(@options.classes.bodyDragover, false)
|
139
|
+
, 1)
|
140
|
+
|
141
|
+
disableDropOutside: (e) ->
|
142
|
+
# run only once for all instances
|
143
|
+
if !_dropOutsideDisabled
|
144
|
+
|
145
|
+
$(document).on 'dragover', (e)->
|
146
|
+
if e.originalEvent.dataTransfer
|
147
|
+
e.originalEvent.dataTransfer.dropEffect = 'none'
|
148
|
+
e.preventDefault()
|
149
|
+
false
|
150
|
+
|
151
|
+
$(document).on 'dragenter', (e) => @docDragEnter(e)
|
152
|
+
|
153
|
+
$(document).on 'dragleave drop', (e)=> @docDragLeave(e)
|
154
|
+
|
155
|
+
_dropOutsideDisabled = true
|
156
|
+
|
157
|
+
isValidFileDrag: (e)->
|
158
|
+
# e.dataTransfer currently causing IE errors
|
159
|
+
# IE9 does NOT support file API, so drag-and-drop is not possible
|
160
|
+
return false if utils.ie() && !utils.ie10()
|
161
|
+
|
162
|
+
dt = e.originalEvent.dataTransfer
|
163
|
+
# do not check dt.types.contains in webkit, because it crashes safari 4
|
164
|
+
isSafari = utils.safari()
|
165
|
+
|
166
|
+
# dt.effectAllowed is none in Safari 5
|
167
|
+
# dt.types.contains check is for firefox
|
168
|
+
effectTest = if utils.ie10() then true else dt.effectAllowed != 'none'
|
169
|
+
dt && effectTest && (dt.files || (!isSafari && dt.types.contains && dt.types.contains('Files')))
|
170
|
+
|
171
|
+
isOrSetDropDisabled: (isDisabled)->
|
172
|
+
@preventDrop = isDisabled if isDisabled != undefined
|
173
|
+
@preventDrop
|
174
|
+
|
175
|
+
attachEvents: ->
|
176
|
+
@$element.on 'dragover', (e) =>
|
177
|
+
return if !@isValidFileDrag(e)
|
178
|
+
|
179
|
+
effect = if utils.ie() ? null else e.originalEvent.dataTransfer.effectAllowed
|
180
|
+
if effect == 'move' || effect == 'linkMove'
|
181
|
+
e.originalEvent.dataTransfer.dropEffect = 'move' # for FF (only move allowed)
|
182
|
+
else
|
183
|
+
e.originalEvent.dataTransfer.dropEffect = 'copy' # for Chrome
|
184
|
+
|
185
|
+
e.stopPropagation()
|
186
|
+
e.preventDefault()
|
187
|
+
false
|
188
|
+
|
189
|
+
@$element.on 'dragenter', (e) =>
|
190
|
+
@docDragEnter(e)
|
191
|
+
if !@isOrSetDropDisabled()
|
192
|
+
return if !@isValidFileDrag(e)
|
193
|
+
@options.onEnter(e)
|
194
|
+
|
195
|
+
@$element.on 'dragleave', (e) =>
|
196
|
+
@docDragLeave(e)
|
197
|
+
return if !@isValidFileDrag(e)
|
198
|
+
|
199
|
+
@options.onLeave(e)
|
200
|
+
|
201
|
+
relatedTarget = document.elementFromPoint(e.clientX, e.clientY)
|
202
|
+
# do not fire when moving a mouse over a descendant
|
203
|
+
return if $.contains(this, relatedTarget)
|
204
|
+
|
205
|
+
@options.onLeaveNotDescendants(e)
|
206
|
+
|
207
|
+
@$element.on 'drop', (e) =>
|
208
|
+
@docDragLeave(e)
|
209
|
+
if !@isOrSetDropDisabled()
|
210
|
+
return if !@isValidFileDrag(e)
|
211
|
+
e.preventDefault()
|
212
|
+
@options.onDrop(e)
|
213
|
+
false
|
214
|
+
|
215
|
+
dropDisabled: (isDisabled) -> @isOrSetDropDisabled(isDisabled)
|
@@ -0,0 +1,101 @@
|
|
1
|
+
utils = @AnjLab.Uploads.Utils
|
2
|
+
|
3
|
+
class @AnjLab.Uploads.UploadHandler
|
4
|
+
constructor: (options)->
|
5
|
+
@options = options
|
6
|
+
@queue = []
|
7
|
+
@log = options.log
|
8
|
+
|
9
|
+
@create: (options) ->
|
10
|
+
opts = $.extend(true, {}, $.fn.uploadHandlerDefaults, options)
|
11
|
+
if utils.isXhrUploadSupported()
|
12
|
+
new AnjLab.Uploads.UploadHandlerXhr(opts)
|
13
|
+
else
|
14
|
+
new AnjLab.Uploads.UploadHandlerForm(opts)
|
15
|
+
|
16
|
+
# Removes element from queue, starts upload of next
|
17
|
+
dequeue: (id) ->
|
18
|
+
i = $.inArray(id, @queue)
|
19
|
+
max = @options.maxConnections
|
20
|
+
|
21
|
+
@queue.splice(i, 1)
|
22
|
+
|
23
|
+
if @queue.length >= max && i < max
|
24
|
+
nextId = @queue[max-1]
|
25
|
+
@uploadFile(nextId)
|
26
|
+
|
27
|
+
upload: (id) ->
|
28
|
+
# if too many active uploads, wait...
|
29
|
+
@uploadFile(id) if @queue.push(id) <= @options.maxConnections
|
30
|
+
|
31
|
+
retry: (id) ->
|
32
|
+
if $.inArray(id, @queue) >= 0
|
33
|
+
@uploadFile(id, true)
|
34
|
+
else
|
35
|
+
@upload(id)
|
36
|
+
|
37
|
+
cancel: (id) ->
|
38
|
+
@log("Cancelling #{id}")
|
39
|
+
@options.paramsStore.remove(id)
|
40
|
+
@cancelFile(id)
|
41
|
+
@dequeue(id)
|
42
|
+
|
43
|
+
cancelAll: ->
|
44
|
+
for fileId in @queue
|
45
|
+
@cancel(fileId)
|
46
|
+
|
47
|
+
@queue = []
|
48
|
+
|
49
|
+
getQueue: -> @queue
|
50
|
+
|
51
|
+
reset: ->
|
52
|
+
@log('Resetting upload handler')
|
53
|
+
@queue = []
|
54
|
+
# all handers should call super in their resets
|
55
|
+
|
56
|
+
uploadComplete: (id) -> @dequeue(id)
|
57
|
+
|
58
|
+
# protocol for handlers to implement
|
59
|
+
|
60
|
+
add: (file) -> @log('Not implemented')
|
61
|
+
uploadFile: (id, m) -> @log('Not implemented')
|
62
|
+
cancelFile: (id) -> @log('Not implemented')
|
63
|
+
getName: (id) -> @log('Not implemented')
|
64
|
+
getUuid: (id) -> @log('Not implemented')
|
65
|
+
isValid: (id) -> @log('Not implemented')
|
66
|
+
getSize: (id) -> null
|
67
|
+
getFile: (id) -> null
|
68
|
+
getResumableFilesData: -> []
|
69
|
+
|
70
|
+
$.fn.uploadHandlerDefaults =
|
71
|
+
debug: false
|
72
|
+
forceMultipart: true
|
73
|
+
paramsStore: {}
|
74
|
+
endpointStore: {}
|
75
|
+
maxConnections: 3 # maximum number of concurrent uploads
|
76
|
+
uuidParamName: 'qquuid'
|
77
|
+
totalFileSizeParamName: 'qqtotalfilesize'
|
78
|
+
chunking:
|
79
|
+
enabled: false
|
80
|
+
partSize: 2000000 # bytes
|
81
|
+
paramNames:
|
82
|
+
partIndex: 'qqpartindex'
|
83
|
+
partByteOffset: 'qqpartbyteoffset'
|
84
|
+
chunkSize: 'qqchunksize'
|
85
|
+
totalParts: 'qqtotalparts'
|
86
|
+
filename: 'qqfilename'
|
87
|
+
resume:
|
88
|
+
enabled: false
|
89
|
+
id: null
|
90
|
+
cookiesExpireIn: 7 #days
|
91
|
+
paramNames:
|
92
|
+
resuming: "qqresume"
|
93
|
+
|
94
|
+
log: (str, level) -> null
|
95
|
+
onProgress: (id, fileName, loaded, total) -> null
|
96
|
+
onComplete: (id, fileName, response, xhr) -> null
|
97
|
+
onCancel: (id, fileName) -> null
|
98
|
+
onUpload: (id, fileName) -> null
|
99
|
+
onUploadChunk: (id, fileName, chunkData) -> null
|
100
|
+
onAutoRetry: (id, fileName, response, xhr) -> null
|
101
|
+
onResume: (id, fileName, chunkData) -> null
|