unity-captcha 1.0.1
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.
- checksums.yaml +7 -0
- data/.DS_Store +0 -0
- data/.gitignore +9 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +165 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/unity/captcha.rb +53 -0
- data/lib/unity/captcha/engine.rb +10 -0
- data/lib/unity/captcha/version.rb +5 -0
- data/unity-captcha.gemspec +35 -0
- data/vendor/.DS_Store +0 -0
- data/vendor/assets/.DS_Store +0 -0
- data/vendor/assets/javascripts/jquery.motionCaptcha.1.0.js +652 -0
- data/vendor/assets/javascripts/jquery.placeholder.1.1.1.min.js +10 -0
- data/vendor/assets/stylesheets/jquery.motionCaptcha.1.0.css +56 -0
- data/vendor/assets/stylesheets/motionCaptcha-shapes.jpg +0 -0
- metadata +96 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 674e45a2d8771ef6be513c4e0699fe7fd02ddae9
|
4
|
+
data.tar.gz: 99ef29699c26e68c8478494669be994ba4afb847
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 555ee315a14a243b5bceaef420f0005d335565f1d00600581ee291527583997a5fb019abfc9e6ecaba75168945c81bc5370fdd2c9291016cc2ba324859946821
|
7
|
+
data.tar.gz: bc433a006182033752483ef1499fb8a0f42258b5beb1909d0418cb17f72b1244f77e67395cc3c1bae0ec69833576c6758fcb870aad18b47df7860f73386c253f
|
data/.DS_Store
ADDED
Binary file
|
data/.gitignore
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
8
|
+
size, disability, ethnicity, gender identity and expression, level of experience,
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity and
|
10
|
+
orientation.
|
11
|
+
|
12
|
+
## Our Standards
|
13
|
+
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
15
|
+
include:
|
16
|
+
|
17
|
+
* Using welcoming and inclusive language
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
19
|
+
* Gracefully accepting constructive criticism
|
20
|
+
* Focusing on what is best for the community
|
21
|
+
* Showing empathy towards other community members
|
22
|
+
|
23
|
+
Examples of unacceptable behavior by participants include:
|
24
|
+
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26
|
+
advances
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28
|
+
* Public or private harassment
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
30
|
+
address, without explicit permission
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
32
|
+
professional setting
|
33
|
+
|
34
|
+
## Our Responsibilities
|
35
|
+
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
38
|
+
response to any instances of unacceptable behavior.
|
39
|
+
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
44
|
+
threatening, offensive, or harmful.
|
45
|
+
|
46
|
+
## Scope
|
47
|
+
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
49
|
+
when an individual is representing the project or its community. Examples of
|
50
|
+
representing a project or community include using an official project e-mail
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
53
|
+
further defined and clarified by project maintainers.
|
54
|
+
|
55
|
+
## Enforcement
|
56
|
+
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
+
reported by contacting the project team at papayalabs@gmail.com. All
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
63
|
+
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
66
|
+
members of the project's leadership.
|
67
|
+
|
68
|
+
## Attribution
|
69
|
+
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71
|
+
available at [http://contributor-covenant.org/version/1/4][version]
|
72
|
+
|
73
|
+
[homepage]: http://contributor-covenant.org
|
74
|
+
[version]: http://contributor-covenant.org/version/1/4/
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Papaya Labs
|
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,165 @@
|
|
1
|
+
# Unity Captcha
|
2
|
+
|
3
|
+
Unity Captcha is a gem that use two levels of Captcha. First level is about Left Hemisphere: Logic and Math. Second level is about Right Hemisphere: Intuition and Drawing. This two levels functioning in complete harmony creates unity and this is the base design of this Captcha.
|
4
|
+
|
5
|
+
The Left Hemisphere Level use a simple question of addition and multiplication. This works in the server side.
|
6
|
+
|
7
|
+
The Right Hemisphere Level use MotionCAPTCHA (https://github.com/wjcrowcroft/MotionCAPTCHA), that is a jQuery CAPTCHA plugin that requires users to sketch the shape they see in the canvas in order to submit a form. This works in the client side.
|
8
|
+
|
9
|
+
Dra. Jill Bolte Taylor got a research opportunity to study from inside of her brain how Left and Right Hemisphere works. I recommend to watch her in this video: https://www.ted.com/talks/jill_bolte_taylor_s_powerful_stroke_of_insight#t-1102370
|
10
|
+
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
Add this line to your application's Gemfile:
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
gem 'unity-captcha'
|
18
|
+
```
|
19
|
+
|
20
|
+
And then execute:
|
21
|
+
|
22
|
+
$ bundle
|
23
|
+
|
24
|
+
Or install it yourself as:
|
25
|
+
|
26
|
+
$ gem install unity-captcha
|
27
|
+
|
28
|
+
## Usage
|
29
|
+
|
30
|
+
For the First level - Left Hemisphere: Logic and Math ( Server Side ) we use some code in the controller and the view.
|
31
|
+
|
32
|
+
1. In your controller, for example if you have a method inside the controller called 'send_invite':
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
class InviteController < ApplicationController
|
36
|
+
def new
|
37
|
+
@captcha = Unity::Captcha::Captcha.new
|
38
|
+
end
|
39
|
+
def send_invite
|
40
|
+
@captcha = Unity::Captcha::Captcha.decrypt(params[:captcha_secret])
|
41
|
+
email = params[:friend_email]
|
42
|
+
unless @captcha.correct?(params[:captcha])
|
43
|
+
@captcha = Unity::Captcha::Captcha.new
|
44
|
+
redirect_to invite_url, :alert => "Please make sure you entered correct value for captcha."
|
45
|
+
else
|
46
|
+
InviteMailer.invite_email(current_user, email).deliver_now
|
47
|
+
flash[:notice] = "Thank you for inviting your friend! Please feel free to invite more!"
|
48
|
+
redirect_to invite_url
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
2. Ask the question in the form, example:
|
55
|
+
|
56
|
+
```erb
|
57
|
+
<%= form_tag(send_invite_path, :method => :post) do %>
|
58
|
+
<%= label_tag :friend_email, "Email" %>
|
59
|
+
<%= text_field_tag :friend_email, nil, :size =>50, :class => "placeholder" %>
|
60
|
+
<%= label_tag :question, "Question: "+@captcha.question %>
|
61
|
+
<%= text_field_tag :captcha %>
|
62
|
+
<%= hidden_field_tag :captcha_secret, @captcha.encrypt %>
|
63
|
+
|
64
|
+
<%= submit_tag "Send Invitation" %>
|
65
|
+
<% end %>
|
66
|
+
```
|
67
|
+
|
68
|
+
For the Second level - Right Hemisphere: Intuition and Drawing ( Cliend Side ) we just need to add some code in the form and make sure to add the plugin scripts: (MotionCAPTCHA is supported down to jQuery 1.4)
|
69
|
+
|
70
|
+
1. Adding plugin scripts ( usually added in application.html.erb ):
|
71
|
+
|
72
|
+
```erb
|
73
|
+
<%= javascript_include_tag "https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"%>
|
74
|
+
<%= stylesheet_link_tag "jquery.motionCaptcha.1.0" %>
|
75
|
+
<%= javascript_include_tag "jquery.motionCaptcha.1.0" %>
|
76
|
+
<%= javascript_include_tag "jquery.placeholder.1.1.1.min" %>
|
77
|
+
```
|
78
|
+
|
79
|
+
2. Replace the submit_tag with the CAPTCHA canvas and a hidden_field_tag, like the following code ( remember to add :id => "mc-form" in form):
|
80
|
+
|
81
|
+
```erb
|
82
|
+
<%= form_tag(send_invite_path, :method => :post, :id => "mc-form") do %>
|
83
|
+
<%= label_tag :friend_email, "Email" %>
|
84
|
+
<%= text_field_tag :friend_email, nil, :size =>50, :class => "placeholder" %
|
85
|
+
<%= label_tag :question, "Question: "+@captcha.question %>
|
86
|
+
<%= text_field_tag :captcha %>
|
87
|
+
<%= hidden_field_tag :captcha_secret, @captcha.encrypt %>
|
88
|
+
|
89
|
+
<p>CAPTCHA: Please draw the shape in the box to submit the form: (<a onclick="window.location.reload()" href="#" title="Click for a new shape">new shape</a>)</p>
|
90
|
+
<canvas id="mc-canvas"></canvas>
|
91
|
+
<%= hidden_field_tag 'mc-action', send_invite_path.to_s %>
|
92
|
+
<% end %>
|
93
|
+
```
|
94
|
+
|
95
|
+
You can also use simple_form:
|
96
|
+
|
97
|
+
```erb
|
98
|
+
<%= simple_form_for send_invite_path, :html => {:class => 'form-horizontal',:id => "mc-form" } do |f| %>
|
99
|
+
<%= f.input :friend_email, label: 'Email',:class => "placeholder", :required => true %>
|
100
|
+
<%= f.input :captcha, label: @captcha.question, :required => true %>
|
101
|
+
<%= f.input :captcha_secret, :as => :hidden, :input_html => { :value => @captcha.encrypt } %>
|
102
|
+
|
103
|
+
<p>CAPTCHA: Please draw the shape in the box to submit the form: (<a onclick="window.location.reload()" href="#" title="Click for a new shape">new shape</a>)</p>
|
104
|
+
<canvas id="mc-canvas"></canvas>
|
105
|
+
<%= f.input :mc_action, :as => :hidden, :input_html => { :value => send_invite_path.to_s } %>
|
106
|
+
<% end %>
|
107
|
+
```
|
108
|
+
|
109
|
+
3. Initialize the javascrit component in the same form:
|
110
|
+
|
111
|
+
```erb
|
112
|
+
<script type="text/javaScript">
|
113
|
+
jQuery(document).ready(function($) {
|
114
|
+
$('#mc-form').motionCaptcha({
|
115
|
+
shapes: ['triangle', 'x', 'rectangle', 'circle', 'check', 'zigzag', 'arrow', 'delete', 'pigtail', 'star']
|
116
|
+
});
|
117
|
+
$("input.placeholder").placeholder();
|
118
|
+
});
|
119
|
+
</script>
|
120
|
+
```
|
121
|
+
|
122
|
+
4. Other options are available to initialize the canvas javascript component:
|
123
|
+
|
124
|
+
```erb
|
125
|
+
<script type="text/javaScript">
|
126
|
+
jQuery(document).ready(function($) {
|
127
|
+
$('#mc-form').motioncaptcha({
|
128
|
+
// Basics:
|
129
|
+
action: '#mc-action', // the ID of the input containing the form action
|
130
|
+
divId: '#mc', // if you use an ID other than '#mc' for the placeholder, pass it in here
|
131
|
+
cssClass: '.mc-active', // this CSS class is applied to the 'mc' div when the plugin is active
|
132
|
+
canvasId: '#mc-canvas', // the ID of the MotionCAPTCHA canvas element
|
133
|
+
|
134
|
+
// An array of shape names that you want MotionCAPTCHA to use:
|
135
|
+
shapes: ['triangle', 'x', 'rectangle', 'circle', 'check', 'caret', 'zigzag', 'arrow', 'leftbracket', 'rightbracket', 'v', 'delete', 'star', 'pigtail'],
|
136
|
+
|
137
|
+
// These messages are displayed inside the canvas after a user finishes drawing:
|
138
|
+
errorMsg: 'Please try again.',
|
139
|
+
successMsg: 'Captcha passed!',
|
140
|
+
|
141
|
+
// This message is displayed if the user's browser doesn't support canvas:
|
142
|
+
noCanvasMsg: "Your browser doesn't support <canvas> - try Chrome, FF4, Safari or IE9."
|
143
|
+
|
144
|
+
// This could be any HTML string (eg. '<label>Draw this shit yo:</label>'):
|
145
|
+
label: '<p>Please draw the shape in the box to submit the form:</p>'
|
146
|
+
});
|
147
|
+
});
|
148
|
+
</script>
|
149
|
+
```
|
150
|
+
|
151
|
+
## Development
|
152
|
+
|
153
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
154
|
+
|
155
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
156
|
+
|
157
|
+
## Contributing
|
158
|
+
|
159
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/papayalabs/unity-captcha. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
160
|
+
|
161
|
+
|
162
|
+
## License
|
163
|
+
|
164
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
165
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "unity/captcha"
|
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,53 @@
|
|
1
|
+
require "unity/captcha/version"
|
2
|
+
require "unity/captcha/engine"
|
3
|
+
|
4
|
+
module Unity
|
5
|
+
module Captcha
|
6
|
+
class Captcha
|
7
|
+
attr_accessor :operand1, :operand2, :operator
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@operand1 = (1..10).to_a.sample
|
11
|
+
@operand2 = (1..10).to_a.sample
|
12
|
+
@operator = [:+, :*].sample
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize_from(secret)
|
16
|
+
yml = YAML.load(Base64.decode64(secret))
|
17
|
+
@operand1, @operand2, @operator = yml[:operand1], yml[:operand2], yml[:operator]
|
18
|
+
end
|
19
|
+
|
20
|
+
def correct?(value)
|
21
|
+
result == value.to_i
|
22
|
+
end
|
23
|
+
|
24
|
+
def encrypt
|
25
|
+
Base64.encode64 to_yaml
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.decrypt(secret)
|
29
|
+
result = new
|
30
|
+
result.initialize_from secret
|
31
|
+
result
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_yaml
|
35
|
+
YAML::dump({
|
36
|
+
:operand1 => @operand1,
|
37
|
+
:operand2 => @operand2,
|
38
|
+
:operator => @operator
|
39
|
+
})
|
40
|
+
end
|
41
|
+
|
42
|
+
def question
|
43
|
+
"What is #{@operand1} #{@operator.to_s} #{@operand2} = ?"
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def result
|
49
|
+
@operand1.send @operator, @operand2
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'unity/captcha/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "unity-captcha"
|
8
|
+
spec.version = Unity::Captcha::VERSION
|
9
|
+
spec.authors = ["Manuel Fernandez"]
|
10
|
+
spec.email = ["papayalabs@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Unity Captcha is a gem that use two levels of captcha based on the hemispheres of the brain}
|
13
|
+
spec.description = %q{Unity Captcha is a gem that use two levels of Captcha. First level is about Left Hemisphere: Logic and Math. Second level is about Right Hemisphere: Intuition and Drawing. This two levels functioning in complete harmony creates unity and this is the base design of this Captcha.}
|
14
|
+
spec.homepage = "https://github.com/papayalabs/unity-captcha"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
18
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
19
|
+
if spec.respond_to?(:metadata)
|
20
|
+
spec.metadata['allowed_push_host'] = "https://rubygems.org"
|
21
|
+
else
|
22
|
+
raise "RubyGems 2.0 or newer is required to protect against " \
|
23
|
+
"public gem pushes."
|
24
|
+
end
|
25
|
+
|
26
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
27
|
+
f.match(%r{^(test|spec|features)/})
|
28
|
+
end
|
29
|
+
spec.bindir = "exe"
|
30
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
31
|
+
spec.require_paths = ["lib"]
|
32
|
+
|
33
|
+
spec.add_development_dependency "bundler", "~> 1.13"
|
34
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
35
|
+
end
|
data/vendor/.DS_Store
ADDED
Binary file
|
Binary file
|
@@ -0,0 +1,652 @@
|
|
1
|
+
/*!
|
2
|
+
* jQuery MotionCAPTCHA v1.0
|
3
|
+
*
|
4
|
+
* Concept and Development by Joss Crowcroft
|
5
|
+
* http://www.josscrowcroft.com
|
6
|
+
*
|
7
|
+
* Adapted and modified by Manuel Fernandez
|
8
|
+
* http://papayalabs.github.io
|
9
|
+
*
|
10
|
+
* Copyright (c) 2017 Papaya Labs | http://papayalabs.github.io
|
11
|
+
*
|
12
|
+
* Incoporates other open source projects, attributed below.
|
13
|
+
*/
|
14
|
+
jQuery.fn.motionCaptcha || (function($) {
|
15
|
+
|
16
|
+
/**
|
17
|
+
* Main plugin function definition
|
18
|
+
*/
|
19
|
+
$.fn.motionCaptcha = function(options) {
|
20
|
+
|
21
|
+
/**
|
22
|
+
* Act on matched form element:
|
23
|
+
* This could be set up to iterate over multiple elements, but tbh would it ever be useful?
|
24
|
+
*/
|
25
|
+
return this.each(function() {
|
26
|
+
|
27
|
+
// Build main options before element iteration:
|
28
|
+
var opts = $.extend({}, $.fn.motionCaptcha.defaults, options);
|
29
|
+
|
30
|
+
// Ensure option ID params are valid #selectors:
|
31
|
+
opts.actionId = '#' + opts.actionId.replace(/\#/g, '');
|
32
|
+
opts.canvasId = '#' + opts.canvasId.replace(/\#/g, '');
|
33
|
+
opts.divId = '#' + opts.divId.replace(/\#/g, '');
|
34
|
+
opts.submitId = ( opts.submitId ) ? '#' + opts.submitId.replace(/\#/g, '') : false;
|
35
|
+
|
36
|
+
// Plugin setup:
|
37
|
+
|
38
|
+
// Set up Harmony vars:
|
39
|
+
var brush,
|
40
|
+
locked = false;
|
41
|
+
|
42
|
+
// Set up MotionCAPTCHA form and jQuery elements:
|
43
|
+
var $body = $('body'),
|
44
|
+
$form = $(this),
|
45
|
+
$container = $(opts.divId),
|
46
|
+
$canvas = $(opts.canvasId);
|
47
|
+
|
48
|
+
// Set up MotionCAPTCHA canvas vars:
|
49
|
+
var canvasWidth = $canvas.width(),
|
50
|
+
canvasHeight = $canvas.height(),
|
51
|
+
borderLeftWidth = 1 * $canvas.css('borderLeftWidth').replace('px', ''),
|
52
|
+
borderTopWidth = 1 * $canvas.css('borderTopWidth').replace('px', '');
|
53
|
+
|
54
|
+
// Canvas setup:
|
55
|
+
|
56
|
+
// Set the canvas DOM element's dimensions to match the display width/height (pretty important):
|
57
|
+
$canvas[0].width = canvasWidth;
|
58
|
+
$canvas[0].height = canvasHeight;
|
59
|
+
|
60
|
+
// Get DOM reference to canvas context:
|
61
|
+
var ctx = $canvas[0].getContext("2d");
|
62
|
+
|
63
|
+
// Add canvasWidth and canvasHeight values to context, for Ribbon brush:
|
64
|
+
ctx.canvasWidth = canvasWidth;
|
65
|
+
ctx.canvasHeight = canvasHeight;
|
66
|
+
|
67
|
+
// Set canvas context font and fillStyle:
|
68
|
+
ctx.font = opts.canvasFont;
|
69
|
+
ctx.fillStyle = opts.canvasTextColor;
|
70
|
+
|
71
|
+
// Set random shape
|
72
|
+
$canvas.addClass( opts.shapes[Math.floor(Math.random() * (opts.shapes.length) )] );
|
73
|
+
|
74
|
+
// Set up Dollar Recognizer and drawing vars:
|
75
|
+
var _isDown = false,
|
76
|
+
_holdStill = false,
|
77
|
+
_points = [],
|
78
|
+
_r = new DollarRecognizer();
|
79
|
+
|
80
|
+
// Create the Harmony Ribbon brush:
|
81
|
+
brush = new Ribbon(ctx);
|
82
|
+
|
83
|
+
|
84
|
+
|
85
|
+
|
86
|
+
// Mousedown event
|
87
|
+
// Start Harmony brushstroke and begin recording DR points:
|
88
|
+
var touchStartEvent = function(event) {
|
89
|
+
if ( locked )
|
90
|
+
return false;
|
91
|
+
|
92
|
+
// Prevent default action:
|
93
|
+
event.preventDefault();
|
94
|
+
|
95
|
+
// Get mouse position inside the canvas:
|
96
|
+
var pos = getPos(event),
|
97
|
+
x = pos[0],
|
98
|
+
y = pos[1];
|
99
|
+
|
100
|
+
// Internal drawing var
|
101
|
+
_isDown = true;
|
102
|
+
|
103
|
+
// Prevent jumpy-touch bug on android, no effect on other platforms:
|
104
|
+
_holdStill = true;
|
105
|
+
|
106
|
+
// Disable text selection:
|
107
|
+
$('body').addClass('mc-noselect');
|
108
|
+
|
109
|
+
// Clear canvas:
|
110
|
+
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
|
111
|
+
|
112
|
+
// Start brushstroke:
|
113
|
+
brush.strokeStart(x, y);
|
114
|
+
|
115
|
+
// Remove 'mc-invalid' and 'mc-valid' classes from canvas:
|
116
|
+
$canvas.removeClass('mc-invalid mc-valid');
|
117
|
+
|
118
|
+
// Add the first point to the points array:
|
119
|
+
_points = [NewPoint(x, y)];
|
120
|
+
|
121
|
+
return false;
|
122
|
+
}; // mousedown/touchstart event
|
123
|
+
|
124
|
+
// Mousemove event:
|
125
|
+
var touchMoveEvent = function(event) {
|
126
|
+
if ( _holdStill ) {
|
127
|
+
return _holdStill = 0;
|
128
|
+
}
|
129
|
+
// If mouse is down and canvas not locked:
|
130
|
+
if ( !locked && _isDown ) {
|
131
|
+
|
132
|
+
// Prevent default action:
|
133
|
+
event.preventDefault();
|
134
|
+
|
135
|
+
// Get mouse position inside the canvas:
|
136
|
+
var pos = getPos(event),
|
137
|
+
x = pos[0],
|
138
|
+
y = pos[1];
|
139
|
+
|
140
|
+
// Append point to points array:
|
141
|
+
_points[_points.length] = NewPoint(x, y);
|
142
|
+
|
143
|
+
// Do brushstroke:
|
144
|
+
brush.stroke(x, y);
|
145
|
+
}
|
146
|
+
return false;
|
147
|
+
}; // mousemove/touchmove event
|
148
|
+
|
149
|
+
|
150
|
+
// Mouseup event:
|
151
|
+
var touchEndEvent = function(event) {
|
152
|
+
// If mouse is down and canvas not locked:
|
153
|
+
if ( !locked && _isDown ) {
|
154
|
+
_isDown = false;
|
155
|
+
|
156
|
+
// Allow text-selection again:
|
157
|
+
$('body').removeClass('mc-noselect');
|
158
|
+
|
159
|
+
// Dollar Recognizer result:
|
160
|
+
if (_points.length >= 10) {
|
161
|
+
var result = _r.Recognize(_points);
|
162
|
+
// Check result:
|
163
|
+
if ( $canvas.attr('class').match(result.Name) && result.Score > 0.7 ) {
|
164
|
+
|
165
|
+
// Lock the canvas:
|
166
|
+
locked = 1;
|
167
|
+
|
168
|
+
// Destroy the Harmony brush (give it time to finish drawing)
|
169
|
+
setTimeout( brush.destroy, 500 );
|
170
|
+
|
171
|
+
// Add 'mc-valid' class to canvas:
|
172
|
+
$canvas.addClass('mc-valid');
|
173
|
+
|
174
|
+
// Write success message into canvas:
|
175
|
+
ctx.fillText(opts.successMsg, 10, 24);
|
176
|
+
|
177
|
+
// Call the onSuccess function to handle the rest of the business:
|
178
|
+
// Pass in the form, the canvas, the canvas context:
|
179
|
+
opts.onSuccess($form, $canvas, ctx);
|
180
|
+
|
181
|
+
} else {
|
182
|
+
|
183
|
+
// Add 'mc-invalid' class to canvas:
|
184
|
+
$canvas.addClass('mc-invalid');
|
185
|
+
|
186
|
+
// Write error message into canvas:
|
187
|
+
ctx.fillText(opts.errorMsg, 10, 24);
|
188
|
+
|
189
|
+
// Pass off to the error callback to finish up:
|
190
|
+
opts.onError($form, $canvas, ctx);
|
191
|
+
}
|
192
|
+
|
193
|
+
} else { // fewer than 10 points were recorded:
|
194
|
+
|
195
|
+
// Add 'mc-invalid' class to canvas:
|
196
|
+
$canvas.addClass('mc-invalid');
|
197
|
+
|
198
|
+
// Write error message into canvas:
|
199
|
+
ctx.fillText(opts.errorMsg, 10, 24);
|
200
|
+
|
201
|
+
// Pass off to the error callback to finish up:
|
202
|
+
opts.onError($form, $canvas, ctx);
|
203
|
+
}
|
204
|
+
}
|
205
|
+
return false;
|
206
|
+
}; // mouseup/touchend event
|
207
|
+
|
208
|
+
// Bind events to canvas:
|
209
|
+
$canvas.bind({
|
210
|
+
mousedown: touchStartEvent,
|
211
|
+
mousemove: touchMoveEvent,
|
212
|
+
mouseup: touchEndEvent,
|
213
|
+
});
|
214
|
+
|
215
|
+
// Mobile touch events:
|
216
|
+
$canvas[0].addEventListener('touchstart', touchStartEvent, false);
|
217
|
+
$canvas[0].addEventListener('touchmove', touchMoveEvent, false);
|
218
|
+
$canvas[0].addEventListener('touchend', touchEndEvent, false);
|
219
|
+
|
220
|
+
// Add active CSS class to form:
|
221
|
+
$form.addClass(opts.cssClass.replace(/\./, ''))
|
222
|
+
|
223
|
+
|
224
|
+
/**
|
225
|
+
* Get X/Y mouse position, relative to (/inside) the canvas
|
226
|
+
*
|
227
|
+
* Handles cross-browser quirks rather nicely, I feel.
|
228
|
+
*
|
229
|
+
* @todo For 1.0, if no way to obtain coordinates, don't activate MotionCAPTCHA.
|
230
|
+
*/
|
231
|
+
function getPos(event) {
|
232
|
+
var x, y;
|
233
|
+
|
234
|
+
// Check for mobile first to avoid android jumpy-touch bug (iOS / Android):
|
235
|
+
if ( event.touches && event.touches.length > 0 ) {
|
236
|
+
// iOS/android uses event.touches, relative to entire page:
|
237
|
+
x = event.touches[0].pageX - $canvas.offset().left + borderLeftWidth;
|
238
|
+
y = event.touches[0].pageY - $canvas.offset().top + borderTopWidth;
|
239
|
+
} else if ( event.offsetX ) {
|
240
|
+
// Chrome/Safari give the event offset relative to the target event:
|
241
|
+
x = event.offsetX - borderLeftWidth;
|
242
|
+
y = event.offsetY - borderTopWidth;
|
243
|
+
} else {
|
244
|
+
// Otherwise, subtract page click from canvas offset (Firefox uses this):
|
245
|
+
x = event.pageX - $canvas.offset().left - borderLeftWidth;
|
246
|
+
y = event.pageY - $canvas.offset().top - borderTopWidth;
|
247
|
+
}
|
248
|
+
return [x,y];
|
249
|
+
}
|
250
|
+
|
251
|
+
}); // this.each
|
252
|
+
|
253
|
+
} // end main plugin function
|
254
|
+
|
255
|
+
|
256
|
+
/**
|
257
|
+
* Exposed default plugin settings, which can be overridden in plugin call.
|
258
|
+
*/
|
259
|
+
$.fn.motionCaptcha.defaults = {
|
260
|
+
actionId: '#mc-action', // The ID of the input containing the form action
|
261
|
+
divId: '#mc', // If you use an ID other than '#mc' for the placeholder, pass it in here
|
262
|
+
canvasId: '#mc-canvas', // The ID of the MotionCAPTCHA canvas element
|
263
|
+
submitId: false, // If your form has multiple submit buttons, give the ID of the main one here
|
264
|
+
cssClass: '.mc-active', // This CSS class is applied to the form, when the plugin is active
|
265
|
+
|
266
|
+
// An array of shape names that you want MotionCAPTCHA to use:
|
267
|
+
shapes: ['triangle', 'x', 'rectangle', 'circle', 'check', 'caret', 'zigzag', 'arrow', 'leftbracket', 'rightbracket', 'v', 'delete', 'star', 'pigtail'],
|
268
|
+
|
269
|
+
// Canvas vars:
|
270
|
+
canvasFont: '15px "Lucida Grande"',
|
271
|
+
canvasTextColor: '#111',
|
272
|
+
|
273
|
+
// These messages are displayed inside the canvas after a user finishes drawing:
|
274
|
+
errorMsg: 'Please try again.',
|
275
|
+
successMsg: 'Captcha passed!',
|
276
|
+
|
277
|
+
// This message is displayed if the user's browser doesn't support canvas:
|
278
|
+
noCanvasMsg: "Your browser doesn't support <canvas> - try Chrome, FF4, Safari or IE9.",
|
279
|
+
|
280
|
+
// This could be any HTML string (eg. '<label>Draw this shit yo:</label>'):
|
281
|
+
label: '<p>Please draw the shape in the box to submit the form:</p>',
|
282
|
+
|
283
|
+
// Callback function to execute when a user successfully draws the shape
|
284
|
+
// Passed in the form, the canvas and the canvas context
|
285
|
+
// Scope (this) is active plugin options object (opts)
|
286
|
+
// NB: The default onSuccess callback function enables the submit button, and adds the form action attribute:
|
287
|
+
onSuccess: function($form, $canvas, ctx) {
|
288
|
+
var opts = this,
|
289
|
+
$submit = opts.submitId ? $form.find(opts.submitId) : $form.find('input[type=submit]:disabled');
|
290
|
+
|
291
|
+
// Set the form action:
|
292
|
+
$form.attr( 'action', $(opts.actionId).val() );
|
293
|
+
|
294
|
+
// Enable the submit button:
|
295
|
+
//$submit.prop('disabled', false);
|
296
|
+
$form.submit();
|
297
|
+
return;
|
298
|
+
},
|
299
|
+
|
300
|
+
// Callback function to execute when a user successfully draws the shape
|
301
|
+
// Passed in the form, the canvas and the canvas context
|
302
|
+
// Scope (this) is active plugin options object (opts)
|
303
|
+
onError: function($form, $canvas, ctx) {
|
304
|
+
var opts = this;
|
305
|
+
return;
|
306
|
+
}
|
307
|
+
};
|
308
|
+
|
309
|
+
|
310
|
+
|
311
|
+
|
312
|
+
|
313
|
+
/*!
|
314
|
+
* Harmony | mrdoob | Ribbon Brush class
|
315
|
+
* http://mrdoob.com/projects/harmony/
|
316
|
+
*/
|
317
|
+
|
318
|
+
function Ribbon( ctx ) {
|
319
|
+
this.init( ctx );
|
320
|
+
}
|
321
|
+
|
322
|
+
Ribbon.prototype = {
|
323
|
+
ctx: null,
|
324
|
+
X: null,
|
325
|
+
Y: null,
|
326
|
+
painters: null,
|
327
|
+
interval: null,
|
328
|
+
init: function( ctx ) {
|
329
|
+
var scope = this,
|
330
|
+
userAgent = navigator.userAgent.toLowerCase(),
|
331
|
+
brushSize = ( userAgent.search("android") > -1 || userAgent.search("iphone") > -1 ) ? 2 : 1,
|
332
|
+
strokeColor = [0, 0, 0];
|
333
|
+
|
334
|
+
this.ctx = ctx;
|
335
|
+
this.ctx.globalCompositeOperation = 'source-over';
|
336
|
+
|
337
|
+
this.X = this.ctx.canvasWidth / 2;
|
338
|
+
this.Y = this.ctx.canvasHeight / 2;
|
339
|
+
|
340
|
+
this.painters = [];
|
341
|
+
|
342
|
+
// Draw each of the lines:
|
343
|
+
for ( var i = 0; i < 38; i++ ) {
|
344
|
+
this.painters.push({
|
345
|
+
dx: this.ctx.canvasWidth / 2,
|
346
|
+
dy: this.ctx.canvasHeight / 2,
|
347
|
+
ax: 0,
|
348
|
+
ay: 0,
|
349
|
+
div: 0.1,
|
350
|
+
ease: Math.random() * 0.18 + 0.60
|
351
|
+
});
|
352
|
+
}
|
353
|
+
|
354
|
+
// Set the ticker:
|
355
|
+
this.interval = setInterval( update, 1000/60 );
|
356
|
+
|
357
|
+
function update() {
|
358
|
+
var i;
|
359
|
+
|
360
|
+
scope.ctx.lineWidth = brushSize;
|
361
|
+
scope.ctx.strokeStyle = "rgba(" + strokeColor[0] + ", " + strokeColor[1] + ", " + strokeColor[2] + ", " + 0.06 + ")";
|
362
|
+
|
363
|
+
for ( i = 0; i < scope.painters.length; i++ ) {
|
364
|
+
scope.ctx.beginPath();
|
365
|
+
scope.ctx.moveTo(scope.painters[i].dx, scope.painters[i].dy);
|
366
|
+
|
367
|
+
scope.painters[i].dx -= scope.painters[i].ax = (scope.painters[i].ax + (scope.painters[i].dx - scope.X) * scope.painters[i].div) * scope.painters[i].ease;
|
368
|
+
scope.painters[i].dy -= scope.painters[i].ay = (scope.painters[i].ay + (scope.painters[i].dy - scope.Y) * scope.painters[i].div) * scope.painters[i].ease;
|
369
|
+
scope.ctx.lineTo(scope.painters[i].dx, scope.painters[i].dy);
|
370
|
+
scope.ctx.stroke();
|
371
|
+
}
|
372
|
+
}
|
373
|
+
},
|
374
|
+
destroy: function() {
|
375
|
+
clearInterval(this.interval);
|
376
|
+
},
|
377
|
+
strokeStart: function( X, Y ) {
|
378
|
+
this.X = X;
|
379
|
+
this.Y = Y
|
380
|
+
|
381
|
+
for (var i = 0; i < this.painters.length; i++) {
|
382
|
+
this.painters[i].dx = X;
|
383
|
+
this.painters[i].dy = Y;
|
384
|
+
}
|
385
|
+
|
386
|
+
this.shouldDraw = true;
|
387
|
+
},
|
388
|
+
stroke: function( X, Y ) {
|
389
|
+
this.X = X;
|
390
|
+
this.Y = Y;
|
391
|
+
}
|
392
|
+
};
|
393
|
+
|
394
|
+
|
395
|
+
|
396
|
+
/*!
|
397
|
+
* The $1 Unistroke Recognizer
|
398
|
+
* http://depts.washington.edu/aimgroup/proj/dollar/
|
399
|
+
*
|
400
|
+
* Jacob O. Wobbrock, Ph.D. | wobbrock@u.washington.edu
|
401
|
+
* Andrew D. Wilson, Ph.D. | awilson@microsoft.com
|
402
|
+
* Yang Li, Ph.D. | yangli@cs.washington.edu
|
403
|
+
*
|
404
|
+
* Modified to include the Protractor gesture recognizing algorithm
|
405
|
+
* http://www.yangl.org/pdf/protractor-chi2010.pdf
|
406
|
+
*
|
407
|
+
* Adapted and modified for purpose by Joss Crowcroft
|
408
|
+
* http://www.josscrowcroft.com
|
409
|
+
*
|
410
|
+
* The original software is distributed under the "New BSD License" agreement
|
411
|
+
*
|
412
|
+
* Copyright (c) 2007-2011, Jacob O. Wobbrock, Andrew D. Wilson and Yang Li. All rights reserved.
|
413
|
+
**/
|
414
|
+
|
415
|
+
// Point class
|
416
|
+
function Point(x, y) {
|
417
|
+
this.X = x;
|
418
|
+
this.Y = y;
|
419
|
+
}
|
420
|
+
|
421
|
+
// Wrapper for Point class (saves mega kb when compressing the template definitions):
|
422
|
+
function NewPoint(x, y) {
|
423
|
+
return new Point(x, y)
|
424
|
+
}
|
425
|
+
|
426
|
+
// Rectangle class
|
427
|
+
function Rectangle(x, y, width, height) {
|
428
|
+
this.X = x;
|
429
|
+
this.Y = y;
|
430
|
+
this.Width = width;
|
431
|
+
this.Height = height;
|
432
|
+
}
|
433
|
+
|
434
|
+
// Template class: a unistroke template
|
435
|
+
function Template(name, points) {
|
436
|
+
this.Name = name;
|
437
|
+
this.Points = Resample(points, NumPoints);
|
438
|
+
var radians = IndicativeAngle(this.Points);
|
439
|
+
this.Points = RotateBy(this.Points, -radians);
|
440
|
+
this.Points = ScaleTo(this.Points, SquareSize);
|
441
|
+
this.Points = TranslateTo(this.Points, Origin);
|
442
|
+
this.Vector = Vectorize(this.Points); // for Protractor
|
443
|
+
}
|
444
|
+
|
445
|
+
// Result class
|
446
|
+
function Result(name, score) {
|
447
|
+
this.Name = name;
|
448
|
+
this.Score = score;
|
449
|
+
}
|
450
|
+
|
451
|
+
// DollarRecognizer class constants
|
452
|
+
var NumTemplates = 16,
|
453
|
+
NumPoints = 64,
|
454
|
+
SquareSize = 250.0,
|
455
|
+
Origin = NewPoint(0,0);
|
456
|
+
|
457
|
+
// DollarRecognizer class
|
458
|
+
function DollarRecognizer() {
|
459
|
+
|
460
|
+
// Predefined templates for each gesture type:
|
461
|
+
this.Templates = [];
|
462
|
+
|
463
|
+
this.Templates.push( new Template("triangle", [NewPoint(137,139),NewPoint(135,141),NewPoint(133,144),NewPoint(132,146),NewPoint(130,149),NewPoint(128,151),NewPoint(126,155),NewPoint(123,160),NewPoint(120,166),NewPoint(116,171),NewPoint(112,177),NewPoint(107,183),NewPoint(102,188),NewPoint(100,191),NewPoint(95,195),NewPoint(90,199),NewPoint(86,203),NewPoint(82,206),NewPoint(80,209),NewPoint(75,213),NewPoint(73,213),NewPoint(70,216),NewPoint(67,219),NewPoint(64,221),NewPoint(61,223),NewPoint(60,225),NewPoint(62,226),NewPoint(65,225),NewPoint(67,226),NewPoint(74,226),NewPoint(77,227),NewPoint(85,229),NewPoint(91,230),NewPoint(99,231),NewPoint(108,232),NewPoint(116,233),NewPoint(125,233),NewPoint(134,234),NewPoint(145,233),NewPoint(153,232),NewPoint(160,233),NewPoint(170,234),NewPoint(177,235),NewPoint(179,236),NewPoint(186,237),NewPoint(193,238),NewPoint(198,239),NewPoint(200,237),NewPoint(202,239),NewPoint(204,238),NewPoint(206,234),NewPoint(205,230),NewPoint(202,222),NewPoint(197,216),NewPoint(192,207),NewPoint(186,198),NewPoint(179,189),NewPoint(174,183),NewPoint(170,178),NewPoint(164,171),NewPoint(161,168),NewPoint(154,160),NewPoint(148,155),NewPoint(143,150),NewPoint(138,148),NewPoint(136,148)]) );
|
464
|
+
|
465
|
+
this.Templates.push( new Template("x", [NewPoint(87,142),NewPoint(89,145),NewPoint(91,148),NewPoint(93,151),NewPoint(96,155),NewPoint(98,157),NewPoint(100,160),NewPoint(102,162),NewPoint(106,167),NewPoint(108,169),NewPoint(110,171),NewPoint(115,177),NewPoint(119,183),NewPoint(123,189),NewPoint(127,193),NewPoint(129,196),NewPoint(133,200),NewPoint(137,206),NewPoint(140,209),NewPoint(143,212),NewPoint(146,215),NewPoint(151,220),NewPoint(153,222),NewPoint(155,223),NewPoint(157,225),NewPoint(158,223),NewPoint(157,218),NewPoint(155,211),NewPoint(154,208),NewPoint(152,200),NewPoint(150,189),NewPoint(148,179),NewPoint(147,170),NewPoint(147,158),NewPoint(147,148),NewPoint(147,141),NewPoint(147,136),NewPoint(144,135),NewPoint(142,137),NewPoint(140,139),NewPoint(135,145),NewPoint(131,152),NewPoint(124,163),NewPoint(116,177),NewPoint(108,191),NewPoint(100,206),NewPoint(94,217),NewPoint(91,222),NewPoint(89,225),NewPoint(87,226),NewPoint(87,224)]) );
|
466
|
+
|
467
|
+
this.Templates.push( new Template("rectangle", [NewPoint(78,149),NewPoint(78,153),NewPoint(78,157),NewPoint(78,160),NewPoint(79,162),NewPoint(79,164),NewPoint(79,167),NewPoint(79,169),NewPoint(79,173),NewPoint(79,178),NewPoint(79,183),NewPoint(80,189),NewPoint(80,193),NewPoint(80,198),NewPoint(80,202),NewPoint(81,208),NewPoint(81,210),NewPoint(81,216),NewPoint(82,222),NewPoint(82,224),NewPoint(82,227),NewPoint(83,229),NewPoint(83,231),NewPoint(85,230),NewPoint(88,232),NewPoint(90,233),NewPoint(92,232),NewPoint(94,233),NewPoint(99,232),NewPoint(102,233),NewPoint(106,233),NewPoint(109,234),NewPoint(117,235),NewPoint(123,236),NewPoint(126,236),NewPoint(135,237),NewPoint(142,238),NewPoint(145,238),NewPoint(152,238),NewPoint(154,239),NewPoint(165,238),NewPoint(174,237),NewPoint(179,236),NewPoint(186,235),NewPoint(191,235),NewPoint(195,233),NewPoint(197,233),NewPoint(200,233),NewPoint(201,235),NewPoint(201,233),NewPoint(199,231),NewPoint(198,226),NewPoint(198,220),NewPoint(196,207),NewPoint(195,195),NewPoint(195,181),NewPoint(195,173),NewPoint(195,163),NewPoint(194,155),NewPoint(192,145),NewPoint(192,143),NewPoint(192,138),NewPoint(191,135),NewPoint(191,133),NewPoint(191,130),NewPoint(190,128),NewPoint(188,129),NewPoint(186,129),NewPoint(181,132),NewPoint(173,131),NewPoint(162,131),NewPoint(151,132),NewPoint(149,132),NewPoint(138,132),NewPoint(136,132),NewPoint(122,131),NewPoint(120,131),NewPoint(109,130),NewPoint(107,130),NewPoint(90,132),NewPoint(81,133),NewPoint(76,133)]) );
|
468
|
+
|
469
|
+
this.Templates.push( new Template("circle", [NewPoint(127,141),NewPoint(124,140),NewPoint(120,139),NewPoint(118,139),NewPoint(116,139),NewPoint(111,140),NewPoint(109,141),NewPoint(104,144),NewPoint(100,147),NewPoint(96,152),NewPoint(93,157),NewPoint(90,163),NewPoint(87,169),NewPoint(85,175),NewPoint(83,181),NewPoint(82,190),NewPoint(82,195),NewPoint(83,200),NewPoint(84,205),NewPoint(88,213),NewPoint(91,216),NewPoint(96,219),NewPoint(103,222),NewPoint(108,224),NewPoint(111,224),NewPoint(120,224),NewPoint(133,223),NewPoint(142,222),NewPoint(152,218),NewPoint(160,214),NewPoint(167,210),NewPoint(173,204),NewPoint(178,198),NewPoint(179,196),NewPoint(182,188),NewPoint(182,177),NewPoint(178,167),NewPoint(170,150),NewPoint(163,138),NewPoint(152,130),NewPoint(143,129),NewPoint(140,131),NewPoint(129,136),NewPoint(126,139)]) );
|
470
|
+
|
471
|
+
this.Templates.push( new Template("check", [NewPoint(91,185),NewPoint(93,185),NewPoint(95,185),NewPoint(97,185),NewPoint(100,188),NewPoint(102,189),NewPoint(104,190),NewPoint(106,193),NewPoint(108,195),NewPoint(110,198),NewPoint(112,201),NewPoint(114,204),NewPoint(115,207),NewPoint(117,210),NewPoint(118,212),NewPoint(120,214),NewPoint(121,217),NewPoint(122,219),NewPoint(123,222),NewPoint(124,224),NewPoint(126,226),NewPoint(127,229),NewPoint(129,231),NewPoint(130,233),NewPoint(129,231),NewPoint(129,228),NewPoint(129,226),NewPoint(129,224),NewPoint(129,221),NewPoint(129,218),NewPoint(129,212),NewPoint(129,208),NewPoint(130,198),NewPoint(132,189),NewPoint(134,182),NewPoint(137,173),NewPoint(143,164),NewPoint(147,157),NewPoint(151,151),NewPoint(155,144),NewPoint(161,137),NewPoint(165,131),NewPoint(171,122),NewPoint(174,118),NewPoint(176,114),NewPoint(177,112),NewPoint(177,114),NewPoint(175,116),NewPoint(173,118)]) );
|
472
|
+
|
473
|
+
this.Templates.push( new Template("caret", [NewPoint(79,245),NewPoint(79,242),NewPoint(79,239),NewPoint(80,237),NewPoint(80,234),NewPoint(81,232),NewPoint(82,230),NewPoint(84,224),NewPoint(86,220),NewPoint(86,218),NewPoint(87,216),NewPoint(88,213),NewPoint(90,207),NewPoint(91,202),NewPoint(92,200),NewPoint(93,194),NewPoint(94,192),NewPoint(96,189),NewPoint(97,186),NewPoint(100,179),NewPoint(102,173),NewPoint(105,165),NewPoint(107,160),NewPoint(109,158),NewPoint(112,151),NewPoint(115,144),NewPoint(117,139),NewPoint(119,136),NewPoint(119,134),NewPoint(120,132),NewPoint(121,129),NewPoint(122,127),NewPoint(124,125),NewPoint(126,124),NewPoint(129,125),NewPoint(131,127),NewPoint(132,130),NewPoint(136,139),NewPoint(141,154),NewPoint(145,166),NewPoint(151,182),NewPoint(156,193),NewPoint(157,196),NewPoint(161,209),NewPoint(162,211),NewPoint(167,223),NewPoint(169,229),NewPoint(170,231),NewPoint(173,237),NewPoint(176,242),NewPoint(177,244),NewPoint(179,250),NewPoint(181,255),NewPoint(182,257)]) );
|
474
|
+
|
475
|
+
this.Templates.push( new Template("zigzag", [NewPoint(307,216),NewPoint(333,186),NewPoint(356,215),NewPoint(375,186),NewPoint(399,216),NewPoint(418,186)]) );
|
476
|
+
|
477
|
+
this.Templates.push( new Template("arrow", [NewPoint(68,222),NewPoint(70,220),NewPoint(73,218),NewPoint(75,217),NewPoint(77,215),NewPoint(80,213),NewPoint(82,212),NewPoint(84,210),NewPoint(87,209),NewPoint(89,208),NewPoint(92,206),NewPoint(95,204),NewPoint(101,201),NewPoint(106,198),NewPoint(112,194),NewPoint(118,191),NewPoint(124,187),NewPoint(127,186),NewPoint(132,183),NewPoint(138,181),NewPoint(141,180),NewPoint(146,178),NewPoint(154,173),NewPoint(159,171),NewPoint(161,170),NewPoint(166,167),NewPoint(168,167),NewPoint(171,166),NewPoint(174,164),NewPoint(177,162),NewPoint(180,160),NewPoint(182,158),NewPoint(183,156),NewPoint(181,154),NewPoint(178,153),NewPoint(171,153),NewPoint(164,153),NewPoint(160,153),NewPoint(150,154),NewPoint(147,155),NewPoint(141,157),NewPoint(137,158),NewPoint(135,158),NewPoint(137,158),NewPoint(140,157),NewPoint(143,156),NewPoint(151,154),NewPoint(160,152),NewPoint(170,149),NewPoint(179,147),NewPoint(185,145),NewPoint(192,144),NewPoint(196,144),NewPoint(198,144),NewPoint(200,144),NewPoint(201,147),NewPoint(199,149),NewPoint(194,157),NewPoint(191,160),NewPoint(186,167),NewPoint(180,176),NewPoint(177,179),NewPoint(171,187),NewPoint(169,189),NewPoint(165,194),NewPoint(164,196)]) );
|
478
|
+
|
479
|
+
this.Templates.push( new Template("leftbracket", [NewPoint(140,124),NewPoint(138,123),NewPoint(135,122),NewPoint(133,123),NewPoint(130,123),NewPoint(128,124),NewPoint(125,125),NewPoint(122,124),NewPoint(120,124),NewPoint(118,124),NewPoint(116,125),NewPoint(113,125),NewPoint(111,125),NewPoint(108,124),NewPoint(106,125),NewPoint(104,125),NewPoint(102,124),NewPoint(100,123),NewPoint(98,123),NewPoint(95,124),NewPoint(93,123),NewPoint(90,124),NewPoint(88,124),NewPoint(85,125),NewPoint(83,126),NewPoint(81,127),NewPoint(81,129),NewPoint(82,131),NewPoint(82,134),NewPoint(83,138),NewPoint(84,141),NewPoint(84,144),NewPoint(85,148),NewPoint(85,151),NewPoint(86,156),NewPoint(86,160),NewPoint(86,164),NewPoint(86,168),NewPoint(87,171),NewPoint(87,175),NewPoint(87,179),NewPoint(87,182),NewPoint(87,186),NewPoint(88,188),NewPoint(88,195),NewPoint(88,198),NewPoint(88,201),NewPoint(88,207),NewPoint(89,211),NewPoint(89,213),NewPoint(89,217),NewPoint(89,222),NewPoint(88,225),NewPoint(88,229),NewPoint(88,231),NewPoint(88,233),NewPoint(88,235),NewPoint(89,237),NewPoint(89,240),NewPoint(89,242),NewPoint(91,241),NewPoint(94,241),NewPoint(96,240),NewPoint(98,239),NewPoint(105,240),NewPoint(109,240),NewPoint(113,239),NewPoint(116,240),NewPoint(121,239),NewPoint(130,240),NewPoint(136,237),NewPoint(139,237),NewPoint(144,238),NewPoint(151,237),NewPoint(157,236),NewPoint(159,237)]) );
|
480
|
+
|
481
|
+
this.Templates.push( new Template("rightbracket", [NewPoint(112,138),NewPoint(112,136),NewPoint(115,136),NewPoint(118,137),NewPoint(120,136),NewPoint(123,136),NewPoint(125,136),NewPoint(128,136),NewPoint(131,136),NewPoint(134,135),NewPoint(137,135),NewPoint(140,134),NewPoint(143,133),NewPoint(145,132),NewPoint(147,132),NewPoint(149,132),NewPoint(152,132),NewPoint(153,134),NewPoint(154,137),NewPoint(155,141),NewPoint(156,144),NewPoint(157,152),NewPoint(158,161),NewPoint(160,170),NewPoint(162,182),NewPoint(164,192),NewPoint(166,200),NewPoint(167,209),NewPoint(168,214),NewPoint(168,216),NewPoint(169,221),NewPoint(169,223),NewPoint(169,228),NewPoint(169,231),NewPoint(166,233),NewPoint(164,234),NewPoint(161,235),NewPoint(155,236),NewPoint(147,235),NewPoint(140,233),NewPoint(131,233),NewPoint(124,233),NewPoint(117,235),NewPoint(114,238),NewPoint(112,238)]) );
|
482
|
+
|
483
|
+
this.Templates.push( new Template("v", [NewPoint(89,164),NewPoint(90,162),NewPoint(92,162),NewPoint(94,164),NewPoint(95,166),NewPoint(96,169),NewPoint(97,171),NewPoint(99,175),NewPoint(101,178),NewPoint(103,182),NewPoint(106,189),NewPoint(108,194),NewPoint(111,199),NewPoint(114,204),NewPoint(117,209),NewPoint(119,214),NewPoint(122,218),NewPoint(124,222),NewPoint(126,225),NewPoint(128,228),NewPoint(130,229),NewPoint(133,233),NewPoint(134,236),NewPoint(136,239),NewPoint(138,240),NewPoint(139,242),NewPoint(140,244),NewPoint(142,242),NewPoint(142,240),NewPoint(142,237),NewPoint(143,235),NewPoint(143,233),NewPoint(145,229),NewPoint(146,226),NewPoint(148,217),NewPoint(149,208),NewPoint(149,205),NewPoint(151,196),NewPoint(151,193),NewPoint(153,182),NewPoint(155,172),NewPoint(157,165),NewPoint(159,160),NewPoint(162,155),NewPoint(164,150),NewPoint(165,148),NewPoint(166,146)]) );
|
484
|
+
|
485
|
+
this.Templates.push( new Template("delete", [NewPoint(123,129),NewPoint(123,131),NewPoint(124,133),NewPoint(125,136),NewPoint(127,140),NewPoint(129,142),NewPoint(133,148),NewPoint(137,154),NewPoint(143,158),NewPoint(145,161),NewPoint(148,164),NewPoint(153,170),NewPoint(158,176),NewPoint(160,178),NewPoint(164,183),NewPoint(168,188),NewPoint(171,191),NewPoint(175,196),NewPoint(178,200),NewPoint(180,202),NewPoint(181,205),NewPoint(184,208),NewPoint(186,210),NewPoint(187,213),NewPoint(188,215),NewPoint(186,212),NewPoint(183,211),NewPoint(177,208),NewPoint(169,206),NewPoint(162,205),NewPoint(154,207),NewPoint(145,209),NewPoint(137,210),NewPoint(129,214),NewPoint(122,217),NewPoint(118,218),NewPoint(111,221),NewPoint(109,222),NewPoint(110,219),NewPoint(112,217),NewPoint(118,209),NewPoint(120,207),NewPoint(128,196),NewPoint(135,187),NewPoint(138,183),NewPoint(148,167),NewPoint(157,153),NewPoint(163,145),NewPoint(165,142),NewPoint(172,133),NewPoint(177,127),NewPoint(179,127),NewPoint(180,125)]) );
|
486
|
+
|
487
|
+
this.Templates.push( new Template("star", [NewPoint(75,250),NewPoint(75,247),NewPoint(77,244),NewPoint(78,242),NewPoint(79,239),NewPoint(80,237),NewPoint(82,234),NewPoint(82,232),NewPoint(84,229),NewPoint(85,225),NewPoint(87,222),NewPoint(88,219),NewPoint(89,216),NewPoint(91,212),NewPoint(92,208),NewPoint(94,204),NewPoint(95,201),NewPoint(96,196),NewPoint(97,194),NewPoint(98,191),NewPoint(100,185),NewPoint(102,178),NewPoint(104,173),NewPoint(104,171),NewPoint(105,164),NewPoint(106,158),NewPoint(107,156),NewPoint(107,152),NewPoint(108,145),NewPoint(109,141),NewPoint(110,139),NewPoint(112,133),NewPoint(113,131),NewPoint(116,127),NewPoint(117,125),NewPoint(119,122),NewPoint(121,121),NewPoint(123,120),NewPoint(125,122),NewPoint(125,125),NewPoint(127,130),NewPoint(128,133),NewPoint(131,143),NewPoint(136,153),NewPoint(140,163),NewPoint(144,172),NewPoint(145,175),NewPoint(151,189),NewPoint(156,201),NewPoint(161,213),NewPoint(166,225),NewPoint(169,233),NewPoint(171,236),NewPoint(174,243),NewPoint(177,247),NewPoint(178,249),NewPoint(179,251),NewPoint(180,253),NewPoint(180,255),NewPoint(179,257),NewPoint(177,257),NewPoint(174,255),NewPoint(169,250),NewPoint(164,247),NewPoint(160,245),NewPoint(149,238),NewPoint(138,230),NewPoint(127,221),NewPoint(124,220),NewPoint(112,212),NewPoint(110,210),NewPoint(96,201),NewPoint(84,195),NewPoint(74,190),NewPoint(64,182),NewPoint(55,175),NewPoint(51,172),NewPoint(49,170),NewPoint(51,169),NewPoint(56,169),NewPoint(66,169),NewPoint(78,168),NewPoint(92,166),NewPoint(107,164),NewPoint(123,161),NewPoint(140,162),NewPoint(156,162),NewPoint(171,160),NewPoint(173,160),NewPoint(186,160),NewPoint(195,160),NewPoint(198,161),NewPoint(203,163),NewPoint(208,163),NewPoint(206,164),NewPoint(200,167),NewPoint(187,172),NewPoint(174,179),NewPoint(172,181),NewPoint(153,192),NewPoint(137,201),NewPoint(123,211),NewPoint(112,220),NewPoint(99,229),NewPoint(90,237),NewPoint(80,244),NewPoint(73,250),NewPoint(69,254),NewPoint(69,252)]) );
|
488
|
+
|
489
|
+
this.Templates.push( new Template("pigtail", [NewPoint(81,219),NewPoint(84,218),NewPoint(86,220),NewPoint(88,220),NewPoint(90,220),NewPoint(92,219),NewPoint(95,220),NewPoint(97,219),NewPoint(99,220),NewPoint(102,218),NewPoint(105,217),NewPoint(107,216),NewPoint(110,216),NewPoint(113,214),NewPoint(116,212),NewPoint(118,210),NewPoint(121,208),NewPoint(124,205),NewPoint(126,202),NewPoint(129,199),NewPoint(132,196),NewPoint(136,191),NewPoint(139,187),NewPoint(142,182),NewPoint(144,179),NewPoint(146,174),NewPoint(148,170),NewPoint(149,168),NewPoint(151,162),NewPoint(152,160),NewPoint(152,157),NewPoint(152,155),NewPoint(152,151),NewPoint(152,149),NewPoint(152,146),NewPoint(149,142),NewPoint(148,139),NewPoint(145,137),NewPoint(141,135),NewPoint(139,135),NewPoint(134,136),NewPoint(130,140),NewPoint(128,142),NewPoint(126,145),NewPoint(122,150),NewPoint(119,158),NewPoint(117,163),NewPoint(115,170),NewPoint(114,175),NewPoint(117,184),NewPoint(120,190),NewPoint(125,199),NewPoint(129,203),NewPoint(133,208),NewPoint(138,213),NewPoint(145,215),NewPoint(155,218),NewPoint(164,219),NewPoint(166,219),NewPoint(177,219),NewPoint(182,218),NewPoint(192,216),NewPoint(196,213),NewPoint(199,212),NewPoint(201,211)]) );
|
490
|
+
|
491
|
+
|
492
|
+
// $1 Gesture Recognizer API (now using Protractor instead)
|
493
|
+
this.Recognize = function(points) {
|
494
|
+
var b = +Infinity,
|
495
|
+
t = 0,
|
496
|
+
radians,
|
497
|
+
i,
|
498
|
+
score,
|
499
|
+
vector;
|
500
|
+
|
501
|
+
points = Resample(points, NumPoints);
|
502
|
+
radians = IndicativeAngle(points);
|
503
|
+
points = RotateBy(points, -radians);
|
504
|
+
vector = Vectorize(points); // for Protractor
|
505
|
+
|
506
|
+
for (i = 0; i < this.Templates.length; i++) {
|
507
|
+
var d = OptimalCosineDistance(this.Templates[i].Vector, vector);
|
508
|
+
if (d < b) {
|
509
|
+
b = d; // best (least) distance
|
510
|
+
t = i; // unistroke template
|
511
|
+
}
|
512
|
+
}
|
513
|
+
return new Result(this.Templates[t].Name, 1 / b);
|
514
|
+
};
|
515
|
+
|
516
|
+
}
|
517
|
+
|
518
|
+
// Helper functions:
|
519
|
+
function Resample(points, n) {
|
520
|
+
var I = PathLength(points) / (n - 1), // interval length
|
521
|
+
D = 0.0,
|
522
|
+
newpoints = new Array(points[0]),
|
523
|
+
i;
|
524
|
+
for (i = 1; i < points.length; i++) {
|
525
|
+
var d = Distance(points[i - 1], points[i]);
|
526
|
+
if ((D + d) >= I) {
|
527
|
+
var qx = points[i - 1].X + ((I - D) / d) * (points[i].X - points[i - 1].X),
|
528
|
+
qy = points[i - 1].Y + ((I - D) / d) * (points[i].Y - points[i - 1].Y),
|
529
|
+
q = NewPoint(qx, qy);
|
530
|
+
newpoints[newpoints.length] = q; // append new point 'q'
|
531
|
+
points.splice(i, 0, q); // insert 'q' at position i in points s.t. 'q' will be the next i
|
532
|
+
D = 0.0;
|
533
|
+
}
|
534
|
+
else D += d;
|
535
|
+
}
|
536
|
+
// somtimes we fall a rounding-error short of adding the last point, so add it if so
|
537
|
+
if (newpoints.length == n - 1) {
|
538
|
+
newpoints[newpoints.length] = NewPoint(points[points.length - 1].X, points[points.length - 1].Y);
|
539
|
+
}
|
540
|
+
return newpoints;
|
541
|
+
}
|
542
|
+
function IndicativeAngle(points) {
|
543
|
+
var c = Centroid(points);
|
544
|
+
return Math.atan2(c.Y - points[0].Y, c.X - points[0].X);
|
545
|
+
}
|
546
|
+
function RotateBy(points, radians) {
|
547
|
+
var c = Centroid(points),
|
548
|
+
cos = Math.cos(radians),
|
549
|
+
sin = Math.sin(radians),
|
550
|
+
newpoints = [],
|
551
|
+
i;
|
552
|
+
for (i = 0; i < points.length; i++) {
|
553
|
+
var qx = (points[i].X - c.X) * cos - (points[i].Y - c.Y) * sin + c.X,
|
554
|
+
qy = (points[i].X - c.X) * sin + (points[i].Y - c.Y) * cos + c.Y;
|
555
|
+
newpoints[newpoints.length] = NewPoint(qx, qy);
|
556
|
+
}
|
557
|
+
return newpoints;
|
558
|
+
}
|
559
|
+
function ScaleTo(points, size) {
|
560
|
+
var B = BoundingBox(points),
|
561
|
+
newpoints = [],
|
562
|
+
i;
|
563
|
+
for (i = 0; i < points.length; i++) {
|
564
|
+
var qx = points[i].X * (size / B.Width),
|
565
|
+
qy = points[i].Y * (size / B.Height);
|
566
|
+
newpoints[newpoints.length] = NewPoint(qx, qy);
|
567
|
+
}
|
568
|
+
return newpoints;
|
569
|
+
}
|
570
|
+
function TranslateTo(points, pt) {
|
571
|
+
var c = Centroid(points),
|
572
|
+
newpoints = [],
|
573
|
+
i;
|
574
|
+
for (i = 0; i < points.length; i++) {
|
575
|
+
var qx = points[i].X + pt.X - c.X,
|
576
|
+
qy = points[i].Y + pt.Y - c.Y;
|
577
|
+
newpoints[newpoints.length] = NewPoint(qx, qy);
|
578
|
+
}
|
579
|
+
return newpoints;
|
580
|
+
}
|
581
|
+
function Vectorize(points) { // for Protractor
|
582
|
+
var sum = 0.0,
|
583
|
+
vector = [],
|
584
|
+
i,
|
585
|
+
magnitude;
|
586
|
+
for ( i = 0; i < points.length; i++) {
|
587
|
+
vector[vector.length] = points[i].X;
|
588
|
+
vector[vector.length] = points[i].Y;
|
589
|
+
sum += points[i].X * points[i].X + points[i].Y * points[i].Y;
|
590
|
+
}
|
591
|
+
magnitude = Math.sqrt(sum);
|
592
|
+
for ( i = 0; i < vector.length; i++ )
|
593
|
+
vector[i] /= magnitude;
|
594
|
+
return vector;
|
595
|
+
}
|
596
|
+
function OptimalCosineDistance(v1, v2) { // for Protractor
|
597
|
+
var a = 0.0,
|
598
|
+
b = 0.0,
|
599
|
+
i,
|
600
|
+
angle;
|
601
|
+
for (i = 0; i < v1.length; i += 2) {
|
602
|
+
a += v1[i] * v2[i] + v1[i + 1] * v2[i + 1];
|
603
|
+
b += v1[i] * v2[i + 1] - v1[i + 1] * v2[i];
|
604
|
+
}
|
605
|
+
angle = Math.atan(b / a);
|
606
|
+
return Math.acos(a * Math.cos(angle) + b * Math.sin(angle));
|
607
|
+
}
|
608
|
+
function Centroid(points) {
|
609
|
+
var x = 0.0,
|
610
|
+
y = 0.0,
|
611
|
+
i;
|
612
|
+
for (i = 0; i < points.length; i++) {
|
613
|
+
x += points[i].X;
|
614
|
+
y += points[i].Y;
|
615
|
+
}
|
616
|
+
x /= points.length;
|
617
|
+
y /= points.length;
|
618
|
+
return NewPoint(x, y);
|
619
|
+
}
|
620
|
+
function BoundingBox(points) {
|
621
|
+
var minX = +Infinity,
|
622
|
+
maxX = -Infinity,
|
623
|
+
minY = +Infinity,
|
624
|
+
maxY = -Infinity,
|
625
|
+
i;
|
626
|
+
for (i = 0; i < points.length; i++) {
|
627
|
+
if (points[i].X < minX)
|
628
|
+
minX = points[i].X;
|
629
|
+
if (points[i].X > maxX)
|
630
|
+
maxX = points[i].X;
|
631
|
+
if (points[i].Y < minY)
|
632
|
+
minY = points[i].Y;
|
633
|
+
if (points[i].Y > maxY)
|
634
|
+
maxY = points[i].Y;
|
635
|
+
}
|
636
|
+
return new Rectangle(minX, minY, maxX - minX, maxY - minY);
|
637
|
+
}
|
638
|
+
function PathLength(points) {
|
639
|
+
var d = 0.0,
|
640
|
+
i;
|
641
|
+
for (i = 1; i < points.length; i++) {
|
642
|
+
d += Distance(points[i - 1], points[i]);
|
643
|
+
}
|
644
|
+
return d;
|
645
|
+
}
|
646
|
+
function Distance(p1, p2) {
|
647
|
+
var dx = p2.X - p1.X,
|
648
|
+
dy = p2.Y - p1.Y;
|
649
|
+
return Math.sqrt(dx * dx + dy * dy);
|
650
|
+
}
|
651
|
+
|
652
|
+
})(jQuery);
|
@@ -0,0 +1,10 @@
|
|
1
|
+
/*!
|
2
|
+
* jQuery Placeholder 1.1.1
|
3
|
+
*
|
4
|
+
* Copyright (c) 2010 Michael J. Ryan (http://tracker1.info/)
|
5
|
+
*
|
6
|
+
* Dual licensed under the MIT and GPL licenses:
|
7
|
+
* http://www.opensource.org/licenses/mit-license.php
|
8
|
+
* http://www.gnu.org/licenses/gpl.html
|
9
|
+
*/
|
10
|
+
(function(e){var g="PLACEHOLDER-INPUT";var f="PLACEHOLDER-LABEL";var a=false;var d={labelClass:"placeholder"};var i=document.createElement("input");if("placeholder" in i){e.fn.placeholder=e.fn.unplaceholder=function(){};delete i;return}delete i;e.fn.placeholder=function(j){c();var k=e.extend(d,j);this.each(function(){var n=Math.random().toString(32).replace(/\./,""),l=e(this),m=e('<label style="position:absolute;display:none;top:0;left:0;"></label>');if(!l.attr("placeholder")||l.data(g)===g){return}if(!l.attr("id")){l.attr("id")="input_"+n}m.attr("id",l.attr("id")+"_placeholder").data(g,"#"+l.attr("id")).attr("for",l.attr("id")).addClass(k.labelClass).addClass(k.labelClass+"-for-"+this.tagName.toLowerCase()).addClass(f).text(l.attr("placeholder"));l.data(f,"#"+m.attr("id")).data(g,g).addClass(g).after(m);b.call(this);h.call(this)})};e.fn.unplaceholder=function(){this.each(function(){var j=e(this),k=e(j.data(f));if(j.data(g)!==g){return}k.remove();j.removeData(g).removeData(f).removeClass(g)})};function c(){if(a){return}e("."+g).live("click",b).live("focusin",b).live("focusout",h);bound=true;a=true}function b(){var j=e(this),k=e(j.data(f));k.css("display","none")}function h(){var j=this;setTimeout(function(){var k=e(j);e(k.data(f)).css("top",k.position().top+"px").css("left",k.position().left+"px").css("display",!!k.val()?"none":"block")},200)}}(jQuery));
|
@@ -0,0 +1,56 @@
|
|
1
|
+
/*!
|
2
|
+
* jQuery MotionCAPTCHA Plugin CSS v1.0
|
3
|
+
*
|
4
|
+
* Concept and Development by Joss Crowcroft
|
5
|
+
* http://www.josscrowcroft.com
|
6
|
+
*
|
7
|
+
* Adapted and modified by Manuel Fernandez
|
8
|
+
* http://papayalabs.github.io
|
9
|
+
*
|
10
|
+
* Copyright (c) 2017 Papaya Labs | http://papayalabs.github.io
|
11
|
+
*
|
12
|
+
* Incoporates other open source projects, attributed below.
|
13
|
+
*
|
14
|
+
* Only these rules are absolutely necessary for the plugin.
|
15
|
+
* NB. The width & height of the canvas can be flexible to fit your layout but there's a recommended min/max
|
16
|
+
* - if you need it much bigger/smaller, you could tweak the background-positions of the shape classes.
|
17
|
+
*/
|
18
|
+
|
19
|
+
/* The canvas: */
|
20
|
+
#mc-canvas {
|
21
|
+
width:220px; /* For best results, set width to between 210px and 240px */
|
22
|
+
height:154px; /* For best results, set height to between 140px and 170px */
|
23
|
+
background:#fff -9999px -9999px no-repeat;
|
24
|
+
background-image: url('motionCaptcha-shapes.jpg') !important;
|
25
|
+
padding:1px;
|
26
|
+
display: block;
|
27
|
+
border: 4px solid #433e45;
|
28
|
+
-webkit-border-radius: 4px;
|
29
|
+
-moz-border-radius: 4px;
|
30
|
+
border-radius: 4px;
|
31
|
+
}
|
32
|
+
|
33
|
+
#mc-canvas.mc-invalid {
|
34
|
+
border: 4px solid #aa4444;
|
35
|
+
}
|
36
|
+
/* Green border when valid */
|
37
|
+
#mc-canvas.mc-valid {
|
38
|
+
border: 4px solid #44aa44;
|
39
|
+
}
|
40
|
+
/* The shapes: */
|
41
|
+
#mc-canvas.triangle { background-position: 10px 10px }
|
42
|
+
#mc-canvas.x { background-position:-200px 10px }
|
43
|
+
#mc-canvas.rectangle { background-position:-400px 10px }
|
44
|
+
#mc-canvas.circle { background-position:-600px 10px }
|
45
|
+
#mc-canvas.check { background-position: 10px -150px }
|
46
|
+
#mc-canvas.caret { background-position:-200px -150px }
|
47
|
+
#mc-canvas.zigzag { background-position:-400px -150px }
|
48
|
+
#mc-canvas.arrow { background-position:-600px -150px }
|
49
|
+
#mc-canvas.leftbracket { background-position: 10px -300px }
|
50
|
+
#mc-canvas.rightbracket { background-position:-200px -300px }
|
51
|
+
#mc-canvas.v { background-position:-400px -300px }
|
52
|
+
#mc-canvas.delete { background-position:-600px -300px }
|
53
|
+
#mc-canvas.leftbrace { background-position: 10px -450px }
|
54
|
+
#mc-canvas.rightbrace { background-position:-200px -450px }
|
55
|
+
#mc-canvas.star { background-position:-400px -450px }
|
56
|
+
#mc-canvas.pigtail { background-position:-600px -450px }
|
Binary file
|
metadata
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: unity-captcha
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Manuel Fernandez
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-07-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.13'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.13'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
description: 'Unity Captcha is a gem that use two levels of Captcha. First level is
|
42
|
+
about Left Hemisphere: Logic and Math. Second level is about Right Hemisphere: Intuition
|
43
|
+
and Drawing. This two levels functioning in complete harmony creates unity and this
|
44
|
+
is the base design of this Captcha.'
|
45
|
+
email:
|
46
|
+
- papayalabs@gmail.com
|
47
|
+
executables: []
|
48
|
+
extensions: []
|
49
|
+
extra_rdoc_files: []
|
50
|
+
files:
|
51
|
+
- ".DS_Store"
|
52
|
+
- ".gitignore"
|
53
|
+
- CODE_OF_CONDUCT.md
|
54
|
+
- Gemfile
|
55
|
+
- LICENSE.txt
|
56
|
+
- README.md
|
57
|
+
- Rakefile
|
58
|
+
- bin/console
|
59
|
+
- bin/setup
|
60
|
+
- lib/unity/captcha.rb
|
61
|
+
- lib/unity/captcha/engine.rb
|
62
|
+
- lib/unity/captcha/version.rb
|
63
|
+
- unity-captcha.gemspec
|
64
|
+
- vendor/.DS_Store
|
65
|
+
- vendor/assets/.DS_Store
|
66
|
+
- vendor/assets/javascripts/jquery.motionCaptcha.1.0.js
|
67
|
+
- vendor/assets/javascripts/jquery.placeholder.1.1.1.min.js
|
68
|
+
- vendor/assets/stylesheets/jquery.motionCaptcha.1.0.css
|
69
|
+
- vendor/assets/stylesheets/motionCaptcha-shapes.jpg
|
70
|
+
homepage: https://github.com/papayalabs/unity-captcha
|
71
|
+
licenses:
|
72
|
+
- MIT
|
73
|
+
metadata:
|
74
|
+
allowed_push_host: https://rubygems.org
|
75
|
+
post_install_message:
|
76
|
+
rdoc_options: []
|
77
|
+
require_paths:
|
78
|
+
- lib
|
79
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
requirements: []
|
90
|
+
rubyforge_project:
|
91
|
+
rubygems_version: 2.4.8
|
92
|
+
signing_key:
|
93
|
+
specification_version: 4
|
94
|
+
summary: Unity Captcha is a gem that use two levels of captcha based on the hemispheres
|
95
|
+
of the brain
|
96
|
+
test_files: []
|