UNPKG

isbn-utils

Version:

Library to parse and normalize ISBNs

218 lines (179 loc) 6.3 kB
var GROUPS = require('./groups'); var ISBN = function (val, groups) { this.groups = groups; this.codes = this.parse(val); } ISBN.prototype = { isValid: function() { return this.codes && this.codes.isValid; }, isIsbn13: function() { return this.isValid() && this.codes.isIsbn13; }, isIsbn10: function() { return this.isValid() && this.codes.isIsbn10; }, asIsbn10: function(hyphen) { return this.isValid() ? hyphen ? this.codes.isbn10h : this.codes.isbn10 : null; }, asIsbn13: function(hyphen) { return this.isValid() ? hyphen ? this.codes.isbn13h : this.codes.isbn13 : null; }, merge: function(lobj, robj) { var key; if (!lobj || !robj) { return null; } for (key in robj) { if (robj.hasOwnProperty(key)) { lobj[key] = robj[key]; } } return lobj; }, parse: function(val) { var ret; // coerce ISBN to string val += ''; // correct for misplaced hyphens // val = val.replace(/ -/,''); ret = val.match(/^\d{9}[\dX]$/) ? this.fill( this.merge({source: val, isValid: true, isIsbn10: true, isIsbn13: false}, this.split(val))) : val.length === 13 && val.match(/^(\d+)-(\d+)-(\d+)-([\dX])$/) ? this.fill({ source: val, isValid: true, isIsbn10: true, isIsbn13: false, group: RegExp.$1, publisher: RegExp.$2, article: RegExp.$3, check: RegExp.$4}) : val.match(/^(978|979)(\d{9}[\dX]$)/) ? this.fill( this.merge({source: val, isValid: true, isIsbn10: false, isIsbn13: true, prefix: RegExp.$1}, this.split(RegExp.$1 + RegExp.$2))) : val.length === 17 && val.match(/^(978|979)-(\d+)-(\d+)-(\d+)-([\dX])$/) ? this.fill({ source: val, isValid: true, isIsbn10: false, isIsbn13: true, prefix: RegExp.$1, group: RegExp.$2, publisher: RegExp.$3, article: RegExp.$4, check: RegExp.$5}) : null; if (!ret) { return {source: val, isValid: false}; } return this.merge(ret, {isValid: ret.check === (ret.isIsbn13 ? ret.check13 : ret.check10)}); }, split: function(isbn) { if (isbn) { if (isbn.length === 10) { isbn = '978' + isbn; } if (isbn.length === 13) { return this.splitToObject(isbn); } } return null; }, splitToArray: function(isbn13) { var rec, key, rest, i, m; rec = this.getGroupRecord(isbn13); if (!rec) { return null; } for (key, i = 0, m = rec.record.ranges.length; i < m; i += 1) { key = rec.rest.substr(0, rec.record.ranges[i][0].length); if (rec.record.ranges[i][0] <= key && rec.record.ranges[i][1] >= key) { rest = rec.rest.substr(key.length); return [rec.group, key, rest.substr(0, rest.length - 1), rest.charAt(rest.length - 1)]; } } return null; }, splitToObject: function(isbn13) { var a = this.splitToArray(isbn13); if (!a || a.length !== 4) { return null; } return {group: a[0], publisher: a[1], article: a[2], check: a[3]}; }, fill: function(codes) { var rec, prefix, ck10, ck13, parts13, parts10; if (!codes) { return null; } prefix = codes.prefix ? codes.prefix : '978'; rec = this.groups[prefix + '-' + codes.group]; if (!rec) { return null; } ck10 = this.calcCheckDigit([ codes.group, codes.publisher, codes.article].join('')); if (!ck10) { return null; } ck13 = this.calcCheckDigit([prefix, codes.group, codes.publisher, codes.article].join('')); if (!ck13) { return null; } parts13 = [prefix, codes.group, codes.publisher, codes.article, ck13]; this.merge(codes, { isbn13: parts13.join(''), isbn13h: parts13.join('-'), check10: ck10, check13: ck13, groupname: rec.name }); if (prefix === '978') { parts10 = [codes.group, codes.publisher, codes.article, ck10]; this.merge(codes, {isbn10: parts10.join(''), isbn10h: parts10.join('-')}); } return codes; }, getGroupRecord: function(isbn13) { var key; for (key in this.groups) { if (isbn13.match('^' + key.replace('-', '') + '(.+)')) { return {group: key.split('-')[1], record: this.groups[key], rest: RegExp.$1}; } } return null; }, calcCheckDigit: function(isbn) { var c, n; if (isbn.match(/^\d{9}[\dX]?$/)) { c = 0; for (n = 0; n < 9; n += 1) { c += (10 - n) * isbn.charAt(n); } c = (11 - c % 11) % 11; return c === 10 ? 'X' : String(c); } else if (isbn.match(/(?:978|979)\d{9}[\dX]?/)) { c = 0; for (n = 0; n < 12; n += 2) { c += Number(isbn.charAt(n)) + 3 * isbn.charAt(n + 1); } return String((10 - c % 10) % 10); } return null; } }; var isbn = { ISBN: ISBN, isValid: function(val, groups) { var me = new ISBN(val, groups ? groups : GROUPS); return me.isValid(); }, parse: function(val, groups) { var me = new ISBN(val, groups ? groups : GROUPS); return me.isValid() ? me : null; }, hyphenate: function(val) { var me = isbn.parse(val); return me ? me.isIsbn13() ? me.asIsbn13(true) : me.asIsbn10(true) : null; }, asIsbn13: function(val, hyphen) { var me = isbn.parse(val); return me ? me.asIsbn13(hyphen) : null; }, asIsbn10: function(val, hyphen) { var me = isbn.parse(val); return me ? me.asIsbn10(hyphen) : null; } }; module.exports = isbn;