Ticket #1604: identifiers.js

File identifiers.js, 4.9 KB (added by ajlyon, 6 years ago)

Code for parsing and validating ISBN and ISSN identifiers

Line 
1// Implementation of ISBN and ISSN check-digit verification
2// Based on ISBN Users' Manual (http://www.isbn.org/standards/home/isbn/international/html/usm4.htm)
3// and the Wikipedia treatment of ISBN (http://en.wikipedia.org/wiki/International_Standard_Book_Number)
4// and the Wikipedia treatment of ISSN (http://en.wikipedia.org/wiki/International_Standard_Serial_Number)
5
6// This will also check ISMN validity, although it does not distinguish from their
7// neighbors in namespace, ISBN-13. It does not handle pre-2008 M-prefixed ISMNs; see
8// http://en.wikipedia.org/wiki/International_Standard_Music_Number
9
10// This does not validate multiple identifiers in one field,
11// but it will gracefully ignore all non-number detritus,
12// such as extraneous hyphens, spaces, and comments.
13
14// It currently maintains hyphens in non-initial and non-final position,
15// discarding consecutive ones beyond the first as well.
16
17// It also adds the customary hyphen to valid ISSNs.
18
19// Takes the first 8 valid digits and tries to read an ISSN,
20// takes the first 10 valid digits and tries to read an ISBN 10,
21// and takes the first 13 valid digits to try to read an ISBN 13
22// Returns an object with three attributes:
23//      "issn"
24//      "isbn10"
25//      "isbn13"
26// Each will be set to a valid identifier if found, and otherwise be a
27// boolean false.
28
29// There could conceivably be a valid ISBN-13 with an ISBN-10
30// substring; this should probably be interpreted as the latter, but it is a
31// client UI issue.
32idCheck = function(isbn) {
33        // For ISBN 10, multiple by these coefficients, take the sum mod 11
34        // and subtract from 11
35        var isbn10 = [10, 9, 8, 7, 6, 5, 4, 3, 2];
36
37        // For ISBN 13, multiple by these coefficients, take the sum mod 10
38        // and subtract from 10
39        var isbn13 = [1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3];
40
41        // For ISSN, multiply by these coefficients, take the sum mod 11
42        // and subtract from 11
43        var issn = [8, 7, 6, 5, 4, 3, 2];
44
45        // We make a single pass through the provided string, interpreting the
46        // first 10 valid characters as an ISBN-10, and the first 13 as an
47        // ISBN-13. We then return an array of booleans and valid detected
48        // ISBNs.
49
50        var j = 0;
51        var sum8 = 0;
52        var num8 = "";
53        var sum10 = 0;
54        var num10 = "";
55        var sum13 = 0;
56        var num13 = "";
57        var chars = [];
58
59        for (var i=0; i < isbn.length; i++) {
60                if (isbn.charAt(i) == " ") {
61                        // Since the space character evaluates as a number,
62                        // it is a special case.
63                } else if (j > 0 && isbn.charAt(i) == "-" && isbn.charAt(i-1) != "-") {
64                        // Preserve hyphens, except in initial and final position
65                        // Also discard consecutive hyphens
66                        if(j < 7) num8 += "-";
67                        if(j < 10) num10 += "-";
68                        if(j < 13) num13 += "-";
69                } else if (j < 7 && ((isbn.charAt(i) - 0) == isbn.charAt(i))) {
70                        sum8 += isbn.charAt(i) * issn[j];
71                        sum10 += isbn.charAt(i) * isbn10[j];
72                        sum13 += isbn.charAt(i) * isbn13[j];
73                        num8 += isbn.charAt(i);
74                        num10 += isbn.charAt(i);
75                        num13 += isbn.charAt(i);
76                        j++;
77                } else if (j == 7 &&
78                        (isbn.charAt(i) == "X" || isbn.charAt(i) == "x" ||
79                                ((isbn.charAt(i) - 0) == isbn.charAt(i)))) {
80                        // In ISSN, an X represents the check digit "10".
81                        if(isbn.charAt(i) == "X" || isbn.charAt(i) == "x") {
82                                var check8 = 10;
83                                num8 += "X";
84                        } else {
85                                var check8 = isbn.charAt(i);
86                                sum10 += isbn.charAt(i) * isbn10[j];
87                                sum13 += isbn.charAt(i) * isbn13[j];
88                                num8 += isbn.charAt(i);
89                                num10 += isbn.charAt(i);
90                                num13 += isbn.charAt(i);
91                                j++;
92                        }
93                } else if (j < 9 && ((isbn.charAt(i) - 0) == isbn.charAt(i))) {
94                        sum10 += isbn.charAt(i) * isbn10[j];
95                        sum13 += isbn.charAt(i) * isbn13[j];
96                        num10 += isbn.charAt(i);
97                        num13 += isbn.charAt(i);
98                        j++;
99                } else if (j == 9 &&
100                        (isbn.charAt(i) == "X" || isbn.charAt(i) == "x" ||
101                                ((isbn.charAt(i) - 0) == isbn.charAt(i)))) {
102                        // In ISBN-10, an X represents the check digit "10".
103                        if(isbn.charAt(i) == "X" || isbn.charAt(i) == "x") {
104                                var check10 = 10;
105                                num10 += "X";
106                        } else {
107                                var check10 = isbn.charAt(i);
108                                sum13 += isbn.charAt(i) * isbn13[j];
109                                num10 += isbn.charAt(i);
110                                num13 += isbn.charAt(i);
111                                j++;
112                        }
113                } else if(j < 12 && ((isbn.charAt(i) - 0) == isbn.charAt(i))) {
114                        sum13 += isbn.charAt(i) * isbn13[j];
115                        num13 += isbn.charAt(i);
116                        j++;
117                } else if (j == 12 && ((isbn.charAt(i) - 0) == isbn.charAt(i))) {
118                        var check13 = isbn.charAt(i);
119                        num13 += isbn.charAt(i);
120                }
121        }
122        var valid8  = ((11 - sum8 % 11) % 11) == check8;
123        var valid10 = ((11 - sum10 % 11) % 11) == check10;
124        var valid13 = (10 - sum13 % 10 == check13);
125        var matches = false;
126       
127        // Since ISSNs have a standard hyphen placement, we can add a hyphen
128        if (valid8 && (matches = num8.match(/([0-9]{4})([0-9]{3}[0-9Xx])/))) {
129                num8 = matches[1] + '-' + matches[2];
130        } 
131
132        if(!valid8) {num8 = false};
133        if(!valid10) {num10 = false};
134        if(!valid13) {num13 = false};
135        return {"isbn10" : num10, "isbn13" : num13, "issn" : num8};
136}