├── LICENSE ├── README.md ├── callnumbers.js ├── ils_style_callnumber_normalization.md ├── locCallClass.js ├── sanity_test.html ├── stacks_data.js ├── testing ├── callnumbers.js ├── left-pad.js ├── norm_test.html ├── readme.md └── test.html └── unit_test1.html /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 rayvoelker 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | js-loc-callnumbers 2 | === 3 | 4 | A JavaScript class to normalize and perform various sorting options on Library of Congress Call Numbers within a library institution. 5 | 6 | My main intended use for this class is to provide sorting and comparison functions for library catalog searches in relationship to the physical location of the material in the library stacks -- although, the other various functions may be useful in other projects. 7 | 8 | This is adapted from the efforts of various other developers and projects; including, but not necessarily limited to the following : 9 | 10 | [http://robotlibrarian.billdueber.com](http://robotlibrarian.billdueber.com/2008/11/normalizing-loc-call-numbers-for-sorting/) 11 | 12 | [http://www-personal.umich.edu/~dfulmer](http://www-personal.umich.edu/~dfulmer/api/lc.html) 13 | 14 | [https://code.google.com/p/library-callnumber-lc](https://code.google.com/p/library-callnumber-lc/wiki/Home) 15 | 16 | Unfortunately, the regular expression used in this project may have very distinct limitations in your particular institution; so please let me know if anyone out there has a more complete regular expression (or a more reliable way to parse and "normalize") that defines any and all perfectly valid Library of Congress Call Numbers. 17 | 18 | TODO: 19 | === 20 | 1. Think about extending the class to a class that maintains "stacks" or "ranges" of call numbers for the purpose of finding items among the those ranges of call numbers. It could be instantiated with either a 1D or 2D array of call numbers in start / end pairs, or with a JSON object that defines the range data (the JSON object being a more easy to maintain solution for the user / system producing the data). 21 | 1. Add a method, or modify the sort method to also return the previous position of each of the sorted items. 22 | Useful situtations for this might inclued inserting a single, or group of call numbers into a pre-sorted array, sorting the array, and then tracking where those items ended up in relationship to the orginal list. 23 | 1. Add a "reshelve" method to make it simple to read a list, and see if a call number is out of position in relationship to the previos and next call numbers in the list. This method may or may not be outside of the scope of this class, but still could be very useful for implementing a more "on-the-fly" or, near-real-time shelving application. 24 | -------------------------------------------------------------------------------- /ils_style_callnumber_normalization.md: -------------------------------------------------------------------------------- 1 | ILS style call number normalization 2 | 3 | 1. alphabetic characters are set to lowercase 4 | 1. remove anything that is considered a "pre-stamp" from the callnumber 5 | 1. identify the "beginning" of the call number 6 | 1. search for 1 to 3 alphabetic characters (letter portion) 7 | followed by 1 to 4 numeric characters (the number portion). 8 | 1. If there is a decimal after the 1 to 4 numeric 9 | characters (followed by more numeric characters) then pull 10 | those values in as part of the number portion 11 | 1. using the above description, we now use this data to 12 | construct the "beginning" of our call number. 13 | 1. using seven character positions ( 1 2 3 4 5 6 7 ) place the 14 | letter portion into positions 1 through 3, and then the number 15 | portion into positions 4 through 7, right justifying, leaving 16 | spaces in the unoccupied positions. For example, given the call 17 | number "QL697.4 .H69 2010", we would identify the "beginning" 18 | as "QL697.4", and fill out our seven character positions in the 19 | following way: 20 | ``` 21 | 1 2 3 4 5 6 7 22 | q l 6 9 7 . 4 23 | ``` 24 | 25 | (note, that we included the ".4" after position 7, as it's 26 | still part of the number portion) 27 | 28 | so, the normalized "beginning" of the call number would look 29 | like the following: 30 | ql 697.4 31 | 32 | *regex for capturing this "beginning" portion* (note that this 33 | should return 1 match per valid LC call number, and have 4 34 | possible groups 1) letter portion, 2) number portion, 3) 35 | possible decimal portion of the number, and 4) the rest of the 36 | call number 37 | 38 | ``` 39 | /([A-Z]{1,3})\s*(\d{1,4})(\.\d+)*(.+)*\ig 40 | ``` 41 | 1. after this beginning portion, the rest of the number is further 42 | normalized 43 | 1. convert any punctuation to spaces 44 | 45 | 1. remove instances of multiple spaces, leaving only one 46 | 47 | 1. *normalize volume data in the call number in the following way: 48 | 1. identify volume characters ("v." and "no") 49 | and then place numbers following the volume characters into a 50 | five character positions, right justifying. For example: 51 | vol 151 52 | normalizes to .. 53 | ``` 54 | 1 2 3 4 5 55 | vol 1 5 1 56 | or ... 57 | vol 151 58 | ``` 59 | 60 | Finishing up with our example, our call number ("QL697.4 .H69 61 | 2010") would final, normalized call number would end up looking 62 | like the following: 63 | ``` 64 | QL697.4 .H69 2010 65 | ql 697.4 h69 2010 66 | ``` 67 | -------------------------------------------------------------------------------- /locCallClass.js: -------------------------------------------------------------------------------- 1 | // js-loc-callnumbers 2 | // A javascript class to normalize and perform various sorting options on 3 | // Library of Congress Call Numbers within a library institution. 4 | 5 | // constructor 6 | function locCallClass() { 7 | //define the regular expressions used in the normalization 8 | this.lc = /([A-Z]{1,3})\s*(\d{1,4})(\.\d+)*(.+)*/ig; 9 | this.punctuation = /[.,\/#!$%\^&\*;:{}=\-_`~()]/ig; 10 | this.lett_num_group = /([A-Z]{1,}\d{1,})/ig; 11 | this.num_letter_groupings = /(\d{1,})([A-Z]{1,})/ig; 12 | this.v_dot = /(v\s)(\d{1,})/ig; 13 | this.no_dot = /(no\s)(\d{1,})/ig; 14 | 15 | //define and cache the padding lengths (max pad length is 9) 16 | this.cache = ['', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']; 17 | 18 | // alias the function name for returnNormLcCall 19 | this.normalize = this.returnNormLcCall; 20 | } 21 | 22 | 23 | // locCallClass.returnNormLcCall(call_number) 24 | // returns a "normalized" call number 25 | locCallClass.prototype.returnNormLcCall = function(callnumber_string) { 26 | // this is our eventual return value 27 | var local_norm = '', 28 | test = []; 29 | 30 | //~ TODO 31 | //~ get cataloging to fix issues with non-normalizing call numbers 32 | //~ so we don't have to use this hack. 33 | //~ Call numbers like this "KBG .A1" should normalize to "kbg 0 a2" 34 | try{ 35 | const exception = /(KBG)(\s*\.)(.+)*/i; 36 | test = exception.exec(callnumber_string); 37 | if (test != null) { 38 | //debugger; 39 | var new_callnumber_string = ""; 40 | if( typeof test[1] !== "undefined" ) new_callnumber_string += test[1] + "0"; 41 | if( typeof test[2] !== "undefined" ) new_callnumber_string += test[2]; 42 | if( typeof test[3] !== "undefined" ) new_callnumber_string += test[3]; 43 | test = []; 44 | return this.returnNormLcCall(new_callnumber_string); 45 | } 46 | 47 | } 48 | catch (e){ 49 | // console.log('undefined call number ' + e); 50 | } 51 | //~ /TODO 52 | 53 | try{ 54 | test = this.lc.exec(callnumber_string.toLowerCase()); 55 | } 56 | catch (e){ 57 | console.log('undefined call number ' + e); 58 | } 59 | 60 | // if input is not LC, return lower case, trimmed input string, 61 | // with certain characters removed 62 | if (test === null){ 63 | console.log("\"" + callnumber_string + "\"" + " not lc"); 64 | return callnumber_string 65 | .toLowerCase() 66 | .trim() 67 | .replace(/\/{1,}|-{1,}/gi, " "); //replaces "/" and "-" with spaces 68 | } 69 | 70 | // TODO : 71 | // remove try block? 72 | try { 73 | if(typeof test[1] !== "undefined"){ 74 | local_norm = test[1]; 75 | } 76 | 77 | local_norm += this.leftPad(test[2],7-test[1].length); 78 | 79 | // append the decimal, if there is one 80 | if(typeof test[3] !== "undefined"){ 81 | local_norm += test[3]; 82 | } 83 | 84 | // remove any leading or trailing whitespace 85 | local_norm = local_norm.trim(); 86 | 87 | // normalize the rest of the call number 88 | var call_number_remainder = ""; 89 | if (typeof test[4] !== 'undefined'){ 90 | // step 4. i. convert any punctuation to spaces 91 | // append letter+number groups with spaces 92 | // step 4. remove instances of multiple spaces, leaving only one 93 | // remove any trailing (or starting) whitespace 94 | call_number_remainder += test[4] 95 | .replace(this.punctuation, " ") //repalce punctuation with spaces 96 | .replace(this.lett_num_group, "$1 ") //append spaces to letter+number groups 97 | .replace(this.num_letter_groupings, "$1 $2") //place space between numbers and letters occuring next to eachother 98 | .replace(/\s{2,}/g," "); //instances of 2 or more consecutive spaces are replaced by one 99 | 100 | 101 | // TODO : 102 | // consider removing the lastIndex for the following? 103 | 104 | // pad out numbers occuring after "v." and "no." 105 | // note: this calls the helper function for replace "vol_pad()" 106 | call_number_remainder = call_number_remainder.replace(this.v_dot, this.vol_pad.bind(this)); 107 | this.v_dot.lastIndex = 0; 108 | 109 | call_number_remainder = call_number_remainder.replace(this.no_dot, this.vol_pad.bind(this)); 110 | this.no_dot.lastIndex = 0; 111 | 112 | // reset the lastIndex so the next regex exec doesn't fail on next try 113 | this.punctuation.lastIndex = 0; 114 | 115 | } 116 | 117 | // add a single space to the front of the call_number_remainder 118 | call_number_remainder = call_number_remainder.trim(); 119 | call_number_remainder = " " + call_number_remainder; 120 | 121 | //append the begining of the callnumber to the remainder of the call number 122 | local_norm += call_number_remainder; 123 | 124 | //trim spaces from the end, or the begining of the string 125 | local_norm = local_norm.trim(); 126 | 127 | } //end try 128 | 129 | catch (e){ 130 | console.log(' no test '); 131 | } 132 | 133 | // reset the lastIndex so the next regex exec doesn't fail on next try 134 | this.lc.lastIndex = 0; 135 | 136 | return local_norm; 137 | 138 | 139 | } //end returnNormLcCall 140 | 141 | 142 | // locCallClass.localeCompare(b,a) 143 | // replicates functionality of the normal compare function 144 | // so that it may be used in external sorting operations: 145 | // 146 | // A negative number if the reference string (a) occurs before the 147 | // given string (b); 148 | // positive if the reference string (a) occurs after 149 | // the compare string (b); 150 | // 0 if they are equivalent. 151 | locCallClass.prototype.localeCompare = function (a, b) { 152 | try { 153 | var a_norm = this.returnNormLcCall(a), 154 | b_norm = this.returnNormLcCall(b); 155 | 156 | return ( a_norm < b_norm ? -1 : (a_norm > b_norm ? 1 : 0) ); 157 | } 158 | catch (err) { 159 | // console.log("error") 160 | } 161 | } 162 | 163 | // locCallClass.sortCallNumbers() 164 | // takes an array of call numbers and returns a sorted array of call 165 | // numbers in their original format. 166 | // Using something like the following works to sort as well: 167 | // var loc = new locCallClass(); 168 | // var sorted_array = loc.unsorted_callnumber_array.sort(function(a,b) {return loc.localeCompare(a,b)}); 169 | locCallClass.prototype.sortCallNumbers = function (callnumbers) { 170 | // also bind the scope of this to the sort function to be able to call 171 | // this.localeCompare 172 | var sorted = callnumbers.sort(function (a,b) { 173 | return this.localeCompare(a,b); 174 | }.bind(this)); 175 | 176 | return sorted; 177 | } 178 | 179 | // locCallClass.isBetween(a,b,c) 180 | // returns true if a <= c <= b 181 | locCallClass.prototype.isBetween = function (a,b,c) { 182 | //this.localeCompare(a, b) <= 0 if in sort order 183 | return ( (this.localeCompare(a,c) <= 0 && this.localeCompare(c,b) <=0) ? true : false ); 184 | } 185 | 186 | // pad the string 187 | locCallClass.prototype.leftPad = function (string, pad_length) { 188 | // make sure that we're treating the string like a string 189 | string = string + ''; 190 | pad_length = pad_length - string.length; 191 | // nothing to pad 192 | if (pad_length <= 0) { 193 | return string; 194 | } 195 | 196 | return this.cache[pad_length] + string; 197 | } // end leftPad 198 | 199 | //helper function for volume padding 200 | locCallClass.prototype.vol_pad = function (match, vol_string, num_string, offset, string) { 201 | // padding out number portion to 5 places 202 | return vol_string.trim() + this.leftPad(num_string, 5); 203 | } 204 | -------------------------------------------------------------------------------- /sanity_test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | sanity test on pre-sorted call numbers 8 | 18 | 19 | 20 | 21 | 22 |

23 | "Sanity Check" order of pre-sorted call numbers against this scripts normalization function: 24 |
one call number per line (script assumes "range start" on odd lines, "range end" on even lines) 25 |

26 | 27 |
28 | 29 |
30 | 31 |
32 | 35 | 36 | 37 | 40 | 41 | 42 | 45 | 46 | 47 |
48 | 49 |
50 | 51 |
 52 | 	
53 | 54 |
55 |
56 | 57 | 110 | 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /stacks_data.js: -------------------------------------------------------------------------------- 1 | //this json data will be used in our app to search for call number locations 2 | 3 | var stacks_data = [ 4 | { 5 | "location" : "", 6 | "floor" : 5, 7 | "row_num" : 1, 8 | "lc_from" : "H 1 .E77 v.1 1976", 9 | "lc_to" : "HA202 .K87 1994", 10 | "img_top" : 845, 11 | "img_left" : 120 12 | }, 13 | { 14 | "location" : "", 15 | "floor" : 5, 16 | "row_num" : 2, 17 | "lc_from" : "HA 203 .A218 no.54", 18 | "lc_to" : "HB 501 .M392", 19 | "img_top" : 820, 20 | "img_left" : 120 21 | }, 22 | { 23 | "location" : "", 24 | "floor" : 5, 25 | "row_num" : 3, 26 | "lc_from" : "HB 501 .M5 A76 1990", 27 | "lc_to" : "HC 240 .N67", 28 | "img_top" : 0, 29 | "img_left" : 0 30 | }, 31 | { 32 | "location" : "", 33 | "floor" : 5, 34 | "row_num" : 4, 35 | "lc_from" : "HC 240 .O4 1917", 36 | "lc_to" : "HD 55 .B7", 37 | "img_top" : 0, 38 | "img_left" : 0 39 | }, 40 | { 41 | "location" : "", 42 | "floor" : 5, 43 | "row_num" : 5, 44 | "lc_from" : "HD 55 .B89", 45 | "lc_to" : "HD 4965.5 .U6 P3", 46 | "img_top" : 0, 47 | "img_left" : 0 48 | }, 49 | { 50 | "location" : "", 51 | "floor" : 5, 52 | "row_num" : 6, 53 | "lc_from" : "HD4965.5.U6 P42", 54 | "lc_to" : "HD 9665.5 .P4", 55 | "img_top" : 0, 56 | "img_left" : 0 57 | }, 58 | { 59 | "location" : "", 60 | "floor" : 5, 61 | "row_num" : 7, 62 | "lc_from" : "HD 9665.5 .P72 1983", 63 | "lc_to" : "HF 5353 .M829", 64 | "img_top" : 0, 65 | "img_left" : 0 66 | }, 67 | { 68 | "location" : "", 69 | "floor" : 5, 70 | "row_num" : 8, 71 | "lc_from" : "HF 5353 .N3", 72 | "lc_to" : "HF 5548.2 .R6298", 73 | "img_top" : 0, 74 | "img_left" : 0 75 | }, 76 | { 77 | "location" : "", 78 | "floor" : 5, 79 | "row_num" : 9, 80 | "lc_from" : "HF 5548.2 .R63", 81 | "lc_to" : "HF 5686 .P24 W44", 82 | "img_top" : 0, 83 | "img_left" : 0 84 | }, 85 | { 86 | "location" : "", 87 | "floor" : 5, 88 | "row_num" : 10, 89 | "lc_from" : "HF 5686 .P3 B87", 90 | "lc_to" : "HG 4811 .A3 J35 1970", 91 | "img_top" : 0, 92 | "img_left" : 0 93 | }, 94 | ]; 95 | -------------------------------------------------------------------------------- /testing/left-pad.js: -------------------------------------------------------------------------------- 1 | /* This program is free software. It comes without any warranty, to 2 | * the extent permitted by applicable law. You can redistribute it 3 | * and/or modify it under the terms of the Do What The Fuck You Want 4 | * To Public License, Version 2, as published by Sam Hocevar. See 5 | * http://www.wtfpl.net/ for more details. */ 6 | 'use strict'; 7 | //module.exports = leftPad; 8 | 9 | var cache = [ 10 | '', 11 | ' ', 12 | ' ', 13 | ' ', 14 | ' ', 15 | ' ', 16 | ' ', 17 | ' ', 18 | ' ', 19 | ' ' 20 | ]; 21 | 22 | function leftPad (str, len, ch) { 23 | // convert `str` to `string` 24 | str = str + ''; 25 | // `len` is the `pad`'s length now 26 | len = len - str.length; 27 | // doesn't need to pad 28 | if (len <= 0) return str; 29 | // `ch` defaults to `' '` 30 | if (!ch && ch !== 0) ch = ' '; 31 | // convert `ch` to `string` 32 | ch = ch + ''; 33 | // cache common use cases 34 | if (ch === ' ' && len < 10) return cache[len] + str; 35 | // `pad` starts with an empty string 36 | var pad = ''; 37 | // loop 38 | while (true) { 39 | // add `ch` to `pad` if `len` is odd 40 | if (len & 1) pad += ch; 41 | // divide `len` by 2, ditch the remainder 42 | len >>= 1; 43 | // "double" the `ch` so this operation count grows logarithmically on `len` 44 | // each time `ch` is "doubled", the `len` would need to be "doubled" too 45 | // similar to finding a value in binary search tree, hence O(log(n)) 46 | if (len) ch += ch; 47 | // `len` is 0, exit the loop 48 | else break; 49 | } 50 | // pad `str`! 51 | return pad + str; 52 | } 53 | -------------------------------------------------------------------------------- /testing/norm_test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 163 | 164 | 165 | 175 | 176 | 177 | 178 | 179 |

testing

180 | 181 | issues 182 |
183 | 184 | 411, 412 185 | *breaks normalization rules 186 |
187 | 188 | 342, 344 189 | *portion of pre-stamp incorrectly pulled into call number 190 |
191 | 192 | 193 | 22, 533 194 | *numbers+letter groupings should be seperated by spaces? 195 | 196 |
197 | 198 | 199 | 28, 40, 47, 70, 9974 200 | *letters+number groups should be seperated by spaces? 201 | 202 |
203 | 204 | 205 | 34 206 | *remove trailing spaces? 207 | 208 |
209 | 210 | 211 | 48 212 | *if only the prestamp remains, return only that? 213 |
214 |
215 | 216 | 217 | 79 218 | no space between "beginning" of call number portion and the rest. 219 | 220 |
221 | 222 | 223 | 540 224 | no. normalization is failing? 225 | 226 |
227 | 228 |
229 |

non-matching call number normalizations

230 | 231 |

232 | 
233 | 
234 | 
282 | 
283 | 
284 | 
285 | 


--------------------------------------------------------------------------------
/testing/readme.md:
--------------------------------------------------------------------------------
 1 | ILS style call number normalization
 2 | ===
 3 | 
 4 | Purpose
 5 | ---
 6 | This method should create normalized forms of library of congress call numbers that are efficiently stored (in a database, spreadsheet, flat file, etc.), as well as constantly identify and parse call numbers into their normalized forms.
 7 | 
 8 | Method Description
 9 | ---
10 | 1. alphabetic characters are set to lowercase
11 | 1. remove anything that is considered a "pre-stamp" from the callnumber
12 | 1. identify the "beginning" of the call number
13 | 	1. search for 1 to 3 alphabetic characters (letter portion) 
14 | 	followed by 1 to 4 numeric characters (the number portion). 
15 | 		1. If there is a decimal after the 1 to 4 numeric 
16 | 		characters (followed by more numeric characters) then pull 
17 | 		those values in as part of the number portion
18 | 	1. using the above description, we now use this data to 
19 | 	construct the "beginning" of our call number.
20 | 		1. using seven character positions ( 1 2 3 4 5 6 7 ) place the 
21 | 		letter portion into positions 1 through 3, and then the number 
22 | 		portion into positions 4 through 7, right justifying, leaving 
23 | 		spaces in the unoccupied positions. For example, given the call 
24 | 		number "QL697.4 .H69 2010", we would identify the "beginning" 
25 | 		as "QL697.4", and fill out our seven character positions in the 
26 | 		following way:
27 | 		```
28 | 		1 2 3 4 5 6 7    
29 | 		q l     6 9 7 . 4
30 | 		```
31 | 		
32 | 		(note, that we included the ".4" after position 7, as it's 
33 | 		still part of the number portion)
34 | 		
35 | 		so, the normalized "beginning" of the call number would look 
36 | 		like the following:
37 | 		```
38 |     ql  697.4
39 | 		```
40 |     
41 | 		**regex for capturing this "beginning" portion** (note that this 
42 | 		should return 1 match per valid LC call number, and have 4 
43 | 		possible groups 1) letter portion, 2) number portion, 3) 
44 | 		possible decimal portion of the number, and 4) the rest of the 
45 | 		call number
46 | 		
47 | 		```
48 | 		/([A-Z]{1,3})\s*(\d{1,4})(\.\d+)*(.+)*\ig 
49 | 		```
50 | 1. after this beginning portion, the rest of the number is further 
51 | normalized
52 | 	1. convert any punctuation to spaces 
53 | 	
54 | 	1. remove instances of multiple spaces, leaving only one 
55 | 	
56 | 	1. *think about maybe normalizing volume data here as well in the 
57 | 	following way:
58 | 		1. identify volume characters ("vol", "v.", "no", "n.", etc) 
59 | 		and then place numbers following the volume characters into a 
60 | 		five character positions, right justifying. For example:
61 | 		vol 151
62 | 		normalizes to ..
63 | 		```
64 | 		    1 2 3 4 5
65 | 		vol     1 5 1
66 | 		```
67 | 		or ...
68 |     		
69 | 		```
70 | 		vol   151
71 | 		```
72 | 		
73 | 	Finishing up with our example, our call number ("QL697.4 .H69 
74 | 	2010") would final, normalized call number would end up looking 
75 | 	like the following:
76 | 	```
77 | 	QL697.4 .H69 2010
78 | 	ql  697.4 h69 2010
79 | 	```
80 |   
81 | 


--------------------------------------------------------------------------------
/testing/test.html:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 | 
 4 | 
 5 | 
 6 | 
 7 | 
 8 | 
38 | 
39 | 
40 | 
50 | 
51 | 
52 | 
53 | 
54 | 

testing

55 | 56 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /unit_test1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 18 | 19 | 20 | 21 | 22 |

testing

23 | 24 | issues 25 |
26 | 27 | 411, 412 28 | *breaks normalization rules 29 |
30 | 31 | 342, 344 32 | *portion of pre-stamp incorrectly pulled into call number 33 |
34 | 35 | 36 | 22, 533 37 | *numbers+letter groupings should be seperated by spaces? 38 | 39 |
40 | 41 | 42 | 28, 40, 47, 70, 9974 43 | *letters+number groups should be seperated by spaces? 44 | 45 |
46 | 47 | 48 | 34 49 | *remove trailing spaces? 50 | 51 |
52 | 53 | 54 | 48 55 | *if only the prestamp remains, return only that? 56 |
57 |
58 | 59 | 60 | 79 61 | no space between "beginning" of call number portion and the rest. 62 | 63 |
64 | 65 | 66 | 540 67 | no. normalization is failing? 68 | 69 |
70 | 71 |
72 |

non-matching call number normalizations

73 | 74 |

 75 | 
 76 | 
 77 | 
125 | 
126 | 
127 | 
128 | 


--------------------------------------------------------------------------------