pail 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 54c0ae0fb36a073adbf4ac0f4231991841572362
4
+ data.tar.gz: 0d75a69b5b42b896fc6d746c3f537389edc76b08
5
+ SHA512:
6
+ metadata.gz: 5ac65e006f9ffc929175457c409b62c78156d5aa0d09faf07a52f31e810ccd9a65f428519769a41652d80316935cb3ada13e2c2c3fd0b9606790177fcaf39299
7
+ data.tar.gz: 24fb2095bb633a60dd922e217b4223e8515acdd915bdce0cc837471ab0f3154e02b12ac6fc1646f41e33e09352d9fcf9e6698f40c55c5042d2ec265654d4aea6
data/.coveralls.yml ADDED
@@ -0,0 +1,2 @@
1
+ service_name: travis-ci
2
+ repo_token: jy5plUVhx9Z8gE8dhQHSB0epLB00iDcBj
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,15 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.0
4
+
5
+ before_install:
6
+ - gem install bundler -v 1.8
7
+
8
+ script: 'bundle exec rspec'
9
+
10
+ notifications:
11
+ email:
12
+ recipients:
13
+ - eddiej@gmail.com
14
+ on_failure: change
15
+ on_success: never
@@ -0,0 +1,13 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4
+
5
+ We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
6
+
7
+ Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
8
+
9
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
10
+
11
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
12
+
13
+ This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in pail.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,36 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ ## Uncomment and set this to only include directories you want to watch
5
+ # directories %w(app lib config test spec features)
6
+
7
+ ## Uncomment to clear the screen before every task
8
+ # clearing :on
9
+
10
+ ## Guard internally checks for changes in the Guardfile and exits.
11
+ ## If you want Guard to automatically start up again, run guard in a
12
+ ## shell loop, e.g.:
13
+ ##
14
+ ## $ while bundle exec guard; do echo "Restarting Guard..."; done
15
+ ##
16
+ ## Note: if you are using the `directories` clause above and you are not
17
+ ## watching the project directory ('.'), then you will want to move
18
+ ## the Guardfile to a watched dir and symlink it back, e.g.
19
+ #
20
+ # $ mkdir config
21
+ # $ mv Guardfile config/
22
+ # $ ln -s config/Guardfile .
23
+ #
24
+ # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
25
+
26
+ guard 'rspec', cmd: "bundle exec rspec" do
27
+ # watch /lib/ files
28
+ watch(%r{^lib/(.+).rb$}) do |m|
29
+ "spec/#{m[1]}_spec.rb"
30
+ end
31
+
32
+ # watch /spec/ files
33
+ watch(%r{^spec/(.+).rb$}) do |m|
34
+ "spec/#{m[1]}.rb"
35
+ end
36
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Eddie Johnston
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,218 @@
1
+ # Pail
2
+
3
+ [![Build Status](https://travis-ci.org/eddiej/pail.png?branch=master)](https://travis-ci.org/eddiej/pail)
4
+ [![Coverage Status](https://coveralls.io/repos/eddiej/pail/badge.png)](https://coveralls.io/r/eddiej/pail)
5
+
6
+
7
+ Pail is a Ruby gem that makes it easy to upload files directly from your views to an Amazon S3 Bucket, bypassing your application server.
8
+
9
+ ![Alt Image Text](https://raw.githubusercontent.com/eddiej/pail/gh-pages/in_progress.png "Optional Title")
10
+
11
+ It uses the Plupload JavaScript uploader originally written for PHP, bundling the required Javascript libraries with convenience methods for generating the policy document and SHA1 signature needed to upload to S3.
12
+ Pail has the option of applying Twitter Boostrap progress-bar styling to the uploader, but it is unstyled by default.
13
+
14
+
15
+ ## Why Do I Need It?
16
+
17
+ To avoid blocking your server during file uploads. There are two approaches to processing and storing file uploads from your Rails app to S3: pass-through uploading and direct uploading.
18
+
19
+ **Pass-through uploading**, which sends files to your Rails application before uploading them from there to S3 storage, is easy to implement, but your application's server threads will be tied up during the upload process which can have a blocking effect on your application.
20
+
21
+ If all of the available threads of your server are occupied by slow uploads, your application will become unavailable until one of the uploads finishes and frees up a thread. Some hosting providers like Heroku terminate requests that last longer than 30 seconds, so uploads that take any longer won't complete.
22
+
23
+ **Direct uploading** makes a direct connection between the end-user's browser and S3, bypassing your application entirely. This will greatly reduce the processing required by your application and keep your threads free for other requests, but it can be complicated to implement. See [Heroku's Guide for direct uploading to S3 with Rails](https://devcenter.heroku.com/articles/direct-to-s3-image-uploads-in-rails) for an example implementation.
24
+
25
+ Pail takes the complication out of direct uploading, allowing users to create a direct uploader with just one line of code.
26
+
27
+
28
+ ## Requirements
29
+
30
+ Before you can upload to S3, you'll need to:
31
+
32
+ - create a bucket
33
+ - configure the bucket to allow uploads
34
+ - generate a Base64 encoded Policy document
35
+ - generate a Base64 encoded SHA1 encrypted signature
36
+
37
+ Your S3 bucket needs to be created and configured manually, but Pail looks after the the policy document and signature generation. Pail needs access to three variables; the name of the bucket you'll be uploading to, an AWS access key id and secret access key. These should be made available as environrment variables in your Rails app. There are various wasy of doing this, you could use an initializer or a gem like [dotenv](https://github.com/bkeepers/dotenv).
38
+
39
+ ```ruby
40
+ ENV['S3_BUCKET']=assets.mydomain.com
41
+ ENV['AWS_ACCESS_KEY_ID']=AKIAJLAP2FSKJEBDFLWWA
42
+ ENV['AWS_SECRET_ACCESS_KEY']=KKdkahb4kdfheb4KGcII8iRzpFymlUanYGszLf1U
43
+ ```
44
+
45
+ ### Configuring Your S3 Bucket
46
+
47
+ Plupload provides a number of different runtimes including HTML5, Flash and Sliverlight. Depending on the runtime you wish to use, the configuration settings are slightly different. Configuring your S3 Bucket for the three main runtimes is documented below, see the [Plupload documentation](http://www.plupload.com/docs/Upload-to-Amazon-S3) for a more detailed explanation.
48
+
49
+ #### HTML5 Runtime
50
+
51
+ To use the HTML5 Runtime, you must add a CORS configuration similar to the one below in the `Permissions` section of the S3 bucket options. This is accessed from your S3 dashboard.
52
+
53
+ ```xml
54
+ <CORSConfiguration>
55
+ <CORSRule>
56
+ <AllowedOrigin>*</AllowedOrigin>
57
+ <AllowedHeader>*</AllowedHeader>
58
+ <AllowedMethod>GET</AllowedMethod>
59
+ <AllowedMethod>POST</AllowedMethod>
60
+ <MaxAgeSeconds>3000</MaxAgeSeconds>
61
+ </CORSRule>
62
+ </CORSConfiguration>
63
+ ```
64
+
65
+
66
+ #### Flash Runtime
67
+
68
+ Flash requires a `crossdomain.xml` policy file to be present at the root of your bucket to support cross-origin requests:
69
+
70
+ ```xml
71
+ <?xml version="1.0"?>
72
+ <!DOCTYPE cross-domain-policy SYSTEM
73
+ "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
74
+ <cross-domain-policy>
75
+ <allow-access-from domain="*" secure="false" />
76
+ </cross-domain-policy>
77
+ ```
78
+
79
+ #### Silverlight Runtime
80
+
81
+ Silverlight uses its own Security Policy File - `clientaccesspolicy.xml`, but when all domains are allowed, it can fallback to `crossdomain.xml`.
82
+
83
+ See the [Plupload documentation](http://www.plupload.com/docs/Upload-to-Amazon-S3#for-silverlight-runtime) for more information on generating `clientaccesspolicy.xml` for allowing only specific domains.
84
+
85
+
86
+ ### Policy Generation
87
+
88
+ In order to upload to S3, each request musy be accompanied with a Base64 encoded policy, a set of rules your request should conform to. Amazon will reject requests with missing or invalid policy documents and respond with a 403 Forbidden error.
89
+
90
+ When you call the `pail()` function, the gem will generate the policy using your private key and secret and inject the encrypted values into the configuration hash on your page. This means that your sensitive information doesn't get exposed on the front-end.
91
+
92
+ ### Signature Generation
93
+ In addition to sending a Policy document, each request must also be signed with a HMAC-SHA1 encrypted and Base64 encoded signature which is created from you AWS Secret Access Key. Pail will generate the signature for you and insert the encryoted values into the configuration hash on the front-end.
94
+
95
+ ## Creating an Uploader
96
+
97
+ To use the Pail, add the Gem to your Gemfile and include the Pail Javascript in your `application.js` file. Note that jQuery is a pre-requisite for Pail.
98
+
99
+ <pre>
100
+ //= require jquery
101
+ //= require jquery_ujs
102
+ //= require turbolinks
103
+ //= require_tree .
104
+ <b>//= require pail</b>
105
+ </pre>
106
+
107
+
108
+
109
+ Inserting an uploader onto a page is as simple as including a small amount of HTML markup and calling the `pail()` function in a view template. In an ERB file, this would look like:
110
+
111
+ ```ruby
112
+ <%= pail() %>
113
+ ```
114
+
115
+ Your HTML markup must provide:
116
+
117
+ - a file select button
118
+ - a cancel button
119
+ - a placeholder element that will display information about the file being uploaded
120
+
121
+ The default IDs for each of these elements is `#selectfile`, `#resetupload`, and `#uploadfile` but these can be overridden via the options hash. The markup below contains the required elements:
122
+
123
+ ```html
124
+ <div id='uploadcontainer'>
125
+ <h3>Upload A File</h3>
126
+ <div id='uploadfile'>
127
+ <div class="progress"></div>
128
+ </div>
129
+ <button type="button" class="btn btn-default btn btn-sm" id="selectfile">Select File</button>
130
+ <button type="button" class="btn btn-danger btn btn-sm disabled" id="resetupload">Reset</button>
131
+ </div>
132
+ ```
133
+
134
+
135
+
136
+ Any placeholder content can be placed within the `#uploadfile` element as long as it is wrapped in a containing element. By default, an empty div with the class 'progress' is inserted which displays an empty progress bar well. This will be replaced with a progress bar once a file is selected and put back again if the upload is reset.
137
+
138
+ ### Parameters
139
+
140
+ Custom options can be sent in a hash parameter of the `pail()` function. The options currently configurable in Pail and their default values are:
141
+
142
+ ```ruby
143
+ options[:key] ||= 'test'
144
+ options[:acl] ||= 'public-read'
145
+ options[:expiration_date] ||= 10.hours.from_now.utc.iso8601
146
+ options[:max_filesize] ||= 500.kilabytes
147
+ options[:content_type] ||= 'image/'
148
+ options[:filter_title] ||= 'Images'
149
+ options[:filter_extentions] ||= 'jpg,jpeg,gif,png,bmp'
150
+ options[:runtimes] ||= 'html5'
151
+ options[:selectid] ||= 'selectfile'
152
+ options[:cancelid] ||= 'resetupload'
153
+ options[:progress_bar_class] ||= 'progress-bar progress-bar-striped active'
154
+ ```
155
+
156
+ The [Plupload Options documentation](http://www.plupload.com/docs/Options) should be referred to for explanations of each value. The `selectid`, `cancelid` and `progress_bar_class` values are relevant only to Pail and not Plupload.
157
+
158
+ ### Upload Progress
159
+
160
+ The upload progress bar uses the same markup as the [Twitter Bootstap progress bar](http://getbootstrap.com/components/#progress).
161
+
162
+ ```html
163
+ <div class="progress">
164
+ <div class="progress-bar" role="progressbar" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100" style="width: 60%;">
165
+ <span class="file-info">Moonshine.jpg (453 kb) 60% </span>
166
+ </div>
167
+ </div>
168
+ ```
169
+
170
+ The control buttons and progress bar will be unstyled by default, but you can apply the Boostrap styles by including the Pail stylesheet from `application.css`:
171
+
172
+ <pre>
173
+ *
174
+ *= require_tree .
175
+ *= require_self
176
+ <b>*= require pail</b>
177
+ */
178
+ </pre>
179
+
180
+ If you already use Twitter Bootstrap in your application, the styles from your theme will automatically be applied.
181
+
182
+ ## Upload Events
183
+
184
+ Plupload defines a number of events that can be bound to, a full list of which can be found in the [Plupload documentation](http://www.plupload.com/docs/Uploader#events). By default, Pail uses event binding to change the style of the progress bar according to the state of the uploader.
185
+
186
+ Additional functionality would generally be bound to the FileUploadaed event to perform additional actions once a file has been uploaded. Examples might include:
187
+
188
+ - Insert the S3 url of the uploaded file into a hidden field and submit a form.
189
+ - Send the S3 url to a controller via a remote AJAX call
190
+ - Redirect to another page that displays the uploaded asset
191
+
192
+ To bind additional events, use the jQuery `bind` function after `<%= pail %>` has been called.
193
+
194
+ ```javascript
195
+ <script>
196
+ window.uploader.bind('FileUploaded', function(up, file, info) { alert (info); })
197
+ </script>
198
+ ```
199
+
200
+ Note that the uploader object created by the Plupload Javascript is added as a property of the window object and can be accessed by `window.uploader`.
201
+
202
+ The default Pail events can be removed using the `unbind` function, e.g:
203
+
204
+ ```javascript
205
+ <script>
206
+ window.uploader.unbind('UploadProgress')
207
+ window.uploader.unbind('FileUploaded')
208
+ ...
209
+ </script>
210
+ ```
211
+
212
+ ## Testing
213
+ Tests are written using RSpec. To start the test suite, run:
214
+
215
+ ```rails
216
+ bundle exec rspec
217
+ ```
218
+ or view the tests and results output on [Travis CI](https://travis-ci.org/eddiej/pail).
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "pail"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
data/lib/pail.rb ADDED
@@ -0,0 +1,194 @@
1
+ require "pail/version"
2
+ require "pail/engine"
3
+
4
+ module Pail
5
+ class Generate
6
+ def self.signature(secret_access_key, policy)
7
+ Base64.encode64(
8
+ OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha1'), secret_access_key, policy)
9
+ ).gsub("\n","")
10
+ end
11
+
12
+ def self.policy(bucket, expiration_date, acl, max_filesize)
13
+ Base64.encode64(
14
+ "{'expiration': '#{expiration_date}',
15
+ 'conditions': [
16
+ {'bucket': '#{bucket}'},
17
+ {'acl': '#{acl}'},
18
+ {'success_action_status': '201'},
19
+ ['content-length-range', 0, #{max_filesize}],
20
+ ['starts-with', '$key', ''],
21
+ ['starts-with', '$Content-Type', ''],
22
+ ['starts-with', '$name', ''],
23
+ ['starts-with', '$Filename', '']
24
+ ]
25
+ }"
26
+ ).gsub(/\n|\r/, '')
27
+ end
28
+ end
29
+
30
+ module PailHelper
31
+
32
+ def pail(options = {})
33
+ # Read in the default bucket, AWS key and secret from environment variables.
34
+
35
+ @bucket = ENV['S3_BUCKET']
36
+ @access_key_id = ENV['AWS_ACCESS_KEY_ID']
37
+ @secret_access_key = ENV['AWS_SECRET_ACCESS_KEY']
38
+ @options = options.dup
39
+
40
+ raise ArgumentError.new('a bucket, access key id and secret access key must be set') unless @bucket && @access_key_id && @secret_access_key
41
+
42
+ @options[:key] ||= 'uploads' # folder on AWS to store file in
43
+ @options[:acl] ||= 'public-read'
44
+ @options[:expiration_date] ||= 10.hours.from_now.utc.iso8601
45
+ @options[:max_filesize] ||= 104857600 # 100.megabytes
46
+ @options[:content_type] ||= 'image/' # Videos would be binary/octet-stream
47
+ @options[:filter_title] ||= 'Images'
48
+ @options[:filter_extensions] ||= 'jpg,jpeg,gif,png,bmp'
49
+ @options[:runtimes] ||= 'html5'
50
+
51
+ @options[:selectid] ||= 'selectfile'
52
+ @options[:cancelid] ||= 'resetupload'
53
+ @options[:progress_bar_class] ||= 'progress-bar progress-bar-striped active'
54
+
55
+ id = @options[:id] ? "_#{@options[:id]}" : ''
56
+
57
+ policy = Pail::Generate::policy(
58
+ @bucket,
59
+ @options[:expiration_date],
60
+ @options[:acl],
61
+ @options[:max_filesize]
62
+ )
63
+
64
+ signature = Pail::Generate::signature(@secret_access_key, policy)
65
+
66
+ out = ""
67
+ filters = "filters : [
68
+ {title : '#{@options[:filter_title]}', extensions : '#{@options[:filter_extensions]}'}
69
+ ],"
70
+ if @options[:filters]
71
+ filters = 'filters : ['
72
+ filters = filters + @options[:filters].join(',')
73
+ filters = filters + "],"
74
+ end
75
+
76
+
77
+ out << javascript_tag(<<JAVASCRIPT
78
+
79
+ uploader = new plupload.Uploader({
80
+ browse_button : '#{@options[:selectid]}',
81
+ container : 'uploadcontainer',
82
+ runtimes : '#{@options[:runtimes]}',
83
+ url : 'https://s3.amazonaws.com/#{@bucket}/',
84
+ max_file_size : '#{number_to_human_size(@options[:max_filesize]).gsub(/ /,'').downcase}',
85
+ multipart: true,
86
+ multipart_params: {
87
+ 'key': '#{@options[:key]}/${filename}',
88
+ 'Filename': '${filename}', // adding this to keep consistency across the runtimes
89
+ 'acl': '#{@options[:acl]}',
90
+ 'Content-Type': '#{@options[:content_type]}',
91
+ 'success_action_status': '201',
92
+ 'AWSAccessKeyId' : '#{@access_key_id}',
93
+ 'policy': '#{policy}',
94
+ 'signature': '#{signature}'
95
+ },
96
+ // optional, but better be specified directly
97
+ file_data_name: 'file',
98
+ // re-use widget (not related to S3, but to Plupload UI Widget)
99
+ multiple_queues: true,
100
+ // Specify what files to browse for
101
+ #{filters}
102
+ // Flash settings
103
+ flash_swf_url : '/assets/plupload/Moxie.swf',
104
+ // Silverlight settings
105
+ silverlight_xap_url : '/assets/plupload/Moxie.xap',
106
+ });
107
+
108
+ placeholder = $('#uploadfile').children().first();
109
+ uploader.init()
110
+ var queueMaxima = 1;
111
+
112
+ // 1. Files Added
113
+ uploader.bind('FilesAdded', function(up, files) {
114
+ $.each(files, function(i, file) {
115
+ $('#uploadfile').empty().append(' \
116
+ <div id="' + file.id + '" class="progress"> \
117
+ <div class="#{@options[:progress_bar_class]}" role="progressbar" aria-valuenow="'+file.percent+'" aria-valuemin="0" aria-valuemax="100"> \
118
+ <span class="file-info">' + file.name + ' (' + plupload.formatSize(file.size) + ')</span>' +
119
+ '</div> \
120
+ </div>');
121
+ });
122
+ $('##{@options[:cancelid]}').removeClass('disabled')
123
+
124
+
125
+
126
+ if(uploader.files.length > queueMaxima){
127
+ while(uploader.files.length > queueMaxima){
128
+ if(uploader.files.length > queueMaxima){
129
+ x = uploader.files[queueMaxima-1]
130
+
131
+ $('#'+x.id).remove();
132
+ uploader.removeFile(uploader.files[queueMaxima-1]);
133
+ uploader.stop()
134
+ }
135
+ }
136
+ if(typeof(plupload_hook_removedExcessFromQueue) == "function"){plupload_hook_removedExcessFromQueue()}
137
+ }
138
+ up.start();
139
+ up.refresh(); // Reposition Flash/Silverlight
140
+ });
141
+
142
+ // 2. Right before Upload Starts
143
+ uploader.bind('BeforeUpload', function(up, file) {
144
+ $('#' + file.id + " .progress-bar")
145
+ .addClass('active')
146
+ });
147
+
148
+ // 2. Upload Progresses
149
+ uploader.bind('UploadProgress', function(up, file) {
150
+ $('#' + file.id + " .progress-bar")
151
+ .width(file.percent + "%")
152
+ .html('<span class="file-info">' + file.name + ' (' + plupload.formatSize(file.size) + ') ' + file.percent + "%</span>");
153
+ });
154
+
155
+ // 3. Error Occurs
156
+ uploader.bind('Error', function(up, err) {
157
+ if($('.file-info').length > 0){
158
+ error_element = $('.file-info')
159
+ }
160
+ else{
161
+ error_element = $('.progress')
162
+ }
163
+ error_element.html("Error: " + err.code + ", " + err.message + (err.file ? " File: " + err.file.name : ""));
164
+ $('#' + err.file.id + " .progress-bar")
165
+ .addClass('progress-bar-danger')
166
+ .removeClass('active')
167
+ });
168
+
169
+ // 4. File is Uploaded
170
+ uploader.bind('FileUploaded', function(up, file) {
171
+ $('#' + file.id + " .progress-bar")
172
+ .addClass('progress-bar-success')
173
+ .removeClass('active')
174
+ });
175
+
176
+ // 5. Stop button is clicked.
177
+ $('#uploadcontainer').on('click', '##{@options[:cancelid]}', function(){
178
+ uploader.stop();
179
+ $('#uploadfile').html(placeholder);
180
+ $('##{@options[:cancelid]}').addClass('disabled')
181
+ $('#' + file.id + " .progress-bar")
182
+ .removeClass('active')
183
+ uploader.refresh(); // Reposition Flash/Silverlight
184
+ });
185
+
186
+ window.uploader = uploader;
187
+ JAVASCRIPT
188
+ )
189
+
190
+ raw(out);
191
+ end
192
+ end
193
+ ActionView::Base.send :include, PailHelper
194
+ end