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.
Files changed (77) hide show
  1. data/.gitignore +8 -0
  2. data/.rspec +2 -0
  3. data/.rvmrc +48 -0
  4. data/Gemfile +7 -0
  5. data/README.md +17 -0
  6. data/Rakefile +31 -0
  7. data/app/.DS_Store +0 -0
  8. data/app/assets/.DS_Store +0 -0
  9. data/app/assets/javascripts/.DS_Store +0 -0
  10. data/app/assets/javascripts/cropped_paperclip/filedrop.js.coffee +254 -0
  11. data/app/assets/javascripts/cropped_paperclip/upload_crop_scale.js.coffee +319 -0
  12. data/app/assets/javascripts/es5-shim.js +1105 -0
  13. data/app/assets/javascripts/uploader.js.coffee +3 -0
  14. data/app/controllers/cropped_paperclip/application_controller.rb +4 -0
  15. data/app/controllers/cropped_paperclip/uploads_controller.rb +40 -0
  16. data/app/helpers/cropped_paperclip/application_helper.rb +4 -0
  17. data/app/models/upload.rb +119 -0
  18. data/config/routes.rb +3 -0
  19. data/cropped_paperclip.gemspec +32 -0
  20. data/db/migrate/20120510103921_uploads.rb +11 -0
  21. data/init.rb +4 -0
  22. data/lib/cropped_paperclip.rb +170 -0
  23. data/lib/cropped_paperclip/engine.rb +7 -0
  24. data/lib/cropped_paperclip/glue.rb +20 -0
  25. data/lib/cropped_paperclip/schema.rb +35 -0
  26. data/lib/cropped_paperclip/version.rb +3 -0
  27. data/lib/paperclip/geometry_transformation.rb +80 -0
  28. data/lib/paperclip/validators/attachment_height_validator.rb +89 -0
  29. data/lib/paperclip/validators/attachment_width_validator.rb +89 -0
  30. data/lib/paperclip_processors/offset_thumbnail.rb +86 -0
  31. data/lib/tasks/cropped_paperclip_tasks.rake +4 -0
  32. data/script/rails +8 -0
  33. data/spec/.DS_Store +0 -0
  34. data/spec/acceptance/acceptance_helper.rb +2 -0
  35. data/spec/controllers/uploads_controller_spec.rb +5 -0
  36. data/spec/dummy/README.rdoc +261 -0
  37. data/spec/dummy/Rakefile +7 -0
  38. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  39. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  40. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  41. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  42. data/spec/dummy/app/mailers/.gitkeep +0 -0
  43. data/spec/dummy/app/models/.gitkeep +0 -0
  44. data/spec/dummy/app/models/thing.rb +15 -0
  45. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  46. data/spec/dummy/config.ru +4 -0
  47. data/spec/dummy/config/application.rb +56 -0
  48. data/spec/dummy/config/boot.rb +10 -0
  49. data/spec/dummy/config/database.yml +25 -0
  50. data/spec/dummy/config/environment.rb +5 -0
  51. data/spec/dummy/config/environments/development.rb +37 -0
  52. data/spec/dummy/config/environments/production.rb +67 -0
  53. data/spec/dummy/config/environments/test.rb +37 -0
  54. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  55. data/spec/dummy/config/initializers/inflections.rb +15 -0
  56. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  57. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  58. data/spec/dummy/config/initializers/session_store.rb +8 -0
  59. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  60. data/spec/dummy/config/locales/en.yml +5 -0
  61. data/spec/dummy/config/routes.rb +3 -0
  62. data/spec/dummy/db/migrate/20120510104910_things.rb +8 -0
  63. data/spec/dummy/db/schema.rb +51 -0
  64. data/spec/dummy/lib/assets/.gitkeep +0 -0
  65. data/spec/dummy/log/.gitkeep +0 -0
  66. data/spec/dummy/public/404.html +26 -0
  67. data/spec/dummy/public/422.html +26 -0
  68. data/spec/dummy/public/500.html +25 -0
  69. data/spec/dummy/public/favicon.ico +0 -0
  70. data/spec/dummy/script/rails +6 -0
  71. data/spec/fixtures/images/.DS_Store +0 -0
  72. data/spec/fixtures/images/icon.png +0 -0
  73. data/spec/fixtures/images/test.jpg +0 -0
  74. data/spec/models/thing_spec.rb +6 -0
  75. data/spec/models/upload_spec.rb +13 -0
  76. data/spec/spec_helper.rb +18 -0
  77. metadata +309 -0
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ spec/dummy/db/*.sqlite3
6
+ spec/dummy/log/*.log
7
+ spec/dummy/tmp/
8
+ .DS_Store
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
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
@@ -0,0 +1,7 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in cropped_paperclip.gemspec
4
+ gemspec
5
+
6
+ # jquery-rails is used by the dummy application
7
+ gem "jquery-rails"
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
+