| 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. |
|---|
| 32 | idCheck = 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 | } |
|---|