├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── index.js ├── lib ├── marc │ ├── control_field.js │ ├── data_field.js │ ├── leader.js │ ├── record.js │ ├── subfield.js │ └── variable_field.js ├── marc4js.js ├── marc_error.js ├── parse.js ├── parse │ ├── iso2709_parser.js │ ├── marcxml_parser.js │ ├── mij_parser.js │ ├── mrk_parser.js │ └── text_parser.js ├── transform.js ├── transform │ ├── iso2709_transformer.js │ ├── marcxml_transformer.js │ ├── mij_transformer.js │ ├── mrk_transformer.js │ └── text_transformer.js └── util.js ├── package.json └── test ├── data ├── PGA_2records.mrc ├── PGA_2records.mrk ├── PGA_2records.txt ├── README.md ├── collection-error.mrc ├── collection.json ├── collection.mrc ├── collection.mrk ├── collection.txt ├── collection.xml ├── marc8.mrc ├── marc8_accented_chars.marc ├── marc8_utf8_mixed.mrc ├── marc_in_json.json ├── marctext_bad.txt ├── sandburg.mrc ├── sandburg.xml ├── selections.mrc ├── the_real_mother_goose.mrc ├── utf8.mrc └── utf8_only.mrc ├── helpers └── chai.js ├── marc ├── control_field.js ├── data_field.js ├── leader.js ├── record.js └── subfield.js ├── marc4js.js ├── marc8.js ├── parse.js ├── transform.js └── util.js /.gitignore: -------------------------------------------------------------------------------- 1 | .* 2 | /node_modules 3 | !.travis.yml 4 | !.npmignore 5 | !.gitignoregit 6 | package-lock.json -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /test 2 | /samples 3 | .travis.yml 4 | .idea 5 | .npmignore -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "9.7" 4 | - "9.6" 5 | - "9.5" 6 | - "9.4" 7 | - "9.3" 8 | - "9.2" 9 | - "9.1" 10 | - "9.0" 11 | - "8.10" 12 | - "7.10" 13 | - "6.13" 14 | 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/jiaola/marc4js.svg?branch=master)](https://travis-ci.org/jiaola/marc4js) 2 | 3 | A Node.js module for handling MARC records 4 | 5 | ## Installation 6 | 7 | ``` 8 | npm install marc4js 9 | ``` 10 | 11 | ## Features 12 | 13 | marc4js provides the following features 14 | 15 | * An easy to use API that can handle large record sets. 16 | * Uses Node.js stream API and pipe functions for parsing and writing ISO2709 format, MarcEdit text (mrk) format, MARC in JSON, and MARCXML. 17 | * Offers callback functions for parsing and writing various formats. 18 | * SAX based MARCXML parsing that doesn't in-memory storage of records while parsing. Able to parse large MARCXML file with ease. 19 | * A MARC record object model for in-memory editing of MARC records, similar to the Marc4J object model 20 | * Supports UTF-8 encoded marc files and MARC-8 encoded marc files (It requires [marc8](https://www.npmjs.com/package/marc8) to handle MARC-8 encoded files). 21 | 22 | ## Examples 23 | 24 | Examples can be found in the the [marc4js_examples](https://github.com/jiaola/marc4js_examples). You can also find 25 | examples in the test directory. 26 | 27 | ## Usage 28 | 29 | ```javascript 30 | var marc4js = require('marc4js'); 31 | ``` 32 | 33 | ### Parsers 34 | 35 | Parsers take various MARC formats and convert them to `marc4js.marc.Record` objects. Marc4js supports ISO2709, text 36 | (MarcEdit .mrc file) and MARCXML formats. 37 | 38 | There are three ways to use a parser. 39 | 40 | #### Callback API 41 | 42 | ``` 43 | marc4js.parse(data, options, function(err, records) { 44 | }); 45 | ``` 46 | 47 | #### Stream API 48 | 49 | ```javascript 50 | var parser = marc4js.parse(options); 51 | parser.on('data', function(record) { 52 | }); 53 | parser.on('end', function() { 54 | }); 55 | parser.on('error', function(err) { 56 | }); 57 | parser.write(data); 58 | parser.end(); 59 | ``` 60 | 61 | All events are based on [the Node.js stream API](http://nodejs.org/api/stream.html). 62 | 63 | Note that the parsers always work in the paused (aka non-flowing) streaming mode - therefore the `objectMode` option of 64 | the stream api is disabled, and is always set to `true`. Listening to the `readable` event will throw an erorr. 65 | 66 | #### Pipe function 67 | 68 | ```javascript 69 | var parser = marc4js.parse(options); 70 | fs.createReadStream('/path/to/your/file').pipe(parser).pipe(transformer).pipe(process.stdout); 71 | ``` 72 | 73 | #### options 74 | 75 | `format`: default `iso2709`, possible values `iso2709`, `marc`, `text`, `mrk`, `marcxml`, `xml` 76 | 77 | #### Different types of parsers 78 | 79 | ##### Iso2709Parser 80 | 81 | Parses ISO2709 format. Used by default or when `format` is `iso2709` or `marc` 82 | 83 | ##### MrkParser 84 | 85 | Parses MarcEdit text format (.mrk files). Used when `format` is `mrk` 86 | 87 | Other options: 88 | 89 | * `spaceReplace`: In MarcEdit mrk files, spaces in data field indicators or control fields are replace by `\`. By default 90 | MrkPaser will convert `\` to space in those places. It can be configured with this option. 91 | 92 | ##### TextParser 93 | 94 | Parses a text format that is slightly different from mrk format. Used when `format` is `text`. 95 | 96 | ##### MarcxmlParser 97 | 98 | Parses MarcEdit text format (.mrk files). Used when `format` is `marcxml` or `xml` 99 | 100 | The stream and pipe API is SAX based so it doesn't require in-memory storage of the records. This is suitable for processing large MARCXML file. 101 | The callback API will read all records in memory and return it in the callback function and is not advised to process large MARCXML file. 102 | 103 | Other options: 104 | 105 | * `strict`: default is `false`. When in `strict` mode, the parser will fail if the XML is not well-formatted. For details, see the `strict` option in [sax-js](https://github.com/isaacs/sax-js). 106 | 107 | ##### MijParser 108 | 109 | Parses [MARC-in-JSON](https://rossfsinger.com/blog/2010/09/a-proposal-to-serialize-marc-in-json/) format. Used when `format` is `json` or `mij`. 110 | 111 | The stream and pipe API uses a sax-like JSON stream parser so it doesn't require in-memory storage of the records. Thus it can process 112 | large number of MARC-in-JSON records. 113 | 114 | ### Transformers 115 | 116 | Transformers transform the `marc4js.marc.Record` objects into various MARC formats. Marc4js supports ISO2709, text 117 | (MarcEdit .mrc file) and MARCXML formats. 118 | 119 | Like parsers, transformers can also be used in three different ways. 120 | 121 | #### Callback API 122 | 123 | ``` 124 | marc4js.transform(records, options, function(err, output) { 125 | }); 126 | ``` 127 | 128 | #### Stream API 129 | 130 | ```javascript 131 | var transformer = marc4js.transform(options); 132 | transformer.on('readable', function(output) { 133 | }); 134 | transformer.on('end', function() { 135 | }); 136 | transformer.on('error', function(err) { 137 | }); 138 | transformer.write(record); // one record 139 | // or to write an array of records 140 | // records.forEach(function(record) { 141 | // transformer.write(record); 142 | // }); 143 | transformer.end(); 144 | ``` 145 | 146 | Note that even though parsers can be only in the flowing mode, the transformers can use either flowing or paused (aka non-flowing) mode in the 147 | [stream API](http://nodejs.org/api/stream.html). In the above example it's using the paused mode, but it can also use the `data` event handler 148 | if flowing mode is used. 149 | 150 | #### Pipe function 151 | 152 | ```javascript 153 | var transformer = marc4js.transform(options); 154 | fs.createReadStream('/path/to/your/file').pipe(parser).pipe(transformer).pipe(process.stdout); 155 | ``` 156 | 157 | #### options 158 | 159 | `format`: default `iso2709`, possible values `iso2709`, `marc`, `text`, `mrk`, `marcxml`, `xml` 160 | `objectMode`: default `false`. Used to switch between the flowing and paused (aka non-flowing) mode in the [stream API](http://nodejs.org/api/stream.html). 161 | 162 | #### Different types of Transformers 163 | 164 | ##### Iso2709Transformer 165 | 166 | Outputs ISO2709 format. Used by default or when `format` is `iso2709` or `marc` 167 | 168 | ##### MrkTransformer 169 | 170 | Outputs MarcEdit text format (.mrk files). Used when `format` is `mrk` 171 | 172 | Other options: 173 | 174 | * `spaceReplace`: by default space in data field indicators and control fields are replaced with `\`. But it can be configured with this option. 175 | 176 | ##### TextTransformer 177 | 178 | Outputs text format, which is slightly different from mrk format. Used when `format` is `text`. 179 | 180 | ##### MarcxmlTransformer 181 | 182 | Outputs MarcEdit text format (.mrk files). Used when `format` is `marcxml` or `xml` 183 | 184 | Other options: 185 | 186 | * `pretty`: default is `true`. Output XML in pretty format. If set to false, new indentation and line-breakers in outputs. 187 | * `indent`: default is `' '` (two spaces). Used to indent lines in pretty format. 188 | * `newline`: default is `\n`. Used in pretty format. 189 | * `declaration`: default is `true`. If set to `false`, the XML declaration line (``) is not included in the output. 190 | * `root`: default is `true`. If `false`, the root `` element is not included in the output. 191 | 192 | ##### MijTransformer 193 | 194 | Outputs [MARC-in-JSON](https://rossfsinger.com/blog/2010/09/a-proposal-to-serialize-marc-in-json/) string. Used when `format` is `json` or `mij`. 195 | 196 | Other options: 197 | 198 | * asArray: default is `true`. By default the output will be in an JSON array format, even if there is only one record. 199 | If this option set to false, the output will not write the enclosing brackets `[` and `]` at the beginning and end of the output. 200 | 201 | 202 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/marc4js'); -------------------------------------------------------------------------------- /lib/marc/control_field.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var VariableField = require('./variable_field'); 4 | 5 | var ControlField = function (tag, data) { 6 | if (typeof tag !== 'undefined') this._tag = tag; 7 | if (typeof data !== 'undefined') this._data = data; 8 | }; 9 | ControlField.prototype = Object.create(VariableField.prototype); 10 | 11 | // Define getters and setters 12 | Object.defineProperties(ControlField.prototype, { 13 | data: { 14 | get: function () { 15 | return this._data; 16 | }, 17 | set: function (val) { 18 | this._data = val; 19 | } 20 | } 21 | }); 22 | 23 | ControlField.prototype.marshal = function () { 24 | return this.data; 25 | }; 26 | 27 | ControlField.prototype.toString = function () { 28 | return this.marshal(); 29 | }; 30 | 31 | module.exports = ControlField; 32 | -------------------------------------------------------------------------------- /lib/marc/data_field.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var VariableField = require('./variable_field'); 4 | var Subfield = require('./subfield'); 5 | var MarcError = require('../marc_error'); 6 | 7 | var DataField = function (tag, ind1, ind2, subfields) { 8 | if (typeof tag !== 'undefined') this._tag = tag; 9 | if (typeof ind1 !== 'undefined') this._indicator1 = ind1; 10 | if (typeof ind2 !== 'undefined') this._indicator2 = ind2; 11 | if (typeof subfields !== 'undefined') { 12 | this._subfields = subfields.map(function(f) { 13 | return new Subfield(f[0], f[1]); 14 | }); 15 | } else { 16 | this._subfields = []; 17 | } 18 | }; 19 | DataField.prototype = Object.create(VariableField.prototype); 20 | 21 | // Define getters and setters 22 | Object.defineProperties(DataField.prototype, { 23 | indicator1: { 24 | get: function () { 25 | return this._indicator1; 26 | }, 27 | set: function (val) { 28 | this._indicator1 = val; 29 | } 30 | }, 31 | indicator2: { 32 | get: function () { 33 | return this._indicator2; 34 | }, 35 | set: function (val) { 36 | this._indicator2 = val; 37 | } 38 | }, 39 | subfields: { 40 | get: function () { 41 | return this._subfields; 42 | }, 43 | set: function (val) { 44 | this._subfields = val; 45 | } 46 | } 47 | }); 48 | 49 | DataField.prototype.unmarshal = function (data) { 50 | this.indicator1 = data.charAt(0); 51 | this.indicator2 = data.charAt(1); 52 | this.subfields = data.substr(3).split('\x1f').map(function(sf) { 53 | var code = sf.substr(0, 1); 54 | if (code < 0) throw new MarcError("unexpected end of data field"); 55 | var subfield = new Subfield(); 56 | subfield.code = code; 57 | subfield.data = sf.substr(1); 58 | return subfield; 59 | }); 60 | }; 61 | 62 | DataField.prototype.addSubfield = function (subfield) { 63 | this.subfields.push(subfield); 64 | }; 65 | 66 | 67 | DataField.prototype.removeSubfield = function (subfield) { 68 | if (this.subfields.indexOf(subfield) != -1) { 69 | this.subfields.splice(this.subfields.indexOf(subfield), 1); 70 | } 71 | }; 72 | 73 | DataField.prototype.findSubfields = function (code) { 74 | return this.subfields.filter(function (el) { 75 | return el.code == code; 76 | }); 77 | }; 78 | 79 | DataField.prototype.findSubfield = function (code) { 80 | return this.findSubfields(code)[0]; 81 | }; 82 | 83 | DataField.prototype.find = function (pattern) { 84 | return this.subfields.some(function (subfield) { 85 | return subfield.find(pattern); 86 | }); 87 | }; 88 | 89 | DataField.prototype.marshal = function () { 90 | var str = this.indicator1 + this.indicator2; 91 | this.subfields.forEach(function (subfield, index, array) { 92 | str = str + '\x1f' + subfield.marshal(); 93 | }); 94 | return str; 95 | }; 96 | 97 | DataField.prototype.toString = function () { 98 | return this.marshal(); 99 | }; 100 | 101 | module.exports = DataField; 102 | -------------------------------------------------------------------------------- /lib/marc/leader.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Util = require("../util"); 4 | 5 | var Leader = function (str) { 6 | if (typeof str !== 'undefined') { 7 | this.unmarshal(str); 8 | } 9 | }; 10 | 11 | // Define getters and setters 12 | Object.defineProperties(Leader.prototype, { 13 | recordLength: { 14 | get: function () { 15 | return this._recordLength; 16 | }, 17 | set: function (val) { 18 | this._recordLength = val; 19 | } 20 | }, 21 | recordStatus: { 22 | get: function () { 23 | return this._recordStatus; 24 | }, 25 | set: function (val) { 26 | this._recordStatus = val; 27 | } 28 | }, 29 | typeOfRecord: { 30 | get: function () { 31 | return this._typeOfRecord; 32 | }, 33 | set: function (val) { 34 | this._typeOfRecord = val; 35 | } 36 | }, 37 | implDefined1: { 38 | get: function () { 39 | return this._implDefined1; 40 | }, 41 | set: function (val) { 42 | this._implDefined1 = val; 43 | } 44 | }, 45 | implDefined2: { 46 | get: function () { 47 | return this._implDefined2; 48 | }, 49 | set: function (val) { 50 | this._implDefined2 = val; 51 | } 52 | }, 53 | charCodingScheme: { 54 | get: function () { 55 | return this._charCodingScheme; 56 | }, 57 | set: function (val) { 58 | this._charCodingScheme = val; 59 | } 60 | }, 61 | indicatorCount: { 62 | get: function () { 63 | return this._indicatorCount; 64 | }, 65 | set: function (val) { 66 | this._indicatorCount = val; 67 | } 68 | }, 69 | subfieldCodeLength: { 70 | get: function () { 71 | return this._subfieldCodeLength; 72 | }, 73 | set: function (val) { 74 | this._subfieldCodeLength = val; 75 | } 76 | }, 77 | baseAddressOfData: { 78 | get: function () { 79 | return this._baseAddressOfData; 80 | }, 81 | set: function (val) { 82 | this._baseAddressOfData = val; 83 | } 84 | }, 85 | entryMap: { 86 | get: function () { 87 | return this._entryMap; 88 | }, 89 | set: function (val) { 90 | this._entryMap = val; 91 | } 92 | } 93 | }); 94 | 95 | Leader.prototype.unmarshal = function (ldr) { 96 | var s = ldr.substring(0, 5); 97 | if (Util.isInt(s)) { 98 | this.recordLength = parseInt(s); 99 | } else { 100 | this.recordLength = 0; 101 | } 102 | this.recordStatus = ldr.charAt(5); 103 | this.typeOfRecord = ldr.charAt(6); 104 | this.implDefined1 = ldr.substring(7, 9); 105 | this.charCodingScheme = ldr.charAt(9); 106 | 107 | s = ldr.charAt(10); 108 | if (Util.isInt(s)) { 109 | this.indicatorCount = parseInt(s); 110 | } else { 111 | this.indicatorCount = 2; 112 | } 113 | 114 | s = ldr.charAt(11); 115 | if (Util.isInt(s)) { 116 | this.subfieldCodeLength = parseInt(s); 117 | } else { 118 | this.subfieldCodeLength = 2; 119 | } 120 | 121 | s = ldr.substring(12, 17); 122 | if (Util.isInt(s)) { 123 | this.baseAddressOfData = parseInt(s); 124 | } else { 125 | this.baseAddressOfData = 0; 126 | } 127 | 128 | this.implDefined2 = ldr.substring(17, 20); 129 | this.entryMap = ldr.substring(20, 24); 130 | }; 131 | 132 | Leader.prototype.marshal = function () { 133 | return Util.formatInteger(this.recordLength, 5) + 134 | this.recordStatus + this.typeOfRecord + this.implDefined1 + 135 | this.charCodingScheme + this.indicatorCount + this.subfieldCodeLength + 136 | Util.formatInteger(this.baseAddressOfData, 5) + this.implDefined2 + this.entryMap; 137 | }; 138 | 139 | Leader.prototype.toString = function () { 140 | return this.marshal(); 141 | }; 142 | 143 | 144 | module.exports = Leader; 145 | -------------------------------------------------------------------------------- /lib/marc/record.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Util = require('../util.js'); 4 | 5 | var Record = function () { 6 | this._controlFields = []; 7 | this._dataFields = []; 8 | }; 9 | 10 | // Define getters and setters 11 | Object.defineProperties(Record.prototype, { 12 | leader: { 13 | get: function () { 14 | return this._leader; 15 | }, 16 | set: function (val) { 17 | this._leader = val; 18 | } 19 | }, 20 | controlFields: { 21 | get: function () { 22 | return this._controlFields; 23 | }, 24 | set: function (val) { 25 | this._controlFields = val; 26 | } 27 | }, 28 | dataFields: { 29 | get: function () { 30 | return this._dataFields; 31 | }, 32 | set: function (val) { 33 | this._dataFields = val; 34 | } 35 | }, 36 | variableFields: { 37 | get: function () { 38 | return this.controlFields.concat(this.dataFields); 39 | } 40 | } 41 | }); 42 | 43 | Record.prototype.addVariableField = function (field) { 44 | if (Util.isControlField(field.tag)) { 45 | this.controlFields.push(field); 46 | } else { 47 | this.dataFields.push(field); 48 | } 49 | }; 50 | 51 | Record.prototype.findDataFields = function (tag) { 52 | return this.dataFields.filter(function (df) { 53 | return df.tag == tag; 54 | }); 55 | }; 56 | 57 | Record.prototype.findDataField = function (tag) { 58 | return this.findDataFields(tag)[0]; 59 | }; 60 | 61 | Record.prototype.marshal = function () { 62 | var buffers = []; 63 | var encoding = 'utf8'; // fot now, marc4js only supports utf-8 output 64 | this.leader.charCodingScheme = 'a'; 65 | 66 | buffers.push(new Buffer(this.leader.marshal(), encoding)); 67 | 68 | var directory = []; 69 | var fields = []; 70 | var start = 0; 71 | 72 | // write directory 73 | this.variableFields.forEach(function (field) { 74 | var chunk = field.marshal(); 75 | chunk = chunk + '\x1e'; 76 | 77 | var len = Buffer.byteLength(chunk, encoding); 78 | fields.push(new Buffer(chunk, encoding)); 79 | directory.push(new Buffer(field.tag, encoding)); 80 | directory.push(new Buffer(Util.formatInteger(len, 4), encoding)); 81 | directory.push(new Buffer(Util.formatInteger(start, 5), encoding)); 82 | start += len; 83 | }); 84 | 85 | buffers = buffers.concat(directory); 86 | buffers = buffers.concat(new Buffer('\x1e')); 87 | buffers = buffers.concat(fields); 88 | buffers.push(new Buffer('\x1d')); 89 | return Buffer.concat(buffers); 90 | }; 91 | 92 | module.exports = Record; 93 | -------------------------------------------------------------------------------- /lib/marc/subfield.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Subfield = function (code, data) { 4 | if (typeof code !== 'undefined') this._code = code; 5 | if (typeof data !== 'undefined') this._data = data; 6 | }; 7 | 8 | // Define getters and setters 9 | Object.defineProperties(Subfield.prototype, { 10 | code: { 11 | get: function () { 12 | return this._code; 13 | }, 14 | set: function (val) { 15 | this._code = val; 16 | } 17 | }, 18 | data: { 19 | get: function () { 20 | return this._data; 21 | }, 22 | set: function (val) { 23 | this._data = val; 24 | } 25 | } 26 | }); 27 | 28 | Subfield.prototype.find = function (pattern) { 29 | return pattern.test(this._data); 30 | }; 31 | 32 | Subfield.prototype.toString = function () { 33 | return "$" + this._code + this._data; 34 | }; 35 | 36 | Subfield.prototype.marshal = function () { 37 | return this._code + this._data; 38 | } 39 | 40 | module.exports = Subfield; 41 | -------------------------------------------------------------------------------- /lib/marc/variable_field.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var VariableField = function () { 4 | }; 5 | 6 | // Define getters and setters 7 | Object.defineProperties(VariableField.prototype, { 8 | tag: { 9 | get: function () { 10 | return this._tag; 11 | }, 12 | set: function (val) { 13 | this._tag = val; 14 | } 15 | } 16 | }); 17 | 18 | module.exports = VariableField; 19 | -------------------------------------------------------------------------------- /lib/marc4js.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.marc = { 4 | Record: require('./marc/record'), 5 | DataField: require('./marc/data_field'), 6 | ControlField: require('./marc/control_field'), 7 | Leader: require('./marc/leader'), 8 | Subfield: require('./marc/subfield'), 9 | VariableField: require('./marc/variable_field') 10 | }; 11 | 12 | exports.parse = require('./parse'); 13 | exports.transform = require('./transform'); 14 | exports.MarcError = require('./marc_error'); 15 | 16 | -------------------------------------------------------------------------------- /lib/marc_error.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var MarcError = function (message) { 4 | Error.captureStackTrace(this); 5 | this.message = message; 6 | this.name = "MarcError"; 7 | }; 8 | 9 | MarcError.prototype = Object.create(Error.prototype); 10 | 11 | module.exports = MarcError; 12 | -------------------------------------------------------------------------------- /lib/parse.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function() { 4 | var callback, called, records, data, options, parser; 5 | if (arguments.length === 3) { 6 | data = (typeof arguments[0] === 'undefined' || arguments[0] === null ) ? "" : arguments[0]; 7 | options = arguments[1]; 8 | callback = arguments[2]; 9 | } else if (arguments.length === 2) { 10 | if (typeof arguments[0] === 'string') { 11 | data = arguments[0]; 12 | } else { 13 | options = arguments[0]; 14 | } 15 | if (typeof arguments[1] === 'function') { 16 | callback = arguments[1]; 17 | } else { 18 | options = arguments[1]; 19 | } 20 | } else if (arguments.length === 1) { 21 | if (typeof arguments[0] === 'function') { 22 | callback = arguments[0]; 23 | } else { 24 | options = arguments[0]; 25 | } 26 | } 27 | if (options == null) { 28 | options = {}; 29 | } 30 | 31 | var parsers = { 32 | iso2709: './parse/iso2709_parser', 33 | marc: './parse/iso2709_parser', 34 | text: './parse/text_parser', 35 | mrk: './parse/mrk_parser', 36 | marcxml: './parse/marcxml_parser', 37 | xml: './parse/marcxml_parser', 38 | json: './parse/mij_parser', 39 | mij: './parse/mij_parser' 40 | }; 41 | 42 | var format = options.format || options.fromFormat || 'iso2709'; 43 | var Parser = require(parsers[format]); 44 | parser = new Parser(options); 45 | if (typeof data !== 'undefined') { 46 | process.nextTick(function() { 47 | parser.write(data); 48 | return parser.end(); 49 | }); 50 | } 51 | if (callback) { 52 | called = false; 53 | records = []; 54 | 55 | var handleData = function(record) { 56 | records.push(record); 57 | }; 58 | parser.on('data', handleData); 59 | parser.once('error', function(err) { 60 | called = true; 61 | parser.removeListener('data', handleData); 62 | return callback(err); 63 | }); 64 | parser.once('end', function() { 65 | if (!called) { 66 | parser.removeListener('data', handleData); 67 | return callback(null, records); 68 | } 69 | }.bind(parser)); 70 | } 71 | return parser; 72 | }; 73 | -------------------------------------------------------------------------------- /lib/parse/iso2709_parser.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Transform = require('stream').Transform; 4 | var util = require('util'); 5 | var MarcError = require('../marc_error'); 6 | var Util = require('../util'); 7 | var ControlField = require('../marc/control_field'); 8 | var DataField = require('../marc/data_field'); 9 | var Record = require('../marc/record'); 10 | var Leader = require('../marc/leader'); 11 | var Subfield = require('../marc/subfield'); 12 | 13 | var Iso2709Parser = function (opts) { 14 | opts = opts || {}; 15 | opts.objectMode = true; // this has to be true. Emit only Record objects 16 | this.forceMARC8 = opts.forceMARC8; 17 | if (typeof this.forceMARC8 === 'undefined') { 18 | this.forceMARC8 = false; 19 | } 20 | this.marc8converter = opts.marc8converter; 21 | 22 | // default encoding is utf8. if the marc file has marc8, 23 | // please use 'binary' encoding 24 | this.encoding = opts.encoding || 'utf8'; 25 | 26 | Transform.call(this, opts); 27 | 28 | this.prevChunk = null; 29 | }; 30 | 31 | util.inherits(Iso2709Parser, Transform); 32 | 33 | Iso2709Parser.prototype._transform = function (chunk, encoding, callback) { 34 | if (typeof chunk == 'string' || chunk instanceof String) { 35 | chunk = new Buffer(chunk, 'binary'); 36 | } else { 37 | encoding = undefined; 38 | } 39 | var err; 40 | try { 41 | var start = 0; 42 | var position = 0; 43 | if (chunk.length === 0) return callback(); 44 | while (position <= chunk.length) { 45 | while (position <= chunk.length && chunk[position] !== 29) { 46 | position++; 47 | } 48 | if (position <= chunk.length) { 49 | var raw; 50 | if (this.prevChunk === null) { 51 | raw = new Buffer(position - start + 1); 52 | chunk.copy(raw, 0, start, position); 53 | } else { 54 | raw = new Buffer(this.prevChunk.length + position + 1); 55 | this.prevChunk.copy(raw); 56 | chunk.copy(raw, this.prevChunk.length, 0, position); 57 | this.prevChunk = null; 58 | } 59 | 60 | var record; 61 | try { 62 | record = this._parse(raw, encoding); 63 | } catch (err) { 64 | this.emit("error", err); 65 | } 66 | 67 | this.push(record); 68 | position++; 69 | start = position; 70 | } 71 | } 72 | if (position !== chunk.length) { 73 | this.prevChunk = chunk.slice(start); 74 | } else { 75 | this.prevChunk = null; 76 | } 77 | return callback(); 78 | } catch (_error) { 79 | err = _error; 80 | return this.emit('error', err); 81 | } 82 | }; 83 | 84 | Iso2709Parser.prototype._parse = function(data, encoding) { 85 | var record = new Record(); 86 | var leader = new Leader(); 87 | leader.unmarshal(data.toString(this.encoding, 0, 24)); 88 | record.leader = leader; 89 | 90 | var g0, g1; 91 | 92 | var directoryLength = leader.baseAddressOfData - (24 + 1); 93 | if ((directoryLength % 12) !== 0) { 94 | throw new MarcError("invalid directory"); 95 | } 96 | var size = directoryLength / 12; 97 | 98 | var pos = directoryLength + 25; 99 | for (var i = 0; i < size; i++) { 100 | var offset = 24 + i * 12; 101 | var tag = data.toString(this.encoding, offset, offset+3); 102 | 103 | var field; 104 | if (Util.isControlField(tag)) { 105 | field = new ControlField(); 106 | var chars = []; 107 | while (data[pos] !== 0x1e) { 108 | chars.push(data[pos++]); 109 | } 110 | pos++; 111 | field.data = this._convert(record.leader.charCodingScheme, chars, {G0: g0, G1: g1}); 112 | } else { 113 | // field = this._parseDataField(); 114 | field = new DataField(); 115 | field.indicator1 = String.fromCharCode(data[pos++]); 116 | field.indicator2 = String.fromCharCode(data[pos++]); 117 | 118 | while (data[pos] !== 0x1e) { 119 | if (data[pos++] !== 0x1f) { 120 | throw new MarcError("Subfield doesn't start with 0x1f"); 121 | } // skip 0x1f 122 | var subfield = new Subfield(); 123 | subfield.code = String.fromCharCode(data[pos++]); 124 | var chars = []; 125 | while (data[pos] !== 0x1f && data[pos] !== 0x1e) { 126 | chars.push(data[pos++]); 127 | } 128 | subfield.data = this._convert(record.leader.charCodingScheme, chars, {G0: g0, G1: g1}); 129 | field.addSubfield(subfield); 130 | } 131 | pos++; 132 | } 133 | 134 | field.tag = tag; 135 | record.addVariableField(field); 136 | 137 | if (field.tag === '066') { 138 | var g0g1 = this._charset(field); 139 | g0 = g0g1[0]; 140 | g1 = g0g1[1]; 141 | } 142 | } 143 | return record; 144 | }; 145 | 146 | Iso2709Parser.prototype._convert = function(charCodingScheme, data, opts) { 147 | var value; 148 | if (charCodingScheme === 'a') { 149 | value = new Buffer(data).toString('utf8'); 150 | } else if (charCodingScheme === ' ') { 151 | if (this.marc8converter) { 152 | value = this.marc8converter(data, opts); 153 | } else { 154 | value = new Buffer(data).toString(this.encoding); 155 | } 156 | } else { 157 | throw new MarcError("Unexpected character coding scheme. " + 158 | "It has to be 'a' or ' ' but value is '" + charCodingScheme + "'"); 159 | } 160 | return value; 161 | }; 162 | 163 | Iso2709Parser.prototype._charset = function(f066) { 164 | var subfields = f066.subfields; 165 | var g0, g1; 166 | var G0_SET = [0x28, 0x2c, 0x24]; // ['(', ',', '$']; 167 | var G1_SET = [0x29, 0x2d, 0x24]; // [')', '-', '$']; 168 | for (var i = 0; i < subfields.length; i++) { 169 | var subfield = subfields[i]; 170 | switch (subfield.code) { 171 | case 'a': 172 | g0 = subfield.data.charCodeAt(1); 173 | break; 174 | case 'b': 175 | g1 = subfield.data.charCodeAt(1); 176 | break; 177 | case 'c': 178 | var c1 = subfield.data.charCodeAt(0); 179 | if (G0_SET.indexOf(c1) >= 0) { 180 | g0 = subfield.data.charCodeAt(1); 181 | } 182 | if (G1_SET.indexOf(c1) >= 0) { 183 | g1 = subfield.data.charCodeAt(1); 184 | } 185 | break; 186 | } 187 | } 188 | return [g0, g1]; 189 | } 190 | 191 | module.exports = Iso2709Parser; 192 | -------------------------------------------------------------------------------- /lib/parse/marcxml_parser.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var sax = require('sax'); 4 | var util = require('util'); 5 | var Transform = require('stream').Transform; 6 | var Record = require('../marc/record'); 7 | var ControlField = require('../marc/control_field'); 8 | var DataField = require('../marc/data_field'); 9 | var Subfield = require('../marc/subfield'); 10 | var Leader = require('../marc/leader'); 11 | 12 | function MarcxmlParser (opts) { 13 | if (!(this instanceof MarcxmlParser)) return new MarcxmlParser(opts); 14 | opts = opts || {}; 15 | opts.objectMode = true; // this has to be true. Emit only Record objects 16 | opts.highWaterMark = 16; // max. # of output records buffered 17 | Transform.call(this, opts); 18 | 19 | this.init(); 20 | 21 | var strict = opts.strict || false; 22 | this.resume_saxerror = opts.resume_saxerror || false; 23 | 24 | // See https://github.com/isaacs/sax-js for more info 25 | this.stream = sax.createStream(strict /* strict mode - no by default */, {lowercase: true, xmlns: true }); 26 | this.stream.on('error', this.handleSaxError.bind(this)); 27 | //this.stream.on('processinginstruction', this.handleProcessingInstruction.bind(this)); 28 | this.stream.on('opentag', this.handleOpenTag.bind(this)); 29 | this.stream.on('closetag',this.handleCloseTag.bind(this)); 30 | this.stream.on('text', this.handleText.bind(this)); 31 | this.stream.on('cdata', this.handleText.bind(this)); 32 | this.stream.once('end', this.handleEnd.bind(this)); 33 | } 34 | util.inherits(MarcxmlParser, Transform); 35 | 36 | MarcxmlParser.prototype.init = function (){ 37 | this.stack = []; 38 | }; 39 | 40 | 41 | MarcxmlParser.prototype.handleEnd = function (){ 42 | this.stream.removeListener('error', this.handleSaxError.bind(this)); 43 | this.stream.removeListener('opentag', this.handleOpenTag.bind(this)); 44 | this.stream.removeListener('closetag',this.handleCloseTag.bind(this)); 45 | this.stream.removeListener('text', this.handleText.bind(this)); 46 | this.stream.removeListener('cdata', this.handleText.bind(this)); 47 | this.push(null); 48 | }; 49 | 50 | MarcxmlParser.prototype.handleSaxError = function (e) { 51 | this.emit('error', e); 52 | if (this.resume_saxerror) { 53 | if (this.stream._parser) { 54 | this.stream._parser.error = null; 55 | this.stream._parser.resume(); 56 | } 57 | } 58 | }; 59 | 60 | MarcxmlParser.prototype.handleError = function (e){ 61 | this.emit('error', e); 62 | }; 63 | 64 | MarcxmlParser.prototype.handleOpenTag = function (node){ 65 | var obj; 66 | switch (node.local) { 67 | case 'record': 68 | obj = new Record(); 69 | break; 70 | case 'leader': 71 | obj = new Leader(); 72 | break; 73 | case 'controlfield': 74 | obj = new ControlField(); 75 | if (node.attributes.tag) { 76 | obj.tag = node.attributes.tag.value; 77 | } else { 78 | // TODO: throw an error 79 | } 80 | break; 81 | case 'datafield': 82 | obj = new DataField(); 83 | if (node.attributes.tag) { 84 | obj.tag = node.attributes.tag.value; 85 | } else { 86 | // TODO: throw an error 87 | } 88 | if (node.attributes.ind1) { 89 | obj.indicator1 = node.attributes.ind1.value; 90 | } else { 91 | // TODO: throw an error 92 | } 93 | if (node.attributes.ind2) { 94 | obj.indicator2 = node.attributes.ind2.value; 95 | } else { 96 | // TODO: throw an error 97 | } 98 | break; 99 | case 'subfield': 100 | obj = new Subfield(); 101 | if (node.attributes.code) { 102 | obj.code = node.attributes.code.value; 103 | } else { 104 | // TODO: throw an error 105 | } 106 | break; 107 | default: 108 | break; 109 | } 110 | if (typeof obj !== 'undefined') this.stack.push(obj); 111 | }; 112 | 113 | MarcxmlParser.prototype.handleCloseTag = function (el){ 114 | var obj = this.stack.pop(); 115 | if (obj instanceof Subfield) { 116 | var field = this.stack.pop(); 117 | field.addSubfield(obj); 118 | this.stack.push(field); 119 | } else if (obj instanceof ControlField || obj instanceof DataField) { 120 | var record = this.stack.pop(); 121 | record.addVariableField(obj); 122 | this.stack.push(record); 123 | } else if (obj instanceof Leader) { 124 | var record = this.stack.pop(); 125 | record.leader = obj; 126 | this.stack.push(record); 127 | } else if (obj instanceof Record) { 128 | this.push(obj); 129 | } 130 | }; 131 | 132 | MarcxmlParser.prototype.handleText = function (text){ 133 | var obj = this.stack.pop(); 134 | if (obj instanceof Subfield || obj instanceof ControlField) { 135 | obj.data = text; 136 | } else if (obj instanceof Leader) { 137 | obj.unmarshal(text); 138 | } 139 | this.stack.push(obj); 140 | }; 141 | 142 | // Naive Stream API 143 | MarcxmlParser.prototype._transform = function (data, encoding, done) { 144 | try { 145 | this.stream.write(data); 146 | done(); 147 | } catch (e) { 148 | done(e); 149 | this.push(null); // Manually trigger and end, since we can't reliably do any more parsing 150 | } 151 | }; 152 | 153 | module.exports = MarcxmlParser; -------------------------------------------------------------------------------- /lib/parse/mij_parser.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var clarinet = require('clarinet'); 4 | var util = require('util'); 5 | var Transform = require('stream').Transform; 6 | var Record = require('../marc/record'); 7 | var ControlField = require('../marc/control_field'); 8 | var DataField = require('../marc/data_field'); 9 | var Subfield = require('../marc/subfield'); 10 | var Leader = require('../marc/leader'); 11 | 12 | function MijParser (opts) { 13 | if (!(this instanceof MijParser)) return new MijParser(opts); 14 | opts = opts || {}; 15 | opts.objectMode = true; // this has to be true. Emit only Record objects 16 | opts.highWaterMark = 16; // max. # of output records buffered 17 | Transform.call(this, opts); 18 | 19 | this.init(); 20 | 21 | // See https://github.com/dscape/clarinet for more info 22 | this.stream = clarinet.createStream({}); 23 | this.stream.on('error', this.handleSaxError.bind(this)); 24 | this.stream.on('openobject', this.handleOpenObject.bind(this)); 25 | this.stream.on('closeobject',this.handleCloseObject.bind(this)); 26 | this.stream.on('value', this.handleValue.bind(this)); 27 | this.stream.on('key', this.handleKey.bind(this)); 28 | this.stream.once('end', this.handleEnd.bind(this)); 29 | } 30 | util.inherits(MijParser, Transform); 31 | 32 | MijParser.prototype.init = function (){ 33 | this.stack = []; 34 | this.keyStack = []; 35 | this.previous = ''; 36 | this.arrayStack = []; 37 | }; 38 | 39 | 40 | MijParser.prototype.handleEnd = function (){ 41 | this.stream.removeListener('error', this.handleSaxError.bind(this)); 42 | this.stream.removeListener('openobject', this.handleOpenObject.bind(this)); 43 | this.stream.removeListener('closeobject',this.handleCloseObject.bind(this)); 44 | this.stream.removeListener('value', this.handleValue.bind(this)); 45 | this.stream.removeListener('key', this.handleKey.bind(this)); 46 | this.push(null); 47 | }; 48 | 49 | MijParser.prototype.handleSaxError = function (e) { 50 | this.emit('error', e); 51 | if (this.options.resume_saxerror) { 52 | if (this.stream._parser) { 53 | this.stream._parser.error = null; 54 | this.stream._parser.resume(); 55 | } 56 | } 57 | }; 58 | 59 | MijParser.prototype.handleError = function (e){ 60 | this.emit('error', e); 61 | }; 62 | 63 | MijParser.prototype.handleOpenObject = function (key){ 64 | this.previous = key; 65 | this.keyStack.push(key); 66 | var obj = null; 67 | if (/\d{3}/.test(key)) { // field 68 | obj = parseInt(key) < 10 ? new ControlField() : new DataField(); 69 | obj.tag = key; 70 | } else if (key.length == 1) { // subfield 71 | obj = new Subfield(); 72 | obj.code = key; 73 | } else if (key == 'leader' || key == 'fields') { 74 | obj = new Record(); 75 | } 76 | if (obj != null) this.stack.push(obj); 77 | }; 78 | 79 | MijParser.prototype.handleCloseObject = function (){ 80 | var key = this.keyStack.pop(); 81 | if (key == 'subfields' || key == 'fields') { 82 | return; 83 | } 84 | var obj = this.stack.pop(); 85 | if (obj instanceof Subfield) { 86 | var field = this.stack.pop(); 87 | field.addSubfield(obj); 88 | this.stack.push(field); 89 | } else if (obj instanceof ControlField || obj instanceof DataField) { 90 | var record = this.stack.pop(); 91 | record.addVariableField(obj); 92 | this.stack.push(record); 93 | } else if (obj instanceof Record) { 94 | if (this.arrayStack.length == 0 || this.arrayStack[this.arrayStack.length-1] == '') { 95 | this.previous = ''; 96 | this.push(obj); 97 | } 98 | } 99 | }; 100 | 101 | MijParser.prototype.handleValue = function(value) { 102 | if (this.previous == 'ind1') { 103 | var field = this.stack.pop(); 104 | field.indicator1 = value; 105 | this.stack.push(field); 106 | } else if (this.previous == 'ind2') { 107 | var field = this.stack.pop(); 108 | field.indicator2 = value; 109 | this.stack.push(field); 110 | } else if (this.previous.length == 1) { 111 | var subfield = this.stack.pop(); 112 | subfield.data = value; 113 | this.stack.push(subfield); 114 | } else if (/\d{3}/.test(this.previous)) { 115 | var field = this.stack.pop(); 116 | field.data = value; 117 | this.stack.push(field); 118 | } else if (this.previous == 'leader') { 119 | var leader = new Leader(value); 120 | var record = this.stack.pop(); 121 | record.leader = leader; 122 | this.stack.push(record); 123 | } 124 | }; 125 | 126 | MijParser.prototype.handleKey = function (key){ 127 | this.previous = key; 128 | }; 129 | 130 | // Naive Stream API 131 | MijParser.prototype._transform = function (data, encoding, done) { 132 | try { 133 | this.stream.write(data); 134 | done(); 135 | } catch (e) { 136 | done(e); 137 | this.push(null); // Manually trigger and end, since we can't reliably do any more parsing 138 | } 139 | }; 140 | 141 | module.exports = MijParser; -------------------------------------------------------------------------------- /lib/parse/mrk_parser.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Transform = require('stream').Transform; 4 | var util = require('util'); 5 | var Util = require('../util'); 6 | var ControlField = require('../marc/control_field'); 7 | var DataField = require('../marc/data_field'); 8 | var Subfield = require('../marc/subfield'); 9 | var Record = require('../marc/record'); 10 | var Leader = require('../marc/leader'); 11 | 12 | var MrkParser = function (opts) { 13 | opts = opts || {}; 14 | opts.objectMode = true; // this has to be true. Emit only Record objects 15 | 16 | Transform.call(this, opts); 17 | 18 | this.spaceReplace = opts.spaceReplace || '\\'; 19 | this.lines = []; 20 | 21 | // position of cursor 22 | this.row = 0; 23 | this.col = 0; 24 | }; 25 | 26 | util.inherits(MrkParser, Transform); 27 | 28 | MrkParser.prototype._transform = function (chunk, encoding, callback) { 29 | if (typeof chunk == 'string' || chunk instanceof String) { 30 | chunk = new Buffer(chunk, encoding); 31 | } 32 | 33 | var lines, i; 34 | lines = (chunk.toString()).split('\n'); 35 | for(i = 0; i < lines.length; i++) { 36 | var line = lines[i].trim(); 37 | this.row += 1; 38 | if (!line) { 39 | continue; 40 | } 41 | if (line.substr(1, 3) === 'LDR') { 42 | if (this.lines.length > 0) { 43 | var record; 44 | try { 45 | record = this._parse(this.lines); 46 | } catch (err) { 47 | this.emit("error", err); 48 | } 49 | this.push(record); 50 | this.lines = []; 51 | } 52 | } 53 | this.lines.push(line); 54 | } 55 | return callback(); 56 | }; 57 | 58 | MrkParser.prototype._flush = function(callback) { 59 | if (this.lines.length > 0) { 60 | var record; 61 | try { 62 | record = this._parse(this.lines); 63 | } catch (err) { 64 | this.emit("error", err); 65 | } 66 | this.push(record); 67 | this.lines = []; 68 | } 69 | return callback(); 70 | }; 71 | 72 | MrkParser.prototype._parse = function (lines) { 73 | var record = new Record(); 74 | var spaceReplace = this.spaceReplace; 75 | lines.forEach(function(line) { 76 | var tag = line.substr(1, 3); 77 | if (tag === 'LDR') { 78 | record.leader = new Leader(line.substr(6)); 79 | } else { 80 | var re = new RegExp(spaceReplace.replace('\\', '\\\\'), 'g'); 81 | var field; 82 | if (Util.isControlField(tag)) { 83 | field = new ControlField(); 84 | field.data = line.substr(6).replace(re, ' '); 85 | } else { 86 | field = new DataField(); 87 | var data = line.substr(6); 88 | field.indicator1 = data.charAt(0).replace(re, ' '); 89 | field.indicator2 = data.charAt(1).replace(re, ' '); 90 | if (isNaN(field.indicator1)) { 91 | throw new MarcError("Wrong indicator format. It has to be a number or a space"); 92 | } 93 | if (isNaN(field.indicator2)) { 94 | throw new MarcError("Wrong indicator format. It has to be a number or a space"); 95 | } 96 | data.substr(3).split('$').forEach(function(sf) { 97 | field.addSubfield(new Subfield(sf.charAt(0), sf.substr(1))); 98 | }); 99 | } 100 | field.tag = tag; 101 | record.addVariableField(field); 102 | } 103 | }); 104 | return record; 105 | }; 106 | 107 | 108 | module.exports = MrkParser; 109 | -------------------------------------------------------------------------------- /lib/parse/text_parser.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Transform = require('stream').Transform; 4 | var util = require('util'); 5 | var Util = require('../util'); 6 | var ControlField = require('../marc/control_field'); 7 | var DataField = require('../marc/data_field'); 8 | var Subfield = require('../marc/subfield'); 9 | var Record = require('../marc/record'); 10 | var Leader = require('../marc/leader'); 11 | 12 | var TextParser = function (opts) { 13 | opts = opts || {}; 14 | opts.objectMode = true; // this has to be true. Emit only Record objects 15 | 16 | Transform.call(this, opts); 17 | 18 | this.lines = []; 19 | 20 | // position of cursor 21 | this.row = 0; 22 | this.col = 0; 23 | }; 24 | 25 | util.inherits(TextParser, Transform); 26 | 27 | TextParser.prototype._transform = function (chunk, encoding, callback) { 28 | if (typeof chunk == 'string' || chunk instanceof String) { 29 | chunk = new Buffer(chunk, encoding); 30 | } 31 | 32 | var lines, i; 33 | lines = (chunk.toString()).split('\n'); 34 | for(i = 0; i < lines.length; i++) { 35 | var line = lines[i].trim(); 36 | this.row += 1; 37 | if (!line) { 38 | continue; 39 | } 40 | if (line.substr(0, 3) === 'LDR') { 41 | if (this.lines.length > 0) { 42 | var record; 43 | try { 44 | record = this._parse(this.lines); 45 | } catch (err) { 46 | this.emit("error", err); 47 | } 48 | this.push(record); 49 | this.lines = []; 50 | } 51 | } 52 | this.lines.push(line); 53 | } 54 | return callback(); 55 | }; 56 | 57 | TextParser.prototype._flush = function(callback) { 58 | if (this.lines.length > 0) { 59 | var record; 60 | try { 61 | record = this._parse(this.lines); 62 | } catch (err) { 63 | this.emit("error", err); 64 | } 65 | this.push(record); 66 | this.lines = []; 67 | } 68 | return callback(); 69 | }; 70 | 71 | TextParser.prototype._parse = function (lines) { 72 | var record = new Record(); 73 | lines.forEach(function(line) { 74 | var tag = line.substr(0, 3); 75 | if (tag === 'LDR') { 76 | record.leader = new Leader(line.substr(7)); 77 | } else { 78 | var field; 79 | if (Util.isControlField(tag)) { 80 | field = new ControlField(); 81 | field.data = line.substr(7); 82 | } else { 83 | field = new DataField(); 84 | var data = line.substr(4); 85 | field.indicator1 = data.charAt(0); 86 | field.indicator2 = data.charAt(1); 87 | if (isNaN(field.indicator1)) { 88 | throw new MarcError("Wrong indicator format. It has to be a number or a space"); 89 | } 90 | if (isNaN(field.indicator2)) { 91 | throw new MarcError("Wrong indicator format. It has to be a number or a space"); 92 | } 93 | data.substr(4).split('$').forEach(function(sf) { 94 | field.addSubfield(new Subfield(sf.charAt(0), sf.substr(1))); 95 | }); 96 | } 97 | field.tag = tag; 98 | record.addVariableField(field); 99 | } 100 | }); 101 | return record; 102 | }; 103 | 104 | 105 | module.exports = TextParser; 106 | -------------------------------------------------------------------------------- /lib/transform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Record = require('./marc/record'); 4 | 5 | module.exports = function() { 6 | var callback, chunks, data, options, transformer; 7 | if (arguments.length === 3) { 8 | if (Array.isArray(arguments[0])) { 9 | data = arguments[0]; 10 | } else { 11 | data = [arguments[0]]; 12 | } 13 | options = arguments[1]; 14 | callback = arguments[2]; 15 | } else if (arguments.length === 2) { 16 | if (Array.isArray(arguments[0])) { 17 | data = arguments[0]; 18 | } else if (arguments[0] instanceof Record) { 19 | data = [arguments[0]]; 20 | } else { 21 | options = arguments[0]; 22 | } 23 | if (typeof arguments[1] === 'function') { 24 | callback = arguments[1]; 25 | } else { 26 | options = arguments[1]; 27 | } 28 | } else if (arguments.length === 1) { 29 | if (typeof arguments[0] === 'function') { 30 | callback = arguments[0]; 31 | } else if (Array.isArray(arguments[0])) { 32 | data = arguments[0]; 33 | } else { 34 | options = arguments[0]; 35 | } 36 | } 37 | if (options == null) { 38 | options = {}; 39 | } 40 | var transformers = { 41 | iso2709: './transform/iso2709_transformer', 42 | marc: './transform/iso2709_transformer', 43 | text: './transform/text_transformer', 44 | mrk: './transform/mrk_transformer', 45 | xml: './transform/marcxml_transformer', 46 | marcxml: './transform/marcxml_transformer', 47 | json: './transform/mij_transformer', 48 | mij: './transform/mij_transformer' 49 | }; 50 | 51 | var format = options.format || options.toFormat || 'iso2709'; 52 | var Transformer = require(transformers[format]); 53 | transformer = new Transformer(options); 54 | if (data) { 55 | process.nextTick(function() { 56 | var d, _i, _len; 57 | for (_i = 0, _len = data.length; _i < _len; _i++) { 58 | d = data[_i]; 59 | transformer.write(d); 60 | } 61 | return transformer.end(); 62 | }); 63 | } 64 | if (callback) { 65 | chunks = []; 66 | var handleReadable = function() { 67 | var chunk, _results; 68 | _results = []; 69 | while (chunk = transformer.read()) { 70 | _results.push(chunks.push(chunk)); 71 | } 72 | return _results; 73 | }; 74 | transformer.on('readable', handleReadable); 75 | transformer.once('error', function(err) { 76 | transformer.removeListener('readable', handleReadable); 77 | return callback(err); 78 | }); 79 | transformer.once('end', function() { 80 | transformer.removeListener('readable', handleReadable); 81 | return callback(null, chunks.join('')); 82 | }); 83 | } 84 | return transformer; 85 | }; -------------------------------------------------------------------------------- /lib/transform/iso2709_transformer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Transform = require('stream').Transform; 4 | var util = require('util'); 5 | var Record = require('../marc/record'); 6 | var Util = require('../util'); 7 | 8 | var Iso2709Transformer = function (opts) { 9 | opts = opts || {}; 10 | Transform.call(this, opts); 11 | opts.objectMode = true; 12 | this.objectMode = true; //opts.objectMode || false; 13 | this.encoding = opts.encoding; 14 | }; 15 | 16 | util.inherits(Iso2709Transformer, Transform); 17 | 18 | Iso2709Transformer.prototype._transform = function (record, encoding, callback) { 19 | this.push(record); 20 | return callback(); 21 | }; 22 | 23 | Iso2709Transformer.prototype.write = function(chunk, encoding, callback) { 24 | var record = chunk; 25 | try { 26 | chunk = record.marshal(); 27 | } catch (err) { 28 | callback(err); 29 | } 30 | return Transform.prototype.write.call(this, chunk, encoding, callback); 31 | }; 32 | 33 | module.exports = Iso2709Transformer; 34 | -------------------------------------------------------------------------------- /lib/transform/marcxml_transformer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Transform = require('stream').Transform; 4 | var util = require('util'); 5 | var Record = require('../marc/record'); 6 | var Util = require('../util'); 7 | var builder = require('xmlbuilder'); 8 | 9 | var MarcxmlTransformer = function (opts) { 10 | opts = opts || {}; 11 | Transform.call(this, opts); 12 | opts.objectMode = true; 13 | this.objectMode = true; //opts.objectMode || false; 14 | this.encoding = opts.encoding; 15 | this.starting = true; 16 | this.pretty = opts.pretty || true; 17 | this.indent = opts.indent || ' '; 18 | this.newline = opts.newline || '\n'; 19 | this.root = opts.root || true; 20 | this.declaration = opts.declaration || true; 21 | }; 22 | 23 | util.inherits(MarcxmlTransformer, Transform); 24 | 25 | MarcxmlTransformer.prototype._transform = function (record, encoding, callback) { 26 | this.push(record); 27 | return callback(); 28 | }; 29 | 30 | MarcxmlTransformer.prototype.write = function(record, encoding, callback) { 31 | var header = ''; 32 | if (this.starting) { 33 | this.starting = false; 34 | if (this.declaration) { 35 | header += '' + this.newline; 36 | } 37 | if (this.root) { 38 | header += ''; 39 | if (this.pretty) header += this.newline; 40 | } 41 | } 42 | var root = builder.create('record'); 43 | root.ele('leader', record.leader.marshal()); 44 | record.controlFields.forEach(function(field) { 45 | root.ele('controlfield', {'tag': field.tag}, field.data); 46 | }); 47 | record.dataFields.forEach(function(field) { 48 | var ele = root.ele('datafield', {'tag': field.tag, 'ind1': field.indicator1, 'ind2': field.indicator2}); 49 | field.subfields.forEach(function(subfield) { 50 | ele.ele('subfield', {'code': subfield.code}, subfield.data); 51 | }); 52 | }); 53 | var offset = this.root ? 1 : 0; 54 | var xmlstr = root.toString({pretty: this.pretty, indent: this.indent, offset: offset, newline: this.newline}); 55 | return Transform.prototype.write.call(this, header + xmlstr, encoding, callback); 56 | }; 57 | 58 | MarcxmlTransformer.prototype._flush = function(callback) { 59 | if (this.root) this.push("\n"); 60 | return callback(); 61 | }; 62 | 63 | module.exports = MarcxmlTransformer; 64 | -------------------------------------------------------------------------------- /lib/transform/mij_transformer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Transform = require('stream').Transform; 4 | var util = require('util'); 5 | var Record = require('../marc/record'); 6 | var Util = require('../util'); 7 | 8 | var MijTransformer = function (opts) { 9 | if (!(this instanceof MijTransformer)) return new MijTransformer(options); 10 | 11 | opts = opts || {}; 12 | Transform.call(this, opts); 13 | 14 | this.encoding = opts.encoding; 15 | this.asArray = opts.asArray || true; 16 | 17 | this.starting = true; 18 | }; 19 | 20 | util.inherits(MijTransformer, Transform); 21 | 22 | MijTransformer.prototype._transform = function (record, encoding, callback) { 23 | this.push(record); 24 | return callback(); 25 | }; 26 | 27 | MijTransformer.prototype.write = function(record, encoding, callback) { 28 | var header = ''; 29 | if (this.starting) { 30 | this.starting = false; 31 | if (this.asArray) { 32 | header = '['; 33 | } 34 | } else { 35 | header = ','; 36 | } 37 | var str = header; 38 | str += '{"leader":"' + record.leader.marshal() + '",'; 39 | str += '"fields":'; 40 | str += '['; 41 | var cfs = record.controlFields.map(function(field) { 42 | return '{"' + field.tag + '":"' + field.data + '"}'; 43 | }); 44 | str += cfs.join(','); 45 | var dfs = record.dataFields.map(function(field) { 46 | var sfs = field.subfields.map(function(subfield) { 47 | return '{"' + subfield.code + '":"' + subfield.data + '"}'; 48 | }); 49 | return '{"' + field.tag + '":{"subfields":[' + sfs.join(',') + '],"ind1":"' 50 | + field.indicator1 + '","ind2":"' + field.indicator2 + '"}}'; 51 | }); 52 | if (dfs.length > 0) { 53 | str += ',' 54 | } 55 | str += dfs.join(','); 56 | str += ']}'; 57 | return Transform.prototype.write.call(this, str, encoding, callback); 58 | }; 59 | 60 | MijTransformer.prototype._flush = function(callback) { 61 | if (this.asArray) this.push(']'); 62 | return callback(); 63 | }; 64 | 65 | module.exports = MijTransformer; 66 | -------------------------------------------------------------------------------- /lib/transform/mrk_transformer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Transform = require('stream').Transform; 4 | var util = require('util'); 5 | var os = require('os'); 6 | var Util = require('../util'); 7 | var Record = require('../marc/record'); 8 | var Leader = require('../marc/leader'); 9 | var ControlField = require('../marc/control_field'); 10 | var DataField = require('../marc/data_field'); 11 | var Subfield = require('../marc/subfield'); 12 | 13 | var MrkTransformer = function (opts) { 14 | opts = opts || {}; 15 | this.objectMode = opts.objectMode || false; 16 | this.encoding = opts.encoding || 'utf8'; 17 | this.spaceReplace = opts.spaceReplace || '\\'; 18 | 19 | Transform.call(this, opts); 20 | }; 21 | 22 | util.inherits(MrkTransformer, Transform); 23 | 24 | MrkTransformer.prototype._transform = function (record, encoding, callback) { 25 | this.push(record); 26 | return callback(); 27 | }; 28 | 29 | MrkTransformer.prototype.write = function(chunk, encoding, callback) { 30 | var record = chunk; 31 | var buffers = []; 32 | if (typeof encoding === 'undefined') { 33 | encoding = this.encoding; 34 | } 35 | try { 36 | buffers.push(new Buffer("=LDR ", encoding)); 37 | buffers.push(new Buffer(record.leader.marshal(), encoding)); 38 | buffers.push(new Buffer(os.EOL, encoding)); 39 | 40 | var spaceReplace = this.spaceReplace; 41 | 42 | // write directory 43 | record.variableFields.forEach(function (field) { 44 | buffers.push(new Buffer("=" + field.tag + " ", encoding)); 45 | if (field instanceof ControlField) { 46 | buffers.push(new Buffer(field.data.replace(/ /g, spaceReplace), encoding)); 47 | buffers.push(new Buffer(os.EOL)); 48 | } else { 49 | buffers.push(new Buffer(field.indicator1.replace(' ', spaceReplace), encoding)); 50 | buffers.push(new Buffer(field.indicator2.replace(' ', spaceReplace), encoding)); 51 | field.subfields.forEach(function (subfield) { 52 | buffers.push(new Buffer("$" + subfield.code, encoding)); 53 | buffers.push(new Buffer(subfield.data, encoding)); 54 | }); 55 | buffers.push(new Buffer(os.EOL)); 56 | } 57 | }); 58 | 59 | buffers.push(new Buffer(os.EOL)); 60 | chunk = Buffer.concat(buffers); 61 | } catch (err) { 62 | console.log('error', err.stack); 63 | callback(err); 64 | } 65 | return Transform.prototype.write.call(this, chunk, encoding, callback); 66 | }; 67 | 68 | module.exports = MrkTransformer; 69 | -------------------------------------------------------------------------------- /lib/transform/text_transformer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Transform = require('stream').Transform; 4 | var util = require('util'); 5 | var os = require('os'); 6 | var Util = require('../util'); 7 | var Record = require('../marc/record'); 8 | var Leader = require('../marc/leader'); 9 | var ControlField = require('../marc/control_field'); 10 | var DataField = require('../marc/data_field'); 11 | var Subfield = require('../marc/subfield'); 12 | 13 | var TextTransformer = function (opts) { 14 | opts = opts || {}; 15 | this.objectMode = opts.objectMode || false; 16 | this.encoding = opts.encoding || 'utf8'; 17 | 18 | Transform.call(this, opts); 19 | }; 20 | 21 | util.inherits(TextTransformer, Transform); 22 | 23 | TextTransformer.prototype._transform = function (record, encoding, callback) { 24 | this.push(record); 25 | return callback(); 26 | }; 27 | 28 | TextTransformer.prototype.write = function(chunk, encoding, callback) { 29 | var record = chunk; 30 | var buffers = []; 31 | if (typeof encoding === 'undefined') { 32 | encoding = this.encoding; 33 | } 34 | try { 35 | buffers.push(new Buffer("LDR ", encoding)); 36 | buffers.push(new Buffer(record.leader.marshal(), encoding)); 37 | buffers.push(new Buffer(os.EOL, encoding)); 38 | 39 | // write directory 40 | record.variableFields.forEach(function (field) { 41 | buffers.push(new Buffer(field.tag + " ", encoding)); 42 | if (field instanceof ControlField) { 43 | buffers.push(new Buffer(" ", encoding)); 44 | buffers.push(new Buffer(field.data, encoding)); 45 | buffers.push(new Buffer(os.EOL)); 46 | } else { 47 | buffers.push(new Buffer(field.indicator1, encoding)); 48 | buffers.push(new Buffer(field.indicator2, encoding)); 49 | buffers.push(new Buffer(" ", encoding)); 50 | field.subfields.forEach(function (subfield) { 51 | buffers.push(new Buffer("$" + subfield.code, encoding)); 52 | buffers.push(new Buffer(subfield.data, encoding)); 53 | }); 54 | buffers.push(new Buffer(os.EOL)); 55 | } 56 | }); 57 | 58 | buffers.push(new Buffer(os.EOL)); 59 | chunk = Buffer.concat(buffers); 60 | } catch (err) { 61 | console.log('error', err.stack); 62 | callback(err); 63 | } 64 | return Transform.prototype.write.call(this, chunk, encoding, callback); 65 | }; 66 | 67 | module.exports = TextTransformer; 68 | -------------------------------------------------------------------------------- /lib/util.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.isInt = function (val) { 4 | return /^\d+$/.test(val); 5 | }; 6 | 7 | /** A formatter that pad 0 in front of an integer if the number of 8 | * digit is less than numDigits (default numDigits is 5). If 9 | * the number of digits in the integer is greater than numDigits, it will 10 | * return a string of length numDigits and all digits are 9s. 11 | */ 12 | exports.formatInteger = function (val, numDigits) { 13 | numDigits = typeof numDigits !== 'undefined' ? numDigits : 5; 14 | var s = val + ""; 15 | var v = ""; 16 | if (s.length > numDigits) { 17 | while (v.length < numDigits) v = '9' + v; 18 | } else { 19 | v = s; 20 | while (v.length < numDigits) v = '0' + v; 21 | } 22 | return v; 23 | }; 24 | 25 | exports.isControlField = function (tag) { 26 | if (tag.length == 3 && tag.charAt(0) == '0' && tag.charAt(1) == '0' && tag.charAt(2) >= '0' && tag.charAt(2) <= '9')// if (Integer.parseInt(tag) < 10) 27 | return true; 28 | return false; 29 | }; 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "marc4js", 3 | "version": "0.0.10", 4 | "description": "a node.js module for handling MARC data", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha test --recursive --require test/helpers/chai.js " 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/jiaola/marc4js.git" 12 | }, 13 | "author": "Dazhi Jiao ", 14 | "license": "Apache", 15 | "bugs": { 16 | "url": "https://github.com/jiaola/marc4js/issues" 17 | }, 18 | "homepage": "https://github.com/jiaola/marc4js", 19 | "engines": { 20 | "node": ">= 0.10.0" 21 | }, 22 | "dependencies": { 23 | "clarinet": "^0.10.0", 24 | "sax": "^0.6.1", 25 | "xmlbuilder": "^13.0.2" 26 | }, 27 | "devDependencies": { 28 | "chai": "^1.10.0", 29 | "marc8": "0.0.4", 30 | "mocha": "latest" 31 | }, 32 | "keywords": [ 33 | "marc4js", 34 | "marc", 35 | "node.js", 36 | "javascript", 37 | "parser", 38 | "marcxml", 39 | "iso2709" 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /test/data/PGA_2records.mrc: -------------------------------------------------------------------------------- 1 | 00307nam a2200085Ia 45e0008004100000100002400041245005100065500005300116856005100169080906s9999 xx 000 0 und d1 aBiggers, Earl Derr.10aCharlie Chan Carries Onh[electronic resource] aAn ebook provided by Project Gutenberg Australia40uhttp://gutenberg.net.au/ebooks07/0700761h.html00287nam a2200085Ia 45e0008004100000100002000041245003500061500005300096856005100149080906s9999 xx 000 0 und d1 aWallace, Edgar.10aSandersh[electronic resource] aAn ebook provided by Project Gutenberg Australia40uhttp://gutenberg.net.au/ebooks07/0700771h.html -------------------------------------------------------------------------------- /test/data/PGA_2records.mrk: -------------------------------------------------------------------------------- 1 | =LDR 00307nam a2200085Ia 45e0 2 | =008 080906s9999\\\\xx\\\\\\\\\\\\000\0\und\d 3 | =100 1\$aBiggers, Earl Derr. 4 | =245 10$aCharlie Chan Carries On$h[electronic resource] 5 | =500 \\$aAn ebook provided by Project Gutenberg Australia 6 | =856 40$uhttp://gutenberg.net.au/ebooks07/0700761h.html 7 | 8 | =LDR 00287nam a2200085Ia 45e0 9 | =008 080906s9999\\\\xx\\\\\\\\\\\\000\0\und\d 10 | =100 1\$aWallace, Edgar. 11 | =245 10$aSanders$h[electronic resource] 12 | =500 \\$aAn ebook provided by Project Gutenberg Australia 13 | =856 40$uhttp://gutenberg.net.au/ebooks07/0700771h.html 14 | -------------------------------------------------------------------------------- /test/data/PGA_2records.txt: -------------------------------------------------------------------------------- 1 | LDR 00307nam a2200085Ia 45e0 2 | 008 080906s9999 xx 000 0 und d 3 | 100 1 $aBiggers, Earl Derr. 4 | 245 10 $aCharlie Chan Carries On$h[electronic resource] 5 | 500 $aAn ebook provided by Project Gutenberg Australia 6 | 856 40 $uhttp://gutenberg.net.au/ebooks07/0700761h.html 7 | 8 | LDR 00287nam a2200085Ia 45e0 9 | 008 080906s9999 xx 000 0 und d 10 | 100 1 $aWallace, Edgar. 11 | 245 10 $aSanders$h[electronic resource] 12 | 500 $aAn ebook provided by Project Gutenberg Australia 13 | 856 40 $uhttp://gutenberg.net.au/ebooks07/0700771h.html 14 | -------------------------------------------------------------------------------- /test/data/README.md: -------------------------------------------------------------------------------- 1 | # Samples 2 | 3 | Here are some sample marc file used in tests. Files are downloaded from the following: 4 | 5 | * [Project Gutenberg Australia](http://gutenberg.net.au/marc.html) 6 | * [E-Discover the Classics](http://www.clicweb.org/e-discover-home) 7 | * [LOC MARCXML Example Documents](http://www.loc.gov/standards/marcxml/) 8 | * [MARC4J](https://github.com/marc4j/marc4j) 9 | * [ruby-marc](https://github.com/ruby-marc/ruby-marc) 10 | * [pymarc](https://github.com/edsu/pymarc) 11 | 12 | -------------------------------------------------------------------------------- /test/data/collection-error.mrc: -------------------------------------------------------------------------------- 1 | 00998njm a22002417a 45000010008000000030004000080050017000120070014000290080041000430100017000840280019001010400013001200500018001332450046001512600041001973000049002385110034002875050136003215000018004576500021004756500030004967000030005265637241DLC19920826084036.0sdubumennmplu910926s1957 nyuuun eng  a 91758335 00a1259bAtlantic aDLCcDLC00aAtlantic 125904aThe Great Ray Charlesh[sound recording]. aNew York, N.Y. :bAtlantic,c[1957?] a1 sound disc :banalog, 33 1/3 rpm ;c12 in.0 aRay Charles, piano & celeste.0 aThe Ray -- My melancholy baby -- Black coffee -- There's no you -- Doodlin' -- Sweet sixteen bars -- I surrender dear -- Undecided. aBrief record. 0aJazzy1951-1960. 0aPiano with jazz ensemble.1 aCharles, Ray,d1930-4prf01832cmmaa2200349 a 45000010009000000050017000090070007000260080041000339060045000749250049001199550123001680100017002910350023003080400023003310420014003540430021003680500012003890820014004012450038004152560019004532600053004725380030005255000053005555200563006086100035011716100055012066100050012616100046013117100026013578560030013838560069014131214912020001005175443.0cr |||000407m19949999dcu g m eng d a0bibcccopycatd1encipf20gy-gencompf0 aundeterminedxweb preservation project (wpp) avb07 (stars done) 08-19-00 to HLCD lk00; AA3s lk29 received for subject Aug 25, 2000; to DEWEY 08-25-00; aa11 08-28-00 a 00530046  a(OCoLC)ocm44279786 aIEUcIEUdN@FdDLC alccopycat an-us-dcan-us---00aF204.W510a975.321304aThe White Househ[computer file]. aComputer data. aWashington, D.C. :bWhite House Web Team,c1994- aMode of access: Internet. aTitle from home page as viewed on Aug. 19, 2000.8 aFeatures the White House. Highlights the Executive Office of the President, which includes senior policy advisors and offices responsible for the President's correspondence and communications, the Office of the Vice President, and the Office of the First Lady. Posts contact information via mailing address, telephone and fax numbers, and e-mail. Contains the Interactive Citizens' Handbook with information on health, travel and tourism, education and training, and housing. Provides a tour and the history of the White House. Links to White House for Kids.20aWhite House (Washington, D.C.)10aUnited States.bExecutive Office of the President.10aUnited States.bOffice of the Vice President.10aUnited States.bOffice of the First Lady.2 aWhite House Web Team.40uhttp://www.whitehouse.gov40uhttp://lcweb.loc.gov/staff/wpp/whitehouse.htmlzWeb site archive -------------------------------------------------------------------------------- /test/data/collection.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "leader":"00925njm 22002777a 4500", 4 | "fields":[ 5 | { 6 | "001":"5637241" 7 | }, 8 | { 9 | "003":"DLC" 10 | }, 11 | { 12 | "005":"19920826084036.0" 13 | }, 14 | { 15 | "007":"sdubumennmplu" 16 | }, 17 | { 18 | "008":"910926s1957 nyuuun eng " 19 | }, 20 | { 21 | "010":{ 22 | "subfields":[ 23 | { 24 | "a":" 91758335 " 25 | } 26 | ], 27 | "ind1":" ", 28 | "ind2":" " 29 | } 30 | }, 31 | { 32 | "028":{ 33 | "subfields":[ 34 | { 35 | "a":"1259" 36 | }, 37 | { 38 | "b":"Atlantic" 39 | } 40 | ], 41 | "ind1":"0", 42 | "ind2":"0" 43 | } 44 | }, 45 | { 46 | "040":{ 47 | "subfields":[ 48 | { 49 | "a":"DLC" 50 | }, 51 | { 52 | "c":"DLC" 53 | } 54 | ], 55 | "ind1":" ", 56 | "ind2":" " 57 | } 58 | }, 59 | { 60 | "050":{ 61 | "subfields":[ 62 | { 63 | "a":"Atlantic 1259" 64 | } 65 | ], 66 | "ind1":"0", 67 | "ind2":"0" 68 | } 69 | }, 70 | { 71 | "245":{ 72 | "subfields":[ 73 | { 74 | "a":"The Great Ray Charles" 75 | }, 76 | { 77 | "h":"[sound recording]." 78 | } 79 | ], 80 | "ind1":"0", 81 | "ind2":"4" 82 | } 83 | }, 84 | { 85 | "260":{ 86 | "subfields":[ 87 | { 88 | "a":"New York, N.Y. :" 89 | }, 90 | { 91 | "b":"Atlantic," 92 | }, 93 | { 94 | "c":"[1957?]" 95 | } 96 | ], 97 | "ind1":" ", 98 | "ind2":" " 99 | } 100 | }, 101 | { 102 | "300":{ 103 | "subfields":[ 104 | { 105 | "a":"1 sound disc :" 106 | }, 107 | { 108 | "b":"analog, 33 1/3 rpm ;" 109 | }, 110 | { 111 | "c":"12 in." 112 | } 113 | ], 114 | "ind1":" ", 115 | "ind2":" " 116 | } 117 | }, 118 | { 119 | "511":{ 120 | "subfields":[ 121 | { 122 | "a":"Ray Charles, piano & celeste." 123 | } 124 | ], 125 | "ind1":"0", 126 | "ind2":" " 127 | } 128 | }, 129 | { 130 | "505":{ 131 | "subfields":[ 132 | { 133 | "a":"The Ray -- My melancholy baby -- Black coffee -- There's no you -- Doodlin' -- Sweet sixteen bars -- I surrender dear -- Undecided." 134 | } 135 | ], 136 | "ind1":"0", 137 | "ind2":" " 138 | } 139 | }, 140 | { 141 | "500":{ 142 | "subfields":[ 143 | { 144 | "a":"Brief record." 145 | } 146 | ], 147 | "ind1":" ", 148 | "ind2":" " 149 | } 150 | }, 151 | { 152 | "650":{ 153 | "subfields":[ 154 | { 155 | "a":"Jazz" 156 | }, 157 | { 158 | "y":"1951-1960." 159 | } 160 | ], 161 | "ind1":" ", 162 | "ind2":"0" 163 | } 164 | }, 165 | { 166 | "650":{ 167 | "subfields":[ 168 | { 169 | "a":"Piano with jazz ensemble." 170 | } 171 | ], 172 | "ind1":" ", 173 | "ind2":"0" 174 | } 175 | }, 176 | { 177 | "700":{ 178 | "subfields":[ 179 | { 180 | "a":"Charles, Ray," 181 | }, 182 | { 183 | "d":"1930-" 184 | }, 185 | { 186 | "4":"prf" 187 | } 188 | ], 189 | "ind1":"1", 190 | "ind2":" " 191 | } 192 | } 193 | ] 194 | }, 195 | { 196 | "leader":"01832cmma 2200349 a 4500", 197 | "fields":[ 198 | { 199 | "001":"12149120" 200 | }, 201 | { 202 | "005":"20001005175443.0" 203 | }, 204 | { 205 | "007":"cr |||" 206 | }, 207 | { 208 | "008":"000407m19949999dcu g m eng d" 209 | }, 210 | { 211 | "906":{ 212 | "subfields":[ 213 | { 214 | "a":"0" 215 | }, 216 | { 217 | "b":"ibc" 218 | }, 219 | { 220 | "c":"copycat" 221 | }, 222 | { 223 | "d":"1" 224 | }, 225 | { 226 | "e":"ncip" 227 | }, 228 | { 229 | "f":"20" 230 | }, 231 | { 232 | "g":"y-gencompf" 233 | } 234 | ], 235 | "ind1":" ", 236 | "ind2":" " 237 | } 238 | }, 239 | { 240 | "925":{ 241 | "subfields":[ 242 | { 243 | "a":"undetermined" 244 | }, 245 | { 246 | "x":"web preservation project (wpp)" 247 | } 248 | ], 249 | "ind1":"0", 250 | "ind2":" " 251 | } 252 | }, 253 | { 254 | "955":{ 255 | "subfields":[ 256 | { 257 | "a":"vb07 (stars done) 08-19-00 to HLCD lk00; AA3s lk29 received for subject Aug 25, 2000; to DEWEY 08-25-00; aa11 08-28-00" 258 | } 259 | ], 260 | "ind1":" ", 261 | "ind2":" " 262 | } 263 | }, 264 | { 265 | "010":{ 266 | "subfields":[ 267 | { 268 | "a":" 00530046 " 269 | } 270 | ], 271 | "ind1":" ", 272 | "ind2":" " 273 | } 274 | }, 275 | { 276 | "035":{ 277 | "subfields":[ 278 | { 279 | "a":"(OCoLC)ocm44279786" 280 | } 281 | ], 282 | "ind1":" ", 283 | "ind2":" " 284 | } 285 | }, 286 | { 287 | "040":{ 288 | "subfields":[ 289 | { 290 | "a":"IEU" 291 | }, 292 | { 293 | "c":"IEU" 294 | }, 295 | { 296 | "d":"N@F" 297 | }, 298 | { 299 | "d":"DLC" 300 | } 301 | ], 302 | "ind1":" ", 303 | "ind2":" " 304 | } 305 | }, 306 | { 307 | "042":{ 308 | "subfields":[ 309 | { 310 | "a":"lccopycat" 311 | } 312 | ], 313 | "ind1":" ", 314 | "ind2":" " 315 | } 316 | }, 317 | { 318 | "043":{ 319 | "subfields":[ 320 | { 321 | "a":"n-us-dc" 322 | }, 323 | { 324 | "a":"n-us---" 325 | } 326 | ], 327 | "ind1":" ", 328 | "ind2":" " 329 | } 330 | }, 331 | { 332 | "050":{ 333 | "subfields":[ 334 | { 335 | "a":"F204.W5" 336 | } 337 | ], 338 | "ind1":"0", 339 | "ind2":"0" 340 | } 341 | }, 342 | { 343 | "082":{ 344 | "subfields":[ 345 | { 346 | "a":"975.3" 347 | }, 348 | { 349 | "2":"13" 350 | } 351 | ], 352 | "ind1":"1", 353 | "ind2":"0" 354 | } 355 | }, 356 | { 357 | "245":{ 358 | "subfields":[ 359 | { 360 | "a":"The White House" 361 | }, 362 | { 363 | "h":"[computer file]." 364 | } 365 | ], 366 | "ind1":"0", 367 | "ind2":"4" 368 | } 369 | }, 370 | { 371 | "256":{ 372 | "subfields":[ 373 | { 374 | "a":"Computer data." 375 | } 376 | ], 377 | "ind1":" ", 378 | "ind2":" " 379 | } 380 | }, 381 | { 382 | "260":{ 383 | "subfields":[ 384 | { 385 | "a":"Washington, D.C. :" 386 | }, 387 | { 388 | "b":"White House Web Team," 389 | }, 390 | { 391 | "c":"1994-" 392 | } 393 | ], 394 | "ind1":" ", 395 | "ind2":" " 396 | } 397 | }, 398 | { 399 | "538":{ 400 | "subfields":[ 401 | { 402 | "a":"Mode of access: Internet." 403 | } 404 | ], 405 | "ind1":" ", 406 | "ind2":" " 407 | } 408 | }, 409 | { 410 | "500":{ 411 | "subfields":[ 412 | { 413 | "a":"Title from home page as viewed on Aug. 19, 2000." 414 | } 415 | ], 416 | "ind1":" ", 417 | "ind2":" " 418 | } 419 | }, 420 | { 421 | "520":{ 422 | "subfields":[ 423 | { 424 | "a":"Features the White House. Highlights the Executive Office of the President, which includes senior policy advisors and offices responsible for the President's correspondence and communications, the Office of the Vice President, and the Office of the First Lady. Posts contact information via mailing address, telephone and fax numbers, and e-mail. Contains the Interactive Citizens' Handbook with information on health, travel and tourism, education and training, and housing. Provides a tour and the history of the White House. Links to White House for Kids." 425 | } 426 | ], 427 | "ind1":"8", 428 | "ind2":" " 429 | } 430 | }, 431 | { 432 | "610":{ 433 | "subfields":[ 434 | { 435 | "a":"White House (Washington, D.C.)" 436 | } 437 | ], 438 | "ind1":"2", 439 | "ind2":"0" 440 | } 441 | }, 442 | { 443 | "610":{ 444 | "subfields":[ 445 | { 446 | "a":"United States." 447 | }, 448 | { 449 | "b":"Executive Office of the President." 450 | } 451 | ], 452 | "ind1":"1", 453 | "ind2":"0" 454 | } 455 | }, 456 | { 457 | "610":{ 458 | "subfields":[ 459 | { 460 | "a":"United States." 461 | }, 462 | { 463 | "b":"Office of the Vice President." 464 | } 465 | ], 466 | "ind1":"1", 467 | "ind2":"0" 468 | } 469 | }, 470 | { 471 | "610":{ 472 | "subfields":[ 473 | { 474 | "a":"United States." 475 | }, 476 | { 477 | "b":"Office of the First Lady." 478 | } 479 | ], 480 | "ind1":"1", 481 | "ind2":"0" 482 | } 483 | }, 484 | { 485 | "710":{ 486 | "subfields":[ 487 | { 488 | "a":"White House Web Team." 489 | } 490 | ], 491 | "ind1":"2", 492 | "ind2":" " 493 | } 494 | }, 495 | { 496 | "856":{ 497 | "subfields":[ 498 | { 499 | "u":"http://www.whitehouse.gov" 500 | } 501 | ], 502 | "ind1":"4", 503 | "ind2":"0" 504 | } 505 | }, 506 | { 507 | "856":{ 508 | "subfields":[ 509 | { 510 | "u":"http://lcweb.loc.gov/staff/wpp/whitehouse.html" 511 | }, 512 | { 513 | "z":"Web site archive" 514 | } 515 | ], 516 | "ind1":"4", 517 | "ind2":"0" 518 | } 519 | } 520 | ] 521 | } 522 | ] -------------------------------------------------------------------------------- /test/data/collection.mrc: -------------------------------------------------------------------------------- 1 | 00798njm a22002417a 45000010008000000030004000080050017000120070014000290080041000430100017000840280019001010400013001200500018001332450046001512600041001973000049002385110034002875050136003215000018004576500021004756500030004967000030005265637241DLC19920826084036.0sdubumennmplu910926s1957 nyuuun eng  a 91758335 00a1259bAtlantic aDLCcDLC00aAtlantic 125904aThe Great Ray Charlesh[sound recording]. aNew York, N.Y. :bAtlantic,c[1957?] a1 sound disc :banalog, 33 1/3 rpm ;c12 in.0 aRay Charles, piano & celeste.0 aThe Ray -- My melancholy baby -- Black coffee -- There's no you -- Doodlin' -- Sweet sixteen bars -- I surrender dear -- Undecided. aBrief record. 0aJazzy1951-1960. 0aPiano with jazz ensemble.1 aCharles, Ray,d1930-4prf01832cmmaa2200349 a 45000010009000000050017000090070007000260080041000339060045000749250049001199550123001680100017002910350023003080400023003310420014003540430021003680500012003890820014004012450038004152560019004532600053004725380030005255000053005555200563006086100035011716100055012066100050012616100046013117100026013578560030013838560069014131214912020001005175443.0cr |||000407m19949999dcu g m eng d a0bibcccopycatd1encipf20gy-gencompf0 aundeterminedxweb preservation project (wpp) avb07 (stars done) 08-19-00 to HLCD lk00; AA3s lk29 received for subject Aug 25, 2000; to DEWEY 08-25-00; aa11 08-28-00 a 00530046  a(OCoLC)ocm44279786 aIEUcIEUdN@FdDLC alccopycat an-us-dcan-us---00aF204.W510a975.321304aThe White Househ[computer file]. aComputer data. aWashington, D.C. :bWhite House Web Team,c1994- aMode of access: Internet. aTitle from home page as viewed on Aug. 19, 2000.8 aFeatures the White House. Highlights the Executive Office of the President, which includes senior policy advisors and offices responsible for the President's correspondence and communications, the Office of the Vice President, and the Office of the First Lady. Posts contact information via mailing address, telephone and fax numbers, and e-mail. Contains the Interactive Citizens' Handbook with information on health, travel and tourism, education and training, and housing. Provides a tour and the history of the White House. Links to White House for Kids.20aWhite House (Washington, D.C.)10aUnited States.bExecutive Office of the President.10aUnited States.bOffice of the Vice President.10aUnited States.bOffice of the First Lady.2 aWhite House Web Team.40uhttp://www.whitehouse.gov40uhttp://lcweb.loc.gov/staff/wpp/whitehouse.htmlzWeb site archive -------------------------------------------------------------------------------- /test/data/collection.mrk: -------------------------------------------------------------------------------- 1 | =LDR 00798njm a22002417a 4500 2 | =001 5637241 3 | =003 DLC 4 | =005 19920826084036.0 5 | =007 sdubumennmplu 6 | =008 910926s1957\\\\nyuuun\\\\\\\\\\\\\\eng\\ 7 | =010 \\$a 91758335 8 | =028 00$a1259$bAtlantic 9 | =040 \\$aDLC$cDLC 10 | =050 00$aAtlantic 1259 11 | =245 04$aThe Great Ray Charles$h[sound recording]. 12 | =260 \\$aNew York, N.Y. :$bAtlantic,$c[1957?] 13 | =300 \\$a1 sound disc :$banalog, 33 1/3 rpm ;$c12 in. 14 | =511 0\$aRay Charles, piano & celeste. 15 | =505 0\$aThe Ray -- My melancholy baby -- Black coffee -- There's no you -- Doodlin' -- Sweet sixteen bars -- I surrender dear -- Undecided. 16 | =500 \\$aBrief record. 17 | =650 \0$aJazz$y1951-1960. 18 | =650 \0$aPiano with jazz ensemble. 19 | =700 1\$aCharles, Ray,$d1930-$4prf 20 | 21 | 22 | 23 | =LDR 01832cmmaa2200349 a 4500 24 | =001 12149120 25 | =005 20001005175443.0 26 | =007 cr\||| 27 | =008 000407m19949999dcu\\\\g\\\m\\\\\\\\eng\d 28 | =906 \\$a0$bibc$ccopycat$d1$encip$f20$gy-gencompf 29 | =925 0\$aundetermined$xweb preservation project (wpp) 30 | =955 \\$avb07 (stars done) 08-19-00 to HLCD lk00; AA3s lk29 received for subject Aug 25, 2000; to DEWEY 08-25-00; aa11 08-28-00 31 | =010 \\$a 00530046 32 | =035 \\$a(OCoLC)ocm44279786 33 | =040 \\$aIEU$cIEU$dN@F$dDLC 34 | =042 \\$alccopycat 35 | =043 \\$an-us-dc$an-us--- 36 | =050 00$aF204.W5 37 | =082 10$a975.3$213 38 | =245 04$aThe White House$h[computer file]. 39 | =256 \\$aComputer data. 40 | =260 \\$aWashington, D.C. :$bWhite House Web Team,$c1994- 41 | =538 \\$aMode of access: Internet. 42 | =500 \\$aTitle from home page as viewed on Aug. 19, 2000. 43 | =520 8\$aFeatures the White House. Highlights the Executive Office of the President, which includes senior policy advisors and offices responsible for the President's correspondence and communications, the Office of the Vice President, and the Office of the First Lady. Posts contact information via mailing address, telephone and fax numbers, and e-mail. Contains the Interactive Citizens' Handbook with information on health, travel and tourism, education and training, and housing. Provides a tour and the history of the White House. Links to White House for Kids. 44 | =610 20$aWhite House (Washington, D.C.) 45 | =610 10$aUnited States.$bExecutive Office of the President. 46 | =610 10$aUnited States.$bOffice of the Vice President. 47 | =610 10$aUnited States.$bOffice of the First Lady. 48 | =710 2\$aWhite House Web Team. 49 | =856 40$uhttp://www.whitehouse.gov 50 | =856 40$uhttp://lcweb.loc.gov/staff/wpp/whitehouse.html$zWeb site archive 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /test/data/collection.txt: -------------------------------------------------------------------------------- 1 | LDR 00798njm a22002417a 4500 2 | 001 5637241 3 | 003 DLC 4 | 005 19920826084036.0 5 | 007 sdubumennmplu 6 | 008 910926s1957 nyuuun eng 7 | 010 $a 91758335 8 | 028 00 $a1259$bAtlantic 9 | 040 $aDLC$cDLC 10 | 050 00 $aAtlantic 1259 11 | 245 04 $aThe Great Ray Charles$h[sound recording]. 12 | 260 $aNew York, N.Y. :$bAtlantic,$c[1957?] 13 | 300 $a1 sound disc :$banalog, 33 1/3 rpm ;$c12 in. 14 | 511 0 $aRay Charles, piano & celeste. 15 | 505 0 $aThe Ray -- My melancholy baby -- Black coffee -- There's no you -- Doodlin' -- Sweet sixteen bars -- I surrender dear -- Undecided. 16 | 500 $aBrief record. 17 | 650 0 $aJazz$y1951-1960. 18 | 650 0 $aPiano with jazz ensemble. 19 | 700 1 $aCharles, Ray,$d1930-$4prf 20 | 21 | LDR 01832cmmaa2200349 a 4500 22 | 001 12149120 23 | 005 20001005175443.0 24 | 007 cr ||| 25 | 008 000407m19949999dcu g m eng d 26 | 906 $a0$bibc$ccopycat$d1$encip$f20$gy-gencompf 27 | 925 0 $aundetermined$xweb preservation project (wpp) 28 | 955 $avb07 (stars done) 08-19-00 to HLCD lk00; AA3s lk29 received for subject Aug 25, 2000; to DEWEY 08-25-00; aa11 08-28-00 29 | 010 $a 00530046 30 | 035 $a(OCoLC)ocm44279786 31 | 040 $aIEU$cIEU$dN@F$dDLC 32 | 042 $alccopycat 33 | 043 $an-us-dc$an-us--- 34 | 050 00 $aF204.W5 35 | 082 10 $a975.3$213 36 | 245 04 $aThe White House$h[computer file]. 37 | 256 $aComputer data. 38 | 260 $aWashington, D.C. :$bWhite House Web Team,$c1994- 39 | 538 $aMode of access: Internet. 40 | 500 $aTitle from home page as viewed on Aug. 19, 2000. 41 | 520 8 $aFeatures the White House. Highlights the Executive Office of the President, which includes senior policy advisors and offices responsible for the President's correspondence and communications, the Office of the Vice President, and the Office of the First Lady. Posts contact information via mailing address, telephone and fax numbers, and e-mail. Contains the Interactive Citizens' Handbook with information on health, travel and tourism, education and training, and housing. Provides a tour and the history of the White House. Links to White House for Kids. 42 | 610 20 $aWhite House (Washington, D.C.) 43 | 610 10 $aUnited States.$bExecutive Office of the President. 44 | 610 10 $aUnited States.$bOffice of the Vice President. 45 | 610 10 $aUnited States.$bOffice of the First Lady. 46 | 710 2 $aWhite House Web Team. 47 | 856 40 $uhttp://www.whitehouse.gov 48 | 856 40 $uhttp://lcweb.loc.gov/staff/wpp/whitehouse.html$zWeb site archive 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /test/data/collection.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 00925njm 22002777a 4500 7 | 5637241 8 | DLC 9 | 19920826084036.0 10 | sdubumennmplu 11 | 910926s1957 nyuuun eng 12 | 13 | 91758335 14 | 15 | 16 | 1259 17 | Atlantic 18 | 19 | 20 | DLC 21 | DLC 22 | 23 | 24 | Atlantic 1259 25 | 26 | 27 | The Great Ray Charles 28 | [sound recording]. 29 | 30 | 31 | New York, N.Y. : 32 | Atlantic, 33 | [1957?] 34 | 35 | 36 | 1 sound disc : 37 | analog, 33 1/3 rpm ; 38 | 12 in. 39 | 40 | 41 | Ray Charles, piano & celeste. 42 | 43 | 44 | The Ray -- My melancholy baby -- Black coffee -- There's no you -- Doodlin' -- Sweet sixteen bars -- I surrender dear -- Undecided. 45 | 46 | 47 | Brief record. 48 | 49 | 50 | Jazz 51 | 1951-1960. 52 | 53 | 54 | Piano with jazz ensemble. 55 | 56 | 57 | Charles, Ray, 58 | 1930- 59 | prf 60 | 61 | 62 | 63 | 01832cmma 2200349 a 4500 64 | 12149120 65 | 20001005175443.0 66 | cr ||| 67 | 000407m19949999dcu g m eng d 68 | 69 | 0 70 | ibc 71 | copycat 72 | 1 73 | ncip 74 | 20 75 | y-gencompf 76 | 77 | 78 | undetermined 79 | web preservation project (wpp) 80 | 81 | 82 | vb07 (stars done) 08-19-00 to HLCD lk00; AA3s lk29 received for subject Aug 25, 2000; to DEWEY 08-25-00; aa11 08-28-00 83 | 84 | 85 | 00530046 86 | 87 | 88 | (OCoLC)ocm44279786 89 | 90 | 91 | IEU 92 | IEU 93 | N@F 94 | DLC 95 | 96 | 97 | lccopycat 98 | 99 | 100 | n-us-dc 101 | n-us--- 102 | 103 | 104 | F204.W5 105 | 106 | 107 | 975.3 108 | 13 109 | 110 | 111 | The White House 112 | [computer file]. 113 | 114 | 115 | Computer data. 116 | 117 | 118 | Washington, D.C. : 119 | White House Web Team, 120 | 1994- 121 | 122 | 123 | Mode of access: Internet. 124 | 125 | 126 | Title from home page as viewed on Aug. 19, 2000. 127 | 128 | 129 | Features the White House. Highlights the Executive Office of the President, which includes senior policy advisors and offices responsible for the President's correspondence and communications, the Office of the Vice President, and the Office of the First Lady. Posts contact information via mailing address, telephone and fax numbers, and e-mail. Contains the Interactive Citizens' Handbook with information on health, travel and tourism, education and training, and housing. Provides a tour and the history of the White House. Links to White House for Kids. 130 | 131 | 132 | White House (Washington, D.C.) 133 | 134 | 135 | United States. 136 | Executive Office of the President. 137 | 138 | 139 | United States. 140 | Office of the Vice President. 141 | 142 | 143 | United States. 144 | Office of the First Lady. 145 | 146 | 147 | White House Web Team. 148 | 149 | 150 | http://www.whitehouse.gov 151 | 152 | 153 | http://lcweb.loc.gov/staff/wpp/whitehouse.html 154 | Web site archive 155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /test/data/marc8.mrc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiaola/marc4js/595a826fd6edfce4df5c66ee5b05c6453bb4201b/test/data/marc8.mrc -------------------------------------------------------------------------------- /test/data/marc8_accented_chars.marc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiaola/marc4js/595a826fd6edfce4df5c66ee5b05c6453bb4201b/test/data/marc8_accented_chars.marc -------------------------------------------------------------------------------- /test/data/marc8_utf8_mixed.mrc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiaola/marc4js/595a826fd6edfce4df5c66ee5b05c6453bb4201b/test/data/marc8_utf8_mixed.mrc -------------------------------------------------------------------------------- /test/data/marc_in_json.json: -------------------------------------------------------------------------------- 1 | { 2 | "leader":"01471cjm a2200349 a 4500", 3 | "fields": 4 | [ 5 | { 6 | "001":"5674874" 7 | }, 8 | { 9 | "005":"20030305110405.0" 10 | }, 11 | { 12 | "007":"sdubsmennmplu" 13 | }, 14 | { 15 | "008":"930331s1963 nyuppn eng d" 16 | }, 17 | { 18 | "035": 19 | { 20 | "subfields": 21 | [ 22 | { 23 | "9":"(DLC) 93707283" 24 | } 25 | ], 26 | "ind1":" ", 27 | "ind2":" " 28 | } 29 | }, 30 | { 31 | "906": 32 | { 33 | "subfields": 34 | [ 35 | { 36 | "a":"7" 37 | }, 38 | { 39 | "b":"cbc" 40 | }, 41 | { 42 | "c":"copycat" 43 | }, 44 | { 45 | "d":"4" 46 | }, 47 | { 48 | "e":"ncip" 49 | }, 50 | { 51 | "f":"19" 52 | }, 53 | { 54 | "g":"y-soundrec" 55 | } 56 | ], 57 | "ind1":" ", 58 | "ind2":" " 59 | } 60 | }, 61 | { 62 | "010": 63 | { 64 | "subfields": 65 | [ 66 | { 67 | "a":" 93707283 " 68 | } 69 | ], 70 | "ind1":" ", 71 | "ind2":" " 72 | } 73 | }, 74 | { 75 | "028": 76 | { 77 | "subfields": 78 | [ 79 | { 80 | "a":"CS 8786" 81 | }, 82 | { 83 | "b":"Columbia" 84 | } 85 | ], 86 | "ind1":"0", 87 | "ind2":"2" 88 | } 89 | }, 90 | { 91 | "035": 92 | { 93 | "subfields": 94 | [ 95 | { 96 | "a":"(OCoLC)13083787" 97 | } 98 | ], 99 | "ind1":" ", 100 | "ind2":" " 101 | } 102 | }, 103 | { 104 | "040": 105 | { 106 | "subfields": 107 | [ 108 | { 109 | "a":"OClU" 110 | }, 111 | { 112 | "c":"DLC" 113 | }, 114 | { 115 | "d":"DLC" 116 | } 117 | ], 118 | "ind1":" ", 119 | "ind2":" " 120 | } 121 | }, 122 | { 123 | "041": 124 | { 125 | "subfields": 126 | [ 127 | { 128 | "d":"eng" 129 | }, 130 | { 131 | "g":"eng" 132 | } 133 | ], 134 | "ind1":"0", 135 | "ind2":" " 136 | } 137 | }, 138 | { 139 | "042": 140 | { 141 | "subfields": 142 | [ 143 | { 144 | "a":"lccopycat" 145 | } 146 | ], 147 | "ind1":" ", 148 | "ind2":" " 149 | } 150 | }, 151 | { 152 | "050": 153 | { 154 | "subfields": 155 | [ 156 | { 157 | "a":"Columbia CS 8786" 158 | } 159 | ], 160 | "ind1":"0", 161 | "ind2":"0" 162 | } 163 | }, 164 | { 165 | "100": 166 | { 167 | "subfields": 168 | [ 169 | { 170 | "a":"Dylan, Bob," 171 | }, 172 | { 173 | "d":"1941-" 174 | } 175 | ], 176 | "ind1":"1", 177 | "ind2":" " 178 | } 179 | }, 180 | { 181 | "245": 182 | { 183 | "subfields": 184 | [ 185 | { 186 | "a":"The freewheelin' Bob Dylan" 187 | }, 188 | { 189 | "h":"[sound recording]." 190 | } 191 | ], 192 | "ind1":"1", 193 | "ind2":"4" 194 | } 195 | }, 196 | { 197 | "260": 198 | { 199 | "subfields": 200 | [ 201 | { 202 | "a":"[New York, N.Y.] :" 203 | }, 204 | { 205 | "b":"Columbia," 206 | }, 207 | { 208 | "c":"[1963]" 209 | } 210 | ], 211 | "ind1":" ", 212 | "ind2":" " 213 | } 214 | }, 215 | { 216 | "300": 217 | { 218 | "subfields": 219 | [ 220 | { 221 | "a":"1 sound disc :" 222 | }, 223 | { 224 | "b":"analog, 33 1/3 rpm, stereo. ;" 225 | }, 226 | { 227 | "c":"12 in." 228 | } 229 | ], 230 | "ind1":" ", 231 | "ind2":" " 232 | } 233 | }, 234 | { 235 | "500": 236 | { 237 | "subfields": 238 | [ 239 | { 240 | "a":"Songs." 241 | } 242 | ], 243 | "ind1":" ", 244 | "ind2":" " 245 | } 246 | }, 247 | { 248 | "511": 249 | { 250 | "subfields": 251 | [ 252 | { 253 | "a":"The composer accompanying himself on the guitar ; in part with instrumental ensemble." 254 | } 255 | ], 256 | "ind1":"0", 257 | "ind2":" " 258 | } 259 | }, 260 | { 261 | "500": 262 | { 263 | "subfields": 264 | [ 265 | { 266 | "a":"Program notes by Nat Hentoff on container." 267 | } 268 | ], 269 | "ind1":" ", 270 | "ind2":" " 271 | } 272 | }, 273 | { 274 | "505": 275 | { 276 | "subfields": 277 | [ 278 | { 279 | "a":"Blowin' in the wind -- Girl from the north country -- Masters of war -- Down the highway -- Bob Dylan's blues -- A hard rain's a-gonna fall -- Don't think twice, it's all right -- Bob Dylan's dream -- Oxford town -- Talking World War III blues -- Corrina, Corrina -- Honey, just allow me one more chance -- I shall be free." 280 | } 281 | ], 282 | "ind1":"0", 283 | "ind2":" " 284 | } 285 | }, 286 | { 287 | "650": 288 | { 289 | "subfields": 290 | [ 291 | { 292 | "a":"Popular music" 293 | }, 294 | { 295 | "y":"1961-1970." 296 | } 297 | ], 298 | "ind1":" ", 299 | "ind2":"0" 300 | } 301 | }, 302 | { 303 | "650": 304 | { 305 | "subfields": 306 | [ 307 | { 308 | "a":"Blues (Music)" 309 | }, 310 | { 311 | "y":"1961-1970." 312 | } 313 | ], 314 | "ind1":" ", 315 | "ind2":"0" 316 | } 317 | }, 318 | { 319 | "856": 320 | { 321 | "subfields": 322 | [ 323 | { 324 | "3":"Preservation copy (limited access)" 325 | }, 326 | { 327 | "u":"http://hdl.loc.gov/loc.mbrsrs/lp0001.dyln" 328 | } 329 | ], 330 | "ind1":"4", 331 | "ind2":"1" 332 | } 333 | }, 334 | { 335 | "952": 336 | { 337 | "subfields": 338 | [ 339 | { 340 | "a":"New" 341 | } 342 | ], 343 | "ind1":" ", 344 | "ind2":" " 345 | } 346 | }, 347 | { 348 | "953": 349 | { 350 | "subfields": 351 | [ 352 | { 353 | "a":"TA28" 354 | } 355 | ], 356 | "ind1":" ", 357 | "ind2":" " 358 | } 359 | }, 360 | { 361 | "991": 362 | { 363 | "subfields": 364 | [ 365 | { 366 | "b":"c-RecSound" 367 | }, 368 | { 369 | "h":"Columbia CS 8786" 370 | }, 371 | { 372 | "w":"MUSIC" 373 | } 374 | ], 375 | "ind1":" ", 376 | "ind2":" " 377 | } 378 | } 379 | ] 380 | } -------------------------------------------------------------------------------- /test/data/marctext_bad.txt: -------------------------------------------------------------------------------- 1 | LDR 00000nam 22002538a 4500 2 | 040 $aMdSSJTT 3 | $cMdSSJTT 4 | 040 $aMdSSJTT 5 | $beng 6 | $cMdSSJTT 7 | 100 14 $aWall, Larry. 8 | 110 1 $aO'Reilly & Associates. 9 | 245 90 $aProgramming Perl / 10 | $aBig Book of Perl / 11 | $cLarry Wall, Tom Christiansen & Jon Orwant. 12 | 250 $a3rd ed. 13 | 250 $a3rd ed. 14 | 260 $aCambridge, Mass. : 15 | $bO'Reilly, 16 | $r2000. 17 | 590 4 $aPersonally signed by Larry. 18 | 856 43 $uhttp://www.perl.com/ 19 | -------------------------------------------------------------------------------- /test/data/sandburg.mrc: -------------------------------------------------------------------------------- 1 | 01142cam 2200301 a 4500001001300000003000400013005001700017008004100034010001700075020002500092040001800117042000900135050002600144082001600170100003200186245008600218250001200304260005200316300004900368500004000417520022800457650003300685650003300718650002400751650002100775650002300796700002100819 92005291 DLC19930521155141.9920219s1993 caua j 000 0 eng  a 92005291  a0152038655 :c$15.95 aDLCcDLCdDLC alcac00aPS3537.A618bA88 199300a811/.522201 aSandburg, Carl,d1878-1967.10aArithmetic /cCarl Sandburg ; illustrated as an anamorphic adventure by Ted Rand. a1st ed. aSan Diego :bHarcourt Brace Jovanovich,cc1993. a1 v. (unpaged) :bill. (some col.) ;c26 cm. aOne Mylar sheet included in pocket. aA poem about numbers and their characteristics. Features anamorphic, or distorted, drawings which can be restored to normal by viewing from a particular angle or by viewing the image's reflection in the provided Mylar cone. 0aArithmeticxJuvenile poetry. 0aChildren's poetry, American. 1aArithmeticxPoetry. 1aAmerican poetry. 1aVisual perception.1 aRand, Ted,eill. -------------------------------------------------------------------------------- /test/data/sandburg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 01142cam 2200301 a 4500 5 | 92005291 6 | DLC 7 | 19930521155141.9 8 | 920219s1993 caua j 000 0 eng 9 | 10 | 92005291 11 | 12 | 13 | 0152038655 : 14 | $15.95 15 | 16 | 17 | DLC 18 | DLC 19 | DLC 20 | 21 | 22 | lcac 23 | 24 | 25 | PS3537.A618 26 | A88 1993 27 | 28 | 29 | 811/.52 30 | 20 31 | 32 | 33 | Sandburg, Carl, 34 | 1878-1967. 35 | 36 | 37 | Arithmetic / 38 | Carl Sandburg ; illustrated as an anamorphic adventure by Ted Rand. 39 | 40 | 41 | 1st ed. 42 | 43 | 44 | San Diego : 45 | Harcourt Brace Jovanovich, 46 | c1993. 47 | 48 | 49 | 1 v. (unpaged) : 50 | ill. (some col.) ; 51 | 26 cm. 52 | 53 | 54 | One Mylar sheet included in pocket. 55 | 56 | 57 | A poem about numbers and their characteristics. Features anamorphic, or distorted, drawings which can be restored to normal by viewing from a particular angle or by viewing the image's reflection in the provided Mylar cone. 58 | 59 | 60 | Arithmetic 61 | Juvenile poetry. 62 | 63 | 64 | Children's poetry, American. 65 | 66 | 67 | Arithmetic 68 | Poetry. 69 | 70 | 71 | American poetry. 72 | 73 | 74 | Visual perception. 75 | 76 | 77 | Rand, Ted, 78 | ill. 79 | 80 | 81 | -------------------------------------------------------------------------------- /test/data/selections.mrc: -------------------------------------------------------------------------------- 1 | 00654nam a22002291i 4500001001600000003000400016005001700020006001500037007001500052008004100067035002500108035002400133035002300157035002400180040001300204100002200217240001600239245004800255260004300303300004800346538003000394MIU01-000023187MiU19880715000000.0m d||||cr bn ---auaua880715s1979 bu c 00011 bulod a(RLIN)MIUG16238265-B a(CaOTULAS)159843284 a(OCoLC)ocm06426495 asdr-nrlf.b123018559 aMiUcMiU1 aRaĭnov, Bogomil.10aSelections.00aIzbrani proizvedenii͡a /cBogomil Raĭnov. aSofii͡a :bBŭlgarski pisatel,c1979. a2 v., [1] leaf of plates :bport. ;c21 cm. aMode of access: Internet. -------------------------------------------------------------------------------- /test/data/the_real_mother_goose.mrc: -------------------------------------------------------------------------------- 1 | 00783nam a2200217Ki 4500001000800000005001700008006001900025007001500044008004100059042000700100092001000107245005000117260007600167300004800243500005500291500003200346540005700378655002200435710002300457856008500480PG1060720101216083600.0m||||||||d||||||||cr||n |||muaua101213s2004 utu o eng d adc aeBook04aThe Real Mother Gooseh[electronic resource]. aSalt Lake City :bProject Gutenberg Literary Archive Foundation,c2004. a1 online resource :bmultiple file formats. aRecords generated from Project Gutenberg RDF data. aISO 639-2 language code: en aApplicable license: http://www.gutenberg.org/license 0aElectronic books.2 aProject Gutenberg.40uhttp://www.gutenberg.org/etext/10607yClick here to access a downloadable ebook. -------------------------------------------------------------------------------- /test/data/utf8.mrc: -------------------------------------------------------------------------------- 1 | 01123cam a2200349 a 4500001000200000005001700002008004100019010003200060035002000092035001300112040001000125041001100135049004700146049004000193050001700233090002300250100002000273240005000293245008000343260004700423300002100470500005500491504005400546650001600600650001000616650002300626650001600649650002200665730005000687951001100737957002500748219900316000000.0840112s1962 pau b 00010 eng  a 61014599/L/r83o00204096 aocmDCLC6114599B 9AAA-0425 dPPT-M1 aengund98aTMYMbBF575.L7 T68 1962c1z39074500724638 aPPCMc1lFred B. Rogers, M.D.oGift0 aBF697b.T623 aBF575.L7bT68 196210aTournier, Paul.10aDe la solitude à la communauté.lEnglish.10aEscape from loneliness /cby Paul Tournier ; translated by John S. Gilmour.0 aPhiladelphia :bWestminster Press,cc1962. a192 p. ;c21 cm. aTranslation of De la solitude à la communauté. aIncludes bibliographical references (p. 190-192). 0aLoneliness. 0aSelf. 0aSocial psychology. 2aLoneliness. 2aSocial Isolation.01aDe la solitude à la communauté.lEnglish. x070101 aBF 575.L7 T728d 1962 -------------------------------------------------------------------------------- /test/data/utf8_only.mrc: -------------------------------------------------------------------------------- 1 | 03644cgm a2200793Ia 4500001001000000007001000010008004100020024001800061028003300079041002800112043001200140050002500152066000700177082001800184099002700202099002300229245028000252246001000532246001800542246002100560260007400581300006800655538002800723511006700751508011900818500004600937500006400983505010201047520015401149546011101303600004501414650003701459650003501496650003401531655002201565655003501587700002601622700002601648700002801674700002801702700002601730700002601756700002501782700002501807700002501832700002901857700004501886710004101931710002601972710003301998710003402031880025102065880002202316880005902338880009502397880002602492880002602518880002602544880002602570880002602596880002602622880002302648880002602671880002602697880002602723880004202749880004002791999001902831428585605vd cvaizu090729p20092008my 560 e vlkor d3 a475913197001542aPMP-D3197bPMP Entertainment1 akorachijchijengjmay aa-ko--- 4aPN1992.77bT.38 2009 c$104a791.45/72222 aDVD 04238 Region 3 & 1 a04238 region 3 & 1006880-01aTʻatchah[viderecording] =bTazza = War of flower = Lao qian /ckihoek, SBS productions ; chejak, Olive 9 ; Chʻaegim pʻŭrodyusŏ Yi Hyŏn-jik ; yŏnchʻul, Kang Sin-hyo, Paek Su-chʻan ; wŏnjak, Hŏ Yŏng-man ; kŭkpon, Sŏl Chun-sŏk, Chin Hŏn-su, Im Chŏng-su31aTazza31aWar of flower316880-02aLao qian aKuala Lumpur :bPMP Entertainment(M) :bHwa Yea Multimedia (M),c2009 a21 episodes on 4 videodiscs (560 min.) :bsd., col. ;c4 3/4 in aDVD; NTSC; region 3 & 11 6880-03aChang Hyŏk, Han Ye-sŭl, Kim Min-jun, Kang Sŏng-yŏn 6880-04aChʻwaryŏng, Yi Kil-bok; pʻyŏnjip, Chang Si-yŏn, Kim Tʻae-yŏng; chomyŏng, Pak Man-chʻang, Song hun aAdapted from Hŏ Yŏng-man's comics Tazza aOriginally aired as 21-episode SBS television drama in 20080 adisc 1: Episodes 1-6 -- disc 2: Episodes 7-12 -- disc 3: Episodes 13-18 -- disc 4: Episodes 19-21 aAfter Goni's mother and stepfather die because of Young-min, and he winds up in jail, Goni learns from a top tazza player in hopes of getting revenge aIn Korean or Mandarin with optional subtitles in traditional Chinese, simplified Chinese, English or Malay10aHŏ, Yŏng-man,d1947-vFilm adaptations 0aCard gameszKorea (South)vDrama 0aGamblerszKorea (South)vDrama 0aRevengezKorea (South)vDrama 7aplays.2mim5oruu 7aTelevision mini-series.2lcgft1 6880-05aYi, Hyŏn-jik1 6880-06aKang, Sin-hyo1 6880-07aPaek, Su-chʻan1 6880-08aSŏl, Chun-sŏk1 6880-09aChin, Hŏn-su1 6880-10aIm, Chŏng-su1 6880-11aChang, Hyŏk1 6880-12aHan, Ye-sŭl1 6880-13aKim, Min-jun1 6880-14aKang, Sŏng-yŏn1 6880-15aHŏ, Yŏng-man,d1947-tTʻatcha2 6880-16aSBS Pʻŭrodŏksyŏn (Korea)2 aOlive 9 (Korean firm)2 aPMP Entertainment (Malaysia)2 aHwa Yea Multimedia (Malaysia)006245-01/$1a타짜h[videorecording] =bTazza = War of flower = 老千 /c기획, SBS productions ; 제작, Olive 9 ; 책임 프로듀서, 이 현직 ; 연출, 강 신효, 백 수찬 ; 원작 허 영만 ; 극본, 설 준석, 진 헌수, 임 정수316246-02/$1a老千1 6511-03/$1a장 혁, 한 예슬, 김 민준, 강 성연 6508-04/$1a촬영, 이 길복; 편집, 장 시연, 김 태영; 조명, 박 만창, 송 훈1 6700-05/$1a이 현직1 6700-06/$1a강 신효1 6700-07/$1a백 수찬1 6700-08/$1a설 준석1 6700-09/$1a진 헌수1 6700-10/$1a임 정수1 6700-11/$1a장 혁1 6700-12/$1a한 예슬1 6700-13/$1a김 민준1 6700-14/$1a강 성연1 6700-15/$1a허 영만,d1947-t타짜2 6710-16/$1aSBS 프로덕션 (Korea) aa006_b4086687703478cgm a2200637Ia 450000100090000000300060000900500170001500700100003200800410004202800230008304100180010604300120012406600070013609000280014324501270017124600660029824600650036426000430042930000570047249000820052953800460061154600580065751100620071550801710077750000530094850000670100152004240106865000270149265000250151965500270154465500340157165500440160570000300164970000310167970000280171070000300173870000270176870000320179571000390182771000310186683000470189788001560194488000510210088000660215188000780221788002040229588000410249988000430254088000350258388000370261888000390265588000390269488000510273388000370278499900190282165168150OCoLC20060811145241.0vd cvaizu060320p20001983ua 134 vlara 40aFDD 867bRūtānā0 aarabengbfre af-ua--- c(3 aPN1997.A1bM83 2000 DVD036880-01aal-Mudminh[videorecording] /cintāj, Sharikat Aflām Miṣr al-Jadīdah ; taʼlīf wa-ikhrāj, Yūsuf Faransīs.1 iEnglish title when optional subtitles are turned on:aAddict.1 iFrench title when optional subtitles are turned on:aAddict. 6880-02a[Cairo] :bRūtānā,c[2000?] a1 videodisc (ca. 134 min.) :bsd., col. ;c4 3/4 in.1 6880-03aKilāsīkīyāt al-sīnimā al-Miṣriyah = Egyptian cinema classics. aDVD NTSC, all regions; Dolby Digital 1.0. aIn Arabic with optional English and French subtitles.1 6880-04aNajwá Ibrāhīm, Aḥmad Zakī, ʻĀdil Adʹham. 6880-05aMuntij fannī, Mamdūḥ al-Laythī ; mūntāj, Saʻīd al-Shaykh ; mudīr al-taṣwīr, Muṣṭafá Imām ; al-mūsīqá al-taṣwīrīyah, Jamāl Salāmah. aOriginally released as a motion picture in 1983. aExtras include a biography and filmography of the actor Zakī. aDramatic work about "Khālid" who lost his wife and son in an accident in which he wasn't hurt. He blamed himself for the death of his wife and son and subsequently became addicted to drugs. He moved to Dr. Aḥmad's hospital who was an old friend of Khālid to be cured. Doctor Aḥmad's daughter, named Dr. Laylá, took care of Khālid but he couldn't stop his addiction so he escaped from the hospital to find drugs. 0aAddictszEgyptvDrama. 0aGriefzEgyptvDrama. 7aFeature films.2lcgft. 7aFeature filmszEgypt.2local. 7aForeign language filmsxArabic.2local.1 6880-06aFaransīs, Yusuf.1 6880-07aIbrāhīm, Najwá.1 6880-08aZakī, Aḥmad.1 6880-09aAdʹham, ʻĀdil.1 6880-10aImam, Mustafa.1 6880-11aLaythī, Mamdūḥ.2 6880-12aAflām Miṣr al-Jadīdah.2 aRotana Distribution (Firm) 0aKilāsīkīyāt al-sīnimā al-Miṣriyah.026245-01/(3/raالمدمنh[videorecording] /cانتاج, شركة افلام مصر الجديدة ؛ تأليف واخراج, يوسف فرنسيس. 6260-02/(3/ra[Cairo] :bروتانا,c[2000?]0 6490-03/(3/raكلاسيكيات السينما المصرية1 6511-04/(3/raنجوى ابراهيم, أحمد زكي, عادل ادهم. 6508-05/(3/raمنتج فني, ممدوح الليثي ؛ مونتاج, سعيد الشيخ ؛ مدير التصوير, مصطفى امام ؛ الموسيقى التصويرية, جمال سلامة.1 6700-06/(3/raفرنسيس, يوسف.1 6700-07/(3/raابراهيم, نجوى.1 6700-08/(3/raزكي, أحمد.1 6700-09/(3/raادهم, عادل.1 6700-10/(3/raامام, مصطفى.1 6700-11/(3/raليثي, ممدوح.2 6710-12/(3/raافلام مصر الجديدة.2 6710-13/(3/raروتانا (Firm) aa008_b5384856103231cgm a2200493Ka 4500001001300000003000600013005001700019007001000036008004100046035002100087043002100108090002100129245008100150260004300231300005000274538002300324546009700347500003600444500003800480500004100518586007500559586008700634586010100721586010000822520025600922520086401178600004402042610007102086650005002157650004402207650005002251650005102301650005802352650006002410650003702470655003002507655003002537655002902567655004402596655002402640700002802664700002602692999001902718ocn720663644OCoLC20120426101915.0vd cvaiz|110506s2009 mx 120 vlspa d a(OCoLC)720663644 an-mx---an-us--- aF1256b.V58 200900a¡Viva México!h[videorecording] /cuna película de Nicolas Défossé. a[Mexico] :bTerra Nostra Films,c2009. a1 videodisc (2 hrs.) :bsd., col ;c4 3/4 in. aDVD video; stereo. aSpanish with subtitles available in: English, French, Italian, German, Portuguese and Greek. aTitle taken from DVD container. aGift of a Western Faculty Member. aExtras: scene selection and trailer. aPremio del Público, III Festival de la Memoria, Tepoztlán, Mexico. aPremio del Público 27°, Encuentros de Ciné Latinoamericano, Burdeos, Francia. aPremio del Público, VI Festival Internacional de Cine de los Derechos Humanos, Sucre, Bolivia. aMención Honorífica Premio, "José Rovirosa" Mejor Documental Mexicano 2009, UNAM, México a"In a journey from the mountains of southeastern Mexico to the northern border with the United States, Subcommander Marcos and the people of Mexico trace the forgotten face of a country. A celebration of the struggle for land and dignity"-- container. a"Los Ángeles, Estados-Unidos. Emigrantes mexicanos son perseguidos por la policía, mientras luchan para sobrevivir sin renunciar a su cultura. Del otro lado de la frontera, en las montañas del sureste mexicano, amanece con neblina. Es 1ero de enero del 2006, miles de indígenas zapatistas despiden a su vocero, el Subcomandante Marcos. Su misión: recorrer durante 6 meses todo el país para escuchar las palabras de resistencia de mexicanos y mexicanas que luchan por un México mejor. Así arranca el viaje que planea llegar hasta la frontera con Estados-Unidos y que apuesta a 'empezar a construir el espejo que somos abajo'. La apuesta no va sin riesgos y lo que empezó como murmullo aislado se hace grande en el grito de cientos de miles : ¡Viva México! ¿Cuál será la respuesta del poder?"--taken from the back of the DVD container.00aMarcos,csubcomandantexTravelzMexico.20aEjército Zapatista de Liberación Nacional (Mexico)xCampaigns. 0aHuman rights movementszMexicoy21st century. 0aSocial movementszMexicoy21st century. 0aCommunity organizationzMexicoy21st century. 0aPolitical participationzMexicoy21st century. 0aMexicanszCaliforniazLos AngelesxSocial conditions. 0aImmigrantszCaliforniazLos AngelesxSocial conditions. 0aPolicezCaliforniazLos Angeles. 7aDocumentary films.2lcgft 7aIndependent films.2lcgft 7aNonfiction films.2lcgft 7aForeign language filmsxSpanish.2local 7aVideos (DVD)2local0 aMarcos,csubcomandante.1 aDéfossé, Nicolas. aa009_b2493798802254cgm a2200493Ia 4500001001300000003000600013005001700019007001000036008004100046028002400087035002200111041001800133043001200151245014100163246007500304260004900379300005300428538000900481546003900490511008200529508008000611500005300691500004400744500002300788520035100811500004601162650003901208650003001247650001801277650003101295651001901326655001901345655003301364700005501397700002301452700003601475700003001511700004001541700002201581700002401603700005801627710005301685999002201738ocm54315363 OCoLC20070104103647.0vd cvaizu040202p20041979ru 142 vlrus d42aK337bKino on Video a(Sirsi) o543153630 arusbenghrus ae-ru---00aOblomovh[videorecording] /ca Mosfilm production ; screenplay by Alexander Adabashian, Nikita Mikhalkov ; directed by Nikita Mikhalkov.1 iAdditional title from container:aTen days in the life of L.L. Oblomov aN[ew] Y[ork], N.Y. :bKino on Video,c[2004] a1 videodisc (142 min.) :bsd., col. ;c4 3/4 in. aDVD. aIn Russian with English subtitles.1 aOleg Tabakov, Yuri Bogatyrev, Andrei Popov, Elena Solovey, Avangard Leontiev. aDirector of photography, Pavel Lebeshev ; music by Bellini and Rachmaninov. aBased on the novel: Oblomov / by I.A. Goncharov. aDVD release of the 1979 motion picture. aWidescreen format. aOblomov is a 19th century Russian aristocrat and landlord who would rather sleep than compete in a modern world of expanding industrialization. Oblomov is shaken from his slumbers by the arrival of a childhood friend, Shtoltz. A series of flashbacks show why it is this friend's presence that gets Oblomov out of his slumber and back on his feet. aSpecial features include: stills gallery. 0aAristocracy (Social class)vDrama. 0aDepressed personsvDrama. 0aSleepvDrama. 0aLife change eventsvDrama. 0aRussiavDrama. 0aForeign films. 0aFeature filmszSoviet Union.1 aAdabashʹi︠a︡n, Aleksandr Artëmovich,d1945-1 aMikhalkov, Nikita.1 aTabakov, Oleg Pavlovich,d1935-1 aBogatyrev, I︠U︡riĭ.1 aPopov, Andreĭ Alekseevich,d1918-1 aSoloveĭ, Elena.1 aLeontiev, Avangard.1 aGoncharov, Ivan Aleksandrovich,d1812-1891.tOblomov.2 aMoskovskai︠a︡ kinostudii︠a︡ "Mosfilʹm". aa001_ocm54315363\02402ngm a2200469Ia 450000100090000000300060000900500170001500700100003200800410004202800250008304100130010804300120012124502840013326000580041730000550047553800090053052100150053954600390055451100960059350802640068950000650095350000340101852002520105265000420130465000290134665000300137565100200140565500270142565500360145265500450148865500400153370000240157370000190159770000220161670000250163871000410166371000270170471000510173171000370178285600940181999900190191347688591OCoLC20020225184157.0vf cbaho-010801p20011999ec 107 vlspa d42a5876901273bVanguard1 aspabeng as-ec---00aRatas, ratones, raterosh[videorecording] /cCabezahueca Producciones Independientes presenta con el apoyo de CEPSA en asociati♭on con Three Moons Entertainment ; una pel♭icula de Sebastian Cordero ; produced by Lisandra I. Rivera ; written and directed by Sebastian Cordero. a[Huntington Beach, Calif.] :bVanguard Cinema,c2001. a1 videocassette (107 min.) :bsd., col. ;c1/2 in. aVHS.8 aNot rated. aIn Spanish with English subtitles.1 aMarco Bustos, Carlos Valencia, Simon Brauer, Cristina Davila, Fabricio Lalama, Irina Lopez. aExecutive producer, Isabel Davalos ; cinematographer, Matthew Jensen ; editors, Sebastian Cordero, Mateo Herrera ; sound, Masakazu Shirane ; music, Sergio Sacoto-Arias ; art direction, Isabel Davalos ; photography, Matthew Jensen ; music, Sergio Sacoto-Arias. aVideocassette release of the 1999 Ecuadorean motion picture. aWidescreen (letterbox format) aSalvador, a young delinquent, gets mixed up in the affairs of his ex-convict cousin Angel, who is in search of easy money and a hideout. Dragging along his family and friends, Salvador ends up destroying the few things that made sense in his life. 0aJuvenile delinquentszEcuadorvDrama. 0aCousinszEcuadorvDrama. 0aFamilieszEcuadorvDrama. 0aEcuadorvDrama. 7aFeature films.2lcgft. 7aFeature filmszEcuador.2local. 7aForeign language filmszSpanish.2local. 7aJuvenile delinquency films.2lcgft.1 aRivera, Lisandra I.1 aBustos, Marco.1 aValencia, Carlos.1 aCordero, Sebastián.2 aVanguard Films (Los Angeles, Calif.)2 aVanguard Cinema (Firm)2 aCabezahueca Producciones Independientes (Firm)2 aThree Moons Entertainment (Firm)42uhttp://www.mrqe.com/lookup?ratas+ratones+rateroszConnect to reviews of this title online aa008_b4654487201994cgm a22003251i 450000100070000000500170000700800410002404100130006524501390007825700110021726000100022856200400023850000740027851103280035250802740068050000440095450003310099865000200132965000290134965100410137865500420141965500190146165500270148070000440150770000380155174000180158974000260160773000190163399900160165217234820080312132327.0080312s1954 ja mleng d aenghjpn10aSeven samurai /cdirector, Akira Kurosawa ; production, Sojiro Motoki ; screenplay, Akira Kurosawa, Shinobu Hashimoto and Hideo Oguni. aJapan. c1954. cUnknown version; dubbed in English. aOriginal release title in Japan: 七人の侍 (Shichinin no samurai).1 aToshiro Mifune (Kikuchiyo); Takashi Shimura (Kambei); Keiko Tsushima (Shino); Yukio Shimazaki (wife); Kamatari Fujiwara (Farmer Manzo); Daisuke Kato (Shichiroji); Ko Kimura (Katsushiro); Minoru Chiaki (Heihachi); Seiji Miyaguchi (Kyuzo); Yoshio Kosugi (Farmer Mosuke); Bokuzen Hidari (Farmer Yohei); Yoshio Inaba (Gorobei). aPhotography, Asaichi Nakai; art direction, So Matsuyama; music, Fumio Hayasaka; assistant director, Hiromichi Horikawa; editing manager, Hiroshi Nezu; historical research, Kohei Ezaki (folklore), Yoshio Sugino (fencing), Ienori Kaneko (archery), Shigeru Endo (archery). aCredits supplied from videodisc jacket. aPlaying time on release in Japan was 206 min., and internationally was 160 min; in 1991, a 190 min. version was released in Great Britain; a 202 min. version was released in Sweden in 2002; a 206 min. restored version was later released in the United States, according to: Internet movie database, as viewed on March 29, 2007. 0aSamuraivDrama. 0aPeasantryzJapanvDrama. 0aJapanxHistoryy16th centuryvDrama. 7aMartial arts films and programs.2mim 7aFeatures.2mim 7aJapanese films.2local1 aKurosawa, Akira,d1910-1998.edirection1 a黑澤明,d1910-1998.edirection00a七人の侍.00aShichinin no samurai.02aSeven Samurai. aa004_172348 2 | -------------------------------------------------------------------------------- /test/helpers/chai.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'); 2 | 3 | chai.config.includeStack = true; 4 | 5 | global.expect = chai.expect; 6 | global.AssertionError = chai.AssertionError; 7 | global.Assertion = chai.Assertion; 8 | global.assert = chai.assert; -------------------------------------------------------------------------------- /test/marc/control_field.js: -------------------------------------------------------------------------------- 1 | var ControlField = require('../../lib/marc/control_field.js'); 2 | 3 | describe('ControlField', function () { 4 | var cf; 5 | 6 | before(function () { 7 | cf = new ControlField(); 8 | }); 9 | 10 | it('should have id 1234', function () { 11 | cf.id = 1234; 12 | expect(cf.id).equal(1234); 13 | }); 14 | 15 | it('should have data abcd', function() { 16 | cf.data = 'abcd'; 17 | expect(cf.data).equal('abcd'); 18 | }); 19 | }); -------------------------------------------------------------------------------- /test/marc/data_field.js: -------------------------------------------------------------------------------- 1 | var DataField = require('../../lib/marc/data_field'); 2 | var Subfield = require('../../lib/marc/subfield'); 3 | 4 | describe('DataField', function () { 5 | var dataField, subfield; 6 | 7 | before(function () { 8 | dataField = new DataField(); 9 | dataField.tag = '246'; 10 | 11 | subfield = new Subfield(); 12 | subfield.data = 'abcd'; 13 | subfield.code = 'a'; 14 | }); 15 | 16 | it('should has no subfields', function () { 17 | expect(dataField.subfields).empty(); 18 | expect(dataField.findSubfields('246')).empty(); 19 | }); 20 | 21 | it('should find /abc/', function () { 22 | dataField.subfields = [subfield]; 23 | expect(dataField.find(/abc/)).equal(true); 24 | }); 25 | 26 | it('should find return nil', function () { 27 | dataField.subfields = []; 28 | expect(dataField.findSubfield('a')).to.be.an('undefined'); 29 | }); 30 | 31 | it('should unmarshal a string', function () { 32 | var df = new DataField(); 33 | var s = '01\x1faNew York (N.Y.)\x1fvFiction.'; 34 | df.unmarshal(s); 35 | expect(df.subfields[0].data).equal('New York (N.Y.)'); 36 | expect(df.subfields[1].code).equal('v'); 37 | }); 38 | 39 | it('should unmarshal a string and mashal it back', function () { 40 | var df = new DataField(); 41 | var s = '01\x1faNew York (N.Y.)\x1fvFiction.'; 42 | df.unmarshal(s); 43 | expect(df.marshal().length).equal(s.length); 44 | //expect(df.marshal().toString()).equal(s); 45 | }); 46 | 47 | it('should create a valid data field with constructor', function() { 48 | var df = new DataField("245", "0", "1", [['a', 'New York (N.Y.)'], ['v', 'Fiction.']]); 49 | expect(df.indicator1).equal('0'); 50 | expect(df.subfields[0].code).equal('a'); 51 | expect(df.subfields[1].data).equal('Fiction.'); 52 | }); 53 | 54 | it('it should marshal to a string', function() { 55 | var df = new DataField("245", "0", "1", [['a', 'New York (N.Y.)'], ['v', 'Fiction.']]); 56 | expect(df.marshal().length).equal('01\x1faNew York (N.Y.)\x1fvFiction.'.length); 57 | expect(df.marshal()).equal('01\x1faNew York (N.Y.)\x1fvFiction.'); 58 | }) 59 | 60 | 61 | }); 62 | -------------------------------------------------------------------------------- /test/marc/leader.js: -------------------------------------------------------------------------------- 1 | var Leader = require('../../lib/marc/leader'); 2 | 3 | describe('Leader', function () { 4 | var leader; 5 | 6 | before(function () { 7 | leader = new Leader(); 8 | }); 9 | 10 | it('should unmarshal', function () { 11 | leader.unmarshal("00714cam a2200205 a 4500"); 12 | expect(leader.subfieldCodeLength).equal(2); 13 | expect(leader.charCodingScheme).equal('a'); 14 | }); 15 | 16 | it('should unmarshal and marshal', function() { 17 | leader.unmarshal('00714cam a2200205 a 4500'); 18 | expect(leader.marshal()).equal('00714cam a2200205 a 4500'); 19 | }); 20 | }); -------------------------------------------------------------------------------- /test/marc/record.js: -------------------------------------------------------------------------------- 1 | var Record = require('../../lib/marc/record'); 2 | var DataField = require('../../lib/marc/data_field'); 3 | 4 | describe('Record', function () { 5 | var record; 6 | var df; 7 | 8 | beforeEach(function () { 9 | record = new Record(); 10 | df = new DataField(); 11 | df.tag = '100'; 12 | var s = '01\x1faNew York (N.Y.)\x1fvFiction.'; 13 | df.unmarshal(s); 14 | }); 15 | 16 | it('should assign leader', function () { 17 | record.leader = '00307nam a2200085Ia 45e0'; 18 | expect(record.leader).equal('00307nam a2200085Ia 45e0'); 19 | }); 20 | 21 | it('should add a variable field', function() { 22 | record.addVariableField(df); 23 | expect(record.dataFields.length).equal(1); 24 | }); 25 | 26 | it('should find the first 100 subfield', function() { 27 | record.addVariableField(df); 28 | 29 | expect(record.findDataFields('100').length).equal(1); 30 | expect(record.findDataField('100')).not.to.be.an('undefined'); 31 | }); 32 | }); -------------------------------------------------------------------------------- /test/marc/subfield.js: -------------------------------------------------------------------------------- 1 | var Subfield = require('../../lib/marc/subfield'); 2 | 3 | describe('Subfield', function () { 4 | var subfield; 5 | 6 | before(function () { 7 | subfield = new Subfield(); 8 | }); 9 | 10 | it('should has id 1234', function () { 11 | subfield.id = 1234; 12 | expect(subfield.id).equal(1234); 13 | }); 14 | 15 | it('should find /abc/', function() { 16 | subfield.data = 'abcd'; 17 | expect(subfield.find(/abc/)).equal(true); 18 | }); 19 | 20 | it('should not find /xyz/', function() { 21 | subfield.data = 'abcd'; 22 | expect(subfield.find(/xyz/)).equal(false); 23 | }); 24 | }); -------------------------------------------------------------------------------- /test/marc4js.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var marc4js = require('../lib/marc4js'); 4 | 5 | describe('marc4js', function () { 6 | before(function () { 7 | 8 | }); 9 | 10 | it('should assign a record with id', function () { 11 | var record = new marc4js.marc.Record(); 12 | record.id = 1234; 13 | expect(record.id).equal(1234); 14 | }); 15 | }); -------------------------------------------------------------------------------- /test/marc8.js: -------------------------------------------------------------------------------- 1 | var parse = require('../lib/parse'); 2 | var transform = require('../lib/transform'); 3 | var fs = require('fs'); 4 | var marc8 = require('marc8'); 5 | 6 | describe('marc8', function () { 7 | it('should parse marc8 encoded file', function(done) { 8 | var utf8 = fs.readFileSync('test/data/utf8.mrc').toString(); 9 | fs.readFile('test/data/marc8.mrc', {encoding: 'binary'}, function(err, data) { 10 | parse(data, {fromFormat: 'iso2709', marc8: true, marc8converter: require('marc8')}, function(err, records) { 11 | expect(records.length).to.equal(1); 12 | var record = records[0]; 13 | var fields = record.dataFields; 14 | fields.forEach(function(field) { 15 | if (field.tag == '240') { 16 | expect(utf8.indexOf(field.subfields[0].data)).to.be.least(0); 17 | } else if (field.tag == '008') { 18 | expect(field.data).equal('840112s1962 pau b 00010 eng'); 19 | } 20 | }); 21 | done(); 22 | }); 23 | }); 24 | }); 25 | 26 | // TODO: This test is failing at the moment. Disable it for now. 27 | // it('should parse marc8 encoded file with accented chars', function(done) { 28 | // fs.readFile('test/data/marc8_accented_chars.mrc', {encoding: 'binary'}, function(err, data) { 29 | // parse(data, {fromFormat: 'iso2709', marc8: false, marc8converter: require('marc8')}, function(err, records) { 30 | // expect(records.length).equal(1); 31 | // }); 32 | // done(); 33 | // }); 34 | // }); 35 | 36 | it('should parse a file with mixed marc8 and utf8 encoding', function(done) { 37 | fs.readFile('test/data/marc8_utf8_mixed.mrc', {encoding: 'binary'}, function(err, data) { 38 | parse(data, {fromFormat: 'iso2709', marc8converter: marc8}, function(err, records) { 39 | expect(records.length).equal(6); 40 | }); 41 | done(); 42 | }); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /test/parse.js: -------------------------------------------------------------------------------- 1 | var parse = require('../lib/parse'); 2 | var transform = require('../lib/transform'); 3 | var fs = require('fs'); 4 | var marc8 = require('marc8'); 5 | 6 | describe('parse', function () { 7 | var stream; 8 | 9 | before(function () { 10 | }); 11 | 12 | it('should parse one record and identify fields', function (done) { 13 | var stream = fs.createReadStream('test/data/sandburg.mrc'); 14 | var parser = parse({objectMode: true}); 15 | 16 | var first; 17 | var count = 0; 18 | parser.on('data', function (record) { 19 | count += 1; 20 | if (count === 1) first = record; 21 | }); 22 | 23 | stream.pipe(parser); 24 | 25 | parser.on('error', function (error) { 26 | console.log("error: ", error); 27 | done(); 28 | }); 29 | 30 | parser.on('end', function () { 31 | expect(count).equal(1); 32 | expect(first.variableFields.length).to.equal(23); 33 | expect(first.controlFields.length).equal(4); 34 | done(); 35 | }); 36 | }); 37 | 38 | it('should parse multiple records', function (done) { 39 | var stream = fs.createReadStream('test/data/collection.mrc'); 40 | var parser = parse({objectMode: true, fromFormat: 'marc'}); 41 | 42 | var count = 0; 43 | parser.on('data', function () { 44 | count += 1; 45 | }); 46 | 47 | stream.pipe(parser); 48 | 49 | parser.on('error', function (error) { 50 | console.log("error: ", error); 51 | done(); 52 | }); 53 | 54 | parser.on('end', function () { 55 | expect(count).equal(2); 56 | done(); 57 | }); 58 | }); 59 | 60 | it('should work with callback API', function(done) { 61 | var data = fs.readFileSync('test/data/collection.mrc'); 62 | parse(data, {objectMode: true}, function(err, records) { 63 | if (err) { 64 | console.log(err); 65 | } else { 66 | expect(records.length).equal(2); 67 | } 68 | done(); 69 | }); 70 | }); 71 | 72 | it('should work with stream API', function(done) { 73 | var data = '00783nam a2200217Ki 4500001000800000005001700008006001900025007001500044008004100059042000700100092001000107245005000117260007600167300004800243500005500291500003200346540005700378655002200435710002300457856008500480PG1060720101216083600.0m||||||||d||||||||cr||n |||muaua101213s2004 utu o eng d adc aeBook04aThe Real Mother Gooseh[electronic resource]. aSalt Lake City :bProject Gutenberg Literary Archive Foundation,c2004. a1 online resource :bmultiple file formats. aRecords generated from Project Gutenberg RDF data. aISO 639-2 language code: en aApplicable license: http://www.gutenberg.org/license 0aElectronic books.2 aProject Gutenberg.40uhttp://www.gutenberg.org/etext/10607yClick here to access a downloadable ebook.'; 74 | var parser = parse({objectMode: true}); 75 | 76 | var count = 0; 77 | parser.on('data', function (record) { 78 | var fields = record.dataFields; 79 | var field = fields[fields.length - 1]; 80 | expect(field.indicator1).equal('4'); 81 | expect(field.indicator2).equal('0'); 82 | count += 1; 83 | }); 84 | 85 | parser.on('error', function (error) { 86 | console.log("error: ", error); 87 | }); 88 | 89 | parser.on('end', function () { 90 | expect(count).equal(1); 91 | done(); 92 | }); 93 | 94 | parser.write(data); 95 | parser.end(); 96 | }); 97 | 98 | it('should parse mrk format', function(done) { 99 | var data = fs.readFileSync('test/data/collection.mrk'); 100 | parse(data, {fromFormat: 'mrk'}, function(err, records) { 101 | if (err) { 102 | console.log(err); 103 | } else { 104 | expect(records.length).to.equal(2); 105 | expect(records[1].leader.marshal()).equal('01832cmmaa2200349 a 4500'); 106 | } 107 | done(); 108 | }); 109 | }); 110 | 111 | it('should parse text format', function(done) { 112 | "use strict"; 113 | var data = fs.readFileSync('test/data/collection.txt'); 114 | parse(data, {fromFormat: 'text'}, function(err, records) { 115 | if (err) { 116 | console.log(err); 117 | } else { 118 | expect(records.length).to.equal(2); 119 | expect(records[1].leader.marshal()).equal('01832cmmaa2200349 a 4500'); 120 | } 121 | done(); 122 | }); 123 | }); 124 | 125 | it('should parse MARCXML', function(done) { 126 | var data = fs.readFileSync('test/data/sandburg.xml'); 127 | parse(data.toString(), {fromFormat: 'marcxml'}, function(err, records) { 128 | if (err) { 129 | console.log(err); 130 | } else { 131 | expect(records.length).equal(1); 132 | expect(records[0].leader.marshal()).equal('01142cam 2200301 a 4500'); 133 | expect(records[0].dataFields[6].subfields[1].code).equal('d'); 134 | } 135 | done(); 136 | }); 137 | }); 138 | 139 | it('should parse MARCXML with multiple records and namespace', function(done) { 140 | var data = fs.readFileSync('test/data/collection.xml'); 141 | parse(data.toString(), {fromFormat: 'marcxml'}, function(err, records) { 142 | if (err) { 143 | console.log(err); 144 | } else { 145 | expect(records.length).equal(2); 146 | expect(records[1].leader.marshal()).equal('01832cmma 2200349 a 4500'); 147 | } 148 | done(); 149 | }); 150 | }); 151 | 152 | it('should parse marc in json', function(done) { 153 | var data = fs.readFileSync('test/data/marc_in_json.json'); 154 | parse(data.toString(), {fromFormat: 'json'}, function(err, records) { 155 | expect(records.length).equal(1); 156 | done(); 157 | }); 158 | }); 159 | 160 | it('should parse marc-in-json with multiple records', function(done) { 161 | var data = fs.readFileSync('test/data/collection.json'); 162 | parse(data.toString(), {fromFormat: 'json'}, function(err, records) { 163 | expect(records.length).equal(2); 164 | expect(records[1].leader.marshal()).equal('01832cmma 2200349 a 4500'); 165 | done(); 166 | }); 167 | }); 168 | 169 | it('should parse file with only utf8', function(done) { 170 | var data = fs.readFileSync('test/data/utf8_only.mrc'); 171 | parse(data, {fromFormat: 'iso2709', marc8converter: marc8}, function(err, records) { 172 | expect(records.length).equal(6); 173 | transform(records, {toFormat: 'text'}, function(err, output) { 174 | expect(output.indexOf('黑澤明')).to.be.least(0); 175 | expect(output.indexOf('Premio del Público 27°')).to.be.least(0); 176 | done(); 177 | }); 178 | }); 179 | }); 180 | 181 | it('should return empty array for a blank string as text', function(done) { 182 | "use strict"; 183 | var data = ""; 184 | parse(data, {fromFormat: 'text'}, function(err, records) { 185 | expect(records.length).to.equal(0); 186 | done(); 187 | }); 188 | }); 189 | 190 | it('should return empty array for a blank string as marc', function(done) { 191 | "use strict"; 192 | var data = ""; 193 | parse(data, {fromFormat: 'marc'}, function(err, records) { 194 | expect(records.length).to.equal(0); 195 | done(); 196 | }); 197 | }); 198 | 199 | it('should return empty array for a null variable as text', function(done) { 200 | "use strict"; 201 | var data = null; 202 | parse(data, {fromFormat: 'text'}, function(err, records) { 203 | expect(records.length).to.equal(0); 204 | done(); 205 | }); 206 | }); 207 | 208 | it('should return empty array for a null variable as MARC', function(done) { 209 | "use strict"; 210 | var data = null; 211 | parse(data, {fromFormat: 'marc'}, function(err, records) { 212 | expect(records.length).to.equal(0); 213 | done(); 214 | }); 215 | }); 216 | // 217 | //it('should throw an error when there is an error', function(done) { 218 | // "use strict"; 219 | // var data = fs.readFileSync('test/data/collection-error.mrc'); 220 | // parse(data, {fromFormat: 'marc'}, function(err, records) { 221 | // expect(err).to.exist; 222 | // done(); 223 | // }); 224 | //}); 225 | }); 226 | -------------------------------------------------------------------------------- /test/transform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var os = require('os'); 5 | var transform = require('../lib/transform'); 6 | var parse = require('../lib/parse'); 7 | var Record = require('../lib/marc/record'); 8 | var Subfield = require('../lib/marc/subfield'); 9 | var DataField = require('../lib/marc/data_field'); 10 | var ControlField = require('../lib/marc/control_field'); 11 | var Leader = require('../lib/marc/leader'); 12 | 13 | 14 | describe('transform', function () { 15 | var records; 16 | before(function () { 17 | records = []; 18 | var r1 = new Record(); 19 | r1.leader = new Leader('00307nam a2200085Ia 45e0'); 20 | r1.addVariableField(new ControlField("008", "080906s9999 xx 000 0 und d")); 21 | r1.addVariableField(new DataField("100", '1', ' ', [["a", "Biggers, Earl Derr."]])); 22 | r1.addVariableField(new DataField("245", '1', '0', [["a", "Charlie Chan Carries On"], ["h", "[electronic resource]"]])); 23 | r1.addVariableField(new DataField("500", ' ', ' ', [["a", "An ebook provided by Project Gutenberg Australia"]])); 24 | r1.addVariableField(new DataField("856", '4', '0', [["u", "http://gutenberg.net.au/ebooks07/0700761h.html"]])); 25 | records.push(r1); 26 | 27 | var r2 = new Record(); 28 | r2.leader = new Leader('00287nam a2200085Ia 45e0'); 29 | r2.addVariableField(new ControlField("008", "080906s9999 xx 000 0 und d")); 30 | r2.addVariableField(new DataField("100", '1', ' ', [["a", "Wallace, Edgar."]])); 31 | r2.addVariableField(new DataField("245", '1', '0', [["a", "Sanders"], ["h", "[electronic resource]"]])); 32 | r2.addVariableField(new DataField("500", ' ', ' ', [["a", "An ebook provided by Project Gutenberg Australia"]])); 33 | r2.addVariableField(new DataField("856", '4', '0', [["u", "http://gutenberg.net.au/ebooks07/0700771h.html"]])); 34 | records.push(r2); 35 | }); 36 | 37 | it('should stringify the records with callback', function(done) { 38 | var data = fs.readFileSync('test/data/PGA_2records.mrc'); 39 | transform(records, function(err, output) { 40 | expect(output.length).equal(data.length); 41 | expect(output).equal(data.toString()); 42 | done(); 43 | }); 44 | }); 45 | 46 | it('should stringify one record', function(done) { 47 | transform(records[0], function(err, output) { 48 | expect(output).to.be.not.null; 49 | done(); 50 | }); 51 | }); 52 | 53 | it('should stringify with a flowing stream API', function(done) { 54 | var transformer = transform({objectMode: true}); 55 | var output = ''; 56 | transformer.on('data', function(record) { 57 | output += record; 58 | }); 59 | transformer.on('error', function(err) { 60 | console.log(err.message); 61 | }); 62 | transformer.on('end', function() { 63 | var data = fs.readFileSync('test/data/PGA_2records.mrc'); 64 | expect(output.length).equal(data.length); 65 | expect(output).equal(data.toString()); 66 | done(); 67 | }); 68 | records.forEach(function(record) { 69 | transformer.write(record); 70 | }); 71 | transformer.end(); 72 | 73 | }); 74 | 75 | it('should stringify with a non-flowing stream API', function(done) { 76 | var transformer = transform({objectMode: false, toFormat: 'iso2709'}); 77 | var output = ''; 78 | transformer.on('readable', function() { 79 | var record; 80 | while (record = transformer.read()) { 81 | output += record; 82 | } 83 | }); 84 | transformer.on('error', function(err) { 85 | console.log(err.message); 86 | }); 87 | transformer.on('end', function() { 88 | var data = fs.readFileSync('test/data/PGA_2records.mrc'); 89 | expect(output.length).equal(data.length); 90 | expect(output).equal(data.toString()); 91 | done(); 92 | }); 93 | records.forEach(function(record) { 94 | transformer.write(record); 95 | }); 96 | transformer.end(); 97 | }); 98 | 99 | it('should pipe to destination', function(done) { 100 | var transformer = transform({objectMode: true}); 101 | var parser = parse({objectMode: true}); 102 | var mrc = '00783nam a2200217Ki 4500001000800000005001700008006001900025007001500044008004100059042000700100092001000107245005000117260007600167300004800243500005500291500003200346540005700378655002200435710002300457856008500480PG1060720101216083600.0m||||||||d||||||||cr||n |||muaua101213s2004 utu o eng d adc aeBook04aThe Real Mother Gooseh[electronic resource]. aSalt Lake City :bProject Gutenberg Literary Archive Foundation,c2004. a1 online resource :bmultiple file formats. aRecords generated from Project Gutenberg RDF data. aISO 639-2 language code: en aApplicable license: http://www.gutenberg.org/license 0aElectronic books.2 aProject Gutenberg.40uhttp://www.gutenberg.org/etext/10607yClick here to access a downloadable ebook.'; 103 | var ws = fs.createWriteStream('/tmp/the_real_mother_goose.mrc'); 104 | var is = fs.createReadStream('test/data/the_real_mother_goose.mrc'); 105 | return is.pipe(parser).pipe(transformer).pipe(ws).on('finish', function() { 106 | return fs.readFile('/tmp/the_real_mother_goose.mrc', function(err, data) { 107 | //expect(data.toString().length).equal(mrc.length); 108 | expect(data.toString()).equal(mrc); 109 | return fs.unlink('/tmp/the_real_mother_goose.mrc', done); 110 | }); 111 | }); 112 | }); 113 | 114 | it('should mrkify the records with callback', function(done) { 115 | var data = fs.readFileSync('test/data/PGA_2records.mrk'); 116 | data = data.toString().replace(/\r\n?/g, os.EOL) + os.EOL; 117 | transform(records, {format: 'mrk'}, function(err, output) { 118 | expect(output.length).equal(data.length); 119 | expect(output).equal(data); 120 | done(); 121 | }); 122 | }); 123 | 124 | it('should mrkify one record', function(done) { 125 | transform(records[0], {toFormat: 'mrk'}, function(err, output) { 126 | expect(output).to.be.not.null; 127 | done(); 128 | }); 129 | }); 130 | 131 | it('should mrkify with a flowing stream API', function(done) { 132 | var textifier = transform({objectMode: true, format: 'mrk'}); 133 | var output = ''; 134 | textifier.on('data', function(record) { 135 | output += record; 136 | }); 137 | textifier.on('error', function(err) { 138 | console.log(err.message); 139 | }); 140 | textifier.on('end', function() { 141 | var data = fs.readFileSync('test/data/PGA_2records.mrk'); 142 | data = data.toString().replace(/\r\n?/g, os.EOL) + os.EOL; 143 | expect(output.length).equal(data.length); 144 | expect(output).equal(data.toString()); 145 | done(); 146 | }); 147 | records.forEach(function(record) { 148 | textifier.write(record); 149 | }); 150 | textifier.end(); 151 | }); 152 | 153 | it('should mrkify with a non-flowing stream API', function(done) { 154 | var textifier = transform({objectMode: false, format: 'mrk'}); 155 | var output = ''; 156 | textifier.on('readable', function() { 157 | var record; 158 | while (record = textifier.read()) { 159 | output += record; 160 | } 161 | }); 162 | textifier.on('error', function(err) { 163 | console.log(err.message); 164 | }); 165 | textifier.on('end', function() { 166 | var data = fs.readFileSync('test/data/PGA_2records.mrk'); 167 | data = data.toString().replace(/\r\n?/g, os.EOL) + os.EOL; 168 | expect(output.length).equal(data.length); 169 | expect(output).equal(data.toString()); 170 | done(); 171 | }); 172 | records.forEach(function(record) { 173 | textifier.write(record); 174 | }); 175 | textifier.end(); 176 | }); 177 | 178 | it('should textify the records with callback', function(done) { 179 | var data = fs.readFileSync('test/data/PGA_2records.txt'); 180 | data = data.toString().replace(/\r\n?/g, os.EOL) + os.EOL; 181 | transform(records, {format: 'text'}, function(err, output) { 182 | expect(output.length).equal(data.length); 183 | expect(output).equal(data); 184 | done(); 185 | }); 186 | }); 187 | 188 | it('should textify one record', function(done) { 189 | transform(records[0], {toFormat: 'text'}, function(err, output) { 190 | expect(output).to.be.not.null; 191 | done(); 192 | }); 193 | }); 194 | 195 | it('should textify with a flowing stream API', function(done) { 196 | var textifier = transform({objectMode: true, format: 'text'}); 197 | var output = ''; 198 | textifier.on('data', function(record) { 199 | output += record; 200 | }); 201 | textifier.on('error', function(err) { 202 | console.log(err.message); 203 | }); 204 | textifier.on('end', function() { 205 | var data = fs.readFileSync('test/data/PGA_2records.txt'); 206 | data = data.toString().replace(/\r\n?/g, os.EOL) + os.EOL; 207 | expect(output.length).equal(data.length); 208 | expect(output).equal(data.toString()); 209 | done(); 210 | }); 211 | records.forEach(function(record) { 212 | textifier.write(record); 213 | }); 214 | textifier.end(); 215 | }); 216 | 217 | it('should textify with a non-flowing stream API', function(done) { 218 | var textifier = transform({objectMode: false, format: 'text'}); 219 | var output = ''; 220 | textifier.on('readable', function() { 221 | var record; 222 | while (record = textifier.read()) { 223 | output += record; 224 | } 225 | }); 226 | textifier.on('error', function(err) { 227 | console.log(err.message); 228 | }); 229 | textifier.on('end', function() { 230 | var data = fs.readFileSync('test/data/PGA_2records.txt'); 231 | data = data.toString().replace(/\r\n?/g, os.EOL) + os.EOL; 232 | expect(output.length).equal(data.length); 233 | expect(output).equal(data.toString()); 234 | done(); 235 | }); 236 | records.forEach(function(record) { 237 | textifier.write(record); 238 | }); 239 | textifier.end(); 240 | }); 241 | 242 | it('should marcxmlify with a non-flowing stream API', function(done) { 243 | transform(records[0], {toFormat: 'marcxml'}, function(err, output) { 244 | expect(output).to.be.not.null; 245 | done(); 246 | }); 247 | }); 248 | 249 | it('should transform to marc-in-json format', function(done) { 250 | transform(records, {toFormat: 'mij'}, function(err, output) { 251 | expect(output).to.be.not.null; 252 | var obj = JSON.parse(output); 253 | expect(obj[0].leader).equal('00307nam a2200085Ia 45e0'); 254 | expect(obj[1].fields.length).equal(5); 255 | done(); 256 | }); 257 | }); 258 | 259 | }); -------------------------------------------------------------------------------- /test/util.js: -------------------------------------------------------------------------------- 1 | var Util = require('../lib/util'); 2 | 3 | describe('Util', function () { 4 | it('should recognize integer with function isInt', function () { 5 | expect(Util.isInt(1234)).to.be.true(); 6 | expect(Util.isInt("1234")).to.be.true(); 7 | expect(Util.isInt(12.34)).to.be.false(); 8 | expect(Util.isInt("12.34")).to.be.false(); 9 | expect(Util.isInt(undefined)).to.be.false(); 10 | expect(Util.isInt(null)).to.be.false(); 11 | expect(Util.isInt("")).to.be.false(); 12 | }); 13 | 14 | it('should format integer', function() { 15 | expect(Util.formatInteger(23, 5)).equal('00023'); 16 | }); 17 | }); --------------------------------------------------------------------------------