headshot 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/MIT-LICENSE +20 -0
- data/README.md +37 -0
- data/Rakefile +38 -0
- data/app/middlewares/flash_session_cookie_middleware.rb +18 -0
- data/lib/app/controllers/headshot_controller.rb +59 -0
- data/lib/app/helpers/headshot_helper.rb +63 -0
- data/lib/app/lib/headshot_support.rb +30 -0
- data/lib/app/models/headshot_photo.rb +7 -0
- data/lib/assets/audio/shutter.mp3 +0 -0
- data/lib/assets/javascripts/headshot.js +206 -0
- data/lib/assets/javascripts/headshot_support.js +27 -0
- data/lib/assets/swfs/headshot.swf +0 -0
- data/lib/db/migrate/create_headshot_photos.rb +13 -0
- data/lib/generators/headshot/config/config_generator.rb +45 -0
- data/lib/generators/headshot/generate/generate_generator.rb +26 -0
- data/lib/generators/headshot/install/install_generator.rb +37 -0
- data/lib/headshot/version.rb +3 -0
- data/lib/headshot.rb +11 -0
- data/lib/initializers/session_store.rb +1 -0
- data/lib/middlewares/flash_session_cookie_middleware.rb +18 -0
- data/lib/tasks/headshot_tasks.rake +4 -0
- data/test/app/controller/headshot_controller_test.rb +38 -0
- data/test/app/helpers/headshot_helper_test.rb +68 -0
- data/test/dummy/README.rdoc +261 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/assets/javascripts/application.js +15 -0
- data/test/dummy/app/assets/javascripts/headshot.js +206 -0
- data/test/dummy/app/assets/javascripts/headshot_support.js +27 -0
- data/test/dummy/app/assets/stylesheets/application.css +13 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/middlewares/flash_session_cookie_middleware.rb +18 -0
- data/test/dummy/app/models/headshot_photo.rb +7 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/config/application.rb +59 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +37 -0
- data/test/dummy/config/environments/production.rb +67 -0
- data/test/dummy/config/environments/test.rb +37 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +15 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +10 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +60 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/migrate/20130111074937_create_headshot_photos.rb +13 -0
- data/test/dummy/db/schema.rb +28 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/development.log +32 -0
- data/test/dummy/log/test.log +871 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +25 -0
- data/test/dummy/public/audio/shutter.mp3 +0 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/public/headshots/empty_file +0 -0
- data/test/dummy/public/headshots/headshot_capture_123_1357896182.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_1383_1357927406.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_1714_1357928157.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_1922_1357933499.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_2223_1357895537.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_2236_1357927697.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_2492_1357927181.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_2517_1357925235.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_2741_1357933673.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_2855_1357897722.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_3004_1357929483.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_3192_1357895507.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_3474_1357899111.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_4030_1357890605.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_4132_1357925086.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_4302_1357898574.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_4439_1357926710.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_4480_1357897965.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_4563_1357930722.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_4779_1357896101.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_4801_1357927203.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_4915_1357927616.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_5027_1357927988.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_5120_1357896023.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_5148_1357928143.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_5319_1357927441.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_5493_1357985442.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_5551_1357895850.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_562_1357899111.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_5902_1357926881.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_5992_1357899281.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_6245_1357896342.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_6272_1357897339.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_6444_1357927728.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_6593_1357926833.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_6857_1357897385.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_7017_1357930283.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_726_1357897738.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_7481_1357929409.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_7655_1357897700.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_7750_1357927107.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_7955_1357928167.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_8080_1357927550.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_8157_1357895552.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_8240_1357930057.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_8406_1357927135.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_8671_1357927946.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_8714_1357897752.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_8940_1357933209.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_9252_1357896077.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_9642_1357899155.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_989_1357926734.jpg +0 -0
- data/test/dummy/public/headshots/headshot_capture_9975_1357926892.jpg +0 -0
- data/test/dummy/public/swfs/headshot.swf +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/dummy/tmp/headshot_capture_12345_1357840944.jpg +1 -0
- data/test/headshot_test.rb +7 -0
- data/test/lib/headshot_support_test.rb +38 -0
- data/test/test_helper.rb +17 -0
- metadata +283 -0
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright 2012 Diwa del Mundo (diwadm@gmail.com)
|
|
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,37 @@
|
|
|
1
|
+
Headshot
|
|
2
|
+
========
|
|
3
|
+
|
|
4
|
+
Headshot is a gem that provides a quick and easy way for Ruby on Rails applications to capture users' photos via a web camera without touching ActionScript or JavaScript code. It works by embedding a Flash component in your page's view to access the user's camera and capture the user's photo. Headshot takes care of the configuration and initialization of the Flash component and other support code in your Rails application simplifying the web cam integration process.
|
|
5
|
+
|
|
6
|
+
Sample Code
|
|
7
|
+
========
|
|
8
|
+
|
|
9
|
+
Below is some sample code to setup Headshot:
|
|
10
|
+
|
|
11
|
+
<b>Controller</b>
|
|
12
|
+
|
|
13
|
+
`class HeadshotDemoController < ApplicationController`
|
|
14
|
+
|
|
15
|
+
`helper :headshot`
|
|
16
|
+
|
|
17
|
+
`end`
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
<b>View</b>
|
|
21
|
+
|
|
22
|
+
`<%= headshot_default_config(Rails.application.config.session_options[:key], headshot_capture_path) %>`
|
|
23
|
+
|
|
24
|
+
`<%= headshot_camera %>`
|
|
25
|
+
|
|
26
|
+
`<%= headshot_snap_button('Shoot') %>`
|
|
27
|
+
|
|
28
|
+
Demo
|
|
29
|
+
========
|
|
30
|
+
|
|
31
|
+
Demo and complete documentation are available here:
|
|
32
|
+
|
|
33
|
+
[http://headshot.hypermediasoft.com](http://headshot.hypermediasoft.com)
|
|
34
|
+
|
|
35
|
+
Copyright and Author
|
|
36
|
+
=======
|
|
37
|
+
Copyright (c) 2013 Diwa del Mundo (diwadm@gmail.com), released under the MIT license
|
data/Rakefile
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env rake
|
|
2
|
+
begin
|
|
3
|
+
require 'bundler/setup'
|
|
4
|
+
rescue LoadError
|
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
|
6
|
+
end
|
|
7
|
+
begin
|
|
8
|
+
require 'rdoc/task'
|
|
9
|
+
rescue LoadError
|
|
10
|
+
require 'rdoc/rdoc'
|
|
11
|
+
require 'rake/rdoctask'
|
|
12
|
+
RDoc::Task = Rake::RDocTask
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
|
16
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
17
|
+
rdoc.title = 'Headshot'
|
|
18
|
+
rdoc.options << '--line-numbers'
|
|
19
|
+
rdoc.rdoc_files.include('README.rdoc')
|
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
Bundler::GemHelper.install_tasks
|
|
27
|
+
|
|
28
|
+
require 'rake/testtask'
|
|
29
|
+
|
|
30
|
+
Rake::TestTask.new(:test) do |t|
|
|
31
|
+
t.libs << 'lib'
|
|
32
|
+
t.libs << 'test'
|
|
33
|
+
t.pattern = 'test/**/*_test.rb'
|
|
34
|
+
t.verbose = false
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
task :default => :test
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require 'rack/utils'
|
|
2
|
+
|
|
3
|
+
class FlashSessionCookieMiddleware
|
|
4
|
+
def initialize(app, session_key = '_session_id')
|
|
5
|
+
@app = app
|
|
6
|
+
@session_key = session_key
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def call(env)
|
|
10
|
+
if env['HTTP_USER_AGENT'] =~ /^(Adobe|Shockwave) Flash/
|
|
11
|
+
req = Rack::Request.new(env)
|
|
12
|
+
env['HTTP_COOKIE'] = [ @session_key, ::Rack::Utils.escape(req.params[@session_key]) ].join('=').freeze unless req.params[@session_key].nil?
|
|
13
|
+
env['HTTP_ACCEPT'] = "#{req.params['_http_accept']}".freeze unless req.params['_http_accept'].nil?
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
@app.call(env)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
class HeadshotController < ApplicationController
|
|
2
|
+
include HeadshotSupport
|
|
3
|
+
def capture
|
|
4
|
+
file_path = ''
|
|
5
|
+
begin
|
|
6
|
+
file_path = method(:headshot_custom_file_path).call
|
|
7
|
+
rescue
|
|
8
|
+
file_path = headshot_file_path
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Pre save hook.
|
|
12
|
+
begin
|
|
13
|
+
method(:headshot_pre_save).call(file_path)
|
|
14
|
+
rescue
|
|
15
|
+
# No pre save hook.
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Method for saving the JPEG file.
|
|
19
|
+
begin
|
|
20
|
+
method(:headshot_custom_save_image).call(file_path, request.raw_post)
|
|
21
|
+
|
|
22
|
+
# Only catch the error if method is undefined.
|
|
23
|
+
rescue NameError => e
|
|
24
|
+
saving_result = headshot_save_image(file_path, request.raw_post)
|
|
25
|
+
|
|
26
|
+
unless saving_result
|
|
27
|
+
render :json => {
|
|
28
|
+
:status => 'Error',
|
|
29
|
+
:message => 'Saving of headshot failed.'
|
|
30
|
+
}
|
|
31
|
+
return
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Post save hook.
|
|
36
|
+
begin
|
|
37
|
+
method(:headshot_post_save).call(file_path)
|
|
38
|
+
rescue
|
|
39
|
+
@headshot_photo = HeadshotPhoto.create(
|
|
40
|
+
:image_file_name => File.basename(file_path),
|
|
41
|
+
:image_content_type => 'image/jpeg',
|
|
42
|
+
:image_updated_at => Time.now
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
headshot_url = ""
|
|
47
|
+
begin
|
|
48
|
+
headshot_url = method(:headshot_custom_image_url).call(File.basename(file_path))
|
|
49
|
+
rescue
|
|
50
|
+
headshot_url = "#{headshot_image_url(File.basename(file_path))}"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
render :json => {
|
|
54
|
+
:status => 'Success',
|
|
55
|
+
:message => 'Headshot saved.',
|
|
56
|
+
:url => headshot_url
|
|
57
|
+
}
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
module HeadshotHelper
|
|
2
|
+
def headshot_snap_button(label = 'Snap', options = {})
|
|
3
|
+
options.reverse_merge!({
|
|
4
|
+
:onclick => 'headshotHandlers.snap()'
|
|
5
|
+
})
|
|
6
|
+
|
|
7
|
+
button_tag(label, options)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def headshot_config_button(label = 'Configure', options = {})
|
|
11
|
+
options.reverse_merge!({
|
|
12
|
+
:onclick => 'headshot.configure()'
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
button_tag(label, options)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def headshot_camera(width = 400, height = 300)
|
|
19
|
+
raw(%{
|
|
20
|
+
<script language="JavaScript">
|
|
21
|
+
headshotInitializer.setup(#{width}, #{height}, params);
|
|
22
|
+
</script>
|
|
23
|
+
})
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def headshot_default_config(session_key, capture_path, options = {})
|
|
27
|
+
options.reverse_merge!(headshot_default_options)
|
|
28
|
+
session_value = u(cookies[session_key])
|
|
29
|
+
raw(%{
|
|
30
|
+
<script language="JavaScript">
|
|
31
|
+
var params = {
|
|
32
|
+
session_key_name: encodeURIComponent('#{session_value}'),
|
|
33
|
+
csrf_token: encodeURI(encodeURI(headshotUtils.getPageMetaValue('csrf-token')))
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
var headshotHandlers = {
|
|
37
|
+
onComplete: function(msg) {
|
|
38
|
+
headshot.reset();
|
|
39
|
+
},
|
|
40
|
+
snap: function() {
|
|
41
|
+
headshot.snap();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
headshot.set_swf_url('#{options[:swf_url]}');
|
|
46
|
+
headshot.set_api_url('#{capture_path}');
|
|
47
|
+
headshot.set_quality(#{options[:quality]});
|
|
48
|
+
headshot.set_shutter_sound(#{options[:shutter_sound]}, '#{options[:shutter_sound_url]}');
|
|
49
|
+
headshot.set_hook('onComplete', '#{options[:on_complete]}');
|
|
50
|
+
</script>
|
|
51
|
+
})
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def headshot_default_options
|
|
55
|
+
{
|
|
56
|
+
:swf_url => '/swfs/headshot.swf',
|
|
57
|
+
:quality => 90,
|
|
58
|
+
:shutter_sound => true,
|
|
59
|
+
:shutter_sound_url => '/audio/shutter.mp3',
|
|
60
|
+
:on_complete => 'headshotHandlers.onComplete'
|
|
61
|
+
}
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module HeadshotSupport
|
|
2
|
+
def headshot_file_path
|
|
3
|
+
file_name = "headshot_capture_#{headshot_file_timestamp}.jpg"
|
|
4
|
+
File.join(Rails.root, 'public', 'headshots', file_name)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def headshot_image_url(file_name)
|
|
8
|
+
'http://' + request.host_with_port + '/headshots/' + file_name
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def headshot_save_image(file_path, raw_jpeg_data)
|
|
12
|
+
if raw_jpeg_data
|
|
13
|
+
begin
|
|
14
|
+
File.open(file_path, 'wb') do |file|
|
|
15
|
+
file.write raw_jpeg_data
|
|
16
|
+
end
|
|
17
|
+
rescue
|
|
18
|
+
raise "Headshot: cannot save headshot on #{file_path}. Please check file permissions."
|
|
19
|
+
end
|
|
20
|
+
else
|
|
21
|
+
return nil
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
true
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def headshot_file_timestamp
|
|
28
|
+
"#{rand(10000)}_#{Time.now.to_i}"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
class HeadshotPhoto < ActiveRecord::Base
|
|
2
|
+
attr_accessible :description, :image_content_type, :image_file_name, :image_file_size, :image_updated_at
|
|
3
|
+
|
|
4
|
+
belongs_to :capturable, :polymorphic => true
|
|
5
|
+
|
|
6
|
+
#has_attached_file :image, :styles => { :medium => "300x300>", :thumb => "100x100>" }
|
|
7
|
+
end
|
|
Binary file
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/* headshot */
|
|
2
|
+
/* Easily capture webcam images on Rails applications. */
|
|
3
|
+
/* Initially based on JPEGCam v1.0.9 by Joseph Huckaby. */
|
|
4
|
+
/* Copyright 2012 - Diwa del Mundo <diwadm@gmail.com> */
|
|
5
|
+
/* Licensed under the GNU Lesser Public License */
|
|
6
|
+
/* http://www.gnu.org/licenses/lgpl.html */
|
|
7
|
+
|
|
8
|
+
/* Usage:
|
|
9
|
+
<script language="JavaScript">
|
|
10
|
+
document.write( headshot.get_html(320, 240) );
|
|
11
|
+
headshot.set_api_url( 'test.php' );
|
|
12
|
+
headshot.set_hook( 'onComplete', 'my_callback_function' );
|
|
13
|
+
function my_callback_function(response) {
|
|
14
|
+
console.log("Server responded: " + response);
|
|
15
|
+
}
|
|
16
|
+
</script>
|
|
17
|
+
<a href="javascript:void(headshot.snap())">Take Snapshot</a>
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
window.headshot = {
|
|
21
|
+
version: '0.1.0',
|
|
22
|
+
|
|
23
|
+
// globals
|
|
24
|
+
ie: !!navigator.userAgent.match(/MSIE/),
|
|
25
|
+
protocol: location.protocol.match(/https/i) ? 'https' : 'http',
|
|
26
|
+
callback: null, // user callback for completed uploads
|
|
27
|
+
swf_url: 'headshot.swf', // URI to headshot.swf movie (defaults to cwd)
|
|
28
|
+
shutter_url: 'shutter.mp3', // URI to shutter.mp3 sound
|
|
29
|
+
api_url: '', // URL to upload script
|
|
30
|
+
loaded: false, // true when headshot movie finishes loading
|
|
31
|
+
quality: 90, // JPEG quality (1 - 100)
|
|
32
|
+
shutter_sound: true, // shutter sound effect on/off
|
|
33
|
+
stealth: false, // stealth mode (do not freeze image upon capture)
|
|
34
|
+
hooks: {
|
|
35
|
+
onLoad: null,
|
|
36
|
+
onComplete: null,
|
|
37
|
+
onError: null
|
|
38
|
+
}, // callback hook functions
|
|
39
|
+
|
|
40
|
+
set_hook: function(name, callback) {
|
|
41
|
+
// set callback hook
|
|
42
|
+
// supported hooks: onLoad, onComplete, onError
|
|
43
|
+
if (typeof(this.hooks[name]) == 'undefined')
|
|
44
|
+
return alert("Hook type not supported: " + name);
|
|
45
|
+
|
|
46
|
+
this.hooks[name] = callback;
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
fire_hook: function(name, value) {
|
|
50
|
+
// fire hook callback, passing optional value to it
|
|
51
|
+
if (this.hooks[name]) {
|
|
52
|
+
if (typeof(this.hooks[name]) == 'function') {
|
|
53
|
+
// callback is function reference, call directly
|
|
54
|
+
this.hooks[name](value);
|
|
55
|
+
}
|
|
56
|
+
else if (typeof(this.hooks[name]) == 'array') {
|
|
57
|
+
// callback is PHP-style object instance method
|
|
58
|
+
this.hooks[name][0][this.hooks[name][1]](value);
|
|
59
|
+
}
|
|
60
|
+
else if (window[this.hooks[name]]) {
|
|
61
|
+
// callback is global function name
|
|
62
|
+
window[ this.hooks[name] ](value);
|
|
63
|
+
}
|
|
64
|
+
else if (typeof(this.hooks[name]) == 'string') {
|
|
65
|
+
// callback is a function but has a namespace
|
|
66
|
+
var funcName = this.hooks[name] + "('" + value + "')";
|
|
67
|
+
eval(funcName);
|
|
68
|
+
}
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
return false; // no hook defined
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
set_api_url: function(url) {
|
|
75
|
+
// set location of upload API script
|
|
76
|
+
this.api_url = url;
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
set_swf_url: function(url) {
|
|
80
|
+
// set location of SWF movie (defaults to headshot.swf in cwd)
|
|
81
|
+
this.swf_url = url;
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
get_html: function(width, height, params) {
|
|
85
|
+
// Return HTML for embedding headshot capture movie
|
|
86
|
+
// Specify pixel width and height (640x480, 320x240, etc.)
|
|
87
|
+
// Server width and height are optional, and default to movie width/height
|
|
88
|
+
server_width = params.server_width ? params.server_width : width;
|
|
89
|
+
server_height = params.server_height ? params.server_height : height;
|
|
90
|
+
session_key_name = params.session_key_name ? params.session_key_name : '';
|
|
91
|
+
csrf_token = params.csrf_token ? params.csrf_token : '';
|
|
92
|
+
|
|
93
|
+
var html = '';
|
|
94
|
+
var flashvars = 'shutter_enabled=' + (this.shutter_sound ? 1 : 0) +
|
|
95
|
+
'&shutter_url=' + escape(this.shutter_url) +
|
|
96
|
+
'&width=' + width +
|
|
97
|
+
'&height=' + height +
|
|
98
|
+
'&server_width=' + server_width +
|
|
99
|
+
'&server_height=' + server_height +
|
|
100
|
+
'&session_key_name=' + session_key_name +
|
|
101
|
+
'&csrf_token=' + csrf_token;
|
|
102
|
+
|
|
103
|
+
if (this.ie) {
|
|
104
|
+
html += '<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="'+this.protocol+'://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" width="'+width+'" height="'+height+'" id="headshot_movie" align="middle"><param name="allowScriptAccess" value="always" /><param name="allowFullScreen" value="false" /><param name="movie" value="'+this.swf_url+'" /><param name="loop" value="false" /><param name="menu" value="false" /><param name="quality" value="best" /><param name="bgcolor" value="#ffffff" /><param name="flashvars" value="'+flashvars+'"/></object>';
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
html += '<embed id="headshot_movie" src="'+this.swf_url+'" loop="false" menu="false" quality="best" bgcolor="#ffffff" width="'+width+'" height="'+height+'" name="headshot_movie" align="middle" allowScriptAccess="always" allowFullScreen="false" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" flashvars="'+flashvars+'" />';
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
this.loaded = false;
|
|
111
|
+
return html;
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
get_movie: function() {
|
|
115
|
+
// get reference to movie object/embed in DOM
|
|
116
|
+
if (!this.loaded) return alert("ERROR: Movie is not loaded yet");
|
|
117
|
+
var movie = document.getElementById('headshot_movie');
|
|
118
|
+
if (!movie) alert("ERROR: Cannot locate movie 'headshot_movie' in DOM");
|
|
119
|
+
return movie;
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
set_stealth: function(stealth) {
|
|
123
|
+
// set or disable stealth mode
|
|
124
|
+
this.stealth = stealth;
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
snap: function(url, callback, stealth) {
|
|
128
|
+
// take snapshot and send to server
|
|
129
|
+
// specify fully-qualified URL to server API script
|
|
130
|
+
// and callback function (string or function object)
|
|
131
|
+
if (callback) this.set_hook('onComplete', callback);
|
|
132
|
+
if (url) this.set_api_url(url);
|
|
133
|
+
if (typeof(stealth) != 'undefined') this.set_stealth( stealth );
|
|
134
|
+
|
|
135
|
+
this.get_movie()._snap( this.api_url, this.quality, this.shutter_sound ? 1 : 0, this.stealth ? 1 : 0 );
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
freeze: function() {
|
|
139
|
+
// freeze headshot image (capture but do not upload)
|
|
140
|
+
this.get_movie()._snap('', this.quality, this.shutter_sound ? 1 : 0, 0 );
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
upload: function(url, callback) {
|
|
144
|
+
// upload image to server after taking snapshot
|
|
145
|
+
// specify fully-qualified URL to server API script
|
|
146
|
+
// and callback function (string or function object)
|
|
147
|
+
if (callback) this.set_hook('onComplete', callback);
|
|
148
|
+
if (url) this.set_api_url(url);
|
|
149
|
+
|
|
150
|
+
this.get_movie()._upload( this.api_url );
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
reset: function() {
|
|
154
|
+
// reset movie after taking snapshot
|
|
155
|
+
this.get_movie()._reset();
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
configure: function(panel) {
|
|
159
|
+
// open flash configuration panel -- specify tab name:
|
|
160
|
+
// "camera", "privacy", "default", "localStorage", "microphone", "settingsManager"
|
|
161
|
+
if (!panel) panel = "camera";
|
|
162
|
+
this.get_movie()._configure(panel);
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
set_quality: function(new_quality) {
|
|
166
|
+
// set the JPEG quality (1 - 100)
|
|
167
|
+
// default is 90
|
|
168
|
+
this.quality = new_quality;
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
set_shutter_sound: function(enabled, url) {
|
|
172
|
+
// enable or disable the shutter sound effect
|
|
173
|
+
// defaults to enabled
|
|
174
|
+
this.shutter_sound = enabled;
|
|
175
|
+
this.shutter_url = url ? url : 'shutter.mp3';
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
flash_notify: function(type, msg) {
|
|
179
|
+
// receive notification from flash about event
|
|
180
|
+
switch (type) {
|
|
181
|
+
case 'flashLoadComplete':
|
|
182
|
+
// movie loaded successfully
|
|
183
|
+
this.loaded = true;
|
|
184
|
+
this.fire_hook('onLoad');
|
|
185
|
+
break;
|
|
186
|
+
|
|
187
|
+
case 'error':
|
|
188
|
+
// HTTP POST error most likely
|
|
189
|
+
if (!this.fire_hook('onError', msg)) {
|
|
190
|
+
alert("Flash Error: " + msg);
|
|
191
|
+
}
|
|
192
|
+
break;
|
|
193
|
+
|
|
194
|
+
case 'success':
|
|
195
|
+
// upload complete, execute user callback function
|
|
196
|
+
// and pass raw API script results to function
|
|
197
|
+
this.fire_hook('onComplete', msg.toString());
|
|
198
|
+
break;
|
|
199
|
+
|
|
200
|
+
default:
|
|
201
|
+
// catch-all, just in case
|
|
202
|
+
alert("flash_notify: " + type + ": " + msg);
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
var headshotUtils = {
|
|
2
|
+
getPageMetaTags:function () {
|
|
3
|
+
return document.getElementsByTagName('meta');
|
|
4
|
+
},
|
|
5
|
+
getPageMetaValue:function (metaName) {
|
|
6
|
+
var metaValue = '';
|
|
7
|
+
var pageMetaTags = headshotUtils.getPageMetaTags();
|
|
8
|
+
if (pageMetaTags.length > 0) {
|
|
9
|
+
for (var i = 0; i < pageMetaTags.length; i++) {
|
|
10
|
+
if (pageMetaTags[i]['name'] == metaName) {
|
|
11
|
+
metaValue = pageMetaTags[i]['content'];
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return metaValue;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
var headshotInitializer = {
|
|
21
|
+
setup: function(width, height, railsTokens) {
|
|
22
|
+
document.write(headshot.get_html(width, height, railsTokens));
|
|
23
|
+
},
|
|
24
|
+
setupHTML: function(width, height, railsTokens) {
|
|
25
|
+
return headshot.get_html(width, height, railsTokens);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
Binary file
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
class CreateHeadshotPhotos < ActiveRecord::Migration
|
|
2
|
+
def change
|
|
3
|
+
create_table :headshot_photos do |t|
|
|
4
|
+
t.string :description
|
|
5
|
+
t.string :image_file_name
|
|
6
|
+
t.string :image_content_type
|
|
7
|
+
t.integer :image_file_size
|
|
8
|
+
t.references :capturable, :polymorphic => true
|
|
9
|
+
t.datetime :image_updated_at
|
|
10
|
+
t.timestamps
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require 'rails'
|
|
2
|
+
|
|
3
|
+
module Headshot
|
|
4
|
+
module Generators
|
|
5
|
+
class ConfigGenerator < ::Rails::Generators::Base
|
|
6
|
+
desc "This generator configures the application so it would be able to communicate with the Headshot Flash binary."
|
|
7
|
+
source_root File.expand_path('../../../../../lib/assets', __FILE__)
|
|
8
|
+
|
|
9
|
+
def copy_flash_middleware
|
|
10
|
+
say_status('copying', 'Copying Flash session cookie middleware ...')
|
|
11
|
+
copy_file('../middlewares/flash_session_cookie_middleware.rb', 'app/middlewares/flash_session_cookie_middleware.rb')
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def load_middlewares_in_application_rb
|
|
15
|
+
application_rb_path = "config/application.rb"
|
|
16
|
+
code_marker = "class Application < Rails::Application"
|
|
17
|
+
loading_code = 'config.autoload_paths += %W(#{config.root}/app/middlewares)'
|
|
18
|
+
say_status('checking', 'Checking if application.rb contains the loading code for the middleware.')
|
|
19
|
+
|
|
20
|
+
inject_into_file application_rb_path, :after => "#{code_marker}\n" do
|
|
21
|
+
"\t\t#{loading_code}\n"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def initialize_session_store
|
|
26
|
+
session_store_rb_path = "config/initializers/session_store.rb"
|
|
27
|
+
loading_code = 'config.autoload_paths += %W(#{config.root}/app/middlewares)'
|
|
28
|
+
say_status('checking', 'Checking if session_store.rb contains the code for initializing for the middleware.')
|
|
29
|
+
initializer_code = "\n"
|
|
30
|
+
|
|
31
|
+
inside(source_paths[0] + '/../initializers', :verbose => true) do
|
|
32
|
+
initializer_code_lines = IO.readlines('session_store.rb')
|
|
33
|
+
initializer_code += initializer_code_lines.join('')
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
append_file(session_store_rb_path, initializer_code)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def create_default_directories
|
|
40
|
+
say_status('creating', 'Creating default headshots directory public/headshots.')
|
|
41
|
+
create_file('public/headshots/empty_file', '')
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'rails'
|
|
2
|
+
require 'rails/generators/migration'
|
|
3
|
+
|
|
4
|
+
module Headshot
|
|
5
|
+
module Generators
|
|
6
|
+
class GenerateGenerator < ::Rails::Generators::Base
|
|
7
|
+
include Rails::Generators::Migration
|
|
8
|
+
desc "This generator creates the template controllers, models, helpers, routes, and migrations."
|
|
9
|
+
source_root File.expand_path('../../../../../', __FILE__)
|
|
10
|
+
|
|
11
|
+
# Copied migration template generator from http://stackoverflow.com/questions/4141739/generators-and-migrations-in-plugins-rails-3
|
|
12
|
+
def self.next_migration_number(path)
|
|
13
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def add_headshot_routes
|
|
17
|
+
route %{post "headshot/capture" => 'headshot#capture', :as => :headshot_capture}
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def create_model_file
|
|
21
|
+
template "lib/app/models/headshot_photo.rb", "app/models/headshot_photo.rb"
|
|
22
|
+
migration_template "lib/db/migrate/create_headshot_photos.rb", "db/migrate/create_headshot_photos.rb"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require 'rails'
|
|
2
|
+
|
|
3
|
+
#if ::Rails.version < '3.1' || !::Rails.application.config.assets.enabled
|
|
4
|
+
|
|
5
|
+
module Headshot
|
|
6
|
+
module Generators
|
|
7
|
+
class InstallGenerator < ::Rails::Generators::Base
|
|
8
|
+
desc "This generator install the Headshot assets (JavaScript, Audio, and Flash application files)."
|
|
9
|
+
source_root File.expand_path('../../../../../lib/assets', __FILE__)
|
|
10
|
+
|
|
11
|
+
def copy_javascripts
|
|
12
|
+
say_status('copying', 'Copying Javascripts ...')
|
|
13
|
+
copy_file('javascripts/headshot.js', 'app/assets/javascripts/headshot.js')
|
|
14
|
+
copy_file('javascripts/headshot_support.js', 'app/assets/javascripts/headshot_support.js')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def copy_audio_files
|
|
18
|
+
say_status('copying', 'Copying Audio files ...')
|
|
19
|
+
copy_file('audio/shutter.mp3', 'public/audio/shutter.mp3')
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def copy_flash_app_files
|
|
23
|
+
say_status('copying', 'Copying Flash application files ...')
|
|
24
|
+
copy_file('swfs/headshot.swf', 'public/swfs/headshot.swf')
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def run_other_generators
|
|
28
|
+
generate("headshot:config")
|
|
29
|
+
generate("headshot:generate")
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# else
|
|
36
|
+
# TODO: what do we do when app is asset-pipelined?
|
|
37
|
+
# end
|
data/lib/headshot.rb
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
require 'active_support/dependencies'
|
|
2
|
+
|
|
3
|
+
%w{controllers helpers lib}.each do |dir|
|
|
4
|
+
path = File.join(File.dirname(__FILE__), 'app', dir)
|
|
5
|
+
$LOAD_PATH << path
|
|
6
|
+
ActiveSupport::Dependencies.autoload_paths << path
|
|
7
|
+
ActionController::Base.helper Headshot
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module Headshot
|
|
11
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Rails.application.config.middleware.insert_before(Rails.application.config.session_store, FlashSessionCookieMiddleware, Rails.application.config.session_options[:key])
|