jack_up 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/LICENSE +20 -0
- data/README.md +183 -0
- data/Rakefile +40 -0
- data/lib/assets/javascripts/jack_up/base.coffee +1 -0
- data/lib/assets/javascripts/jack_up/drag_and_drop.coffee +24 -0
- data/lib/assets/javascripts/jack_up/events.coffee +11 -0
- data/lib/assets/javascripts/jack_up/file_uploader.coffee +54 -0
- data/lib/assets/javascripts/jack_up/jquery.coffee +7 -0
- data/lib/assets/javascripts/jack_up/processor.coffee +38 -0
- data/lib/assets/javascripts/jack_up.js +3 -0
- data/lib/jack_up/engine.rb +5 -0
- data/lib/jack_up/version.rb +3 -0
- data/lib/jack_up.rb +4 -0
- metadata +77 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2012 Josh Steiner, Josh Clayton, thoughtbot, inc.
|
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.md
ADDED
@@ -0,0 +1,183 @@
|
|
1
|
+
# JackUp
|
2
|
+
|
3
|
+
## Easy AJAX file uploading in Rails.
|
4
|
+
|
5
|
+
### Install
|
6
|
+
|
7
|
+
Modify your `Gemfile`:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'jack_up'
|
11
|
+
```
|
12
|
+
|
13
|
+
and run `bundle install` from your shell.
|
14
|
+
|
15
|
+
Modify your `application.js` manifest:
|
16
|
+
|
17
|
+
```javascript
|
18
|
+
//= require jquery
|
19
|
+
//= require underscore
|
20
|
+
//= require jack_up
|
21
|
+
//= require_tree .
|
22
|
+
```
|
23
|
+
|
24
|
+
### Requirements
|
25
|
+
|
26
|
+
Rails 3.1 (for the asset pipeline), CoffeeScript, and both jQuery and
|
27
|
+
Underscore.js included in your `application.js` manifest.
|
28
|
+
|
29
|
+
### Usage
|
30
|
+
|
31
|
+
Create a JackUp.Processor, binding to various events emitted.
|
32
|
+
|
33
|
+
```coffeescript
|
34
|
+
$ -> # when the document is ready
|
35
|
+
# create a new processor with the endpoint to where your assets are uploaded
|
36
|
+
jackUp = new JackUp.Processor(path: '/assets')
|
37
|
+
|
38
|
+
# called if upload is an image; returns an image jQuery object with src attribute assigned
|
39
|
+
jackUp.on 'upload:imageRenderReady', (e, options) ->
|
40
|
+
# assigns a data-attribute with the file guid for later referencing
|
41
|
+
# set the border color to red, denoting that the image is still being uploaded
|
42
|
+
options.image.attr("data-id", options.file.__guid__).css(border: "5px solid red")
|
43
|
+
$('.file-drop').append(options.image)
|
44
|
+
|
45
|
+
# upload has been sent to server; server will handle processing
|
46
|
+
jackUp.on "upload:sentToServer", (e, options) ->
|
47
|
+
# change the border color to yellow to signify successful upload (server is still processing)
|
48
|
+
$("img[data-id='#{options.file.__guid__}']").css borderColor: 'yellow'
|
49
|
+
|
50
|
+
# when server responds successfully
|
51
|
+
jackUp.on "upload:success", (e, options) ->
|
52
|
+
# server has completed processing the image and has returned a response
|
53
|
+
$("img[data-id='#{options.file.__guid__}']").css(borderColor: "green")
|
54
|
+
|
55
|
+
# when server returns a non-200 response
|
56
|
+
jackUp.on "upload:failure", (e, options) ->
|
57
|
+
# alert the file name
|
58
|
+
alert("'#{options.file.name}' upload failed; please retry")
|
59
|
+
# remove the image from the dom since the upload failed
|
60
|
+
$("img[data-id='#{options.file.__guid__}']").remove()
|
61
|
+
```
|
62
|
+
|
63
|
+
Once the processor is set up, wire up drag-and-drop support:
|
64
|
+
|
65
|
+
```coffeescript
|
66
|
+
$('.file-drop').jackUpDragAndDrop(jackUp)
|
67
|
+
```
|
68
|
+
|
69
|
+
If you just want to bind to a standard `<input type='file'>`:
|
70
|
+
|
71
|
+
```coffeescript
|
72
|
+
$('.standard-attachment').jackUpAjax(jackUp)
|
73
|
+
```
|
74
|
+
|
75
|
+
You can use both at the same time, referencing the same `JackUp.Processor`, in
|
76
|
+
order to provide both options to your users.
|
77
|
+
|
78
|
+
### Example Rails Setup
|
79
|
+
|
80
|
+
For instant file uploading:
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
# Gemfile
|
84
|
+
gem 'rails'
|
85
|
+
gem 'paperclip'
|
86
|
+
gem 'rack-raw-upload'
|
87
|
+
```
|
88
|
+
|
89
|
+
Using the `rack-raw-upload` gem allows for accessing the file posted to the
|
90
|
+
controller via `params[:file]`; this makes it incredibly easy to handle file
|
91
|
+
uploads.
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
# app/models/asset.rb
|
95
|
+
class Asset < ActiveRecord::Base
|
96
|
+
has_attached_file :photo
|
97
|
+
attr_accessible :photo
|
98
|
+
end
|
99
|
+
|
100
|
+
# app/controllers/assets_controller.rb
|
101
|
+
class AssetsController < ApplicationController
|
102
|
+
def create
|
103
|
+
@asset = Asset.new(photo: params[:file])
|
104
|
+
|
105
|
+
if @asset.save
|
106
|
+
render json: @asset
|
107
|
+
else
|
108
|
+
head :bad_request
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
```
|
113
|
+
|
114
|
+
This view code could be placed anywhere for immediate uploading:
|
115
|
+
|
116
|
+
```haml
|
117
|
+
.file-drop
|
118
|
+
%span{ 'data-placeholder' => 'Drop files here' } Drop files here
|
119
|
+
|
120
|
+
%input.standard-attachment{ name: 'standard_attachment', accept: 'image/*', type: :file, multiple: :multiple }
|
121
|
+
```
|
122
|
+
|
123
|
+
If attaching assets to a different model, additionally use:
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
# app/models/post.rb
|
127
|
+
class Post < ActiveRecord::Base
|
128
|
+
has_many :assets, dependent: :destroy
|
129
|
+
|
130
|
+
attr_accessible :asset_ids, :assets_attributes
|
131
|
+
accepts_nested_attributes_for :assets
|
132
|
+
end
|
133
|
+
|
134
|
+
# app/controllers/posts_controller.rb
|
135
|
+
class PostsController < ApplicationController
|
136
|
+
def new
|
137
|
+
@post = Post.new
|
138
|
+
@post.assets.build
|
139
|
+
end
|
140
|
+
|
141
|
+
def create
|
142
|
+
@post = Post.new(params[:post])
|
143
|
+
@post.save
|
144
|
+
respond_with @post
|
145
|
+
end
|
146
|
+
end
|
147
|
+
```
|
148
|
+
|
149
|
+
To wire up the posts view:
|
150
|
+
|
151
|
+
```haml
|
152
|
+
# app/views/posts/new.html.haml
|
153
|
+
= form_for @post, html: { multipart: true } do |form|
|
154
|
+
= form.text_field :title, { placeholder: 'Title' }
|
155
|
+
|
156
|
+
.file-drop
|
157
|
+
%span{ 'data-placeholder' => 'Drop files here' } Drop files here
|
158
|
+
|
159
|
+
%input.standard-attachment{ name: 'standard_attachment', accept: "image/*", type: :file, multiple: :multiple }
|
160
|
+
|
161
|
+
= form.submit 'Create Post'
|
162
|
+
```
|
163
|
+
|
164
|
+
```coffeescript
|
165
|
+
# app/assets/javascripts/posts.coffee
|
166
|
+
# truncated from above to demonstrate additional code to associate uploads
|
167
|
+
# with posts
|
168
|
+
jackUp.on "upload:success", (e, options) ->
|
169
|
+
$("img[data-id='#{options.file.__guid__}']").css(borderColor: "green")
|
170
|
+
|
171
|
+
# read the response from the server
|
172
|
+
asset = JSON.parse(options.responseText)
|
173
|
+
assetId = asset.id
|
174
|
+
# create a hidden input containing the asset id of the uploaded file
|
175
|
+
assetIdsElement = $("<input type='hidden' name='post[asset_ids][]'>").val(assetId)
|
176
|
+
# append it to the form so saving the form associates the created post
|
177
|
+
# with the uploaded assets
|
178
|
+
$(".file-drop").parent("form").append(assetIdsElement)
|
179
|
+
```
|
180
|
+
|
181
|
+
## License
|
182
|
+
|
183
|
+
JackUp is copyright 2012 Josh Steiner, Josh Clayton, and thoughtbot, inc., and may be redistributed under the terms specified in the LICENSE file.
|
data/Rakefile
ADDED
@@ -0,0 +1,40 @@
|
|
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 = 'JackUp'
|
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("../test/dummy/Rakefile", __FILE__)
|
24
|
+
load 'rails/tasks/engine.rake'
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
Bundler::GemHelper.install_tasks
|
29
|
+
|
30
|
+
require 'rake/testtask'
|
31
|
+
|
32
|
+
Rake::TestTask.new(:test) do |t|
|
33
|
+
t.libs << 'lib'
|
34
|
+
t.libs << 'test'
|
35
|
+
t.pattern = 'test/**/*_test.rb'
|
36
|
+
t.verbose = false
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
task :default => :test
|
@@ -0,0 +1 @@
|
|
1
|
+
class @JackUp
|
@@ -0,0 +1,24 @@
|
|
1
|
+
ignoreEvent = (event) ->
|
2
|
+
event.stopPropagation()
|
3
|
+
event.preventDefault()
|
4
|
+
|
5
|
+
class @JackUp.DragAndDrop
|
6
|
+
constructor: (@droppableElement, @processor) ->
|
7
|
+
@droppableElement
|
8
|
+
.bind("dragenter", @_drag)
|
9
|
+
.bind("drop", @_drop)
|
10
|
+
.bind("drop", @_dragOut)
|
11
|
+
|
12
|
+
_drag: (event) =>
|
13
|
+
ignoreEvent event
|
14
|
+
event.originalEvent.dataTransfer.dropEffect = "copy"
|
15
|
+
@droppableElement.addClass("hover")
|
16
|
+
|
17
|
+
_dragOut: (event) =>
|
18
|
+
ignoreEvent event
|
19
|
+
@droppableElement.removeClass("hover")
|
20
|
+
|
21
|
+
_drop: (event) =>
|
22
|
+
ignoreEvent event
|
23
|
+
@droppableElement.find('[data-placeholder]').hide()
|
24
|
+
@processor.processFilesForEvent(event)
|
@@ -0,0 +1,54 @@
|
|
1
|
+
railsCSRFData = ->
|
2
|
+
csrfParam = $('meta[name=csrf-param]').attr('content')
|
3
|
+
csrfToken = $('meta[name=csrf-token]').attr('content')
|
4
|
+
|
5
|
+
formData = {}
|
6
|
+
formData[csrfParam] = csrfToken
|
7
|
+
JSON.stringify formData
|
8
|
+
|
9
|
+
class @JackUp.FileUploader
|
10
|
+
constructor: (@options) ->
|
11
|
+
@path = @options.path
|
12
|
+
@responded = false
|
13
|
+
|
14
|
+
_onProgressHandler: (file) =>
|
15
|
+
(progress) =>
|
16
|
+
if progress.lengthComputable
|
17
|
+
percent = progress.loaded/progress.total*100
|
18
|
+
@trigger 'upload:percentComplete', percentComplete: percent, progress: progress
|
19
|
+
|
20
|
+
if percent == 100
|
21
|
+
@trigger 'upload:sentToServer', file: file
|
22
|
+
|
23
|
+
_onReadyStateChangeHandler: (file) =>
|
24
|
+
(event) =>
|
25
|
+
status = null
|
26
|
+
return if event.target.readyState != 4
|
27
|
+
|
28
|
+
try
|
29
|
+
status = event.target.status
|
30
|
+
catch error
|
31
|
+
return
|
32
|
+
|
33
|
+
if status > 0 && status != 200
|
34
|
+
@trigger 'upload:failure', responseText: event.target.responseText, event: event, file: file
|
35
|
+
|
36
|
+
if status == 200 && event.target.responseText && !@responded
|
37
|
+
@responded = true
|
38
|
+
@trigger 'upload:success', responseText: event.target.responseText, event: event, file: file
|
39
|
+
|
40
|
+
upload: (file) ->
|
41
|
+
xhr = new XMLHttpRequest()
|
42
|
+
xhr.upload.addEventListener 'progress', @_onProgressHandler(file), false
|
43
|
+
xhr.addEventListener 'readystatechange', @_onReadyStateChangeHandler(file), false
|
44
|
+
|
45
|
+
xhr.open 'POST', @path, true
|
46
|
+
|
47
|
+
xhr.setRequestHeader 'Content-Type', file.type
|
48
|
+
xhr.setRequestHeader 'X-File-Name', file.name
|
49
|
+
xhr.setRequestHeader 'X-Query-Params', railsCSRFData()
|
50
|
+
|
51
|
+
@trigger 'upload:start', file: file
|
52
|
+
xhr.send file
|
53
|
+
|
54
|
+
_.extend JackUp.FileUploader.prototype, JackUp.Events
|
@@ -0,0 +1,38 @@
|
|
1
|
+
getFilesFromEvent = (event) ->
|
2
|
+
if event.originalEvent.dataTransfer?
|
3
|
+
event.originalEvent.dataTransfer.files
|
4
|
+
else if event.originalEvent.currentTarget? && event.originalEvent.currentTarget.files?
|
5
|
+
event.originalEvent.currentTarget.files
|
6
|
+
else if event.originalEvent.target? && event.originalEvent.target.files?
|
7
|
+
event.originalEvent.target.files
|
8
|
+
else
|
9
|
+
[]
|
10
|
+
|
11
|
+
filesWithData = (event) ->
|
12
|
+
_.map getFilesFromEvent(event), (file) ->
|
13
|
+
file.__guid__ = Math.random().toString(36)
|
14
|
+
file
|
15
|
+
|
16
|
+
class @JackUp.Processor
|
17
|
+
constructor: (options) ->
|
18
|
+
@uploadPath = options.path
|
19
|
+
|
20
|
+
processFilesForEvent: (event) =>
|
21
|
+
_.each filesWithData(event), (file) =>
|
22
|
+
reader = new FileReader()
|
23
|
+
reader.onload = (event) =>
|
24
|
+
@trigger 'upload:dataRenderReady', result: event.target.result, file: file
|
25
|
+
|
26
|
+
if /^data:image/.test event.target.result
|
27
|
+
image = $("<img>").attr("src", event.target.result)
|
28
|
+
@trigger 'upload:imageRenderReady', image: image, file: file
|
29
|
+
|
30
|
+
reader.readAsDataURL(file)
|
31
|
+
|
32
|
+
fileUploader = new JackUp.FileUploader(path: @uploadPath)
|
33
|
+
@bubble 'upload:success', 'upload:failure', 'upload:sentToServer',
|
34
|
+
from: fileUploader
|
35
|
+
|
36
|
+
fileUploader.upload file
|
37
|
+
|
38
|
+
_.extend JackUp.Processor.prototype, JackUp.Events
|
data/lib/jack_up.rb
ADDED
metadata
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jack_up
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Josh Steiner
|
9
|
+
- Josh Clayton
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2012-08-03 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rails
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ~>
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '3.1'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ~>
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '3.1'
|
31
|
+
description: Easy AJAX file uploading in Rails
|
32
|
+
email:
|
33
|
+
- josh@jsteiner.me
|
34
|
+
- jclayton@thoughtbot.com
|
35
|
+
executables: []
|
36
|
+
extensions: []
|
37
|
+
extra_rdoc_files: []
|
38
|
+
files:
|
39
|
+
- lib/assets/javascripts/jack_up/base.coffee
|
40
|
+
- lib/assets/javascripts/jack_up/drag_and_drop.coffee
|
41
|
+
- lib/assets/javascripts/jack_up/events.coffee
|
42
|
+
- lib/assets/javascripts/jack_up/file_uploader.coffee
|
43
|
+
- lib/assets/javascripts/jack_up/jquery.coffee
|
44
|
+
- lib/assets/javascripts/jack_up/processor.coffee
|
45
|
+
- lib/assets/javascripts/jack_up.js
|
46
|
+
- lib/jack_up/engine.rb
|
47
|
+
- lib/jack_up/version.rb
|
48
|
+
- lib/jack_up.rb
|
49
|
+
- LICENSE
|
50
|
+
- Rakefile
|
51
|
+
- README.md
|
52
|
+
homepage: http://github.com/thoughtbot/jack_up
|
53
|
+
licenses: []
|
54
|
+
post_install_message:
|
55
|
+
rdoc_options: []
|
56
|
+
require_paths:
|
57
|
+
- lib
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
60
|
+
requirements:
|
61
|
+
- - ! '>='
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '0'
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
requirements: []
|
71
|
+
rubyforge_project:
|
72
|
+
rubygems_version: 1.8.23
|
73
|
+
signing_key:
|
74
|
+
specification_version: 3
|
75
|
+
summary: Easy AJAX file uploading in Rails
|
76
|
+
test_files: []
|
77
|
+
has_rdoc:
|