uploads 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|