diff --git a/.travis.yml b/.travis.yml index d9fa4ca..19844a2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,6 @@ language: node_js node_js: + - "0.12" - "0.11" - "0.10" before_script: diff --git a/Gruntfile.js b/Gruntfile.js index 3b7ebc2..7602aea 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -79,6 +79,17 @@ module.exports = function (grunt) { } }, + browserify: { + dist: { + options: { + transform: ['debowerify'] + }, + files: { + '.tmp/ng-iban.js': '.tmp/ng-iban.js' + } + } + }, + karma: { unit: { configFile: 'test/karma.conf.coffee', @@ -90,6 +101,7 @@ module.exports = function (grunt) { grunt.registerTask('test', [ 'clean:tmp', 'coffee', + 'browserify', 'connect:test', 'karma' ]); @@ -100,4 +112,4 @@ module.exports = function (grunt) { 'ngAnnotate', 'uglify' ]); -} +}; diff --git a/README.adoc b/README.adoc new file mode 100644 index 0000000..af98830 --- /dev/null +++ b/README.adoc @@ -0,0 +1,30 @@ +:figure-caption!: + +image::https://travis-ci.org/mmjmanders/ng-iban.svg?branch=master[title="travis status", alt="travis status", link="https://travis-ci.org/mmjmanders/ng-iban"] + +image::https://app.wercker.com/status/eb4337041c62e162c5dd7af43122647c/m[title="wercker status", alt="wercker status", link="https://app.wercker.com/project/bykey/eb4337041c62e162c5dd7af43122647c"] + += ng-iban - validate input fields as IBAN +The goal is to provide an easy way to validate an input field as an IBAN number with https://angularjs.org/[AngularJS]. +From version `0.4.0` the module uses https://github.com/arhs/iban.js[iban.js] for validation. + +== Usage +First add + +* `AngularJS` +* `ng-iban` + +to your HTML file. + +Make sure you require `mm.iban` as a dependency of your AngularJS module. + +== Installation +`bower install ng-iban` + +=== directive +[source,html] +---- + +---- + +To use this directive the `ngModel` directive must also be used because this directive depends on it. diff --git a/README.md b/README.md deleted file mode 100644 index ebfd0fa..0000000 --- a/README.md +++ /dev/null @@ -1,102 +0,0 @@ -[![Build Status](https://travis-ci.org/mmjmanders/ng-iban.svg?branch=master)](https://travis-ci.org/mmjmanders/ng-iban) - -# ng-iban - validate input fields as IBAN -The goals is to provide an easy way to validate an input field as an IBAN number with [AngularJS](https://angularjs.org/). - -## Usage -First add - -- `AngularJS` -- `ng-iban` - -to your HTML file. - -Make sure you require `mm.iban` as a dependency of your AngularJS module. - -## Installation -`bower install ng-iban` - -### directive -```html - -``` - -or - -```html - -``` - -where `[Country code]` is one of the country codes from the table below. - -To use this directive the `ngModel` directive must also be used because this directive depends on it. - -## Predefined countries -Country | Country code | IBAN format | IBAN length | IBAN example -------- | ------------ | ----------- | ----------- | ------------ -Albania | AL | AL[0-9]{2}[0-9]{8}[A-Z0-9]{16} | 28 | AL47212110090000000235698741 -Andorra | AD | AD[0-9]{2}[0-9]{4}[0-9]{4}[A-Z0-9]{12} | 24 | AD1200012030200359100100 -Austria | AT | AT[0-9]{2}[0-9]{5}[0-9]{11} | 20 | AT611904300234573201 -Azerbaijan, Republic of | AZ | AZ[0-9]{2}[A-Z]{4}[A-Z0-9]{20} | 28 | AZ21NABZ00000000137010001944 -Bahrain | BH | BH[0-9]{2}[A-Z]{4}[A-Z0-9]{14} | 22 | BH67BMAG00001299123456 -Belgium | BE | BE[0-9]{2}[0-9]{3}[0-9]{7}[0-9]{2} | 16 | BE68539007547034 -Bosnia and Herzegovina | BA | BA[0-9]{2}[0-9]{3}[0-9]{3}[0-9]{8}[0-9]{2} | 20 | BA391290079401028494 -Brazil | BR | BR[0-9]{2}[0-9]{8}[0-9]{5}[0-9]{10}[A-Z]{1}[A-Z0-9]{1} | 29 | BR7724891749412660603618210F3 -Bulgaria | BG | BG[0-9]{2}[A-Z]{4}[0-9]{4}[0-9]{2}[A-Z0-9]{8} | 22 | BG80BNBG96611020345678 -Costa Rica | CR | CR[0-9]{2}[0-9]{3}[0-9]{14} | 21 | CR0515202001026284066 -Croatia | HR | HR[0-9]{2}[0-9]{7}[0-9]{10} | 21 | HR1210010051863000160 -Cyprus | CY | CY[0-9]{2}[0-9]{3}[0-9]{5}[A-Z0-9]{16} | 28 | CY17002001280000001200527600 -Czech Republic | CZ | CZ[0-9]{2}[0-9]{4}[0-9]{6}[0-9]{10} | 24 | CZ6508000000192000145399 -Denmark | DK | DK[0-9]{2}[0-9]{4}[0-9]{9}[0-9]{1} | 18 | DK5000400440116243 -Dominican Republic | DO | DO[0-9]{2}[A-Z0-9]{4}[0-9]{20} | 28 | DO28BAGR00000001212453611324 -Estonia | EE | EE[0-9]{2}[0-9]{2}[0-9]{2}[0-9]{11}[0-9]{1} | 20 | EE382200221020145685 -Faroe Islands | FO | FO[0-9]{2}[0-9]{4}[0-9]{9}[0-9]{1} | 18 | FO6264600001631634 -Finland | FI | FI[0-9]{2}[0-9]{6}[0-9]{7}[0-9]{1} | 18 | FI2112345600000785 -France | FR | FR[0-9]{2}[0-9]{5}[0-9]{5}[A-Z0-9]{11}[0-9]{2} | 27 | FR1420041010050500013M02606 -Georgia | GE | GE[0-9]{2}[A-Z]{2}[0-9]{16} | 22 | GE29NB0000000101904917 -Germany | DE | DE[0-9]{2}[0-9]{8}[0-9]{10} | 22 | DE89370400440532013000 -Gibraltar | GI | GI[0-9]{2}[A-Z]{4}[A-Z0-9]{15} | 23 | GI75NWBK000000007099453 -Greece | GR | GR[0-9]{2}[0-9]{3}[0-9]{4}[A-Z0-9]{16} | 27 | GR1601101250000000012300695 -Greenland | GL | GL[0-9]{2}[0-9]{4}[0-9]{9}[0-9]{1} | 18 | GL8964710001000206 -Guatemala | GT | GT[0-9]{2}[A-Z0-9]{4}[A-Z0-9]{20} | 28 | GT82TRAJ01020000001210029690 -Hungary | HU | HU[0-9]{2}[0-9]{3}[0-9]{4}[0-9]{1}[0-9]{15}[0-9]{1} | 28 | HU42117730161111101800000000 -Iceland | IS | IS[0-9]{2}[0-9]{4}[0-9]{2}[0-9]{6}[0-9]{10} | 26 | IS140159260076545510730339 -Ireland | IE | IE[0-9]{2}[A-Z]{4}[0-9]{6}[0-9]{8} | 22 | IE29AIBK93115212345678 -Israel | IL | IL[0-9]{2}[0-9]{3}[0-9]{3}[0-9]{13} | 23 | IL620108000000099999999 -Italy | IT | IT[0-9]{2}[A-Z]{1}[0-9]{5}[0-9]{5}[A-Z0-9]{12} | 27 | IT60X0542811101000000123456 -Kazakhstan | KZ | KZ[0-9]{2}[0-9]{3}[A-Z0-9]{13} | 20 | KZ86125KZT5004100100 -Kuwait | KW | KW[0-9]{2}[A-Z]{4}[A-Z0-9]{22} | 30 | KW81CBKU0000000000001234560101 -Latvia | LV | LV[0-9]{2}[A-Z]{4}[A-Z0-9]{13} | 21 | LV80BANK0000435195001 -Lebanon | LB | LB[0-9]{2}[0-9]{4}[A-Z0-9]{20} | 28 | LB62099900000001001901229114 -Liechtenstein (Principality of) | LI | LI[0-9]{2}[0-9]{5}[A-Z0-9]{12} | 21 | LI21088100002324013AA -Lithuania | LT | LT[0-9]{2}[0-9]{5}[0-9]{11} | 20 | LT121000011101001000 -Luxembourg | LU | LU[0-9]{2}[0-9]{3}[A-Z0-9]{13} | 20 | LU280019400644750000 -Macedonia | MK | MK[0-9]{2}[0-9]{3}[A-Z0-9]{10}[0-9]{2} | 19 | MK07250120000058984 -Malta | MT | MT[0-9]{2}[A-Z]{4}[0-9]{5}[A-Z0-9]{18} | 31 | MT84MALT011000012345MTLCAST001S -Mauritania | MR | MR[0-9]{2}[0-9]{5}[0-9]{5}[0-9]{11}[0-9]{2} | 27 | MR1300020001010000123456753 -Mauritius | MU | MU[0-9]{2}[A-Z]{4}[0-9]{2}[0-9]{2}[0-9]{12}[0-9]{3}[A-Z]{3} | 30 | MU17BOMM0101101030300200000MUR -Moldova, Republic of | MD | MD[0-9]{2}[A-Z0-9]{20} | 24 | MD24AG000225100013104168 -Monaco | MC | MC[0-9]{2}[0-9]{5}[0-9]{5}[A-Z0-9]{11}[0-9]{2} | 27 | MC5811222000010123456789030 -Montenegro | ME | ME[0-9]{2}[0-9]{3}[0-9]{13}[0-9]{2} | 22 | ME25505000012345678951 -Netherlands | NL | NL[0-9]{2}[A-Z]{4}[0-9]{10} | 18 | NL91ABNA0417164300 -Norway | NO | NO[0-9]{2}[0-9]{4}[0-9]{6}[0-9]{1} | 15 | NO9386011117947 -Pakistan | PK | PK[0-9]{2}[A-Z]{4}[A-Z0-9]{16} | 24 | PK36SCBL0000001123456702 -Poland | PL | PL[0-9]{2}[0-9]{8}[0-9]{16} | 28 | PL61109010140000071219812874 -Palestinian Territory, Occupied | PS | PS[0-9]{2}[A-Z]{4}[A-Z0-9]{21} | 29 | PS92PALS000000000400123456702 -Portugal | PT | PT[0-9]{2}[0-9]{4}[0-9]{4}[0-9]{11}[0-9]{2} | 25 | PT50000201231234567890154 -Qatar | QA | QA[0-9]{2}[A-Z]{4}[A-Z0-9]{21} | 29 | QA58DOHB00001234567890ABCDEFG -Romania | RO | RO[0-9]{2}[A-Z]{4}[A-Z0-9]{16} | 24 | RO49AAAA1B31007593840000 -San Marino | SM | SM[0-9]{2}[A-Z]{1}[0-9]{5}[0-9]{5}[A-Z0-9]{12} | 27 | SM86U0322509800000000270100 -Saudi Arabia | SA | SA[0-9]{2}[0-9]{2}[A-Z0-9]{18} | 24 | SA0380000000608010167519 -Serbia | RS | RS[0-9]{2}[0-9]{3}[0-9]{13}[0-9]{2} | 22 | RS35260005601001611379 -Slovak Republic | SK | SK[0-9]{2}[0-9]{4}[0-9]{6}[0-9]{10} | 24 | SK3112000000198742637541 -Slovenia | SI | SI[0-9]{2}[0-9]{5}[0-9]{8}[0-9]{2} | 19 | SI56263300012039086 -Spain | ES | ES[0-9]{2}[0-9]{4}[0-9]{4}[0-9]{1}[0-9]{1}[0-9]{10} | 24 | ES9121000418450200051332 -Sweden | SE | SE[0-9]{2}[0-9]{3}[0-9]{16}[0-9]{1} | 24 | SE4550000000058398257466 -Switzerland | CH | CH[0-9]{2}[0-9]{5}[A-Z0-9]{12} | 21 | CH9300762011623852957 -Timor-Leste | TL | TL[0-9]{2}[0-9]{3}[0-9]{14}[0-9]{2} | 23 | TL380080012345678910157 -Tunisia | TN | TN[0-9]{2}[0-9]{2}[0-9]{3}[0-9]{13}[0-9]{2} | 24 | TN5910006035183598478831 -Turkey | TR | TR[0-9]{2}[0-9]{5}[A-Z0-9]{1}[A-Z0-9]{16} | 26 | TR330006100519786457841326 -United Arab Emirates | AE | AE[0-9]{2}[0-9]{3}[0-9]{16} | 23 | AE070331234567890123456 -United Kingdom | GB | GB[0-9]{2}[A-Z]{4}[0-9]{6}[0-9]{8} | 22 | GB29NWBK60161331926819 -Virgin Islands, British | VG | VG[0-9]{2}[A-Z]{4}[0-9]{16} | 24 | VG96VPVG0000012345678901 diff --git a/bower.json b/bower.json index 37aa93f..83c108c 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "ng-iban", - "version": "0.3.2", + "version": "0.4.0", "authors": [ "Mark Manders " ], @@ -25,6 +25,7 @@ "angular": ">=1 <2" }, "devDependencies": { - "angular-mocks": ">=1 <2" + "angular-mocks": ">=1 <2", + "iban": "~0.0.5" } } diff --git a/dist/ng-iban.js b/dist/ng-iban.js index c3b8e57..a368f0e 100644 --- a/dist/ng-iban.js +++ b/dist/ng-iban.js @@ -1,273 +1,13 @@ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= A && code <= Z) { - return code - A + 10; - } else { - return n; - } - }).join(''); - while (remainder.length > 2) { - block = remainder.slice(0, 9); - remainder = __modulo(parseInt(block, 10), 97) + remainder.slice(block.length); - } - return __modulo(parseInt(remainder, 10), 97) === 1; + return self.IBAN.isValid(iban); }; ctrl.$parsers.unshift(function(viewValue) { var parsed, valid; @@ -342,6 +63,410 @@ }); } }; - }]); + }); }).call(this); + +},{"./..\\bower_components\\iban\\iban.js":2}],2:[function(require,module,exports){ +(function(exports){ + + // Array.prototype.map polyfill + // code from https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/map + if (!Array.prototype.map){ + Array.prototype.map = function(fun /*, thisArg */){ + "use strict"; + + if (this === void 0 || this === null) + throw new TypeError(); + + var t = Object(this); + var len = t.length >>> 0; + if (typeof fun !== "function") + throw new TypeError(); + + var res = new Array(len); + var thisArg = arguments.length >= 2 ? arguments[1] : void 0; + for (var i = 0; i < len; i++) + { + // NOTE: Absolute correctness would demand Object.defineProperty + // be used. But this method is fairly new, and failure is + // possible only if Object.prototype or Array.prototype + // has a property |i| (very unlikely), so use a less-correct + // but more portable alternative. + if (i in t) + res[i] = fun.call(thisArg, t[i], i, t); + } + + return res; + }; + } + + var A = 'A'.charCodeAt(0), + Z = 'Z'.charCodeAt(0); + + /** + * Prepare an IBAN for mod 97 computation by moving the first 4 chars to the end and transforming the letters to + * numbers (A = 10, B = 11, ..., Z = 35), as specified in ISO13616. + * + * @param {string} iban the IBAN + * @returns {string} the prepared IBAN + */ + function iso13616Prepare(iban) { + iban = iban.toUpperCase(); + iban = iban.substr(4) + iban.substr(0,4); + + return iban.split('').map(function(n){ + var code = n.charCodeAt(0); + if (code >= A && code <= Z){ + // A = 10, B = 11, ... Z = 35 + return code - A + 10; + } else { + return n; + } + }).join(''); + } + + /** + * Calculates the MOD 97 10 of the passed IBAN as specified in ISO7064. + * + * @param iban + * @returns {number} + */ + function iso7064Mod97_10(iban) { + var remainder = iban, + block; + + while (remainder.length > 2){ + block = remainder.slice(0, 9); + remainder = parseInt(block, 10) % 97 + remainder.slice(block.length); + } + + return parseInt(remainder, 10) % 97; + } + + /** + * Parse the BBAN structure used to configure each IBAN Specification and returns a matching regular expression. + * A structure is composed of blocks of 3 characters (one letter and 2 digits). Each block represents + * a logical group in the typical representation of the BBAN. For each group, the letter indicates which characters + * are allowed in this group and the following 2-digits number tells the length of the group. + * + * @param {string} structure the structure to parse + * @returns {RegExp} + */ + function parseStructure(structure){ + // split in blocks of 3 chars + var regex = structure.match(/(.{3})/g).map(function(block){ + + // parse each structure block (1-char + 2-digits) + var format, + pattern = block.slice(0, 1), + repeats = parseInt(block.slice(1), 10); + + switch (pattern){ + case "A": format = "0-9A-Za-z"; break; + case "B": format = "0-9A-Z"; break; + case "C": format = "A-Za-z"; break; + case "F": format = "0-9"; break; + case "L": format = "a-z"; break; + case "U": format = "A-Z"; break; + case "W": format = "0-9a-z"; break; + } + + return '([' + format + ']{' + repeats + '})'; + }); + + return new RegExp('^' + regex.join('') + '$'); + } + + /** + * Create a new Specification for a valid IBAN number. + * + * @param countryCode the code of the country + * @param length the length of the IBAN + * @param structure the structure of the undernying BBAN (for validation and formatting) + * @param example an example valid IBAN + * @constructor + */ + function Specification(countryCode, length, structure, example){ + + this.countryCode = countryCode; + this.length = length; + this.structure = structure; + this.example = example; + } + + /** + * Lazy-loaded regex (parse the structure and construct the regular expression the first time we need it for validation) + */ + Specification.prototype._regex = function(){ + return this._cachedRegex || (this._cachedRegex = parseStructure(this.structure)) + }; + + /** + * Check if the passed iban is valid according to this specification. + * + * @param {String} iban the iban to validate + * @returns {boolean} true if valid, false otherwise + */ + Specification.prototype.isValid = function(iban){ + return this.length == iban.length + && this.countryCode === iban.slice(0,2) + && this._regex().test(iban.slice(4)) + && iso7064Mod97_10(iso13616Prepare(iban)) == 1; + }; + + /** + * Convert the passed IBAN to a country-specific BBAN. + * + * @param iban the IBAN to convert + * @param separator the separator to use between BBAN blocks + * @returns {string} the BBAN + */ + Specification.prototype.toBBAN = function(iban, separator) { + return this._regex().exec(iban.slice(4)).slice(1).join(separator); + }; + + /** + * Convert the passed BBAN to an IBAN for this country specification. + * Please note that "generation of the IBAN shall be the exclusive responsibility of the bank/branch servicing the account". + * This method implements the preferred algorithm described in http://en.wikipedia.org/wiki/International_Bank_Account_Number#Generating_IBAN_check_digits + * + * @param bban the BBAN to convert to IBAN + * @returns {string} the IBAN + */ + Specification.prototype.fromBBAN = function(bban) { + if (!this.isValidBBAN(bban)){ + throw new Error('Invalid BBAN'); + } + + var remainder = iso7064Mod97_10(iso13616Prepare(this.countryCode + '00' + bban)), + checkDigit = ('0' + (98 - remainder)).slice(-2); + + return this.countryCode + checkDigit + bban; + }; + + /** + * Check of the passed BBAN is valid. + * This function only checks the format of the BBAN (length and matching the letetr/number specs) but does not + * verify the check digit. + * + * @param bban the BBAN to validate + * @returns {boolean} true if the passed bban is a valid BBAN according to this specification, false otherwise + */ + Specification.prototype.isValidBBAN = function(bban) { + return this.length - 4 == bban.length + && this._regex().test(bban); + }; + + var countries = {}; + + function addSpecification(IBAN){ + countries[IBAN.countryCode] = IBAN; + } + + addSpecification(new Specification("AD", 24, "F04F04A12", "AD1200012030200359100100")); + addSpecification(new Specification("AE", 23, "F03F16", "AE070331234567890123456")); + addSpecification(new Specification("AL", 28, "F08A16", "AL47212110090000000235698741")); + addSpecification(new Specification("AT", 20, "F05F11", "AT611904300234573201")); + addSpecification(new Specification("AZ", 28, "U04A20", "AZ21NABZ00000000137010001944")); + addSpecification(new Specification("BA", 20, "F03F03F08F02", "BA391290079401028494")); + addSpecification(new Specification("BE", 16, "F03F07F02", "BE68539007547034")); + addSpecification(new Specification("BG", 22, "U04F04F02A08", "BG80BNBG96611020345678")); + addSpecification(new Specification("BH", 22, "U04A14", "BH67BMAG00001299123456")); + addSpecification(new Specification("BR", 29, "F08F05F10U01A01", "BR9700360305000010009795493P1")); + addSpecification(new Specification("CH", 21, "F05A12", "CH9300762011623852957")); + addSpecification(new Specification("CR", 21, "F03F14", "CR0515202001026284066")); + addSpecification(new Specification("CY", 28, "F03F05A16", "CY17002001280000001200527600")); + addSpecification(new Specification("CZ", 24, "F04F06F10", "CZ6508000000192000145399")); + addSpecification(new Specification("DE", 22, "F08F10", "DE89370400440532013000")); + addSpecification(new Specification("DK", 18, "F04F09F01", "DK5000400440116243")); + addSpecification(new Specification("DO", 28, "U04F20", "DO28BAGR00000001212453611324")); + addSpecification(new Specification("EE", 20, "F02F02F11F01", "EE382200221020145685")); + addSpecification(new Specification("ES", 24, "F04F04F01F01F10", "ES9121000418450200051332")); + addSpecification(new Specification("FI", 18, "F06F07F01", "FI2112345600000785")); + addSpecification(new Specification("FO", 18, "F04F09F01", "FO6264600001631634")); + addSpecification(new Specification("FR", 27, "F05F05A11F02", "FR1420041010050500013M02606")); + addSpecification(new Specification("GB", 22, "U04F06F08", "GB29NWBK60161331926819")); + addSpecification(new Specification("GE", 22, "U02F16", "GE29NB0000000101904917")); + addSpecification(new Specification("GI", 23, "U04A15", "GI75NWBK000000007099453")); + addSpecification(new Specification("GL", 18, "F04F09F01", "GL8964710001000206")); + addSpecification(new Specification("GR", 27, "F03F04A16", "GR1601101250000000012300695")); + addSpecification(new Specification("GT", 28, "A04A20", "GT82TRAJ01020000001210029690")); + addSpecification(new Specification("HR", 21, "F07F10", "HR1210010051863000160")); + addSpecification(new Specification("HU", 28, "F03F04F01F15F01", "HU42117730161111101800000000")); + addSpecification(new Specification("IE", 22, "U04F06F08", "IE29AIBK93115212345678")); + addSpecification(new Specification("IL", 23, "F03F03F13", "IL620108000000099999999")); + addSpecification(new Specification("IS", 26, "F04F02F06F10", "IS140159260076545510730339")); + addSpecification(new Specification("IT", 27, "U01F05F05A12", "IT60X0542811101000000123456")); + addSpecification(new Specification("KW", 30, "U04A22", "KW81CBKU0000000000001234560101")); + addSpecification(new Specification("KZ", 20, "F03A13", "KZ86125KZT5004100100")); + addSpecification(new Specification("LB", 28, "F04A20", "LB62099900000001001901229114")); + addSpecification(new Specification("LI", 21, "F05A12", "LI21088100002324013AA")); + addSpecification(new Specification("LT", 20, "F05F11", "LT121000011101001000")); + addSpecification(new Specification("LU", 20, "F03A13", "LU280019400644750000")); + addSpecification(new Specification("LV", 21, "U04A13", "LV80BANK0000435195001")); + addSpecification(new Specification("MC", 27, "F05F05A11F02", "MC5811222000010123456789030")); + addSpecification(new Specification("MD", 24, "U02F18", "MD24AG000225100013104168")); + addSpecification(new Specification("ME", 22, "F03F13F02", "ME25505000012345678951")); + addSpecification(new Specification("MK", 19, "F03A10F02", "MK07250120000058984")); + addSpecification(new Specification("MR", 27, "F05F05F11F02", "MR1300020001010000123456753")); + addSpecification(new Specification("MT", 31, "U04F05A18", "MT84MALT011000012345MTLCAST001S")); + addSpecification(new Specification("MU", 30, "U04F02F02F12F03U03", "MU17BOMM0101101030300200000MUR")); + addSpecification(new Specification("NL", 18, "U04F10", "NL91ABNA0417164300")); + addSpecification(new Specification("NO", 15, "F04F06F01", "NO9386011117947")); + addSpecification(new Specification("PK", 24, "U04A16", "PK36SCBL0000001123456702")); + addSpecification(new Specification("PL", 28, "F08F16", "PL61109010140000071219812874")); + addSpecification(new Specification("PS", 29, "U04A21", "PS92PALS000000000400123456702")); + addSpecification(new Specification("PT", 25, "F04F04F11F02", "PT50000201231234567890154")); + addSpecification(new Specification("RO", 24, "U04A16", "RO49AAAA1B31007593840000")); + addSpecification(new Specification("RS", 22, "F03F13F02", "RS35260005601001611379")); + addSpecification(new Specification("SA", 24, "F02A18", "SA0380000000608010167519")); + addSpecification(new Specification("SE", 24, "F03F16F01", "SE4550000000058398257466")); + addSpecification(new Specification("SI", 19, "F05F08F02", "SI56263300012039086")); + addSpecification(new Specification("SK", 24, "F04F06F10", "SK3112000000198742637541")); + addSpecification(new Specification("SM", 27, "U01F05F05A12", "SM86U0322509800000000270100")); + addSpecification(new Specification("TN", 24, "F02F03F13F02", "TN5910006035183598478831")); + addSpecification(new Specification("TR", 26, "F05A01A16", "TR330006100519786457841326")); + addSpecification(new Specification("VG", 24, "U04F16", "VG96VPVG0000012345678901")); + + // Angola + addSpecification(new Specification("AO", 25, "F21", "AO69123456789012345678901")); + // Burkina + addSpecification(new Specification("BF", 27, "F23", "BF2312345678901234567890123")); + // Burundi + addSpecification(new Specification("BI", 16, "F12", "BI41123456789012")); + // Benin + addSpecification(new Specification("BJ", 28, "F24", "BJ39123456789012345678901234")); + // Ivory + addSpecification(new Specification("CI", 28, "U01F23", "CI17A12345678901234567890123")); + // Cameron + addSpecification(new Specification("CM", 27, "F23", "CM9012345678901234567890123")); + // Cape Verde + addSpecification(new Specification("CV", 25, "F21", "CV30123456789012345678901")); + // Algeria + addSpecification(new Specification("DZ", 24, "F20", "DZ8612345678901234567890")); + // Iran + addSpecification(new Specification("IR", 26, "F22", "IR861234568790123456789012")); + // Jordan + addSpecification(new Specification("JO", 30, "A04F22", "JO15AAAA1234567890123456789012")); + // Madagascar + addSpecification(new Specification("MG", 27, "F23", "MG1812345678901234567890123")); + // Mali + addSpecification(new Specification("ML", 28, "U01F23", "ML15A12345678901234567890123")); + // Mozambique + addSpecification(new Specification("MZ", 25, "F21", "MZ25123456789012345678901")); + // Quatar + addSpecification(new Specification("QA", 29, "U04A21", "QA30AAAA123456789012345678901")); + // Senegal + addSpecification(new Specification("SN", 28, "U01F23", "SN52A12345678901234567890123")); + // Ukraine + addSpecification(new Specification("UA", 29, "F25", "UA511234567890123456789012345")); + + var NON_ALPHANUM = /[^a-zA-Z0-9]/g, + EVERY_FOUR_CHARS =/(.{4})(?!$)/g; + + /** + * Utility function to check if a variable is a String. + * + * @param v + * @returns {boolean} true if the passed variable is a String, false otherwise. + */ + function isString(v){ + return (typeof v == 'string' || v instanceof String); + } + + /** + * Check if an IBAN is valid. + * + * @param {String} iban the IBAN to validate. + * @returns {boolean} true if the passed IBAN is valid, false otherwise + */ + exports.isValid = function(iban){ + if (!isString(iban)){ + return false; + } + iban = this.electronicFormat(iban); + var countryStructure = countries[iban.slice(0,2)]; + return !!countryStructure && countryStructure.isValid(iban); + }; + + /** + * Convert an IBAN to a BBAN. + * + * @param iban + * @param {String} [separator] the separator to use between the blocks of the BBAN, defaults to ' ' + * @returns {string|*} + */ + exports.toBBAN = function(iban, separator){ + if (typeof separator == 'undefined'){ + separator = ' '; + } + iban = this.electronicFormat(iban); + var countryStructure = countries[iban.slice(0,2)]; + if (!countryStructure) { + throw new Error('No country with code ' + iban.slice(0,2)); + } + return countryStructure.toBBAN(iban, separator); + }; + + /** + * Convert the passed BBAN to an IBAN for this country specification. + * Please note that "generation of the IBAN shall be the exclusive responsibility of the bank/branch servicing the account". + * This method implements the preferred algorithm described in http://en.wikipedia.org/wiki/International_Bank_Account_Number#Generating_IBAN_check_digits + * + * @param countryCode the country of the BBAN + * @param bban the BBAN to convert to IBAN + * @returns {string} the IBAN + */ + exports.fromBBAN = function(countryCode, bban){ + var countryStructure = countries[countryCode]; + if (!countryStructure) { + throw new Error('No country with code ' + countryCode); + } + return countryStructure.fromBBAN(this.electronicFormat(bban)); + }; + + /** + * Check the validity of the passed BBAN. + * + * @param countryCode the country of the BBAN + * @param bban the BBAN to check the validity of + */ + exports.isValidBBAN = function(countryCode, bban){ + if (!isString(bban)){ + return false; + } + var countryStructure = countries[countryCode]; + return countryStructure && countryStructure.isValidBBAN(this.electronicFormat(bban)); + }; + + /** + * + * @param iban + * @param separator + * @returns {string} + */ + exports.printFormat = function(iban, separator){ + if (typeof separator == 'undefined'){ + separator = ' '; + } + return this.electronicFormat(iban).replace(EVERY_FOUR_CHARS, "$1" + separator); + }; + + /** + * + * @param iban + * @returns {string} + */ + exports.electronicFormat = function(iban){ + return iban.replace(NON_ALPHANUM, '').toUpperCase(); + }; + + /** + * An object containing all the known IBAN specifications. + */ + exports.countries = countries; + +})(typeof exports == 'undefined' ? this.IBAN = {} : exports); + +},{}]},{},[1]); diff --git a/dist/ng-iban.min.js b/dist/ng-iban.min.js index aac8b9d..eeb40bb 100644 --- a/dist/ng-iban.min.js +++ b/dist/ng-iban.min.js @@ -1 +1 @@ -(function(){"use strict";var a=function(a,b){return(a%b+ +b)%b};angular.module("mm.iban",["ng"]).constant("ngIbanCountries",{AL:{name:"Albania",regex:/^AL[0-9]{2}[0-9]{8}[A-Z0-9]{16}$/},AD:{name:"Andorra",regex:/^AD[0-9]{2}[0-9]{4}[0-9]{4}[A-Z0-9]{12}$/},AT:{name:"Austria",regex:/^AT[0-9]{2}[0-9]{5}[0-9]{11}$/},AZ:{name:"Azerbaijan, Republic of",regex:/^AZ[0-9]{2}[A-Z]{4}[A-Z0-9]{20}$/},BH:{name:"Bahrain",regex:/^BH[0-9]{2}[A-Z]{4}[A-Z0-9]{14}$/},BE:{name:"Belgium",regex:/^BE[0-9]{2}[0-9]{3}[0-9]{7}[0-9]{2}$/},BA:{name:"Bosnia and Herzegovina",regex:/^BA[0-9]{2}[0-9]{3}[0-9]{3}[0-9]{8}[0-9]{2}$/},BR:{name:"Brazil",regex:/^BR[0-9]{2}[0-9]{8}[0-9]{5}[0-9]{10}[A-Z]{1}[A-Z0-9]{1}$/},BG:{name:"Bulgaria",regex:/^BG[0-9]{2}[A-Z]{4}[0-9]{4}[0-9]{2}[A-Z0-9]{8}$/},CR:{name:"Costa Rica",regex:/^CR[0-9]{2}[0-9]{3}[0-9]{14}$/},HR:{name:"Croatia",regex:/^HR[0-9]{2}[0-9]{7}[0-9]{10}$/},CY:{name:"Cyprus",regex:/^CY[0-9]{2}[0-9]{3}[0-9]{5}[A-Z0-9]{16}$/},CZ:{name:"Czech Republic",regex:/^CZ[0-9]{2}[0-9]{4}[0-9]{6}[0-9]{10}$/},DK:{name:"Denmark",regex:/^DK[0-9]{2}[0-9]{4}[0-9]{9}[0-9]{1}$/},DO:{name:"Dominican Republic",regex:/^DO[0-9]{2}[A-Z0-9]{4}[0-9]{20}$/},EE:{name:"Estonia",regex:/^EE[0-9]{2}[0-9]{2}[0-9]{2}[0-9]{11}[0-9]{1}$/},FO:{name:"Faroe Islands",regex:/^FO[0-9]{2}[0-9]{4}[0-9]{9}[0-9]{1}$/},FI:{name:"Finland",regex:/^FI[0-9]{2}[0-9]{6}[0-9]{7}[0-9]{1}$/},FR:{name:"France",regex:/^FR[0-9]{2}[0-9]{5}[0-9]{5}[A-Z0-9]{11}[0-9]{2}$/},GE:{name:"Georgia",regex:/^GE[0-9]{2}[A-Z]{2}[0-9]{16}$/},DE:{name:"Germany",regex:/^DE[0-9]{2}[0-9]{8}[0-9]{10}$/},GI:{name:"Gibraltar",regex:/^GI[0-9]{2}[A-Z]{4}[A-Z0-9]{15}$/},GR:{name:"Greece",regex:/^GR[0-9]{2}[0-9]{3}[0-9]{4}[A-Z0-9]{16}$/},GL:{name:"Greenland",regex:/^GL[0-9]{2}[0-9]{4}[0-9]{9}[0-9]{1}$/},GT:{name:"Guatemala",regex:/^GT[0-9]{2}[A-Z0-9]{4}[A-Z0-9]{20}$/},HU:{name:"Hungary",regex:/^HU[0-9]{2}[0-9]{3}[0-9]{4}[0-9]{1}[0-9]{15}[0-9]{1}$/},IS:{name:"Iceland",regex:/^IS[0-9]{2}[0-9]{4}[0-9]{2}[0-9]{6}[0-9]{10}$/},IE:{name:"Ireland",regex:/^IE[0-9]{2}[A-Z]{4}[0-9]{6}[0-9]{8}$/},IL:{name:"Israel",regex:/^IL[0-9]{2}[0-9]{3}[0-9]{3}[0-9]{13}$/},IT:{name:"Italy",regex:/^IT[0-9]{2}[A-Z]{1}[0-9]{5}[0-9]{5}[A-Z0-9]{12}$/},KZ:{name:"Kazakhstan",regex:/^KZ[0-9]{2}[0-9]{3}[A-Z0-9]{13}$/},KW:{name:"Kuwait",regex:/^KW[0-9]{2}[A-Z]{4}[A-Z0-9]{22}$/},LV:{name:"Latvia",regex:/^LV[0-9]{2}[A-Z]{4}[A-Z0-9]{13}$/},LB:{name:"Lebanon",regex:/^LB[0-9]{2}[0-9]{4}[A-Z0-9]{20}$/},LI:{name:"Liechtenstein (Principality of)",regex:/^LI[0-9]{2}[0-9]{5}[A-Z0-9]{12}$/},LT:{name:"Lithuania",regex:/^LT[0-9]{2}[0-9]{5}[0-9]{11}$/},LU:{name:"Luxembourg",regex:/^LU[0-9]{2}[0-9]{3}[A-Z0-9]{13}$/},MK:{name:"Macedonia",regex:/^MK[0-9]{2}[0-9]{3}[A-Z0-9]{10}[0-9]{2}$/},MT:{name:"Malta",regex:/^MT[0-9]{2}[A-Z]{4}[0-9]{5}[A-Z0-9]{18}$/},MR:{name:"Mauritania",regex:/^MR[0-9]{2}[0-9]{5}[0-9]{5}[0-9]{11}[0-9]{2}$/},MU:{name:"Mauritius",regex:/^MU[0-9]{2}[A-Z]{4}[0-9]{2}[0-9]{2}[0-9]{12}[0-9]{3}[A-Z]{3}$/},MD:{name:"Moldova, Republic of",regex:/^MD[0-9]{2}[A-Z0-9]{20}$/},MC:{name:"Monaco",regex:/^MC[0-9]{2}[0-9]{5}[0-9]{5}[A-Z0-9]{11}[0-9]{2}$/},ME:{name:"Montenegro",regex:/^ME[0-9]{2}[0-9]{3}[0-9]{13}[0-9]{2}$/},NL:{name:"Netherlands",regex:/^NL[0-9]{2}[A-Z]{4}[0-9]{10}$/},NO:{name:"Norway",regex:/^NO[0-9]{2}[0-9]{4}[0-9]{6}[0-9]{1}$/},PK:{name:"Pakistan",regex:/^PK[0-9]{2}[A-Z]{4}[A-Z0-9]{16}$/},PL:{name:"Poland",regex:/^PL[0-9]{2}[0-9]{8}[0-9]{16}$/},PS:{name:"Palestinian Territory, Occupied",regex:/^PS[0-9]{2}[A-Z]{4}[A-Z0-9]{21}$/},PT:{name:"Portugal",regex:/^PT[0-9]{2}[0-9]{4}[0-9]{4}[0-9]{11}[0-9]{2}$/},QA:{name:"Qatar",regex:/^QA[0-9]{2}[A-Z]{4}[A-Z0-9]{21}$/},RO:{name:"Romania",regex:/^RO[0-9]{2}[A-Z]{4}[A-Z0-9]{16}$/},SM:{name:"San Marino",regex:/^SM[0-9]{2}[A-Z]{1}[0-9]{5}[0-9]{5}[A-Z0-9]{12}$/},SA:{name:"Saudi Arabia",regex:/^SA[0-9]{2}[0-9]{2}[A-Z0-9]{18}$/},RS:{name:"Serbia",regex:/^RS[0-9]{2}[0-9]{3}[0-9]{13}[0-9]{2}$/},SK:{name:"Slovak Republic",regex:/^SK[0-9]{2}[0-9]{4}[0-9]{6}[0-9]{10}$/},SI:{name:"Slovenia",regex:/^SI[0-9]{2}[0-9]{5}[0-9]{8}[0-9]{2}$/},ES:{name:"Spain",regex:/^ES[0-9]{2}[0-9]{4}[0-9]{4}[0-9]{1}[0-9]{1}[0-9]{10}$/},SE:{name:"Sweden",regex:/^SE[0-9]{2}[0-9]{3}[0-9]{16}[0-9]{1}$/},CH:{name:"Switzerland",regex:/^CH[0-9]{2}[0-9]{5}[A-Z0-9]{12}$/},TL:{name:"Timor-Leste",regex:/^TL[0-9]{2}[0-9]{3}[0-9]{14}[0-9]{2}$/},TN:{name:"Tunisia",regex:/^TN[0-9]{2}[0-9]{2}[0-9]{3}[0-9]{13}[0-9]{2}$/},TR:{name:"Turkey",regex:/^TR[0-9]{2}[0-9]{5}[A-Z0-9]{1}[A-Z0-9]{16}$/},AE:{name:"United Arab Emirates",regex:/^AE[0-9]{2}[0-9]{3}[0-9]{16}$/},GB:{name:"United Kingdom",regex:/^GB[0-9]{2}[A-Z]{4}[0-9]{6}[0-9]{8}$/},VG:{name:"Virgin Islands, British",regex:/^VG[0-9]{2}[A-Z]{4}[0-9]{16}$/}}).directive("ngIban",["ngIbanCountries",function(b){return{restrict:"A",require:"ngModel",link:function(c,d,e,f){var g,h;return h=function(a){return null!=a?a.toUpperCase().replace(/\s/g,""):void 0},g=function(c){var d,f,g,i,j;if(!e.required&&!c)return!0;if(d="A".charCodeAt(0),f="Z".charCodeAt(0),i=h(c),!(""===e.ngIban||e.ngIban in b&&b[e.ngIban].regex.test(i)))return!1;for(i=i.substr(4)+i.substr(0,4),j=i.split("").map(function(a){var b;return b=a.charCodeAt(0),b>=d&&f>=b?b-d+10:a}).join("");j.length>2;)g=j.slice(0,9),j=a(parseInt(g,10),97)+j.slice(g.length);return 1===a(parseInt(j,10),97)},f.$parsers.unshift(function(a){var b,c;return null!=a?(c=g(a),f.$setValidity("iban",c),c?(b=h(a),b!==a&&(f.$setViewValue(b),f.$render()),b):void 0):void 0}),f.$formatters.unshift(function(a){var b,d;return null!=a?(d=g(a),f.$setValidity("iban",d),d?(b=h(a),b!==a&&(c[e.ngModel]=b),b):a):void 0})}}}])}).call(this); \ No newline at end of file +!function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c?c:a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g=h&&i>=b?b-h+10:a}).join("")}function c(a){for(var b,c=a;c.length>2;)b=c.slice(0,9),c=parseInt(b,10)%97+c.slice(b.length);return parseInt(c,10)%97}function d(a){var b=a.match(/(.{3})/g).map(function(a){var b,c=a.slice(0,1),d=parseInt(a.slice(1),10);switch(c){case"A":b="0-9A-Za-z";break;case"B":b="0-9A-Z";break;case"C":b="A-Za-z";break;case"F":b="0-9";break;case"L":b="a-z";break;case"U":b="A-Z";break;case"W":b="0-9a-z"}return"(["+b+"]{"+d+"})"});return new RegExp("^"+b.join("")+"$")}function e(a,b,c,d){this.countryCode=a,this.length=b,this.structure=c,this.example=d}function f(a){j[a.countryCode]=a}function g(a){return"string"==typeof a||a instanceof String}Array.prototype.map||(Array.prototype.map=function(a){"use strict";if(void 0===this||null===this)throw new TypeError;var b=Object(this),c=b.length>>>0;if("function"!=typeof a)throw new TypeError;for(var d=new Array(c),e=arguments.length>=2?arguments[1]:void 0,f=0;c>f;f++)f in b&&(d[f]=a.call(e,b[f],f,b));return d});var h="A".charCodeAt(0),i="Z".charCodeAt(0);e.prototype._regex=function(){return this._cachedRegex||(this._cachedRegex=d(this.structure))},e.prototype.isValid=function(a){return this.length==a.length&&this.countryCode===a.slice(0,2)&&this._regex().test(a.slice(4))&&1==c(b(a))},e.prototype.toBBAN=function(a,b){return this._regex().exec(a.slice(4)).slice(1).join(b)},e.prototype.fromBBAN=function(a){if(!this.isValidBBAN(a))throw new Error("Invalid BBAN");var d=c(b(this.countryCode+"00"+a)),e=("0"+(98-d)).slice(-2);return this.countryCode+e+a},e.prototype.isValidBBAN=function(a){return this.length-4==a.length&&this._regex().test(a)};var j={};f(new e("AD",24,"F04F04A12","AD1200012030200359100100")),f(new e("AE",23,"F03F16","AE070331234567890123456")),f(new e("AL",28,"F08A16","AL47212110090000000235698741")),f(new e("AT",20,"F05F11","AT611904300234573201")),f(new e("AZ",28,"U04A20","AZ21NABZ00000000137010001944")),f(new e("BA",20,"F03F03F08F02","BA391290079401028494")),f(new e("BE",16,"F03F07F02","BE68539007547034")),f(new e("BG",22,"U04F04F02A08","BG80BNBG96611020345678")),f(new e("BH",22,"U04A14","BH67BMAG00001299123456")),f(new e("BR",29,"F08F05F10U01A01","BR9700360305000010009795493P1")),f(new e("CH",21,"F05A12","CH9300762011623852957")),f(new e("CR",21,"F03F14","CR0515202001026284066")),f(new e("CY",28,"F03F05A16","CY17002001280000001200527600")),f(new e("CZ",24,"F04F06F10","CZ6508000000192000145399")),f(new e("DE",22,"F08F10","DE89370400440532013000")),f(new e("DK",18,"F04F09F01","DK5000400440116243")),f(new e("DO",28,"U04F20","DO28BAGR00000001212453611324")),f(new e("EE",20,"F02F02F11F01","EE382200221020145685")),f(new e("ES",24,"F04F04F01F01F10","ES9121000418450200051332")),f(new e("FI",18,"F06F07F01","FI2112345600000785")),f(new e("FO",18,"F04F09F01","FO6264600001631634")),f(new e("FR",27,"F05F05A11F02","FR1420041010050500013M02606")),f(new e("GB",22,"U04F06F08","GB29NWBK60161331926819")),f(new e("GE",22,"U02F16","GE29NB0000000101904917")),f(new e("GI",23,"U04A15","GI75NWBK000000007099453")),f(new e("GL",18,"F04F09F01","GL8964710001000206")),f(new e("GR",27,"F03F04A16","GR1601101250000000012300695")),f(new e("GT",28,"A04A20","GT82TRAJ01020000001210029690")),f(new e("HR",21,"F07F10","HR1210010051863000160")),f(new e("HU",28,"F03F04F01F15F01","HU42117730161111101800000000")),f(new e("IE",22,"U04F06F08","IE29AIBK93115212345678")),f(new e("IL",23,"F03F03F13","IL620108000000099999999")),f(new e("IS",26,"F04F02F06F10","IS140159260076545510730339")),f(new e("IT",27,"U01F05F05A12","IT60X0542811101000000123456")),f(new e("KW",30,"U04A22","KW81CBKU0000000000001234560101")),f(new e("KZ",20,"F03A13","KZ86125KZT5004100100")),f(new e("LB",28,"F04A20","LB62099900000001001901229114")),f(new e("LI",21,"F05A12","LI21088100002324013AA")),f(new e("LT",20,"F05F11","LT121000011101001000")),f(new e("LU",20,"F03A13","LU280019400644750000")),f(new e("LV",21,"U04A13","LV80BANK0000435195001")),f(new e("MC",27,"F05F05A11F02","MC5811222000010123456789030")),f(new e("MD",24,"U02F18","MD24AG000225100013104168")),f(new e("ME",22,"F03F13F02","ME25505000012345678951")),f(new e("MK",19,"F03A10F02","MK07250120000058984")),f(new e("MR",27,"F05F05F11F02","MR1300020001010000123456753")),f(new e("MT",31,"U04F05A18","MT84MALT011000012345MTLCAST001S")),f(new e("MU",30,"U04F02F02F12F03U03","MU17BOMM0101101030300200000MUR")),f(new e("NL",18,"U04F10","NL91ABNA0417164300")),f(new e("NO",15,"F04F06F01","NO9386011117947")),f(new e("PK",24,"U04A16","PK36SCBL0000001123456702")),f(new e("PL",28,"F08F16","PL61109010140000071219812874")),f(new e("PS",29,"U04A21","PS92PALS000000000400123456702")),f(new e("PT",25,"F04F04F11F02","PT50000201231234567890154")),f(new e("RO",24,"U04A16","RO49AAAA1B31007593840000")),f(new e("RS",22,"F03F13F02","RS35260005601001611379")),f(new e("SA",24,"F02A18","SA0380000000608010167519")),f(new e("SE",24,"F03F16F01","SE4550000000058398257466")),f(new e("SI",19,"F05F08F02","SI56263300012039086")),f(new e("SK",24,"F04F06F10","SK3112000000198742637541")),f(new e("SM",27,"U01F05F05A12","SM86U0322509800000000270100")),f(new e("TN",24,"F02F03F13F02","TN5910006035183598478831")),f(new e("TR",26,"F05A01A16","TR330006100519786457841326")),f(new e("VG",24,"U04F16","VG96VPVG0000012345678901")),f(new e("AO",25,"F21","AO69123456789012345678901")),f(new e("BF",27,"F23","BF2312345678901234567890123")),f(new e("BI",16,"F12","BI41123456789012")),f(new e("BJ",28,"F24","BJ39123456789012345678901234")),f(new e("CI",28,"U01F23","CI17A12345678901234567890123")),f(new e("CM",27,"F23","CM9012345678901234567890123")),f(new e("CV",25,"F21","CV30123456789012345678901")),f(new e("DZ",24,"F20","DZ8612345678901234567890")),f(new e("IR",26,"F22","IR861234568790123456789012")),f(new e("JO",30,"A04F22","JO15AAAA1234567890123456789012")),f(new e("MG",27,"F23","MG1812345678901234567890123")),f(new e("ML",28,"U01F23","ML15A12345678901234567890123")),f(new e("MZ",25,"F21","MZ25123456789012345678901")),f(new e("QA",29,"U04A21","QA30AAAA123456789012345678901")),f(new e("SN",28,"U01F23","SN52A12345678901234567890123")),f(new e("UA",29,"F25","UA511234567890123456789012345"));var k=/[^a-zA-Z0-9]/g,l=/(.{4})(?!$)/g;a.isValid=function(a){if(!g(a))return!1;a=this.electronicFormat(a);var b=j[a.slice(0,2)];return!!b&&b.isValid(a)},a.toBBAN=function(a,b){"undefined"==typeof b&&(b=" "),a=this.electronicFormat(a);var c=j[a.slice(0,2)];if(!c)throw new Error("No country with code "+a.slice(0,2));return c.toBBAN(a,b)},a.fromBBAN=function(a,b){var c=j[a];if(!c)throw new Error("No country with code "+a);return c.fromBBAN(this.electronicFormat(b))},a.isValidBBAN=function(a,b){if(!g(b))return!1;var c=j[a];return c&&c.isValidBBAN(this.electronicFormat(b))},a.printFormat=function(a,b){return"undefined"==typeof b&&(b=" "),this.electronicFormat(a).replace(l,"$1"+b)},a.electronicFormat=function(a){return a.replace(k,"").toUpperCase()},a.countries=j}("undefined"==typeof c?this.IBAN={}:c)},{}]},{},[1]); \ No newline at end of file diff --git a/package.json b/package.json index 6090952..9566a2d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ng-iban", - "version": "0.3.2", + "version": "0.4.0", "description": "IBAN directive for AngularJS", "main": "./dist/ng-iban.js", "scripts": { @@ -23,7 +23,9 @@ }, "devDependencies": { "coffee-script": "^1.7.1", + "debowerify": "^1.3.1", "grunt": "^0.4.5", + "grunt-browserify": "^3.8.0", "grunt-contrib-clean": "^0.5.0", "grunt-contrib-coffee": "^0.10.1", "grunt-contrib-connect": "^0.8.0", diff --git a/src/ng-iban.coffee b/src/ng-iban.coffee index 26da0b7..7d55d95 100644 --- a/src/ng-iban.coffee +++ b/src/ng-iban.coffee @@ -1,207 +1,12 @@ 'use strict' +@IBAN = require 'iban' + +self = @ + angular .module 'mm.iban', ['ng'] - .constant 'ngIbanCountries', - AL: - name: 'Albania' - regex: /^AL[0-9]{2}[0-9]{8}[A-Z0-9]{16}$/ - AD: - name: 'Andorra' - regex: /^AD[0-9]{2}[0-9]{4}[0-9]{4}[A-Z0-9]{12}$/ - AT: - name: 'Austria' - regex: /^AT[0-9]{2}[0-9]{5}[0-9]{11}$/ - AZ: - name: 'Azerbaijan, Republic of' - regex: /^AZ[0-9]{2}[A-Z]{4}[A-Z0-9]{20}$/ - BH: - name: 'Bahrain' - regex: /^BH[0-9]{2}[A-Z]{4}[A-Z0-9]{14}$/ - BE: - name: 'Belgium' - regex: /^BE[0-9]{2}[0-9]{3}[0-9]{7}[0-9]{2}$/ - BA: - name: 'Bosnia and Herzegovina' - regex: /^BA[0-9]{2}[0-9]{3}[0-9]{3}[0-9]{8}[0-9]{2}$/ - BR: - name: 'Brazil' - regex: /^BR[0-9]{2}[0-9]{8}[0-9]{5}[0-9]{10}[A-Z]{1}[A-Z0-9]{1}$/ - BG: - name: 'Bulgaria' - regex: /^BG[0-9]{2}[A-Z]{4}[0-9]{4}[0-9]{2}[A-Z0-9]{8}$/ - CR: - name: 'Costa Rica' - regex: /^CR[0-9]{2}[0-9]{3}[0-9]{14}$/ - HR: - name: 'Croatia' - regex: /^HR[0-9]{2}[0-9]{7}[0-9]{10}$/ - CY: - name: 'Cyprus' - regex: /^CY[0-9]{2}[0-9]{3}[0-9]{5}[A-Z0-9]{16}$/ - CZ: - name: 'Czech Republic' - regex: /^CZ[0-9]{2}[0-9]{4}[0-9]{6}[0-9]{10}$/ - DK: - name: 'Denmark' - regex: /^DK[0-9]{2}[0-9]{4}[0-9]{9}[0-9]{1}$/ - DO: - name: 'Dominican Republic' - regex: /^DO[0-9]{2}[A-Z0-9]{4}[0-9]{20}$/ - EE: - name: 'Estonia' - regex: /^EE[0-9]{2}[0-9]{2}[0-9]{2}[0-9]{11}[0-9]{1}$/ - FO: - name: 'Faroe Islands' - regex: /^FO[0-9]{2}[0-9]{4}[0-9]{9}[0-9]{1}$/ - FI: - name: 'Finland' - regex: /^FI[0-9]{2}[0-9]{6}[0-9]{7}[0-9]{1}$/ - FR: - name: 'France' - regex: /^FR[0-9]{2}[0-9]{5}[0-9]{5}[A-Z0-9]{11}[0-9]{2}$/ - GE: - name: 'Georgia' - regex: /^GE[0-9]{2}[A-Z]{2}[0-9]{16}$/ - DE: - name: 'Germany' - regex: /^DE[0-9]{2}[0-9]{8}[0-9]{10}$/ - GI: - name: 'Gibraltar' - regex: /^GI[0-9]{2}[A-Z]{4}[A-Z0-9]{15}$/ - GR: - name: 'Greece' - regex: /^GR[0-9]{2}[0-9]{3}[0-9]{4}[A-Z0-9]{16}$/ - GL: - name: 'Greenland' - regex: /^GL[0-9]{2}[0-9]{4}[0-9]{9}[0-9]{1}$/ - GT: - name: 'Guatemala' - regex: /^GT[0-9]{2}[A-Z0-9]{4}[A-Z0-9]{20}$/ - HU: - name: 'Hungary' - regex: /^HU[0-9]{2}[0-9]{3}[0-9]{4}[0-9]{1}[0-9]{15}[0-9]{1}$/ - IS: - name: 'Iceland' - regex: /^IS[0-9]{2}[0-9]{4}[0-9]{2}[0-9]{6}[0-9]{10}$/ - IE: - name: 'Ireland' - regex: /^IE[0-9]{2}[A-Z]{4}[0-9]{6}[0-9]{8}$/ - IL: - name: 'Israel' - regex: /^IL[0-9]{2}[0-9]{3}[0-9]{3}[0-9]{13}$/ - IT: - name: 'Italy' - regex: /^IT[0-9]{2}[A-Z]{1}[0-9]{5}[0-9]{5}[A-Z0-9]{12}$/ - KZ: - name: 'Kazakhstan' - regex: /^KZ[0-9]{2}[0-9]{3}[A-Z0-9]{13}$/ - KW: - name: 'Kuwait' - regex: /^KW[0-9]{2}[A-Z]{4}[A-Z0-9]{22}$/ - LV: - name: 'Latvia' - regex: /^LV[0-9]{2}[A-Z]{4}[A-Z0-9]{13}$/ - LB: - name: 'Lebanon' - regex: /^LB[0-9]{2}[0-9]{4}[A-Z0-9]{20}$/ - LI: - name: 'Liechtenstein (Principality of)' - regex: /^LI[0-9]{2}[0-9]{5}[A-Z0-9]{12}$/ - LT: - name: 'Lithuania' - regex: /^LT[0-9]{2}[0-9]{5}[0-9]{11}$/ - LU: - name: 'Luxembourg' - regex: /^LU[0-9]{2}[0-9]{3}[A-Z0-9]{13}$/ - MK: - name: 'Macedonia' - regex: /^MK[0-9]{2}[0-9]{3}[A-Z0-9]{10}[0-9]{2}$/ - MT: - name: 'Malta' - regex: /^MT[0-9]{2}[A-Z]{4}[0-9]{5}[A-Z0-9]{18}$/ - MR: - name: 'Mauritania' - regex: /^MR[0-9]{2}[0-9]{5}[0-9]{5}[0-9]{11}[0-9]{2}$/ - MU: - name: 'Mauritius' - regex: /^MU[0-9]{2}[A-Z]{4}[0-9]{2}[0-9]{2}[0-9]{12}[0-9]{3}[A-Z]{3}$/ - MD: - name: 'Moldova, Republic of' - regex: /^MD[0-9]{2}[A-Z0-9]{20}$/ - MC: - name: 'Monaco' - regex: /^MC[0-9]{2}[0-9]{5}[0-9]{5}[A-Z0-9]{11}[0-9]{2}$/ - ME: - name: 'Montenegro' - regex: /^ME[0-9]{2}[0-9]{3}[0-9]{13}[0-9]{2}$/ - NL: - name: 'Netherlands' - regex: /^NL[0-9]{2}[A-Z]{4}[0-9]{10}$/ - NO: - name: 'Norway' - regex: /^NO[0-9]{2}[0-9]{4}[0-9]{6}[0-9]{1}$/ - PK: - name: 'Pakistan' - regex: /^PK[0-9]{2}[A-Z]{4}[A-Z0-9]{16}$/ - PL: - name: 'Poland' - regex: /^PL[0-9]{2}[0-9]{8}[0-9]{16}$/ - PS: - name: 'Palestinian Territory, Occupied' - regex: /^PS[0-9]{2}[A-Z]{4}[A-Z0-9]{21}$/ - PT: - name: 'Portugal' - regex: /^PT[0-9]{2}[0-9]{4}[0-9]{4}[0-9]{11}[0-9]{2}$/ - QA: - name: 'Qatar' - regex: /^QA[0-9]{2}[A-Z]{4}[A-Z0-9]{21}$/ - RO: - name: 'Romania' - regex: /^RO[0-9]{2}[A-Z]{4}[A-Z0-9]{16}$/ - SM: - name: 'San Marino' - regex: /^SM[0-9]{2}[A-Z]{1}[0-9]{5}[0-9]{5}[A-Z0-9]{12}$/ - SA: - name: 'Saudi Arabia' - regex: /^SA[0-9]{2}[0-9]{2}[A-Z0-9]{18}$/ - RS: - name: 'Serbia' - regex: /^RS[0-9]{2}[0-9]{3}[0-9]{13}[0-9]{2}$/ - SK: - name: 'Slovak Republic' - regex: /^SK[0-9]{2}[0-9]{4}[0-9]{6}[0-9]{10}$/ - SI: - name: 'Slovenia' - regex: /^SI[0-9]{2}[0-9]{5}[0-9]{8}[0-9]{2}$/ - ES: - name: 'Spain' - regex: /^ES[0-9]{2}[0-9]{4}[0-9]{4}[0-9]{1}[0-9]{1}[0-9]{10}$/ - SE: - name: 'Sweden' - regex: /^SE[0-9]{2}[0-9]{3}[0-9]{16}[0-9]{1}$/ - CH: - name: 'Switzerland' - regex: /^CH[0-9]{2}[0-9]{5}[A-Z0-9]{12}$/ - TL: - name: 'Timor-Leste' - regex: /^TL[0-9]{2}[0-9]{3}[0-9]{14}[0-9]{2}$/ - TN: - name: 'Tunisia' - regex: /^TN[0-9]{2}[0-9]{2}[0-9]{3}[0-9]{13}[0-9]{2}$/ - TR: - name: 'Turkey' - regex: /^TR[0-9]{2}[0-9]{5}[A-Z0-9]{1}[A-Z0-9]{16}$/ - AE: - name: 'United Arab Emirates' - regex: /^AE[0-9]{2}[0-9]{3}[0-9]{16}$/ - GB: - name: 'United Kingdom' - regex: /^GB[0-9]{2}[A-Z]{4}[0-9]{6}[0-9]{8}$/ - VG: - name: 'Virgin Islands, British' - regex: /^VG[0-9]{2}[A-Z]{4}[0-9]{16}$/ - .directive 'ngIban', (ngIbanCountries) -> + .directive 'ngIban', -> restrict: 'A' require: 'ngModel' link: (scope, elem, attrs, ctrl) -> @@ -210,24 +15,9 @@ angular isValidIban = (value) -> return true if not(attrs.required or value) - A = 'A'.charCodeAt 0 - Z = 'Z'.charCodeAt 0 iban = parseIban(value) - return false if attrs.ngIban isnt '' and (not !!(attrs.ngIban of ngIbanCountries) or not ngIbanCountries[attrs.ngIban].regex.test iban) - - iban = iban.substr(4) + iban.substr 0, 4 - - remainder = iban.split('').map (n) -> - code = n.charCodeAt 0 - if code >= A and code <= Z then code - A + 10 else n - .join '' - - while remainder.length > 2 - block = remainder.slice 0, 9 - remainder = parseInt(block, 10) %% 97 + remainder.slice block.length - - parseInt(remainder, 10) %% 97 is 1 + self.IBAN.isValid iban ctrl.$parsers.unshift (viewValue) -> if viewValue? diff --git a/test/karma.conf.coffee b/test/karma.conf.coffee index 17cbfb2..a918e15 100644 --- a/test/karma.conf.coffee +++ b/test/karma.conf.coffee @@ -13,7 +13,7 @@ module.exports = (config) -> files: [ 'bower_components/angular/angular.js' 'bower_components/angular-mocks/angular-mocks.js' - 'src/**/*.coffee' + '.tmp/**/*.js' 'test/spec/**/*.coffee' ], diff --git a/test/spec/ng-iban.coffee b/test/spec/ng-iban.coffee index 10c4376..633b426 100644 --- a/test/spec/ng-iban.coffee +++ b/test/spec/ng-iban.coffee @@ -16,8 +16,7 @@ describe 'Directive: iban', -> compile('
' + '' + '' + - '' + - '' + + '' + # can be used but does nothing '
')(scope) form = scope.form @@ -63,12 +62,6 @@ describe 'Directive: iban', -> expect(scope.iban).toEqual undefined expect(form.iban.$valid).toBe false - it 'invalidcountry should fail with invalid country code', -> - form.invalidcountry.$setViewValue 'NL91 ABNA 0417 1643 00' - scope.$digest() - expect(scope.invalidcountry).toEqual undefined - expect(form.invalidcountry.$valid).toBe false - it 'country should pass with valid IBAN', -> form.country.$setViewValue 'NL91 ABNA 0417 1643 00' scope.$digest() diff --git a/wercker.yml b/wercker.yml index 2ea83e9..ee480c5 100644 --- a/wercker.yml +++ b/wercker.yml @@ -1,10 +1,7 @@ -box: wercker/nodejs +box: olger/box-yeoman build: steps: - npm-install - - npm-install: - options: bower - - plasticine/bower-install@0.0.4: - tasks: bower-install - - wercker/grunt@1.0.0: - tasks: test + - olger/yeoman-bower-install + - grunt: + tasks: default