cropped_paperclip 0.1.0
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/.gitignore +8 -0
- data/.rspec +2 -0
- data/.rvmrc +48 -0
- data/Gemfile +7 -0
- data/README.md +17 -0
- data/Rakefile +31 -0
- data/app/.DS_Store +0 -0
- data/app/assets/.DS_Store +0 -0
- data/app/assets/javascripts/.DS_Store +0 -0
- data/app/assets/javascripts/cropped_paperclip/filedrop.js.coffee +254 -0
- data/app/assets/javascripts/cropped_paperclip/upload_crop_scale.js.coffee +319 -0
- data/app/assets/javascripts/es5-shim.js +1105 -0
- data/app/assets/javascripts/uploader.js.coffee +3 -0
- data/app/controllers/cropped_paperclip/application_controller.rb +4 -0
- data/app/controllers/cropped_paperclip/uploads_controller.rb +40 -0
- data/app/helpers/cropped_paperclip/application_helper.rb +4 -0
- data/app/models/upload.rb +119 -0
- data/config/routes.rb +3 -0
- data/cropped_paperclip.gemspec +32 -0
- data/db/migrate/20120510103921_uploads.rb +11 -0
- data/init.rb +4 -0
- data/lib/cropped_paperclip.rb +170 -0
- data/lib/cropped_paperclip/engine.rb +7 -0
- data/lib/cropped_paperclip/glue.rb +20 -0
- data/lib/cropped_paperclip/schema.rb +35 -0
- data/lib/cropped_paperclip/version.rb +3 -0
- data/lib/paperclip/geometry_transformation.rb +80 -0
- data/lib/paperclip/validators/attachment_height_validator.rb +89 -0
- data/lib/paperclip/validators/attachment_width_validator.rb +89 -0
- data/lib/paperclip_processors/offset_thumbnail.rb +86 -0
- data/lib/tasks/cropped_paperclip_tasks.rake +4 -0
- data/script/rails +8 -0
- data/spec/.DS_Store +0 -0
- data/spec/acceptance/acceptance_helper.rb +2 -0
- data/spec/controllers/uploads_controller_spec.rb +5 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +15 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.gitkeep +0 -0
- data/spec/dummy/app/models/.gitkeep +0 -0
- data/spec/dummy/app/models/thing.rb +15 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +56 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +67 -0
- data/spec/dummy/config/environments/test.rb +37 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +3 -0
- data/spec/dummy/db/migrate/20120510104910_things.rb +8 -0
- data/spec/dummy/db/schema.rb +51 -0
- data/spec/dummy/lib/assets/.gitkeep +0 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/fixtures/images/.DS_Store +0 -0
- data/spec/fixtures/images/icon.png +0 -0
- data/spec/fixtures/images/test.jpg +0 -0
- data/spec/models/thing_spec.rb +6 -0
- data/spec/models/upload_spec.rb +13 -0
- data/spec/spec_helper.rb +18 -0
- metadata +309 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
# This is an RVM Project .rvmrc file, used to automatically load the ruby
|
4
|
+
# development environment upon cd'ing into the directory
|
5
|
+
|
6
|
+
# First we specify our desired <ruby>[@<gemset>], the @gemset name is optional,
|
7
|
+
# Only full ruby name is supported here, for short names use:
|
8
|
+
# echo "rvm use 1.9.3" > .rvmrc
|
9
|
+
environment_id="ruby-1.9.3-p194"
|
10
|
+
|
11
|
+
# Uncomment the following lines if you want to verify rvm version per project
|
12
|
+
# rvmrc_rvm_version="1.13.8 (master)" # 1.10.1 seams as a safe start
|
13
|
+
# eval "$(echo ${rvm_version}.${rvmrc_rvm_version} | awk -F. '{print "[[ "$1*65536+$2*256+$3" -ge "$4*65536+$5*256+$6" ]]"}' )" || {
|
14
|
+
# echo "This .rvmrc file requires at least RVM ${rvmrc_rvm_version}, aborting loading."
|
15
|
+
# return 1
|
16
|
+
# }
|
17
|
+
|
18
|
+
# First we attempt to load the desired environment directly from the environment
|
19
|
+
# file. This is very fast and efficient compared to running through the entire
|
20
|
+
# CLI and selector. If you want feedback on which environment was used then
|
21
|
+
# insert the word 'use' after --create as this triggers verbose mode.
|
22
|
+
if [[ -d "${rvm_path:-$HOME/.rvm}/environments"
|
23
|
+
&& -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
|
24
|
+
then
|
25
|
+
\. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
|
26
|
+
[[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]] &&
|
27
|
+
\. "${rvm_path:-$HOME/.rvm}/hooks/after_use" || true
|
28
|
+
else
|
29
|
+
# If the environment file has not yet been created, use the RVM CLI to select.
|
30
|
+
rvm --create "$environment_id" || {
|
31
|
+
echo "Failed to create RVM environment '${environment_id}'."
|
32
|
+
return 1
|
33
|
+
}
|
34
|
+
fi
|
35
|
+
|
36
|
+
# If you use bundler, this might be useful to you:
|
37
|
+
# if [[ -s Gemfile ]] && {
|
38
|
+
# ! builtin command -v bundle >/dev/null ||
|
39
|
+
# builtin command -v bundle | GREP_OPTIONS= \grep $rvm_path/bin/bundle >/dev/null
|
40
|
+
# }
|
41
|
+
# then
|
42
|
+
# printf "%b" "The rubygem 'bundler' is not installed. Installing it now.\n"
|
43
|
+
# gem install bundler
|
44
|
+
# fi
|
45
|
+
# if [[ -s Gemfile ]] && builtin command -v bundle >/dev/null
|
46
|
+
# then
|
47
|
+
# bundle install | GREP_OPTIONS= \grep -vE '^Using|Your bundle is complete'
|
48
|
+
# fi
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# CroppedPaperclip
|
2
|
+
|
3
|
+
This is a rails-specific gem. It uses paperclip to provide a general-purpose upload-and-crop mechanism
|
4
|
+
in which a single upload object (with attached file) can be shared between any number of models, and
|
5
|
+
each model can have any number of upload relations. Each associate has its own cropping paramaters.
|
6
|
+
|
7
|
+
## Status
|
8
|
+
|
9
|
+
New and in progress. Extracted from working code but likely to have suffered in the process. Please file issues.
|
10
|
+
|
11
|
+
## Copyright
|
12
|
+
|
13
|
+
Developed by spanner for Socionical and released under the MIT license.
|
14
|
+
|
15
|
+
## Bugs and issues
|
16
|
+
|
17
|
+
Very likely. Please file github issues or write to will at spanner dot org.
|
data/Rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
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 = 'CroppedPaperclip'
|
18
|
+
rdoc.options << '--line-numbers'
|
19
|
+
rdoc.rdoc_files.include('README.rdoc')
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
21
|
+
end
|
22
|
+
|
23
|
+
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
24
|
+
load 'rails/tasks/engine.rake'
|
25
|
+
|
26
|
+
Bundler::GemHelper.install_tasks
|
27
|
+
|
28
|
+
require 'rspec/core/rake_task'
|
29
|
+
|
30
|
+
RSpec::Core::RakeTask.new(:spec)
|
31
|
+
task :default => :spec
|
data/app/.DS_Store
ADDED
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,254 @@
|
|
1
|
+
#
|
2
|
+
#
|
3
|
+
# This is a somewhat hacked up version of filedrop, by Weixi Yen:
|
4
|
+
#
|
5
|
+
# Email: [Firstname][Lastname]@gmail.com
|
6
|
+
#
|
7
|
+
# Copyright (c) 2010 Resopollution
|
8
|
+
#
|
9
|
+
# Licensed under the MIT license:
|
10
|
+
# http://www.opensource.org/licenses/mit-license.php
|
11
|
+
#
|
12
|
+
# Project home:
|
13
|
+
# http://www.github.com/weixiyen/jquery-filedrop
|
14
|
+
#
|
15
|
+
# Version: 0.1.0
|
16
|
+
#
|
17
|
+
# Features:
|
18
|
+
# Allows sending of extra parameters with file.
|
19
|
+
# Works with Firefox 3.6+
|
20
|
+
# Future-compliant with HTML5 spec (will work with Webkit browsers and IE9)
|
21
|
+
# Usage:
|
22
|
+
# See README at project homepage
|
23
|
+
|
24
|
+
Modernizr.addTest 'filereader', ->
|
25
|
+
!!(window.File && window.FileList && window.FileReader)
|
26
|
+
|
27
|
+
jQuery ($) ->
|
28
|
+
jQuery.event.props.push "dataTransfer"
|
29
|
+
|
30
|
+
opts = {}
|
31
|
+
errors = [ "BrowserNotSupported", "TooManyFiles", "FileTooLarge" ]
|
32
|
+
doc_leave_timer = undefined
|
33
|
+
stop_loop = false
|
34
|
+
files_count = 0
|
35
|
+
files = undefined
|
36
|
+
|
37
|
+
drop = (e) ->
|
38
|
+
e.preventDefault()
|
39
|
+
opts.drop e
|
40
|
+
files = e.dataTransfer.files
|
41
|
+
unless Modernizr.filereader && files?
|
42
|
+
opts.error(errors[0])
|
43
|
+
return false
|
44
|
+
files_count = files.length
|
45
|
+
upload()
|
46
|
+
false
|
47
|
+
|
48
|
+
pick = (e, filefield) ->
|
49
|
+
e.preventDefault()
|
50
|
+
files = filefield.files
|
51
|
+
unless Modernizr.filereader && files?
|
52
|
+
opts.error(errors[0])
|
53
|
+
return false
|
54
|
+
files_count = files.length
|
55
|
+
upload()
|
56
|
+
false
|
57
|
+
|
58
|
+
getBuilder = (filename, filedata, boundary) ->
|
59
|
+
dashdash = "--"
|
60
|
+
crlf = "\r\n"
|
61
|
+
builder = ""
|
62
|
+
$.each opts.data, (i, val) ->
|
63
|
+
val = val() if typeof val is "function"
|
64
|
+
builder += dashdash
|
65
|
+
builder += boundary
|
66
|
+
builder += crlf
|
67
|
+
builder += "Content-Disposition: form-data; name=\"" + i + "\""
|
68
|
+
builder += crlf
|
69
|
+
builder += crlf
|
70
|
+
builder += val
|
71
|
+
builder += crlf
|
72
|
+
|
73
|
+
builder += dashdash
|
74
|
+
builder += boundary
|
75
|
+
builder += crlf
|
76
|
+
builder += "Content-Disposition: form-data; name=\"" + opts.paramname + "\""
|
77
|
+
builder += "; filename=\"" + filename + "\""
|
78
|
+
builder += crlf
|
79
|
+
builder += "Content-Type: image/jpeg"
|
80
|
+
builder += crlf
|
81
|
+
builder += crlf
|
82
|
+
builder += filedata
|
83
|
+
builder += crlf
|
84
|
+
builder += dashdash
|
85
|
+
builder += boundary
|
86
|
+
builder += dashdash
|
87
|
+
builder += crlf
|
88
|
+
builder
|
89
|
+
|
90
|
+
progress = (e) ->
|
91
|
+
if e.lengthComputable
|
92
|
+
percentage = Math.round((e.loaded * 100) / e.total)
|
93
|
+
unless @currentProgress is percentage
|
94
|
+
@currentProgress = percentage
|
95
|
+
opts.progressUpdated @index, @file, @currentProgress
|
96
|
+
elapsed = new Date().getTime()
|
97
|
+
diffTime = elapsed - @currentStart
|
98
|
+
if diffTime >= opts.refresh
|
99
|
+
diffData = e.loaded - @startData
|
100
|
+
speed = diffData / diffTime
|
101
|
+
opts.speedUpdated @index, @file, speed
|
102
|
+
@startData = e.loaded
|
103
|
+
@currentStart = elapsed
|
104
|
+
|
105
|
+
upload = ->
|
106
|
+
send = (e) ->
|
107
|
+
e.target.index = getIndexBySize(e.total) if e.target.index is `undefined`
|
108
|
+
xhr = new XMLHttpRequest()
|
109
|
+
ul = xhr.upload
|
110
|
+
file = files[e.target.index]
|
111
|
+
index = e.target.index
|
112
|
+
start_time = new Date().getTime()
|
113
|
+
boundary = "------multipartformboundary" + (new Date).getTime()
|
114
|
+
builder = undefined
|
115
|
+
newName = rename(file.name)
|
116
|
+
if typeof newName is "string"
|
117
|
+
builder = getBuilder(newName, e.target.result, boundary)
|
118
|
+
else
|
119
|
+
builder = getBuilder(file.name, e.target.result, boundary)
|
120
|
+
ul.index = index
|
121
|
+
ul.file = file
|
122
|
+
ul.downloadStartTime = start_time
|
123
|
+
ul.currentStart = start_time
|
124
|
+
ul.currentProgress = 0
|
125
|
+
ul.startData = 0
|
126
|
+
ul.addEventListener "progress", progress, false
|
127
|
+
xhr.open "POST", opts.url, true
|
128
|
+
xhr.setRequestHeader "content-type", "multipart/form-data; boundary=" + boundary
|
129
|
+
xhr.sendAsBinary builder
|
130
|
+
opts.uploadStarted index, file, files_count
|
131
|
+
xhr.onload = ->
|
132
|
+
if xhr.responseText
|
133
|
+
now = new Date().getTime()
|
134
|
+
timeDiff = now - start_time
|
135
|
+
result = opts.uploadFinished(index, file, xhr.responseText, timeDiff)
|
136
|
+
filesDone++
|
137
|
+
afterAll() if filesDone is files_count - filesRejected
|
138
|
+
stop_loop = true if result is false
|
139
|
+
|
140
|
+
stop_loop = false
|
141
|
+
unless files
|
142
|
+
opts.error errors[0]
|
143
|
+
return false
|
144
|
+
filesDone = 0
|
145
|
+
filesRejected = 0
|
146
|
+
if files_count > opts.maxfiles
|
147
|
+
opts.error errors[1]
|
148
|
+
return false
|
149
|
+
i = 0
|
150
|
+
|
151
|
+
while i < files_count
|
152
|
+
return false if stop_loop
|
153
|
+
try
|
154
|
+
unless beforeEach(files[i]) is false
|
155
|
+
return if i is files_count
|
156
|
+
reader = new FileReader()
|
157
|
+
max_file_size = 1048576 * opts.maxfilesize
|
158
|
+
reader.index = i
|
159
|
+
if files[i].size > max_file_size
|
160
|
+
opts.error errors[2], files[i], i
|
161
|
+
filesRejected++
|
162
|
+
continue
|
163
|
+
reader.onloadend = send
|
164
|
+
reader.readAsBinaryString files[i]
|
165
|
+
else
|
166
|
+
filesRejected++
|
167
|
+
catch err
|
168
|
+
opts.error errors[0]
|
169
|
+
return false
|
170
|
+
i++
|
171
|
+
getIndexBySize = (size) ->
|
172
|
+
i = 0
|
173
|
+
|
174
|
+
while i < files_count
|
175
|
+
return i if files[i].size is size
|
176
|
+
i++
|
177
|
+
`undefined`
|
178
|
+
rename = (name) ->
|
179
|
+
opts.rename name
|
180
|
+
beforeEach = (file) ->
|
181
|
+
opts.beforeEach file
|
182
|
+
afterAll = ->
|
183
|
+
opts.afterAll()
|
184
|
+
dragEnter = (e) ->
|
185
|
+
clearTimeout doc_leave_timer
|
186
|
+
e.preventDefault()
|
187
|
+
opts.dragEnter e
|
188
|
+
dragOver = (e) ->
|
189
|
+
clearTimeout doc_leave_timer
|
190
|
+
e.preventDefault()
|
191
|
+
opts.docOver e
|
192
|
+
opts.dragOver e
|
193
|
+
dragLeave = (e) ->
|
194
|
+
clearTimeout doc_leave_timer
|
195
|
+
opts.dragLeave e
|
196
|
+
e.stopPropagation()
|
197
|
+
docDrop = (e) ->
|
198
|
+
e.preventDefault()
|
199
|
+
opts.docLeave e
|
200
|
+
false
|
201
|
+
docEnter = (e) ->
|
202
|
+
clearTimeout doc_leave_timer
|
203
|
+
e.preventDefault()
|
204
|
+
opts.docEnter e
|
205
|
+
false
|
206
|
+
docOver = (e) ->
|
207
|
+
clearTimeout doc_leave_timer
|
208
|
+
e.preventDefault()
|
209
|
+
opts.docOver e
|
210
|
+
false
|
211
|
+
docLeave = (e) ->
|
212
|
+
doc_leave_timer = setTimeout(->
|
213
|
+
opts.docLeave e
|
214
|
+
, 200)
|
215
|
+
empty = ->
|
216
|
+
|
217
|
+
default_opts =
|
218
|
+
url: ""
|
219
|
+
refresh: 1000
|
220
|
+
paramname: "userfile"
|
221
|
+
maxfiles: 25
|
222
|
+
maxfilesize: 1
|
223
|
+
data: {}
|
224
|
+
drop: empty
|
225
|
+
dragEnter: empty
|
226
|
+
dragOver: empty
|
227
|
+
dragLeave: empty
|
228
|
+
docEnter: empty
|
229
|
+
docOver: empty
|
230
|
+
docLeave: empty
|
231
|
+
beforeEach: empty
|
232
|
+
afterAll: empty
|
233
|
+
rename: empty
|
234
|
+
error: (err, file, i) ->
|
235
|
+
alert err
|
236
|
+
|
237
|
+
uploadStarted: empty
|
238
|
+
uploadFinished: empty
|
239
|
+
progressUpdated: empty
|
240
|
+
speedUpdated: empty
|
241
|
+
|
242
|
+
$.fn.filedrop = (options) ->
|
243
|
+
opts = $.extend({}, default_opts, options)
|
244
|
+
@bind("drop", drop).bind("pick", pick).bind("dragenter", dragEnter).bind("dragover", dragOver).bind "dragleave", dragLeave
|
245
|
+
$(document).bind("drop", docDrop).bind("dragenter", docEnter).bind("dragover", docOver).bind "dragleave", docLeave
|
246
|
+
|
247
|
+
try
|
248
|
+
return if XMLHttpRequest::sendAsBinary
|
249
|
+
XMLHttpRequest::sendAsBinary = (datastr) ->
|
250
|
+
byteValue = (x) ->
|
251
|
+
x.charCodeAt(0) & 0xff
|
252
|
+
ords = Array::map.call(datastr, byteValue)
|
253
|
+
ui8a = new Uint8Array(ords)
|
254
|
+
@send ui8a.buffer
|
@@ -0,0 +1,319 @@
|
|
1
|
+
jQuery ($) ->
|
2
|
+
$.fn.uploader = (opts) ->
|
3
|
+
@each ->
|
4
|
+
options = $.extend {}, opts
|
5
|
+
dropbox = $(@)
|
6
|
+
csrf_token = dropbox.parents("form").find('input[name="authenticity_token"]').val()
|
7
|
+
filefield_selector = options.filefield ? 'input[type="file"]'
|
8
|
+
filefield = dropbox.find(filefield_selector)
|
9
|
+
url = options.url ? dropbox.attr("data-upload-path") ? dropbox.attr("rel")
|
10
|
+
paramname = options.paramname ? "upload[file]"
|
11
|
+
|
12
|
+
finisher = (i, file, response, time) ->
|
13
|
+
dropbox.find(".progress_holder").remove()
|
14
|
+
dropbox.find(".waiter").remove()
|
15
|
+
new Cropper(response, dropbox)
|
16
|
+
|
17
|
+
dropbox.filedrop
|
18
|
+
maxfiles: 1
|
19
|
+
maxfilesize: 10
|
20
|
+
url: url
|
21
|
+
paramname: paramname
|
22
|
+
data:
|
23
|
+
authenticity_token: csrf_token
|
24
|
+
|
25
|
+
error: (err, file) ->
|
26
|
+
switch err
|
27
|
+
when "BrowserNotSupported"
|
28
|
+
auth = $('input[name="authenticity_token"]').clone()
|
29
|
+
form = $('<form id="uform" method="post" enctype="multipart/form-data" />').append(auth)
|
30
|
+
iframe = $('<iframe id="uframe" name="uframe" />').appendTo($('body'))
|
31
|
+
newff = filefield.clone()
|
32
|
+
filefield.before(newff).attr("name", paramname)
|
33
|
+
form.append(filefield).appendTo("body").attr("action", url).attr("target", "uframe")
|
34
|
+
newff.change((e) ->
|
35
|
+
dropbox.trigger "pick", filefield[0]
|
36
|
+
)
|
37
|
+
filefield = newff
|
38
|
+
iframe.bind "load", () ->
|
39
|
+
response = iframe[0].contentWindow.document.body.innerHTML
|
40
|
+
if response and response isnt ""
|
41
|
+
finisher.call this, null, null, response, null
|
42
|
+
iframe.remove()
|
43
|
+
form.remove()
|
44
|
+
|
45
|
+
dropbox.find(".instructions").hide()
|
46
|
+
dropbox.find(".img").fadeTo('slow', 0.1)
|
47
|
+
dropbox.find(".waiter").show()
|
48
|
+
form.submit()
|
49
|
+
|
50
|
+
when "TooManyFiles"
|
51
|
+
alert "You can only upload 1 file."
|
52
|
+
|
53
|
+
when "FileTooLarge"
|
54
|
+
alert "#{file.name} is too large! Files up to 10MB are allowed"
|
55
|
+
|
56
|
+
else
|
57
|
+
alert "#{file.name} caused an unknown error: #{err}"
|
58
|
+
|
59
|
+
dragOver: ->
|
60
|
+
dropbox.addClass "hover"
|
61
|
+
|
62
|
+
dragLeave: ->
|
63
|
+
dropbox.removeClass "hover"
|
64
|
+
|
65
|
+
beforeEach: (file) ->
|
66
|
+
dropbox.removeClass "hover"
|
67
|
+
unless file.type.match(/^image\//)
|
68
|
+
alert "Sorry: only image files are allowed!"
|
69
|
+
false
|
70
|
+
|
71
|
+
afterAll: ->
|
72
|
+
filefield.val ""
|
73
|
+
|
74
|
+
uploadStarted: (i, file, len) ->
|
75
|
+
dropbox.find("img").fadeTo "fast", 0.5
|
76
|
+
dropbox.find("p.instructions").hide()
|
77
|
+
dropbox.append "<div class=\"progress_holder\"><div class=\"progress\"></div><div class=\"commentary\">0% uploaded</div></div>"
|
78
|
+
|
79
|
+
progressUpdated: (i, file, progress) ->
|
80
|
+
dropbox.find("div.progress").width progress + "%"
|
81
|
+
dropbox.find("div.commentary").text progress + "% uploaded"
|
82
|
+
|
83
|
+
uploadFinished: finisher
|
84
|
+
|
85
|
+
dropbox.find("a.picker").picker()
|
86
|
+
filefield.change (e) ->
|
87
|
+
dropbox.trigger "pick", filefield[0]
|
88
|
+
@
|
89
|
+
|
90
|
+
$.fn.recropper = ->
|
91
|
+
dropbox = $("div.dropbox")
|
92
|
+
@click (e) ->
|
93
|
+
e.preventDefault()
|
94
|
+
dropbox.find("div.waiter").show()
|
95
|
+
$.get $(this).attr("href"), ((response) ->
|
96
|
+
new Cropper(response, dropbox)
|
97
|
+
), "html"
|
98
|
+
@
|
99
|
+
|
100
|
+
$.fn.picker = ->
|
101
|
+
@click (e) ->
|
102
|
+
e.preventDefault()
|
103
|
+
e.stopPropagation()
|
104
|
+
$("#file_upload").trigger('click')
|
105
|
+
@
|
106
|
+
|
107
|
+
$.fn.click_proxy = (target_selector) ->
|
108
|
+
this.bind "click", (e) ->
|
109
|
+
e.preventDefault()
|
110
|
+
$(target_selector).click()
|
111
|
+
|
112
|
+
class Cropper
|
113
|
+
constructor: (response, container) ->
|
114
|
+
@element = $(response)
|
115
|
+
@container = container
|
116
|
+
@preview = @element.find("div.preview")
|
117
|
+
@fields = @element.find("fieldset.crop")
|
118
|
+
@overflow = $("<div class=\"overflow\">").append(@preview.find("img").clone())
|
119
|
+
@controls = @container.find(".controls")
|
120
|
+
@container.find("div.preview").remove()
|
121
|
+
@container.find("div.img").after(@preview)
|
122
|
+
@container.find("div.waiter").hide()
|
123
|
+
@container.append @fields
|
124
|
+
@container.before @overflow
|
125
|
+
|
126
|
+
@top = @preview.position().top
|
127
|
+
@left = @preview.position().left
|
128
|
+
@lastX = 0
|
129
|
+
@lastY = 0
|
130
|
+
|
131
|
+
range = @fields.find("input[type=\"range\"]")
|
132
|
+
@scaler = new Scaler range,
|
133
|
+
drag: @showOverflow
|
134
|
+
move: @resize
|
135
|
+
drop: @hideOverflow
|
136
|
+
|
137
|
+
@controls.find(".cancel a").bind "click", @cancel
|
138
|
+
@preview.bind "mousedown", @drag
|
139
|
+
|
140
|
+
@recalculateLimits()
|
141
|
+
@setOverflow()
|
142
|
+
@setControls()
|
143
|
+
|
144
|
+
drag: (e) =>
|
145
|
+
e.preventDefault()
|
146
|
+
$(document).bind "mousemove", @move
|
147
|
+
$(document).bind "mouseup", @drop
|
148
|
+
@lastY = e.pageY
|
149
|
+
@lastX = e.pageX
|
150
|
+
@showOverflow()
|
151
|
+
|
152
|
+
move: (e) =>
|
153
|
+
e.preventDefault()
|
154
|
+
@moveTop e.pageY - @lastY
|
155
|
+
@moveLeft e.pageX - @lastX
|
156
|
+
@lastY = e.pageY
|
157
|
+
@lastX = e.pageX
|
158
|
+
@setOverflow()
|
159
|
+
|
160
|
+
resize: (w) =>
|
161
|
+
h = Math.round(w * @aspect)
|
162
|
+
deltaT = Math.round((w - @preview.width()) / 2)
|
163
|
+
deltaL = Math.round((h - @preview.height()) / 2)
|
164
|
+
@preview.css
|
165
|
+
width: w
|
166
|
+
height: h
|
167
|
+
@fields.find("input.sh").val h
|
168
|
+
@recalculateLimits()
|
169
|
+
@moveTop(-deltaT)
|
170
|
+
@moveLeft(-deltaL)
|
171
|
+
@setOverflow()
|
172
|
+
|
173
|
+
recalculateLimits: (argument) =>
|
174
|
+
@toplimit = @container.height() - @preview.height()
|
175
|
+
@leftlimit = @container.width() - @preview.width()
|
176
|
+
@aspect = @preview.height() / @preview.width()
|
177
|
+
|
178
|
+
moveTop: (y) =>
|
179
|
+
@top = @top + y
|
180
|
+
@top = 0 if @top > 0
|
181
|
+
@top = @toplimit if @top < @toplimit
|
182
|
+
@preview.css "top", @top
|
183
|
+
@fields.find("input.ot").val @top
|
184
|
+
|
185
|
+
moveLeft: (x) =>
|
186
|
+
@left = @left + x
|
187
|
+
@left = 0 if @left > 0
|
188
|
+
@left = @leftlimit if @left < @leftlimit
|
189
|
+
@preview.css "left", @left
|
190
|
+
@fields.find("input.ol").val @left
|
191
|
+
|
192
|
+
drop: (e) =>
|
193
|
+
$(document).unbind "mousemove", @move
|
194
|
+
$(document).unbind "mouseup", @drop
|
195
|
+
@move e
|
196
|
+
@hideOverflow()
|
197
|
+
|
198
|
+
showOverflow: =>
|
199
|
+
@overflow.fadeTo('normal', 0.3)
|
200
|
+
|
201
|
+
hideOverflow: =>
|
202
|
+
@overflow.fadeOut('normal')
|
203
|
+
|
204
|
+
setOverflow: (argument) =>
|
205
|
+
@overflow.css
|
206
|
+
width: @preview.width()
|
207
|
+
height: @preview.height()
|
208
|
+
@overflow.offset @preview.offset()
|
209
|
+
|
210
|
+
cancel: (e) =>
|
211
|
+
e.preventDefault()
|
212
|
+
@preview.remove()
|
213
|
+
@overflow.remove()
|
214
|
+
@scaler.remove()
|
215
|
+
@fields.remove()
|
216
|
+
@resetControls()
|
217
|
+
@container.find("img").fadeIn "slow"
|
218
|
+
@container.find("p.instructions").show()
|
219
|
+
|
220
|
+
complete: (e) =>
|
221
|
+
e.preventDefault()
|
222
|
+
@scaler.hide()
|
223
|
+
@hideOverflow()
|
224
|
+
@preview.unbind "mousedown", @drag
|
225
|
+
@preview.css "cursor", 'auto'
|
226
|
+
@container.find(".range_marker").hide()
|
227
|
+
@resetControls()
|
228
|
+
@controls.find(".recrop").removeClass('unavailable').unbind('click').bind "click", @resume
|
229
|
+
|
230
|
+
resume: (e) =>
|
231
|
+
e.preventDefault()
|
232
|
+
@scaler.show()
|
233
|
+
@showOverflow()
|
234
|
+
@preview.bind "mousedown", @drag
|
235
|
+
@preview.css "cursor", 'move'
|
236
|
+
@container.find(".range_marker").show()
|
237
|
+
@setControls()
|
238
|
+
|
239
|
+
setControls: =>
|
240
|
+
@controls.show()
|
241
|
+
@controls.find(".edit").hide()
|
242
|
+
@controls.find(".cancel").show()
|
243
|
+
@controls.find("a.picker").addClass("unavailable").unbind "click"
|
244
|
+
@controls.find(".save a").removeClass("unavailable").bind "click", @complete
|
245
|
+
|
246
|
+
resetControls: =>
|
247
|
+
@controls.find(".cancel").hide()
|
248
|
+
@controls.find(".edit").show()
|
249
|
+
@controls.find("a.picker").removeClass("unavailable").picker()
|
250
|
+
@controls.find(".save a").addClass("unavailable").unbind("click")
|
251
|
+
|
252
|
+
|
253
|
+
class Scaler
|
254
|
+
constructor: (range, callbacks) ->
|
255
|
+
@callbacks = $.extend {}, callbacks
|
256
|
+
@input = $(range)
|
257
|
+
@pos = 0
|
258
|
+
@value = @input.val()
|
259
|
+
@max = parseInt(@input.attr("max"), 10)
|
260
|
+
@min = parseInt(@input.attr("min"), 10)
|
261
|
+
@slider = $("<span class=\"slider\"><span class=\"scale\"><span class=\"marker\"></span></span></span>")
|
262
|
+
@scale = @slider.find(".scale")
|
263
|
+
@scale_width = 150
|
264
|
+
|
265
|
+
@marker = @slider.find(".marker")
|
266
|
+
@lastX = 0
|
267
|
+
|
268
|
+
@reposition()
|
269
|
+
@marker.bind("mousedown", @drag)
|
270
|
+
@input.before(@slider).hide()
|
271
|
+
|
272
|
+
drag: (e) =>
|
273
|
+
e.preventDefault()
|
274
|
+
@lastX = e.pageX
|
275
|
+
$(document).bind "mousemove", @move
|
276
|
+
$(document).bind "mouseup", @drop
|
277
|
+
@callbacks.drag?.call @, @value
|
278
|
+
|
279
|
+
move: (e) =>
|
280
|
+
deltaX = e.pageX - @lastX
|
281
|
+
@.pos = @pos + e.pageX - @lastX
|
282
|
+
@.pos = 0 if @pos < 0
|
283
|
+
@.pos = 400 if @pos > 400
|
284
|
+
@.placeMarker(@pos)
|
285
|
+
@.recalculate()
|
286
|
+
@.lastX = e.pageX
|
287
|
+
@callbacks.move?.call @, @value
|
288
|
+
|
289
|
+
drop: (e) =>
|
290
|
+
@move e
|
291
|
+
$(document).unbind "mousemove", @move
|
292
|
+
$(document).unbind "mouseup", @drop
|
293
|
+
@callbacks.drop?.call @, @value
|
294
|
+
|
295
|
+
recalculate: =>
|
296
|
+
origin = @min
|
297
|
+
pixel_proportion = (@pos / @scale_width)
|
298
|
+
value_width = @max - @min
|
299
|
+
@value = Math.round(origin + (value_width * pixel_proportion))
|
300
|
+
@input.val(@value)
|
301
|
+
|
302
|
+
reposition: =>
|
303
|
+
origin = @min
|
304
|
+
value_proportion = (@value - origin) / (@max - origin)
|
305
|
+
@pos = Math.round(@scale_width * value_proportion)
|
306
|
+
@placeMarker @pos
|
307
|
+
|
308
|
+
placeMarker: (x) =>
|
309
|
+
@marker.css "left", x - 3
|
310
|
+
|
311
|
+
remove: =>
|
312
|
+
@slider.remove()
|
313
|
+
|
314
|
+
hide: =>
|
315
|
+
@slider.hide()
|
316
|
+
|
317
|
+
show: =>
|
318
|
+
@slider.show()
|
319
|
+
|