zxcvbn-ruby 1.1.0 → 1.2.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 +4 -4
- data/CHANGELOG.md +29 -3
- data/README.md +5 -5
- data/lib/zxcvbn/clock.rb +10 -0
- data/lib/zxcvbn/password_strength.rb +3 -3
- data/lib/zxcvbn/version.rb +3 -1
- metadata +9 -77
- data/.gitignore +0 -18
- data/.rspec +0 -1
- data/.travis.yml +0 -12
- data/CODE_OF_CONDUCT.md +0 -130
- data/Gemfile +0 -10
- data/Guardfile +0 -26
- data/Rakefile +0 -22
- data/spec/dictionary_ranker_spec.rb +0 -12
- data/spec/feedback_giver_spec.rb +0 -212
- data/spec/matchers/date_spec.rb +0 -109
- data/spec/matchers/dictionary_spec.rb +0 -30
- data/spec/matchers/digits_spec.rb +0 -15
- data/spec/matchers/l33t_spec.rb +0 -87
- data/spec/matchers/repeat_spec.rb +0 -18
- data/spec/matchers/sequences_spec.rb +0 -21
- data/spec/matchers/spatial_spec.rb +0 -20
- data/spec/matchers/year_spec.rb +0 -15
- data/spec/omnimatch_spec.rb +0 -24
- data/spec/scorer_spec.rb +0 -5
- data/spec/scoring/crack_time_spec.rb +0 -106
- data/spec/scoring/entropy_spec.rb +0 -216
- data/spec/scoring/math_spec.rb +0 -135
- data/spec/spec_helper.rb +0 -54
- data/spec/support/js_helpers.rb +0 -35
- data/spec/support/js_source/adjacency_graphs.js +0 -8
- data/spec/support/js_source/compiled.js +0 -1188
- data/spec/support/js_source/frequency_lists.js +0 -10
- data/spec/support/js_source/init.coffee +0 -63
- data/spec/support/js_source/init.js +0 -95
- data/spec/support/js_source/matching.coffee +0 -444
- data/spec/support/js_source/matching.js +0 -685
- data/spec/support/js_source/scoring.coffee +0 -270
- data/spec/support/js_source/scoring.js +0 -390
- data/spec/support/matcher.rb +0 -35
- data/spec/tester_spec.rb +0 -99
- data/spec/zxcvbn_spec.rb +0 -24
- data/zxcvbn-ruby.gemspec +0 -31
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
describe Zxcvbn::Entropy do
|
|
4
|
-
include Zxcvbn::Math
|
|
5
|
-
|
|
6
|
-
let(:entropy) {
|
|
7
|
-
Class.new do
|
|
8
|
-
include Zxcvbn::Entropy
|
|
9
|
-
def data
|
|
10
|
-
Zxcvbn::Data.new
|
|
11
|
-
end
|
|
12
|
-
end.new
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
describe '#repeat_entropy' do
|
|
16
|
-
it 'returns the correct value' do
|
|
17
|
-
match = Zxcvbn::Match.new(:token => '2222')
|
|
18
|
-
expect(entropy.repeat_entropy(match)).to eq 5.321928094887363
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
describe '#sequence_entropy' do
|
|
23
|
-
let(:match) { Zxcvbn::Match.new(:token => token, :ascending => true) }
|
|
24
|
-
|
|
25
|
-
{'a' => 'abcdefg', '1' => '1234567'}.each do |first_char, token|
|
|
26
|
-
context "when the first char is #{first_char}" do
|
|
27
|
-
let(:token) { token }
|
|
28
|
-
|
|
29
|
-
it 'returns the correct value' do
|
|
30
|
-
expect(entropy.sequence_entropy(match)).to eq 3.807354922057604
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
context 'when the first character is a digit' do
|
|
36
|
-
let(:token) { '23456' }
|
|
37
|
-
|
|
38
|
-
it 'returns the correct value' do
|
|
39
|
-
expect(entropy.sequence_entropy(match)).to eq 5.643856189774725
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
context 'when the first character is a lowercase letter' do
|
|
44
|
-
let(:token) { 'bcdef' }
|
|
45
|
-
|
|
46
|
-
it 'returns the correct value' do
|
|
47
|
-
expect(entropy.sequence_entropy(match)).to eq 7.022367813028454
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
context 'when the first character is an uppercase letter' do
|
|
52
|
-
let(:token) { 'BCDEF' }
|
|
53
|
-
|
|
54
|
-
it 'returns the correct value' do
|
|
55
|
-
expect(entropy.sequence_entropy(match)).to eq 8.022367813028454
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
context 'when the match is ascending' do
|
|
60
|
-
before { match.ascending = false }
|
|
61
|
-
let(:token) { 'bcdef' }
|
|
62
|
-
|
|
63
|
-
it 'returns the correct value' do
|
|
64
|
-
expect(entropy.sequence_entropy(match)).to eq 8.022367813028454
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
describe '#digits_entropy' do
|
|
70
|
-
it 'returns the correct value' do
|
|
71
|
-
match = Zxcvbn::Match.new(:token => '12345678')
|
|
72
|
-
expect(entropy.digits_entropy(match)).to eq 26.5754247590989
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
describe '#year_entropy' do
|
|
77
|
-
it 'returns the correct value' do
|
|
78
|
-
expect(entropy.year_entropy(nil)).to eq 6.894817763307944
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
describe '#date_entropy' do
|
|
83
|
-
context 'with a two digit year' do
|
|
84
|
-
it 'returns the correct value' do
|
|
85
|
-
match = Zxcvbn::Match.new(:year => 98)
|
|
86
|
-
expect(entropy.date_entropy(match)).to eq 15.183015000882756
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
context 'with a four digit year' do
|
|
91
|
-
it 'returns the correct value' do
|
|
92
|
-
match = Zxcvbn::Match.new(:year => 2012)
|
|
93
|
-
expect(entropy.date_entropy(match)).to eq 15.433976574415976
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
context 'with a separator' do
|
|
98
|
-
it 'returns the correct value' do
|
|
99
|
-
match = Zxcvbn::Match.new(:year => 2012, :separator => '/')
|
|
100
|
-
expect(entropy.date_entropy(match)).to eq 17.433976574415976
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
describe '#dictionary_entropy' do
|
|
106
|
-
let(:match) { Zxcvbn::Match.new(:token => token, :rank => rank, :l33t => l33t, :sub => sub) }
|
|
107
|
-
let(:l33t) { false }
|
|
108
|
-
let(:sub) { {} }
|
|
109
|
-
let(:calculated_entropy) { entropy.dictionary_entropy(match) }
|
|
110
|
-
|
|
111
|
-
context 'a simple dictionary word, all lower case and no l33t subs' do
|
|
112
|
-
let(:token) { 'you' }
|
|
113
|
-
let(:rank) { 1 }
|
|
114
|
-
|
|
115
|
-
specify { expect(calculated_entropy).to eq 0 }
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
context 'with all upper case characters' do
|
|
119
|
-
let(:token) { 'YOU' }
|
|
120
|
-
let(:rank) { 1 }
|
|
121
|
-
|
|
122
|
-
specify { expect(calculated_entropy).to eq 1 }
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
context 'starting with uppercase' do
|
|
126
|
-
let(:token) { 'You' }
|
|
127
|
-
let(:rank) { 1 }
|
|
128
|
-
|
|
129
|
-
specify { expect(calculated_entropy).to eq 1 }
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
context 'starting with uppercase' do
|
|
133
|
-
let(:token) { 'yoU' }
|
|
134
|
-
let(:rank) { 1 }
|
|
135
|
-
|
|
136
|
-
specify { expect(calculated_entropy).to eq 1 }
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
context 'mixed upper and lower' do
|
|
140
|
-
let(:token) { 'tEsTiNg' }
|
|
141
|
-
let(:rank) { 1 }
|
|
142
|
-
|
|
143
|
-
specify { expect(calculated_entropy).to eq 6 }
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
context 'starting with digits' do
|
|
147
|
-
let(:token) { '12345' }
|
|
148
|
-
let(:rank) { 1 }
|
|
149
|
-
|
|
150
|
-
specify { expect(calculated_entropy).to eq 0 }
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
context 'extra l33t entropy' do
|
|
154
|
-
let(:token) { 'p3rs0n' }
|
|
155
|
-
let(:rank) { 1 }
|
|
156
|
-
let(:l33t) { true }
|
|
157
|
-
let(:sub) { {'3' => 'e', '0' => 'o'} }
|
|
158
|
-
|
|
159
|
-
specify { expect(calculated_entropy).to eq 1 }
|
|
160
|
-
end
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
describe '#spatial_entropy' do
|
|
164
|
-
let(:match) { Zxcvbn::Match.new(:token => '123wsclf', :turns => 1) }
|
|
165
|
-
|
|
166
|
-
context 'when keyboard is qwerty' do
|
|
167
|
-
it 'should return the correct entropy' do
|
|
168
|
-
match.graph = 'qwerty'
|
|
169
|
-
|
|
170
|
-
expect(entropy.spatial_entropy(match)).to eql 11.562242424221074
|
|
171
|
-
end
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
context 'when keyboard is dvorak' do
|
|
175
|
-
it 'should return the correct entropy' do
|
|
176
|
-
match.graph = 'dvorak'
|
|
177
|
-
|
|
178
|
-
expect(entropy.spatial_entropy(match)).to eql 11.562242424221074
|
|
179
|
-
end
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
context 'when keyboard is not qwerty or dvorak' do
|
|
183
|
-
it 'should return the correct entropy' do
|
|
184
|
-
match.graph = 'keypad'
|
|
185
|
-
|
|
186
|
-
expect(entropy.spatial_entropy(match)).to eql 9.05528243550119
|
|
187
|
-
end
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
context 'when match includes several turns' do
|
|
191
|
-
it 'should return the correct entropy' do
|
|
192
|
-
match.turns = 5
|
|
193
|
-
|
|
194
|
-
expect(entropy.spatial_entropy(match)).to eql 21.761397858718993
|
|
195
|
-
end
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
context 'when match includes shifted count' do
|
|
199
|
-
it 'should return the correct entropy' do
|
|
200
|
-
match.shiffted_count = 5
|
|
201
|
-
|
|
202
|
-
expect(entropy.spatial_entropy(match)).to eql 9.05528243550119
|
|
203
|
-
end
|
|
204
|
-
end
|
|
205
|
-
|
|
206
|
-
context 'when match includes shifted count and several turns' do
|
|
207
|
-
it 'should return the correct entropy' do
|
|
208
|
-
match.shiffted_count = 5
|
|
209
|
-
match.turns = 5
|
|
210
|
-
|
|
211
|
-
expect(entropy.spatial_entropy(match)).to eql 21.761397858718993
|
|
212
|
-
end
|
|
213
|
-
end
|
|
214
|
-
end
|
|
215
|
-
|
|
216
|
-
end
|
data/spec/scoring/math_spec.rb
DELETED
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
describe Zxcvbn::Math do
|
|
4
|
-
include Zxcvbn::Math
|
|
5
|
-
|
|
6
|
-
def data
|
|
7
|
-
Zxcvbn::Data.new
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
describe '#bruteforce_cardinality' do
|
|
11
|
-
context 'when empty password' do
|
|
12
|
-
it 'should return 0 if empty password' do
|
|
13
|
-
expect(bruteforce_cardinality('')).to eql 0
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
context 'when password is one character long' do
|
|
18
|
-
context 'and a digit' do
|
|
19
|
-
it 'should return 10' do
|
|
20
|
-
(0..9).each do |digit|
|
|
21
|
-
expect(bruteforce_cardinality(digit.to_s)).to eql 10
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
context 'and an upper case character' do
|
|
27
|
-
it 'should return 26' do
|
|
28
|
-
('A'..'Z').each do |character|
|
|
29
|
-
expect(bruteforce_cardinality(character)).to eql 26
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
context 'and a lower case character' do
|
|
35
|
-
it 'should return 26' do
|
|
36
|
-
('a'..'z').each do |character|
|
|
37
|
-
expect(bruteforce_cardinality(character)).to eql 26
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
context 'and a symbol' do
|
|
43
|
-
it 'should return 33' do
|
|
44
|
-
%w|/ [ ` {|.each do |symbol|
|
|
45
|
-
expect(bruteforce_cardinality(symbol)).to eql 33
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
context 'when password is more than one character long' do
|
|
52
|
-
context 'and only digits' do
|
|
53
|
-
it 'should return 10' do
|
|
54
|
-
expect(bruteforce_cardinality('123456789')).to eql 10
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
context 'and only lowercase characters' do
|
|
59
|
-
it 'should return 26' do
|
|
60
|
-
expect(bruteforce_cardinality('password')).to eql 26
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
context 'and only uppercase characters' do
|
|
65
|
-
it 'should return 26' do
|
|
66
|
-
expect(bruteforce_cardinality('PASSWORD')).to eql 26
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
context 'and only symbols' do
|
|
71
|
-
it 'should return 33' do
|
|
72
|
-
expect(bruteforce_cardinality('/ [ ` {')).to eql 33
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
context 'and a mixed of character types' do
|
|
77
|
-
it 'should add up every character type cardinality' do
|
|
78
|
-
expect(bruteforce_cardinality('p1SsWorD!')).to eql 95
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
describe '#average_degree_for_graph' do
|
|
85
|
-
context 'when keyboard is qwerty' do
|
|
86
|
-
it 'returns the correct average degree over all keys' do
|
|
87
|
-
expect(average_degree_for_graph('qwerty')).to eql 4.595744680851064
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
context 'when keyboard is dvorak' do
|
|
92
|
-
it 'returns the correct average degree over all keys' do
|
|
93
|
-
expect(average_degree_for_graph('dvorak')).to eql 4.595744680851064
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
context 'when keyboard is keypad' do
|
|
98
|
-
it 'returns the correct average degree over all keys' do
|
|
99
|
-
expect(average_degree_for_graph('keypad')).to eql 5.066666666666666
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
context 'when keyboard is mac keypad' do
|
|
104
|
-
it 'returns the correct average degree over all keys' do
|
|
105
|
-
expect(average_degree_for_graph('mac_keypad')).to eql 5.25
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
describe '#starting_positions_for_graph' do
|
|
111
|
-
context 'when keyboard is qwerty' do
|
|
112
|
-
it 'returns the correct average degree over all keys' do
|
|
113
|
-
expect(starting_positions_for_graph('qwerty')).to eql 94
|
|
114
|
-
end
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
context 'when keyboard is dvorak' do
|
|
118
|
-
it 'returns the correct average degree over all keys' do
|
|
119
|
-
expect(starting_positions_for_graph('dvorak')).to eql 94
|
|
120
|
-
end
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
context 'when keyboard is keypad' do
|
|
124
|
-
it 'returns the correct average degree over all keys' do
|
|
125
|
-
expect(starting_positions_for_graph('keypad')).to eql 15
|
|
126
|
-
end
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
context 'when keyboard is mac keypad' do
|
|
130
|
-
it 'returns the correct average degree over all keys' do
|
|
131
|
-
expect(starting_positions_for_graph('mac_keypad')).to eql 16
|
|
132
|
-
end
|
|
133
|
-
end
|
|
134
|
-
end
|
|
135
|
-
end
|
data/spec/spec_helper.rb
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
require 'bundler/setup'
|
|
2
|
-
require 'rspec'
|
|
3
|
-
require 'zxcvbn'
|
|
4
|
-
|
|
5
|
-
Dir[Pathname.new(File.expand_path('../', __FILE__)).join('support/**/*.rb')].each {|f| require f}
|
|
6
|
-
|
|
7
|
-
RSpec.configure do |config|
|
|
8
|
-
config.include JsHelpers
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
TEST_PASSWORDS = [
|
|
12
|
-
'zxcvbn',
|
|
13
|
-
'qwER43@!',
|
|
14
|
-
'Tr0ub4dour&3',
|
|
15
|
-
'correcthorsebatterystaple',
|
|
16
|
-
'coRrecth0rseba++ery9.23.2007staple$',
|
|
17
|
-
|
|
18
|
-
'D0g..................',
|
|
19
|
-
'abcdefghijk987654321',
|
|
20
|
-
'neverforget13/3/1997',
|
|
21
|
-
'1qaz2wsx3edc',
|
|
22
|
-
|
|
23
|
-
'temppass22',
|
|
24
|
-
'briansmith',
|
|
25
|
-
'briansmith4mayor',
|
|
26
|
-
'password1',
|
|
27
|
-
'viking',
|
|
28
|
-
'thx1138',
|
|
29
|
-
'ScoRpi0ns',
|
|
30
|
-
'do you know',
|
|
31
|
-
|
|
32
|
-
'ryanhunter2000',
|
|
33
|
-
'rianhunter2000',
|
|
34
|
-
|
|
35
|
-
'asdfghju7654rewq',
|
|
36
|
-
'AOEUIDHG&*()LS_',
|
|
37
|
-
|
|
38
|
-
'12345678',
|
|
39
|
-
'defghi6789',
|
|
40
|
-
|
|
41
|
-
'rosebud',
|
|
42
|
-
'Rosebud',
|
|
43
|
-
'ROSEBUD',
|
|
44
|
-
'rosebuD',
|
|
45
|
-
'ros3bud99',
|
|
46
|
-
'r0s3bud99',
|
|
47
|
-
'R0$38uD99',
|
|
48
|
-
|
|
49
|
-
'verlineVANDERMARK',
|
|
50
|
-
|
|
51
|
-
'eheuczkqyq',
|
|
52
|
-
'rWibMFACxAUGZmxhVncy',
|
|
53
|
-
'Ba9ZyWABu99[BK#6MBgbH88Tofv)vs$w'
|
|
54
|
-
]
|
data/spec/support/js_helpers.rb
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
require 'v8'
|
|
2
|
-
require 'json'
|
|
3
|
-
|
|
4
|
-
module JsHelpers
|
|
5
|
-
class JsMethodInvoker
|
|
6
|
-
JS_SOURCE_PATH = Pathname(File.expand_path('../js_source/', __FILE__))
|
|
7
|
-
|
|
8
|
-
def initialize
|
|
9
|
-
@ctx = V8::Context.new do |ctx|
|
|
10
|
-
ctx.eval(JS_SOURCE_PATH.join('compiled.js').read)
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def eval(string)
|
|
15
|
-
@ctx.eval(string)
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def eval_convert_object(string)
|
|
19
|
-
serialized = eval("JSON.stringify(#{string})")
|
|
20
|
-
JSON.parse(serialized)
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def method_invoker
|
|
25
|
-
$method_invoker ||= JsMethodInvoker.new
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def run_js(javascript)
|
|
29
|
-
method_invoker.eval_convert_object(javascript)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def js_zxcvbn(password)
|
|
33
|
-
run_js("zxcvbn('#{password}')")
|
|
34
|
-
end
|
|
35
|
-
end
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
var qwerty = {"!": ["`~", null, null, "2@", "qQ", null], "\"": [";:", "[{", "]}", null, null, "/?"], "#": ["2@", null, null, "4$", "eE", "wW"], "$": ["3#", null, null, "5%", "rR", "eE"], "%": ["4$", null, null, "6^", "tT", "rR"], "&": ["6^", null, null, "8*", "uU", "yY"], "'": [";:", "[{", "]}", null, null, "/?"], "(": ["8*", null, null, "0)", "oO", "iI"], ")": ["9(", null, null, "-_", "pP", "oO"], "*": ["7&", null, null, "9(", "iI", "uU"], "+": ["-_", null, null, null, "]}", "[{"], ",": ["mM", "kK", "lL", ".>", null, null], "-": ["0)", null, null, "=+", "[{", "pP"], ".": [",<", "lL", ";:", "/?", null, null], "/": [".>", ";:", "'\"", null, null, null], "0": ["9(", null, null, "-_", "pP", "oO"], "1": ["`~", null, null, "2@", "qQ", null], "2": ["1!", null, null, "3#", "wW", "qQ"], "3": ["2@", null, null, "4$", "eE", "wW"], "4": ["3#", null, null, "5%", "rR", "eE"], "5": ["4$", null, null, "6^", "tT", "rR"], "6": ["5%", null, null, "7&", "yY", "tT"], "7": ["6^", null, null, "8*", "uU", "yY"], "8": ["7&", null, null, "9(", "iI", "uU"], "9": ["8*", null, null, "0)", "oO", "iI"], ":": ["lL", "pP", "[{", "'\"", "/?", ".>"], ";": ["lL", "pP", "[{", "'\"", "/?", ".>"], "<": ["mM", "kK", "lL", ".>", null, null], "=": ["-_", null, null, null, "]}", "[{"], ">": [",<", "lL", ";:", "/?", null, null], "?": [".>", ";:", "'\"", null, null, null], "@": ["1!", null, null, "3#", "wW", "qQ"], "A": [null, "qQ", "wW", "sS", "zZ", null], "B": ["vV", "gG", "hH", "nN", null, null], "C": ["xX", "dD", "fF", "vV", null, null], "D": ["sS", "eE", "rR", "fF", "cC", "xX"], "E": ["wW", "3#", "4$", "rR", "dD", "sS"], "F": ["dD", "rR", "tT", "gG", "vV", "cC"], "G": ["fF", "tT", "yY", "hH", "bB", "vV"], "H": ["gG", "yY", "uU", "jJ", "nN", "bB"], "I": ["uU", "8*", "9(", "oO", "kK", "jJ"], "J": ["hH", "uU", "iI", "kK", "mM", "nN"], "K": ["jJ", "iI", "oO", "lL", ",<", "mM"], "L": ["kK", "oO", "pP", ";:", ".>", ",<"], "M": ["nN", "jJ", "kK", ",<", null, null], "N": ["bB", "hH", "jJ", "mM", null, null], "O": ["iI", "9(", "0)", "pP", "lL", "kK"], "P": ["oO", "0)", "-_", "[{", ";:", "lL"], "Q": [null, "1!", "2@", "wW", "aA", null], "R": ["eE", "4$", "5%", "tT", "fF", "dD"], "S": ["aA", "wW", "eE", "dD", "xX", "zZ"], "T": ["rR", "5%", "6^", "yY", "gG", "fF"], "U": ["yY", "7&", "8*", "iI", "jJ", "hH"], "V": ["cC", "fF", "gG", "bB", null, null], "W": ["qQ", "2@", "3#", "eE", "sS", "aA"], "X": ["zZ", "sS", "dD", "cC", null, null], "Y": ["tT", "6^", "7&", "uU", "hH", "gG"], "Z": [null, "aA", "sS", "xX", null, null], "[": ["pP", "-_", "=+", "]}", "'\"", ";:"], "\\": ["]}", null, null, null, null, null], "]": ["[{", "=+", null, "\\|", null, "'\""], "^": ["5%", null, null, "7&", "yY", "tT"], "_": ["0)", null, null, "=+", "[{", "pP"], "`": [null, null, null, "1!", null, null], "a": [null, "qQ", "wW", "sS", "zZ", null], "b": ["vV", "gG", "hH", "nN", null, null], "c": ["xX", "dD", "fF", "vV", null, null], "d": ["sS", "eE", "rR", "fF", "cC", "xX"], "e": ["wW", "3#", "4$", "rR", "dD", "sS"], "f": ["dD", "rR", "tT", "gG", "vV", "cC"], "g": ["fF", "tT", "yY", "hH", "bB", "vV"], "h": ["gG", "yY", "uU", "jJ", "nN", "bB"], "i": ["uU", "8*", "9(", "oO", "kK", "jJ"], "j": ["hH", "uU", "iI", "kK", "mM", "nN"], "k": ["jJ", "iI", "oO", "lL", ",<", "mM"], "l": ["kK", "oO", "pP", ";:", ".>", ",<"], "m": ["nN", "jJ", "kK", ",<", null, null], "n": ["bB", "hH", "jJ", "mM", null, null], "o": ["iI", "9(", "0)", "pP", "lL", "kK"], "p": ["oO", "0)", "-_", "[{", ";:", "lL"], "q": [null, "1!", "2@", "wW", "aA", null], "r": ["eE", "4$", "5%", "tT", "fF", "dD"], "s": ["aA", "wW", "eE", "dD", "xX", "zZ"], "t": ["rR", "5%", "6^", "yY", "gG", "fF"], "u": ["yY", "7&", "8*", "iI", "jJ", "hH"], "v": ["cC", "fF", "gG", "bB", null, null], "w": ["qQ", "2@", "3#", "eE", "sS", "aA"], "x": ["zZ", "sS", "dD", "cC", null, null], "y": ["tT", "6^", "7&", "uU", "hH", "gG"], "z": [null, "aA", "sS", "xX", null, null], "{": ["pP", "-_", "=+", "]}", "'\"", ";:"], "|": ["]}", null, null, null, null, null], "}": ["[{", "=+", null, "\\|", null, "'\""], "~": [null, null, null, "1!", null, null]};
|
|
2
|
-
|
|
3
|
-
var dvorak = {"!": ["`~", null, null, "2@", "'\"", null], "\"": [null, "1!", "2@", ",<", "aA", null], "#": ["2@", null, null, "4$", ".>", ",<"], "$": ["3#", null, null, "5%", "pP", ".>"], "%": ["4$", null, null, "6^", "yY", "pP"], "&": ["6^", null, null, "8*", "gG", "fF"], "'": [null, "1!", "2@", ",<", "aA", null], "(": ["8*", null, null, "0)", "rR", "cC"], ")": ["9(", null, null, "[{", "lL", "rR"], "*": ["7&", null, null, "9(", "cC", "gG"], "+": ["/?", "]}", null, "\\|", null, "-_"], ",": ["'\"", "2@", "3#", ".>", "oO", "aA"], "-": ["sS", "/?", "=+", null, null, "zZ"], ".": [",<", "3#", "4$", "pP", "eE", "oO"], "/": ["lL", "[{", "]}", "=+", "-_", "sS"], "0": ["9(", null, null, "[{", "lL", "rR"], "1": ["`~", null, null, "2@", "'\"", null], "2": ["1!", null, null, "3#", ",<", "'\""], "3": ["2@", null, null, "4$", ".>", ",<"], "4": ["3#", null, null, "5%", "pP", ".>"], "5": ["4$", null, null, "6^", "yY", "pP"], "6": ["5%", null, null, "7&", "fF", "yY"], "7": ["6^", null, null, "8*", "gG", "fF"], "8": ["7&", null, null, "9(", "cC", "gG"], "9": ["8*", null, null, "0)", "rR", "cC"], ":": [null, "aA", "oO", "qQ", null, null], ";": [null, "aA", "oO", "qQ", null, null], "<": ["'\"", "2@", "3#", ".>", "oO", "aA"], "=": ["/?", "]}", null, "\\|", null, "-_"], ">": [",<", "3#", "4$", "pP", "eE", "oO"], "?": ["lL", "[{", "]}", "=+", "-_", "sS"], "@": ["1!", null, null, "3#", ",<", "'\""], "A": [null, "'\"", ",<", "oO", ";:", null], "B": ["xX", "dD", "hH", "mM", null, null], "C": ["gG", "8*", "9(", "rR", "tT", "hH"], "D": ["iI", "fF", "gG", "hH", "bB", "xX"], "E": ["oO", ".>", "pP", "uU", "jJ", "qQ"], "F": ["yY", "6^", "7&", "gG", "dD", "iI"], "G": ["fF", "7&", "8*", "cC", "hH", "dD"], "H": ["dD", "gG", "cC", "tT", "mM", "bB"], "I": ["uU", "yY", "fF", "dD", "xX", "kK"], "J": ["qQ", "eE", "uU", "kK", null, null], "K": ["jJ", "uU", "iI", "xX", null, null], "L": ["rR", "0)", "[{", "/?", "sS", "nN"], "M": ["bB", "hH", "tT", "wW", null, null], "N": ["tT", "rR", "lL", "sS", "vV", "wW"], "O": ["aA", ",<", ".>", "eE", "qQ", ";:"], "P": [".>", "4$", "5%", "yY", "uU", "eE"], "Q": [";:", "oO", "eE", "jJ", null, null], "R": ["cC", "9(", "0)", "lL", "nN", "tT"], "S": ["nN", "lL", "/?", "-_", "zZ", "vV"], "T": ["hH", "cC", "rR", "nN", "wW", "mM"], "U": ["eE", "pP", "yY", "iI", "kK", "jJ"], "V": ["wW", "nN", "sS", "zZ", null, null], "W": ["mM", "tT", "nN", "vV", null, null], "X": ["kK", "iI", "dD", "bB", null, null], "Y": ["pP", "5%", "6^", "fF", "iI", "uU"], "Z": ["vV", "sS", "-_", null, null, null], "[": ["0)", null, null, "]}", "/?", "lL"], "\\": ["=+", null, null, null, null, null], "]": ["[{", null, null, null, "=+", "/?"], "^": ["5%", null, null, "7&", "fF", "yY"], "_": ["sS", "/?", "=+", null, null, "zZ"], "`": [null, null, null, "1!", null, null], "a": [null, "'\"", ",<", "oO", ";:", null], "b": ["xX", "dD", "hH", "mM", null, null], "c": ["gG", "8*", "9(", "rR", "tT", "hH"], "d": ["iI", "fF", "gG", "hH", "bB", "xX"], "e": ["oO", ".>", "pP", "uU", "jJ", "qQ"], "f": ["yY", "6^", "7&", "gG", "dD", "iI"], "g": ["fF", "7&", "8*", "cC", "hH", "dD"], "h": ["dD", "gG", "cC", "tT", "mM", "bB"], "i": ["uU", "yY", "fF", "dD", "xX", "kK"], "j": ["qQ", "eE", "uU", "kK", null, null], "k": ["jJ", "uU", "iI", "xX", null, null], "l": ["rR", "0)", "[{", "/?", "sS", "nN"], "m": ["bB", "hH", "tT", "wW", null, null], "n": ["tT", "rR", "lL", "sS", "vV", "wW"], "o": ["aA", ",<", ".>", "eE", "qQ", ";:"], "p": [".>", "4$", "5%", "yY", "uU", "eE"], "q": [";:", "oO", "eE", "jJ", null, null], "r": ["cC", "9(", "0)", "lL", "nN", "tT"], "s": ["nN", "lL", "/?", "-_", "zZ", "vV"], "t": ["hH", "cC", "rR", "nN", "wW", "mM"], "u": ["eE", "pP", "yY", "iI", "kK", "jJ"], "v": ["wW", "nN", "sS", "zZ", null, null], "w": ["mM", "tT", "nN", "vV", null, null], "x": ["kK", "iI", "dD", "bB", null, null], "y": ["pP", "5%", "6^", "fF", "iI", "uU"], "z": ["vV", "sS", "-_", null, null, null], "{": ["0)", null, null, "]}", "/?", "lL"], "|": ["=+", null, null, null, null, null], "}": ["[{", null, null, null, "=+", "/?"], "~": [null, null, null, "1!", null, null]};
|
|
4
|
-
|
|
5
|
-
var keypad = {"*": ["/", null, null, null, "-", "+", "9", "8"], "+": ["9", "*", "-", null, null, null, null, "6"], "-": ["*", null, null, null, null, null, "+", "9"], ".": ["0", "2", "3", null, null, null, null, null], "/": [null, null, null, null, "*", "9", "8", "7"], "0": [null, "1", "2", "3", ".", null, null, null], "1": [null, null, "4", "5", "2", "0", null, null], "2": ["1", "4", "5", "6", "3", ".", "0", null], "3": ["2", "5", "6", null, null, null, ".", "0"], "4": [null, null, "7", "8", "5", "2", "1", null], "5": ["4", "7", "8", "9", "6", "3", "2", "1"], "6": ["5", "8", "9", "+", null, null, "3", "2"], "7": [null, null, null, "/", "8", "5", "4", null], "8": ["7", null, "/", "*", "9", "6", "5", "4"], "9": ["8", "/", "*", "-", "+", null, "6", "5"]};
|
|
6
|
-
|
|
7
|
-
var mac_keypad = {"*": ["/", null, null, null, null, null, "-", "9"], "+": ["6", "9", "-", null, null, null, null, "3"], "-": ["9", "/", "*", null, null, null, "+", "6"], ".": ["0", "2", "3", null, null, null, null, null], "/": ["=", null, null, null, "*", "-", "9", "8"], "0": [null, "1", "2", "3", ".", null, null, null], "1": [null, null, "4", "5", "2", "0", null, null], "2": ["1", "4", "5", "6", "3", ".", "0", null], "3": ["2", "5", "6", "+", null, null, ".", "0"], "4": [null, null, "7", "8", "5", "2", "1", null], "5": ["4", "7", "8", "9", "6", "3", "2", "1"], "6": ["5", "8", "9", "-", "+", null, "3", "2"], "7": [null, null, null, "=", "8", "5", "4", null], "8": ["7", null, "=", "/", "9", "6", "5", "4"], "9": ["8", "=", "/", "*", "-", "+", "6", "5"], "=": [null, null, null, null, "/", "9", "8", "7"]};
|
|
8
|
-
|