cw_credit_card_validations 3.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.hound.yml +2 -0
- data/.travis.yml +26 -0
- data/Changelog.md +115 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +162 -0
- data/Rakefile +13 -0
- data/credit_card_validations.gemspec +29 -0
- data/gemfiles/rails1-4.gemfile +11 -0
- data/gemfiles/rails5.gemfile +10 -0
- data/lib/active_model/credit_card_number_validator.rb +81 -0
- data/lib/credit_card_validations.rb +36 -0
- data/lib/credit_card_validations/detector.rb +148 -0
- data/lib/credit_card_validations/error.rb +4 -0
- data/lib/credit_card_validations/factory.rb +64 -0
- data/lib/credit_card_validations/luhn.rb +22 -0
- data/lib/credit_card_validations/mmi.rb +43 -0
- data/lib/credit_card_validations/plugins/diners_us.rb +6 -0
- data/lib/credit_card_validations/plugins/en_route.rb +5 -0
- data/lib/credit_card_validations/plugins/laser.rb +4 -0
- data/lib/credit_card_validations/string.rb +22 -0
- data/lib/credit_card_validations/version.rb +3 -0
- data/lib/data/brands.yaml +409 -0
- data/spec/active_model_spec.rb +70 -0
- data/spec/credit_card_validations_spec.rb +175 -0
- data/spec/factory_spec.rb +18 -0
- data/spec/fixtures/invalid_cards.yml +6 -0
- data/spec/fixtures/valid_cards.yml +119 -0
- data/spec/models/credit_card.rb +19 -0
- data/spec/string_spec.rb +32 -0
- data/spec/test_helper.rb +20 -0
- metadata +153 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e1941b4388616c9c9c9b918c90ad8e34851c4eb1
|
4
|
+
data.tar.gz: 13b338b1b100f3417faf93d894f0642846d3a8a6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a70bcec1d3353715b44dcfd02a168580d0358b2724dbeb7ff5406c4b33071b571d339f405546fb6c1153f019726be0ef216b7b6ab6184bc958c79d8419edb23a
|
7
|
+
data.tar.gz: a5de82d23064e50834e4261c10d4131903ed90492ef15a2d4bdd5bc2d9a0b0f244e703e676d32dca9c17cbc55b4ec30246e1fb72bad7fd76f820e66e54fd9100
|
data/.hound.yml
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 1.9.3
|
4
|
+
- 2.0.0
|
5
|
+
- 2.1.9
|
6
|
+
- 2.2.5
|
7
|
+
- 2.3.1
|
8
|
+
- jruby-9.1.6.0
|
9
|
+
gemfile:
|
10
|
+
- gemfiles/rails1-4.gemfile
|
11
|
+
- gemfiles/rails5.gemfile
|
12
|
+
matrix:
|
13
|
+
exclude:
|
14
|
+
- gemfile: gemfiles/rails5.gemfile
|
15
|
+
rvm: 1.9.3
|
16
|
+
- gemfile: gemfiles/rails5.gemfile
|
17
|
+
rvm: 2.0.0
|
18
|
+
- gemfile: gemfiles/rails5.gemfile
|
19
|
+
rvm: 2.1.9
|
20
|
+
- gemfile: gemfiles/rails5.gemfile
|
21
|
+
rvm: jruby-9.1.6.0
|
22
|
+
before_install:
|
23
|
+
- gem update --system
|
24
|
+
- rvm @global do gem uninstall bundler -a -x
|
25
|
+
- rvm @global do gem install bundler -v 1.13.7
|
26
|
+
- gem --version
|
data/Changelog.md
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
# 3.4.0
|
2
|
+
* Elo support enhanced, PR #75
|
3
|
+
|
4
|
+
# 3.3.0
|
5
|
+
* Added support for MIR card brand
|
6
|
+
|
7
|
+
# 3.2.2
|
8
|
+
* Relaxing dependency requirements to support Rails 5
|
9
|
+
|
10
|
+
# 3.2.1
|
11
|
+
* Improved support for new Mastercard range (222100 – 272099)
|
12
|
+
|
13
|
+
# 3.2.0
|
14
|
+
* Added support for new Mastercard range (222100 – 272099)
|
15
|
+
|
16
|
+
# 3.1.0
|
17
|
+
* Added support for ELO brand
|
18
|
+
|
19
|
+
# 3.0.0
|
20
|
+
* Remove Laser brand because Laser cards were withdrawn from the market on February 28 2014
|
21
|
+
* Remove Diners US brand for similar reason
|
22
|
+
* Move Laser ranges to Maestro. Add 6390 range to Maestro
|
23
|
+
* Add plugins for removed brands
|
24
|
+
|
25
|
+
# 2.0.2
|
26
|
+
* fix using ActiveModel Validator's message option
|
27
|
+
|
28
|
+
# 2.0.1
|
29
|
+
* fix typo dankrot -> dankort
|
30
|
+
|
31
|
+
# 2.0.0
|
32
|
+
|
33
|
+
* added support for full brand names
|
34
|
+
* added possibility to remove card brands globally
|
35
|
+
* support for procs using :brand option for CreditCardNumberValidator
|
36
|
+
* :only and :except options for CreditCardNumberValidator
|
37
|
+
* credit card generator
|
38
|
+
* test unit -> specs migration
|
39
|
+
* card rules structure changed to allow custom options
|
40
|
+
* yaml storage implemented
|
41
|
+
* Maestro detection fix
|
42
|
+
* JCB detection fix
|
43
|
+
|
44
|
+
|
45
|
+
# 1.5.1
|
46
|
+
|
47
|
+
* Maestro detection fix
|
48
|
+
|
49
|
+
# 1.5.0
|
50
|
+
|
51
|
+
* Rupay detection support
|
52
|
+
* Hipercard detection support
|
53
|
+
|
54
|
+
# 1.4.7
|
55
|
+
|
56
|
+
* Maestro and Switch detection fix
|
57
|
+
|
58
|
+
# 1.4.6
|
59
|
+
|
60
|
+
* JCB detection fix
|
61
|
+
|
62
|
+
# 1.4.5
|
63
|
+
|
64
|
+
* Diners detection fix
|
65
|
+
|
66
|
+
# 1.4.4
|
67
|
+
|
68
|
+
* Visa detection fix
|
69
|
+
|
70
|
+
# 1.4.3
|
71
|
+
|
72
|
+
* fix for Rails 4.+ dependency
|
73
|
+
* Maestro detection fixes
|
74
|
+
|
75
|
+
# 1.4.2
|
76
|
+
|
77
|
+
* fix for Rails 4.1 dependency
|
78
|
+
* UnionPay and Discover detection fixes
|
79
|
+
|
80
|
+
# 1.4.1
|
81
|
+
|
82
|
+
* fixed ActiveModel Validator syntax
|
83
|
+
|
84
|
+
# 1.4.0
|
85
|
+
|
86
|
+
* Dankort brand support
|
87
|
+
|
88
|
+
# 1.3.0
|
89
|
+
|
90
|
+
* MMI detection support
|
91
|
+
|
92
|
+
# 1.2.0
|
93
|
+
|
94
|
+
* Allow brand restriction when detecting brand
|
95
|
+
* fix for Rails 4.0 dependency
|
96
|
+
* Allow to skip Luhn validation for certain brands
|
97
|
+
|
98
|
+
# 1.1.2
|
99
|
+
|
100
|
+
* Credit card number sanitizing fix
|
101
|
+
* :any instead of brand name can be used with ActiveModel Validator
|
102
|
+
|
103
|
+
# 1.1.1
|
104
|
+
|
105
|
+
* ActiveModel Validator was moved from root namespace
|
106
|
+
|
107
|
+
# 1.1.0
|
108
|
+
|
109
|
+
* Added rules for Maestro brand
|
110
|
+
* String extension added
|
111
|
+
|
112
|
+
# 1.0.1
|
113
|
+
|
114
|
+
* Dependency fixes
|
115
|
+
* Added usage instructions to README
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Igor Fedoronchuk
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
# CreditCardValidations
|
2
|
+
|
3
|
+
[![Build Status](http://img.shields.io/travis/Fivell/credit_card_validations.svg)](https://travis-ci.org/Fivell/credit_card_validations)
|
4
|
+
[![Dependency Status](http://img.shields.io/gemnasium/Fivell/credit_card_validations.svg)](https://gemnasium.com/Fivell/credit_card_validations)
|
5
|
+
[![Coverage Status](http://img.shields.io/coveralls/Fivell/credit_card_validations.svg)](https://coveralls.io/r/Fivell/credit_card_validations)
|
6
|
+
[![Code Climate](http://img.shields.io/codeclimate/github/Fivell/credit_card_validations.svg)](https://codeclimate.com/github/Fivell/credit_card_validations)
|
7
|
+
[![Gem Version](http://img.shields.io/gem/v/credit_card_validations.svg)](https://rubygems.org/gems/credit_card_validations)
|
8
|
+
[![License](http://img.shields.io/:license-mit-blue.svg)](http://Fivell.mit-license.org)
|
9
|
+
|
10
|
+
|
11
|
+
Gem adds validator to check whether or not a given number actually falls within the ranges of possible numbers prior to performing such verification, and, as such, CreditCardValidations simply verifies that the credit card number provided is well-formed.
|
12
|
+
|
13
|
+
More info about card BIN numbers http://en.wikipedia.org/wiki/Bank_card_number
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
Add this line to your application's Gemfile:
|
18
|
+
|
19
|
+
```sh
|
20
|
+
$ gem 'credit_card_validations'
|
21
|
+
```
|
22
|
+
|
23
|
+
And then execute:
|
24
|
+
|
25
|
+
```sh
|
26
|
+
$ bundle
|
27
|
+
```
|
28
|
+
|
29
|
+
Or install it yourself as:
|
30
|
+
|
31
|
+
```sh
|
32
|
+
$ gem install credit_card_validations
|
33
|
+
```
|
34
|
+
|
35
|
+
## Usage
|
36
|
+
|
37
|
+
|
38
|
+
The following issuing institutes are accepted:
|
39
|
+
|
40
|
+
| Name | Key |
|
41
|
+
--------------------- | ------------|
|
42
|
+
[American Express](http://en.wikipedia.org/wiki/American_Express) | :amex
|
43
|
+
[China UnionPay](http://en.wikipedia.org/wiki/China_UnionPay) | :unionpay
|
44
|
+
[Dankort](http://en.wikipedia.org/wiki/Dankort) | :dankort
|
45
|
+
[Diners Club](http://en.wikipedia.org/wiki/Diners_Club_International) | :diners
|
46
|
+
[Elo](https://pt.wikipedia.org/wiki/Elo_Participa%C3%A7%C3%B5es_S/A) | :elo
|
47
|
+
[Discover](http://en.wikipedia.org/wiki/Discover_Card) | :discover
|
48
|
+
[Hipercard](http://pt.wikipedia.org/wiki/Hipercard) | :hipercard
|
49
|
+
[JCB](http://en.wikipedia.org/wiki/Japan_Credit_Bureau) | :jcb
|
50
|
+
[Maestro](http://en.wikipedia.org/wiki/Maestro_%28debit_card%29) | :maestro
|
51
|
+
[MasterCard](http://en.wikipedia.org/wiki/MasterCard) | :mastercard
|
52
|
+
[MIR](http://www.nspk.ru/en/cards-mir/) | :mir
|
53
|
+
[Rupay](http://en.wikipedia.org/wiki/RuPay) | :rupay
|
54
|
+
[Solo](http://en.wikipedia.org/wiki/Solo_(debit_card)) | :solo
|
55
|
+
[Switch](http://en.wikipedia.org/wiki/Switch_(debit_card)) | :switch
|
56
|
+
[Visa](http://en.wikipedia.org/wiki/Visa_Inc.) | :visa
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
The following are supported with with plugins
|
61
|
+
|
62
|
+
| Name | Key |
|
63
|
+
--------------------- | ------------|
|
64
|
+
[Diners Club US](http://en.wikipedia.org/wiki/Diners_Club_International#MasterCard_alliance) | :diners_us
|
65
|
+
[EnRoute](https://en.wikipedia.org/wiki/EnRoute_(credit_card)) | :en_route
|
66
|
+
[Laser](https://en.wikipedia.org/wiki/Laser_%28debit_card%29) | :laser
|
67
|
+
|
68
|
+
|
69
|
+
|
70
|
+
### Examples using string monkey patch
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
require 'credit_card_validations/string'
|
74
|
+
'5274 5763 9425 9961'.credit_card_brand #=> :mastercard
|
75
|
+
'5274 5763 9425 9961'.credit_card_brand_name #=> "MasterCard"
|
76
|
+
'5274 5763 9425 9961'.valid_credit_card_brand?(:mastercard, :visa) #=> true
|
77
|
+
'5274 5763 9425 9961'.valid_credit_card_brand?(:amex) #=> false
|
78
|
+
'5274 5763 9425 9961'.valid_credit_card_brand?('MasterCard') #=> true
|
79
|
+
```
|
80
|
+
|
81
|
+
### ActiveModel support
|
82
|
+
|
83
|
+
only for certain brands
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
class CreditCardModel
|
87
|
+
attr_accessor :number
|
88
|
+
include ActiveModel::Validations
|
89
|
+
validates :number, credit_card_number: {brands: [:amex, :maestro]}
|
90
|
+
end
|
91
|
+
```
|
92
|
+
|
93
|
+
for all known brands
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
validates :number, presence: true, credit_card_number: true
|
97
|
+
```
|
98
|
+
|
99
|
+
### Examples using CreditCardValidations::Detector class
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
number = "4111111111111111"
|
103
|
+
detector = CreditCardValidations::Detector.new(number)
|
104
|
+
detector.brand #:visa
|
105
|
+
detector.visa? #true
|
106
|
+
detector.valid?(:mastercard,:maestro) #false
|
107
|
+
detector.valid?(:visa, :mastercard) #true
|
108
|
+
detector.issuer_category #"Banking and financial"
|
109
|
+
```
|
110
|
+
|
111
|
+
### Also You can add your own brand rules to detect other credit card brands/types
|
112
|
+
passing name,length(integer/array of integers) and prefix(string/array of strings)
|
113
|
+
Example
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
CreditCardValidations.add_brand(:voyager, {length: 15, prefixes: '86'})
|
117
|
+
voyager_test_card_number = '869926275400212'
|
118
|
+
CreditCardValidations::Detector.new(voyager_test_card_number).brand #:voyager
|
119
|
+
CreditCardValidations::Detector.new(voyager_test_card_number).voyager? #true
|
120
|
+
```
|
121
|
+
|
122
|
+
### Remove brands also supported
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
CreditCardValidations::Detector.delete_brand(:maestro)
|
126
|
+
```
|
127
|
+
|
128
|
+
### Check luhn
|
129
|
+
|
130
|
+
```ruby
|
131
|
+
CreditCardValidations::Detector.new(@credit_card_number).valid_luhn?
|
132
|
+
#or
|
133
|
+
CreditCardValidations::Luhn.valid?(@credit_card_number)
|
134
|
+
```
|
135
|
+
|
136
|
+
### Generate credit card numbers that pass validation
|
137
|
+
|
138
|
+
```ruby
|
139
|
+
CreditCardValidations::Factory.random(:amex)
|
140
|
+
# => "348051773827666"
|
141
|
+
CreditCardValidations::Factory.random(:maestro)
|
142
|
+
# => "6010430241237266856"
|
143
|
+
```
|
144
|
+
|
145
|
+
### Plugins
|
146
|
+
|
147
|
+
```ruby
|
148
|
+
require 'credit_card_validations/plugins/en_route'
|
149
|
+
require 'credit_card_validations/plugins/laser'
|
150
|
+
require 'credit_card_validations/plugins/diners_us'
|
151
|
+
```
|
152
|
+
|
153
|
+
## Contributing
|
154
|
+
|
155
|
+
1. Fork it
|
156
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
157
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
158
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
159
|
+
5. Create new Pull Request
|
160
|
+
|
161
|
+
|
162
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'credit_card_validations/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "cw_credit_card_validations"
|
8
|
+
gem.version = CreditCardValidations::VERSION
|
9
|
+
gem.authors = ["Igor"]
|
10
|
+
gem.email = ["fedoronchuk@gmail.com"]
|
11
|
+
gem.description = %q{A ruby gem for validating credit card numbers}
|
12
|
+
gem.summary = "gem should be used for credit card numbers validation, card brands detections, luhn checks"
|
13
|
+
gem.homepage = "http://fivell.github.io/credit_card_validations/"
|
14
|
+
gem.license = "MIT"
|
15
|
+
|
16
|
+
gem.files = `git ls-files`.split($/)
|
17
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
18
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
19
|
+
gem.require_paths = ["lib"]
|
20
|
+
|
21
|
+
|
22
|
+
gem.add_dependency "activemodel", ">= 3", "<= 6"
|
23
|
+
gem.add_dependency "activesupport", ">= 3", "<= 6"
|
24
|
+
|
25
|
+
|
26
|
+
gem.add_development_dependency "mocha", '1.1.0'
|
27
|
+
gem.add_development_dependency 'rake', '~> 10'
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
|
3
|
+
gem "activemodel", ">= 3", '< 5'
|
4
|
+
gem "activesupport", ">= 3", '< 5'
|
5
|
+
group :test do
|
6
|
+
gem 'tins', '< 1.3.4', platforms: [:ruby_19, :jruby]
|
7
|
+
gem 'term-ansicolor', '~> 1.3.0', platforms: [:ruby_19, :jruby]
|
8
|
+
gem 'coveralls', require: false # Test coverage website. Go to https://coveralls.io
|
9
|
+
end
|
10
|
+
|
11
|
+
gemspec :path => "../"
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# == ActiveModel Validations CreditCardNumberValidator
|
2
|
+
# Can be used in combination with the +validates+ method
|
3
|
+
#
|
4
|
+
# Only Amex and Maestro
|
5
|
+
#
|
6
|
+
# class CreditCard
|
7
|
+
# attr_accessor :number
|
8
|
+
# include ActiveModel::Validations
|
9
|
+
# validates :number, credit_card_number: {only: [:amex, :maestro]}
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# All numbers are valid except Maestro
|
13
|
+
#
|
14
|
+
# class CreditCard
|
15
|
+
# attr_accessor :number
|
16
|
+
# include ActiveModel::Validations
|
17
|
+
# validates :number, credit_card_number: {except: [:maestro]}
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# Proc can be used as well
|
21
|
+
#
|
22
|
+
# class CreditCard
|
23
|
+
# attr_accessor :number, :card_type
|
24
|
+
# include ActiveModel::Validations
|
25
|
+
# validates :number, credit_card_number: {brands: ->{|record| Array(record.accepted_brands) } }
|
26
|
+
#
|
27
|
+
# def accepted_brands
|
28
|
+
# if card_type == 'Maestro'
|
29
|
+
# :maestro
|
30
|
+
# elsif card_type == 'American Express'
|
31
|
+
# :amex
|
32
|
+
# else
|
33
|
+
# :visa
|
34
|
+
# end
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
#
|
40
|
+
|
41
|
+
module ActiveModel
|
42
|
+
module Validations
|
43
|
+
class CreditCardNumberValidator < EachValidator
|
44
|
+
|
45
|
+
def validate_each(record, attribute, value)
|
46
|
+
record.errors.add(attribute, options[:message] || :invalid) unless credit_card_valid?(value, extract_brands(record, options))
|
47
|
+
end
|
48
|
+
|
49
|
+
def credit_card_valid?(number, brands = [])
|
50
|
+
CreditCardValidations::Detector.new(number).valid?(*brands)
|
51
|
+
end
|
52
|
+
|
53
|
+
protected
|
54
|
+
|
55
|
+
def extract_brands(record, options)
|
56
|
+
if options.has_key?(:brands)
|
57
|
+
with_brands(record, options[:brands])
|
58
|
+
elsif options.has_key?(:only)
|
59
|
+
Array(options[:only])
|
60
|
+
elsif options.has_key?(:except)
|
61
|
+
Array(CreditCardValidations::Detector.brands.keys) - Array(options[:except])
|
62
|
+
else
|
63
|
+
[]
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
def with_brands(record, brands)
|
69
|
+
if brands.is_a?(Proc)
|
70
|
+
brands.call(record)
|
71
|
+
elsif options[:brands] == :any
|
72
|
+
[]
|
73
|
+
else
|
74
|
+
Array(options[:brands])
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|