From ef6f08b6bc12a7d4cc1dfb97e09312841fbc7b05 Mon Sep 17 00:00:00 2001 From: alma Date: Sun, 20 Apr 2025 21:50:30 +0200 Subject: [PATCH] carnet panel contact --- app/carnet/page.tsx | 126 +--- node_modules/.bin/quoted-printable | 1 + node_modules/.package-lock.json | 28 + node_modules/quoted-printable/LICENSE-MIT.txt | 20 + node_modules/quoted-printable/README.md | 151 ++++ .../quoted-printable/bin/quoted-printable | 116 +++ .../quoted-printable/man/quoted-printable.1 | 58 ++ node_modules/quoted-printable/package.json | 57 ++ .../quoted-printable/quoted-printable.js | 153 ++++ node_modules/utf8/LICENSE-MIT.txt | 20 + node_modules/utf8/README.md | 119 +++ node_modules/utf8/package.json | 39 + node_modules/utf8/utf8.js | 244 ++++++ node_modules/vcard-js/.npmignore | 6 + node_modules/vcard-js/README.md | 89 +++ node_modules/vcard-js/index.js | 1 + node_modules/vcard-js/lib/VCard.js | 713 ++++++++++++++++++ node_modules/vcard-js/package.json | 31 + node_modules/vcard-js/test/helloWorld.js | 21 + node_modules/vcard-js/test/mix.vcf | 73 ++ node_modules/vcard-js/test/mix.vcf.json | 498 ++++++++++++ node_modules/vcard-js/test/mix.vcf.txt | 67 ++ node_modules/vcard-js/test/test.js | 66 ++ package-lock.json | 31 +- package.json | 3 +- types/vcard-js.d.ts | 8 + yarn.lock | 20 + 27 files changed, 2657 insertions(+), 102 deletions(-) create mode 120000 node_modules/.bin/quoted-printable create mode 100644 node_modules/quoted-printable/LICENSE-MIT.txt create mode 100644 node_modules/quoted-printable/README.md create mode 100755 node_modules/quoted-printable/bin/quoted-printable create mode 100644 node_modules/quoted-printable/man/quoted-printable.1 create mode 100644 node_modules/quoted-printable/package.json create mode 100644 node_modules/quoted-printable/quoted-printable.js create mode 100644 node_modules/utf8/LICENSE-MIT.txt create mode 100644 node_modules/utf8/README.md create mode 100644 node_modules/utf8/package.json create mode 100644 node_modules/utf8/utf8.js create mode 100644 node_modules/vcard-js/.npmignore create mode 100644 node_modules/vcard-js/README.md create mode 100644 node_modules/vcard-js/index.js create mode 100644 node_modules/vcard-js/lib/VCard.js create mode 100644 node_modules/vcard-js/package.json create mode 100644 node_modules/vcard-js/test/helloWorld.js create mode 100644 node_modules/vcard-js/test/mix.vcf create mode 100644 node_modules/vcard-js/test/mix.vcf.json create mode 100644 node_modules/vcard-js/test/mix.vcf.txt create mode 100644 node_modules/vcard-js/test/test.js create mode 100644 types/vcard-js.d.ts diff --git a/app/carnet/page.tsx b/app/carnet/page.tsx index 8950cbc6..845c1da9 100644 --- a/app/carnet/page.tsx +++ b/app/carnet/page.tsx @@ -11,6 +11,7 @@ import { useMediaQuery } from "@/hooks/use-media-query"; import { ContactsView } from '@/components/carnet/contacts-view'; import { X, Menu } from "lucide-react"; import { ContactDetails } from '@/components/carnet/contact-details'; +import { VCard } from 'vcard-js'; // Layout modes export enum PaneLayout { @@ -163,93 +164,21 @@ export default function CarnetPage() { const parseVCard = (content: string): Contact[] => { try { - console.log('Raw VCF content:', content); - - // Split the content into individual vCards - const vcardSections = content.split('BEGIN:VCARD').filter(section => section.trim()); - console.log('Found vCard sections:', vcardSections.length); - - return vcardSections.map(section => { - const lines = section.split('\n').filter(line => line.trim()); - console.log('Processing vCard section with lines:', lines.length); + const vcards = content.split('BEGIN:VCARD').filter(section => section.trim()); + return vcards.map(section => { + const vcard = new VCard(); + vcard.parse('BEGIN:VCARD' + section); - const contact: Partial = { - id: Math.random().toString(36).substr(2, 9), - fullName: '', - email: '', - phone: '', - organization: '', - address: '', - notes: '' + return { + id: vcard.getProperty('UID')?.value || Math.random().toString(36).substr(2, 9), + fullName: vcard.getProperty('FN')?.value || '', + email: vcard.getProperty('EMAIL')?.value || '', + phone: vcard.getProperty('TEL')?.value || '', + organization: vcard.getProperty('ORG')?.value || '', + address: vcard.getProperty('ADR')?.value || '', + notes: vcard.getProperty('NOTE')?.value || '', + group: vcard.getProperty('CATEGORIES')?.value || '' }; - - lines.forEach(line => { - console.log('Processing line:', line); - // Handle FN (Formatted Name) - if (line.startsWith('FN:')) { - contact.fullName = line.substring(3).trim(); - console.log('Found full name:', contact.fullName); - } - // Handle N (Name components) - else if (line.startsWith('N:')) { - const nameParts = line.substring(2).split(';'); - if (nameParts.length >= 2) { - const lastName = nameParts[0]?.trim(); - const firstName = nameParts[1]?.trim(); - if (!contact.fullName) { - contact.fullName = `${firstName} ${lastName}`.trim(); - console.log('Constructed full name from N field:', contact.fullName); - } - } - } - // Handle EMAIL - else if (line.startsWith('EMAIL;')) { - const email = line.split(':')[1]; - if (email) { - contact.email = email.trim(); - console.log('Found email:', contact.email); - } - } - // Handle TEL - else if (line.startsWith('TEL;')) { - const phone = line.split(':')[1]; - if (phone) { - contact.phone = phone.trim(); - console.log('Found phone:', contact.phone); - } - } - // Handle ORG - else if (line.startsWith('ORG:')) { - contact.organization = line.substring(4).trim(); - console.log('Found organization:', contact.organization); - } - // Handle ADR - else if (line.startsWith('ADR;')) { - const addressParts = line.split(':')[1].split(';'); - if (addressParts.length >= 7) { - const street = addressParts[2]?.trim(); - const city = addressParts[3]?.trim(); - const state = addressParts[4]?.trim(); - const zip = addressParts[5]?.trim(); - const country = addressParts[6]?.trim(); - contact.address = [street, city, state, zip, country].filter(Boolean).join(', '); - console.log('Found address:', contact.address); - } - } - // Handle NOTE - else if (line.startsWith('NOTE:')) { - contact.notes = line.substring(5).trim(); - console.log('Found notes:', contact.notes); - } - }); - - if (!contact.fullName) { - contact.fullName = 'Unknown Contact'; - console.log('No name found, using default'); - } - - console.log('Final contact object:', contact); - return contact as Contact; }); } catch (error) { console.error('Error parsing VCF content:', error); @@ -548,21 +477,18 @@ export default function CarnetPage() { }; const generateVCard = (contact: Contact): string => { - const vcard = [ - 'BEGIN:VCARD', - 'VERSION:3.0', - `UID:${contact.id}`, - `FN:${contact.fullName}`, - contact.email ? `EMAIL;TYPE=INTERNET:${contact.email}` : '', - contact.phone ? `TEL;TYPE=CELL:${contact.phone}` : '', - contact.organization ? `ORG:${contact.organization}` : '', - contact.address ? `ADR;TYPE=HOME:;;${contact.address}` : '', - contact.notes ? `NOTE:${contact.notes}` : '', - contact.group ? `CATEGORIES:${contact.group}` : '', - 'END:VCARD' - ].filter(line => line !== '').join('\r\n'); - - return vcard; + const vcard = new VCard(); + + vcard.setProperty('UID', contact.id); + vcard.setProperty('FN', contact.fullName); + if (contact.email) vcard.setProperty('EMAIL', contact.email); + if (contact.phone) vcard.setProperty('TEL', contact.phone); + if (contact.organization) vcard.setProperty('ORG', contact.organization); + if (contact.address) vcard.setProperty('ADR', contact.address); + if (contact.notes) vcard.setProperty('NOTE', contact.notes); + if (contact.group) vcard.setProperty('CATEGORIES', contact.group); + + return vcard.toString(); }; if (isLoading) { diff --git a/node_modules/.bin/quoted-printable b/node_modules/.bin/quoted-printable new file mode 120000 index 00000000..49c5de4d --- /dev/null +++ b/node_modules/.bin/quoted-printable @@ -0,0 +1 @@ +../quoted-printable/bin/quoted-printable \ No newline at end of file diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json index 9c8c7ec0..773dd779 100644 --- a/node_modules/.package-lock.json +++ b/node_modules/.package-lock.json @@ -4603,6 +4603,18 @@ "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", "license": "MIT" }, + "node_modules/quoted-printable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/quoted-printable/-/quoted-printable-1.0.1.tgz", + "integrity": "sha512-cihC68OcGiQOjGiXuo5Jk6XHANTHl1K4JLk/xlEJRTIXfy19Sg6XzB95XonYgr+1rB88bCpr7WZE7D7AlZow4g==", + "license": "MIT", + "dependencies": { + "utf8": "^2.1.0" + }, + "bin": { + "quoted-printable": "bin/quoted-printable" + } + }, "node_modules/react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", @@ -5487,6 +5499,12 @@ "semver": "~5.3.0" } }, + "node_modules/utf8": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-2.1.2.tgz", + "integrity": "sha512-QXo+O/QkLP/x1nyi54uQiG0XrODxdysuQvE5dtVqv7F5K2Qb6FsN+qbr6KhF5wQ20tfcV3VQp0/2x1e1MRSPWg==", + "license": "MIT" + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -5514,6 +5532,16 @@ "react-dom": "^16.8 || ^17.0 || ^18.0" } }, + "node_modules/vcard-js": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/vcard-js/-/vcard-js-1.2.2.tgz", + "integrity": "sha512-YShz0/tIXJ3gOg2o/OCNzuPCb7KTnhjn81cUaTZg5+elb99yVr+MRMkbD+vljTny6YuocPI0Dtb1ttG2Yf8oMg==", + "license": "ISC", + "dependencies": { + "quoted-printable": "^1.0.0", + "utf8": "^2.1.1" + } + }, "node_modules/victory-vendor": { "version": "36.9.2", "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", diff --git a/node_modules/quoted-printable/LICENSE-MIT.txt b/node_modules/quoted-printable/LICENSE-MIT.txt new file mode 100644 index 00000000..a41e0a7e --- /dev/null +++ b/node_modules/quoted-printable/LICENSE-MIT.txt @@ -0,0 +1,20 @@ +Copyright Mathias Bynens + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/quoted-printable/README.md b/node_modules/quoted-printable/README.md new file mode 100644 index 00000000..f8d2b4c9 --- /dev/null +++ b/node_modules/quoted-printable/README.md @@ -0,0 +1,151 @@ +# quoted-printable [![Build status](https://travis-ci.org/mathiasbynens/quoted-printable.svg?branch=master)](https://travis-ci.org/mathiasbynens/quoted-printable) [![Code coverage status](https://coveralls.io/repos/mathiasbynens/quoted-printable/badge.svg)](https://coveralls.io/r/mathiasbynens/quoted-printable) [![Dependency status](https://gemnasium.com/mathiasbynens/quoted-printable.svg)](https://gemnasium.com/mathiasbynens/quoted-printable) + +_quoted-printable_ is a character encoding–agnostic JavaScript implementation of [the `Quoted-Printable` content transfer encoding as defined by RFC 2045](https://tools.ietf.org/html/rfc2045#section-6.7). It can be used to encode plaintext to its `Quoted-Printable` encoding, or the other way around (i.e. decoding). [Here’s an online demo using the UTF-8 character encoding.](https://mothereff.in/quoted-printable) + +## Installation + +Via [npm](https://www.npmjs.com/): + +```bash +npm install quoted-printable +``` + +Via [Bower](http://bower.io/): + +```bash +bower install quoted-printable +``` + +Via [Component](https://github.com/component/component): + +```bash +component install mathiasbynens/quoted-printable +``` + +In a browser: + +```html + +``` + +In [Node.js](https://nodejs.org/), [io.js](https://iojs.org/), [Narwhal](http://narwhaljs.org/), and [RingoJS](http://ringojs.org/): + +```js +var quotedPrintable = require('quoted-printable'); +``` + +In [Rhino](http://www.mozilla.org/rhino/): + +```js +load('quoted-printable.js'); +``` + +Using an AMD loader like [RequireJS](http://requirejs.org/): + +```js +require( + { + 'paths': { + 'quoted-printable': 'path/to/quoted-printable' + } + }, + ['quoted-printable'], + function(quotedPrintable) { + console.log(quotedPrintable); + } +); +``` + +## API + +### `quotedPrintable.version` + +A string representing the semantic version number. + +### `quotedPrintable.encode(input)` + +This function takes an encoded byte string (the `input` parameter) and `Quoted-Printable`-encodes it. Each item in the input string represents an octet as per the desired character encoding. Here’s an example that uses UTF-8: + +```js +var utf8 = require('utf8'); + +quotedPrintable.encode(utf8.encode('foo=bar')); +// → 'foo=3Dbar' + +quotedPrintable.encode(utf8.encode('Iñtërnâtiônàlizætiøn☃💩')); +// → 'I=C3=B1t=C3=ABrn=C3=A2ti=C3=B4n=C3=A0liz=C3=A6ti=C3=B8n=E2=98=83=F0=9F=92=\r\n=A9' +``` + +### `quotedPrintable.decode(text)` + +This function takes a string of text (the `text` parameter) and `Quoted-Printable`-decodes it. The return value is a ‘byte string’, i.e. a string of which each item represents an octet as per the character encoding that’s being used. Here’s an example that uses UTF-8: + +```js +var utf8 = require('utf8'); + +utf8.decode(quotedPrintable.decode('foo=3Dbar')); +// → 'foo=bar' + +utf8.decode(quotedPrintable.decode('I=C3=B1t=C3=ABrn=C3=A2ti=C3=B4n=C3=A0liz=C3=A6ti=C3=B8n=E2=98=83=F0=9F=92=\r\n=A9')); +// → 'Iñtërnâtiônàlizætiøn☃💩' +``` + +### Using the `quoted-printable` binary + +To use the `quoted-printable` binary in your shell, simply install _quoted-printable_ globally using npm: + +```bash +npm install -g quoted-printable +``` + +After that, you’ll be able to use `quoted-printable` on the command line. Note that while the _quoted-printable_ library itself is character encoding–agnostic, the command-line tool applies the UTF-8 character encoding on all input. + +```bash +$ quoted-printable --encode 'foo=bar' +foo=3Dbar + +$ quoted-printable --decode 'foo=3Dbar' +foo=bar +``` + +Read a local text file, `Quoted-Printable`-encode it, and save the result to a new file: + +```bash +$ quoted-printable --encode < foo.txt > foo-quoted-printable.txt +``` + +Or do the same with an online text file: + +```bash +$ curl -sL 'https://mths.be/brh' | quoted-printable --encode > quoted-printable.txt +``` + +Or, the opposite — read a local file containing a `Quoted-Printable`-encoded message, decode it back to plain text, and save the result to a new file: + +```bash +$ quoted-printable --decode < quoted-printable.txt > original.txt +``` + +See `quoted-printable --help` for the full list of options. + +## Support + +_quoted-printable_ is designed to work in at least Node.js v0.10.0, io.js v1.0.0, Narwhal 0.3.2, RingoJS 0.8-0.11, PhantomJS 1.9.0, Rhino 1.7RC4, as well as old and modern versions of Chrome, Firefox, Safari, Opera, and Internet Explorer. + +## Unit tests & code coverage + +After cloning this repository, run `npm install` to install the dependencies needed for development and testing. You may want to install Istanbul _globally_ using `npm install istanbul -g`. + +Once that’s done, you can run the unit tests in Node using `npm test` or `node tests/tests.js`. To run the tests in Rhino, Ringo, Narwhal, and web browsers as well, use `grunt test`. + +To generate the code coverage report, use `grunt cover`. + +## Author + +| [![twitter/mathias](https://gravatar.com/avatar/24e08a9ea84deb17ae121074d0f17125?s=70)](https://twitter.com/mathias "Follow @mathias on Twitter") | +|---| +| [Mathias Bynens](https://mathiasbynens.be/) | + +## License + +_quoted-printable_ is available under the [MIT](https://mths.be/mit) license. diff --git a/node_modules/quoted-printable/bin/quoted-printable b/node_modules/quoted-printable/bin/quoted-printable new file mode 100755 index 00000000..fc424160 --- /dev/null +++ b/node_modules/quoted-printable/bin/quoted-printable @@ -0,0 +1,116 @@ +#!/usr/bin/env node +(function() { + + var fs = require('fs'); + var utf8 = require('utf8'); + var quotedPrintable = require('../quoted-printable.js'); + var strings = process.argv.splice(2); + var stdin = process.stdin; + var data; + var timeout; + var action; + var options = {}; + var log = console.log; + + var main = function() { + var option = strings[0]; + var count = 0; + + if (/^(?:-h|--help|undefined)$/.test(option)) { + log( + 'quoted-printable v%s - https://mths.be/quoted-printable', + quotedPrintable.version + ); + log([ + '\nEncode or decode messages using the `Quoted-Printable` encoding (and UTF-8).', + '\nUsage:\n', + '\tquoted-printable [-e | --encode] string', + '\tquoted-printable [-d | --decode] string', + '\tquoted-printable [-v | --version]', + '\tquoted-printable [-h | --help]', + '\nExamples:\n', + '\tquoted-printable --encode \'foo = bar ©\'', + '\techo \'foo =3D bar =C2=A9\' | quoted-printable --decode', + ].join('\n')); + return process.exit(1); + } + + if (/^(?:-v|--version)$/.test(option)) { + log('v%s', quotedPrintable.version); + return process.exit(1); + } + + strings.forEach(function(string) { + // Process options + if (string == '-e' || string == '--encode') { + action = 'encode'; + return; + } + if (string == '-d' || string == '--decode') { + action = 'decode'; + return; + } + // Process string(s) + var result; + if (!action) { + log('Error: quoted-printable requires at least one option and a string argument.'); + log('Try `quoted-printable --help` for more information.'); + return process.exit(1); + } + try { + if (action == 'encode') { + result = quotedPrintable.encode(utf8.encode(string, options)); + } else if (action == 'decode') { + result = utf8.decode(quotedPrintable.decode(string, options)); + } + log(result); + count++; + } catch (exception) { + log(exception.message + '\n'); + log('Error: failed to %s.', action); + log('If you think this is a bug in quoted-printable, please report it:'); + log('https://github.com/mathiasbynens/quoted-printable/issues/new'); + log('\nStack trace using quoted-printable@%s:\n', quotedPrintable.version); + log(exception.stack); + return process.exit(1); + } + }); + if (!count) { + log('Error: quoted-printable requires a string argument.'); + log('Try `quoted-printable --help` for more information.'); + return process.exit(1); + } + // Return with exit status 0 outside of the `forEach` loop, in case + // multiple strings were passed in. + return process.exit(0); + }; + + if (stdin.isTTY) { + // handle shell arguments + main(); + } else { + // Either the script is called from within a non-TTY context, or `stdin` + // content is being piped in. + if (!process.stdout.isTTY) { + // The script was called from a non-TTY context. This is a rather uncommon + // use case we don’t actively support. However, we don’t want the script + // to wait forever in such cases, so… + timeout = setTimeout(function() { + // …if no piped data arrived after a whole minute, handle shell + // arguments instead. + main(); + }, 60000); + } + data = ''; + stdin.on('data', function(chunk) { + clearTimeout(timeout); + data += chunk; + }); + stdin.on('end', function() { + strings.push(data.trim()); + main(); + }); + stdin.resume(); + } + +}()); diff --git a/node_modules/quoted-printable/man/quoted-printable.1 b/node_modules/quoted-printable/man/quoted-printable.1 new file mode 100644 index 00000000..a841f00d --- /dev/null +++ b/node_modules/quoted-printable/man/quoted-printable.1 @@ -0,0 +1,58 @@ +.Dd May 4, 2014 +.Dt quoted-printable 1 +.Sh NAME +.Nm quoted-printable +.Nd encode or decode messages using the `Quoted-Printable` content transfer encoding +.Sh SYNOPSIS +.Nm +.Op Fl e | -encode Ar string +.br +.Op Fl d | -decode Ar string +.br +.Op Fl v | -version +.br +.Op Fl h | -help +.Sh DESCRIPTION +.Nm +encode or decode messages using the `Quoted-Printable` content transfer encoding. +.Sh OPTIONS +.Bl -ohang -offset +.It Sy "--encode" +Encode a string of text using the `Quoted-Printable` content transfer encoding. +.It Sy "--decode" +Decode a string of text using the `Quoted-Printable` content transfer encoding. +.It Sy "-v, --version" +Print quoted-printable's version. +.It Sy "-h, --help" +Show the help screen. +.El +.Sh EXIT STATUS +The +.Nm quoted-printable +utility exits with one of the following values: +.Pp +.Bl -tag -width flag -compact +.It Li 0 +.Nm +successfully encoded/decoded the input and printed the result. +.It Li 1 +.Nm +wasn't instructed to encode/decode anything (for example, the +.Ar --help +flag was set); or, an error occurred. +.El +.Sh EXAMPLES +.Bl -ohang -offset +.It Sy "quoted-printable --encode 'foo = bar'" +Print an encoded version of the given string. +.It Sy "quoted-printable --decode 'foo=3Dbar'" +Print the decoded version of the given `Quoted-Printable`-encoded message. +.It Sy "echo\ 'foo = bar'\ |\ quoted-printable --encode" +Print the encoded version of the string that gets piped in. +.El +.Sh BUGS +quoted-printable's bug tracker is located at . +.Sh AUTHOR +Mathias Bynens +.Sh WWW + diff --git a/node_modules/quoted-printable/package.json b/node_modules/quoted-printable/package.json new file mode 100644 index 00000000..eaa9189d --- /dev/null +++ b/node_modules/quoted-printable/package.json @@ -0,0 +1,57 @@ +{ + "name": "quoted-printable", + "version": "1.0.1", + "description": "A robust & character encoding–agnostic JavaScript implementation of the `Quoted-Printable` content transfer encoding as defined by RFC 2045.", + "homepage": "https://mths.be/quoted-printable", + "main": "quoted-printable.js", + "bin": "bin/quoted-printable", + "man": "man/quoted-printable.1", + "keywords": [ + "decode", + "decoding", + "encode", + "encoding", + "quoted-printable", + "string" + ], + "license": "MIT", + "author": { + "name": "Mathias Bynens", + "url": "https://mathiasbynens.be/" + }, + "repository": { + "type": "git", + "url": "https://github.com/mathiasbynens/quoted-printable.git" + }, + "bugs": "https://github.com/mathiasbynens/quoted-printable/issues", + "files": [ + "LICENSE-MIT.txt", + "quoted-printable.js", + "bin/", + "man/" + ], + "directories": { + "bin": "bin", + "man": "man", + "test": "tests" + }, + "scripts": { + "test": "node tests/tests.js" + }, + "dependencies": { + "utf8": "^2.1.0" + }, + "devDependencies": { + "coveralls": "^2.11.1", + "grunt": "^0.4.5", + "grunt-shell": "^1.1.1", + "grunt-template": "^0.2.3", + "istanbul": "^0.3.5", + "jsesc": "^0.5.0", + "qunit-extras": "^1.4.0", + "qunitjs": "~1.11.0", + "regenerate": "^1.2.1", + "requirejs": "^2.1.15", + "string.fromcodepoint": "^0.2.1" + } +} diff --git a/node_modules/quoted-printable/quoted-printable.js b/node_modules/quoted-printable/quoted-printable.js new file mode 100644 index 00000000..7753f2a1 --- /dev/null +++ b/node_modules/quoted-printable/quoted-printable.js @@ -0,0 +1,153 @@ +/*! https://mths.be/quoted-printable v1.0.1 by @mathias | MIT license */ +;(function(root) { + + // Detect free variables `exports`. + var freeExports = typeof exports == 'object' && exports; + + // Detect free variable `module`. + var freeModule = typeof module == 'object' && module && + module.exports == freeExports && module; + + // Detect free variable `global`, from Node.js or Browserified code, and use + // it as `root`. + var freeGlobal = typeof global == 'object' && global; + if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) { + root = freeGlobal; + } + + /*--------------------------------------------------------------------------*/ + + var stringFromCharCode = String.fromCharCode; + var decode = function(input) { + return input + // https://tools.ietf.org/html/rfc2045#section-6.7, rule 3: + // “Therefore, when decoding a `Quoted-Printable` body, any trailing white + // space on a line must be deleted, as it will necessarily have been added + // by intermediate transport agents.” + .replace(/[\t\x20]$/gm, '') + // Remove hard line breaks preceded by `=`. Proper `Quoted-Printable`- + // encoded data only contains CRLF line endings, but for compatibility + // reasons we support separate CR and LF too. + .replace(/=(?:\r\n?|\n|$)/g, '') + // Decode escape sequences of the form `=XX` where `XX` is any + // combination of two hexidecimal digits. For optimal compatibility, + // lowercase hexadecimal digits are supported as well. See + // https://tools.ietf.org/html/rfc2045#section-6.7, note 1. + .replace(/=([a-fA-F0-9]{2})/g, function($0, $1) { + var codePoint = parseInt($1, 16); + return stringFromCharCode(codePoint); + }); + }; + + var handleTrailingCharacters = function(string) { + return string + .replace(/\x20$/, '=20') // Handle trailing space. + .replace(/\t$/, '=09') // Handle trailing tab. + }; + + var regexUnsafeSymbols = /[\0-\x08\n-\x1F=\x7F-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g; + var encode = function(string) { + + // Encode symbols that are definitely unsafe (i.e. unsafe in any context). + var encoded = string.replace(regexUnsafeSymbols, function(symbol) { + if (symbol > '\xFF') { + throw RangeError( + '`quotedPrintable.encode()` expects extended ASCII input only. ' + + 'Don\u2019t forget to encode the input first using a character ' + + 'encoding like UTF-8.' + ); + } + var codePoint = symbol.charCodeAt(0); + var hexadecimal = codePoint.toString(16).toUpperCase(); + return '=' + ('0' + hexadecimal).slice(-2); + }); + + // Limit lines to 76 characters (not counting the CRLF line endings). + var lines = encoded.split(/\r\n?|\n/g); + var lineIndex = -1; + var lineCount = lines.length; + var result = []; + while (++lineIndex < lineCount) { + var line = lines[lineIndex]; + // Leave room for the trailing `=` for soft line breaks. + var LINE_LENGTH = 75; + var index = 0; + var length = line.length; + while (index < length) { + var buffer = encoded.slice(index, index + LINE_LENGTH); + // If this line ends with `=`, optionally followed by a single uppercase + // hexadecimal digit, we broke an escape sequence in half. Fix it by + // moving these characters to the next line. + if (/=$/.test(buffer)) { + buffer = buffer.slice(0, LINE_LENGTH - 1); + index += LINE_LENGTH - 1; + } else if (/=[A-F0-9]$/.test(buffer)) { + buffer = buffer.slice(0, LINE_LENGTH - 2); + index += LINE_LENGTH - 2; + } else { + index += LINE_LENGTH; + } + result.push(buffer); + } + } + + // Encode space and tab characters at the end of encoded lines. Note that + // with the current implementation, this can only occur at the very end of + // the encoded string — every other line ends with `=` anyway. + var lastLineLength = buffer.length; + if (/[\t\x20]$/.test(buffer)) { + // There’s a space or a tab at the end of the last encoded line. Remove + // this line from the `result` array, as it needs to change. + result.pop(); + if (lastLineLength + 2 <= LINE_LENGTH + 1) { + // It’s possible to encode the character without exceeding the line + // length limit. + result.push( + handleTrailingCharacters(buffer) + ); + } else { + // It’s not possible to encode the character without exceeding the line + // length limit. Remvoe the character from the line, and insert a new + // line that contains only the encoded character. + result.push( + buffer.slice(0, lastLineLength - 1), + handleTrailingCharacters( + buffer.slice(lastLineLength - 1, lastLineLength) + ) + ); + } + } + + // `Quoted-Printable` uses CRLF. + return result.join('=\r\n'); + }; + + var quotedPrintable = { + 'encode': encode, + 'decode': decode, + 'version': '1.0.1' + }; + + // Some AMD build optimizers, like r.js, check for specific condition patterns + // like the following: + if ( + typeof define == 'function' && + typeof define.amd == 'object' && + define.amd + ) { + define(function() { + return quotedPrintable; + }); + } else if (freeExports && !freeExports.nodeType) { + if (freeModule) { // in Node.js, io.js, or RingoJS v0.8.0+ + freeModule.exports = quotedPrintable; + } else { // in Narwhal or RingoJS v0.7.0- + for (var key in quotedPrintable) { + quotedPrintable.hasOwnProperty(key) && (freeExports[key] = quotedPrintable[key]); + } + } + } else { // in Rhino or a web browser + root.quotedPrintable = quotedPrintable; + } + +}(this)); diff --git a/node_modules/utf8/LICENSE-MIT.txt b/node_modules/utf8/LICENSE-MIT.txt new file mode 100644 index 00000000..a41e0a7e --- /dev/null +++ b/node_modules/utf8/LICENSE-MIT.txt @@ -0,0 +1,20 @@ +Copyright Mathias Bynens + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/utf8/README.md b/node_modules/utf8/README.md new file mode 100644 index 00000000..36b8de07 --- /dev/null +++ b/node_modules/utf8/README.md @@ -0,0 +1,119 @@ +# utf8.js [![Build status](https://travis-ci.org/mathiasbynens/utf8.js.svg?branch=master)](https://travis-ci.org/mathiasbynens/utf8.js) [![Code coverage status](http://img.shields.io/coveralls/mathiasbynens/utf8.js/master.svg)](https://coveralls.io/r/mathiasbynens/utf8.js) [![Dependency status](https://gemnasium.com/mathiasbynens/utf8.js.svg)](https://gemnasium.com/mathiasbynens/utf8.js) + +_utf8.js_ is a well-tested UTF-8 encoder/decoder written in JavaScript. Unlike many other JavaScript solutions, it is designed to be a _proper_ UTF-8 encoder/decoder: it can encode/decode any scalar Unicode code point values, as per [the Encoding Standard](https://encoding.spec.whatwg.org/#utf-8). [Here’s an online demo.](https://mothereff.in/utf-8) + +Feel free to fork if you see possible improvements! + +## Installation + +Via [npm](https://www.npmjs.com/): + +```bash +npm install utf8 +``` + +Via [Bower](http://bower.io/): + +```bash +bower install utf8 +``` + +Via [Component](https://github.com/component/component): + +```bash +component install mathiasbynens/utf8.js +``` + +In a browser: + +```html + +``` + +In [Narwhal](http://narwhaljs.org/), [Node.js](https://nodejs.org/), and [RingoJS ≥ v0.8.0](http://ringojs.org/): + +```js +var utf8 = require('utf8'); +``` + +In [Rhino](http://www.mozilla.org/rhino/): + +```js +load('utf8.js'); +``` + +Using an AMD loader like [RequireJS](http://requirejs.org/): + +```js +require( + { + 'paths': { + 'utf8': 'path/to/utf8' + } + }, + ['utf8'], + function(utf8) { + console.log(utf8); + } +); +``` + +## API + +### `utf8.encode(string)` + +Encodes any given JavaScript string (`string`) as UTF-8, and returns the UTF-8-encoded version of the string. It throws an error if the input string contains a non-scalar value, i.e. a lone surrogate. (If you need to be able to encode non-scalar values as well, use [WTF-8](https://mths.be/wtf8) instead.) + +```js +// U+00A9 COPYRIGHT SIGN; see http://codepoints.net/U+00A9 +utf8.encode('\xA9'); +// → '\xC2\xA9' +// U+10001 LINEAR B SYLLABLE B038 E; see http://codepoints.net/U+10001 +utf8.encode('\uD800\uDC01'); +// → '\xF0\x90\x80\x81' +``` + +### `utf8.decode(byteString)` + +Decodes any given UTF-8-encoded string (`byteString`) as UTF-8, and returns the UTF-8-decoded version of the string. It throws an error when malformed UTF-8 is detected. (If you need to be able to decode encoded non-scalar values as well, use [WTF-8](https://mths.be/wtf8) instead.) + +```js +utf8.decode('\xC2\xA9'); +// → '\xA9' + +utf8.decode('\xF0\x90\x80\x81'); +// → '\uD800\uDC01' +// → U+10001 LINEAR B SYLLABLE B038 E +``` + +### `utf8.version` + +A string representing the semantic version number. + +## Support + +utf8.js has been tested in at least Chrome 27-39, Firefox 3-34, Safari 4-8, Opera 10-28, IE 6-11, Node.js v0.10.0, Narwhal 0.3.2, RingoJS 0.8-0.11, PhantomJS 1.9.0, and Rhino 1.7RC4. + +## Unit tests & code coverage + +After cloning this repository, run `npm install` to install the dependencies needed for development and testing. You may want to install Istanbul _globally_ using `npm install istanbul -g`. + +Once that’s done, you can run the unit tests in Node using `npm test` or `node tests/tests.js`. To run the tests in Rhino, Ringo, Narwhal, PhantomJS, and web browsers as well, use `grunt test`. + +To generate the code coverage report, use `grunt cover`. + +## FAQ + +### Why is the first release named v2.0.0? Haven’t you heard of [semantic versioning](http://semver.org/)? + +Long before utf8.js was created, the `utf8` module on npm was registered and used by another (slightly buggy) library. @ryanmcgrath was kind enough to give me access to the `utf8` package on npm when I told him about utf8.js. Since there has already been a v1.0.0 release of the old library, and to avoid breaking backwards compatibility with projects that rely on the `utf8` npm package, I decided the tag the first release of utf8.js as v2.0.0 and take it from there. + +## Author + +| [![twitter/mathias](https://gravatar.com/avatar/24e08a9ea84deb17ae121074d0f17125?s=70)](https://twitter.com/mathias "Follow @mathias on Twitter") | +|---| +| [Mathias Bynens](https://mathiasbynens.be/) | + +## License + +utf8.js is available under the [MIT](https://mths.be/mit) license. diff --git a/node_modules/utf8/package.json b/node_modules/utf8/package.json new file mode 100644 index 00000000..91dd9f27 --- /dev/null +++ b/node_modules/utf8/package.json @@ -0,0 +1,39 @@ +{ + "name": "utf8", + "version": "2.1.2", + "description": "A well-tested UTF-8 encoder/decoder written in JavaScript.", + "homepage": "https://mths.be/utf8js", + "main": "utf8.js", + "keywords": [ + "charset", + "encoding", + "unicode", + "utf8" + ], + "license": "MIT", + "author": { + "name": "Mathias Bynens", + "url": "https://mathiasbynens.be/" + }, + "repository": { + "type": "git", + "url": "https://github.com/mathiasbynens/utf8.js.git" + }, + "bugs": "https://github.com/mathiasbynens/utf8.js/issues", + "files": [ + "LICENSE-MIT.txt", + "utf8.js" + ], + "scripts": { + "test": "node tests/tests.js" + }, + "devDependencies": { + "coveralls": "^2.11.14", + "grunt": "^1.0.1", + "grunt-shell": "^1.1.2", + "istanbul": "^0.4.5", + "qunit-extras": "^1.4.2", + "qunitjs": "~1.11.0", + "requirejs": "^2.3.2" + } +} diff --git a/node_modules/utf8/utf8.js b/node_modules/utf8/utf8.js new file mode 100644 index 00000000..c55e85cc --- /dev/null +++ b/node_modules/utf8/utf8.js @@ -0,0 +1,244 @@ +/*! https://mths.be/utf8js v2.1.2 by @mathias */ +;(function(root) { + + // Detect free variables `exports` + var freeExports = typeof exports == 'object' && exports; + + // Detect free variable `module` + var freeModule = typeof module == 'object' && module && + module.exports == freeExports && module; + + // Detect free variable `global`, from Node.js or Browserified code, + // and use it as `root` + var freeGlobal = typeof global == 'object' && global; + if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) { + root = freeGlobal; + } + + /*--------------------------------------------------------------------------*/ + + var stringFromCharCode = String.fromCharCode; + + // Taken from https://mths.be/punycode + function ucs2decode(string) { + var output = []; + var counter = 0; + var length = string.length; + var value; + var extra; + while (counter < length) { + value = string.charCodeAt(counter++); + if (value >= 0xD800 && value <= 0xDBFF && counter < length) { + // high surrogate, and there is a next character + extra = string.charCodeAt(counter++); + if ((extra & 0xFC00) == 0xDC00) { // low surrogate + output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000); + } else { + // unmatched surrogate; only append this code unit, in case the next + // code unit is the high surrogate of a surrogate pair + output.push(value); + counter--; + } + } else { + output.push(value); + } + } + return output; + } + + // Taken from https://mths.be/punycode + function ucs2encode(array) { + var length = array.length; + var index = -1; + var value; + var output = ''; + while (++index < length) { + value = array[index]; + if (value > 0xFFFF) { + value -= 0x10000; + output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800); + value = 0xDC00 | value & 0x3FF; + } + output += stringFromCharCode(value); + } + return output; + } + + function checkScalarValue(codePoint) { + if (codePoint >= 0xD800 && codePoint <= 0xDFFF) { + throw Error( + 'Lone surrogate U+' + codePoint.toString(16).toUpperCase() + + ' is not a scalar value' + ); + } + } + /*--------------------------------------------------------------------------*/ + + function createByte(codePoint, shift) { + return stringFromCharCode(((codePoint >> shift) & 0x3F) | 0x80); + } + + function encodeCodePoint(codePoint) { + if ((codePoint & 0xFFFFFF80) == 0) { // 1-byte sequence + return stringFromCharCode(codePoint); + } + var symbol = ''; + if ((codePoint & 0xFFFFF800) == 0) { // 2-byte sequence + symbol = stringFromCharCode(((codePoint >> 6) & 0x1F) | 0xC0); + } + else if ((codePoint & 0xFFFF0000) == 0) { // 3-byte sequence + checkScalarValue(codePoint); + symbol = stringFromCharCode(((codePoint >> 12) & 0x0F) | 0xE0); + symbol += createByte(codePoint, 6); + } + else if ((codePoint & 0xFFE00000) == 0) { // 4-byte sequence + symbol = stringFromCharCode(((codePoint >> 18) & 0x07) | 0xF0); + symbol += createByte(codePoint, 12); + symbol += createByte(codePoint, 6); + } + symbol += stringFromCharCode((codePoint & 0x3F) | 0x80); + return symbol; + } + + function utf8encode(string) { + var codePoints = ucs2decode(string); + var length = codePoints.length; + var index = -1; + var codePoint; + var byteString = ''; + while (++index < length) { + codePoint = codePoints[index]; + byteString += encodeCodePoint(codePoint); + } + return byteString; + } + + /*--------------------------------------------------------------------------*/ + + function readContinuationByte() { + if (byteIndex >= byteCount) { + throw Error('Invalid byte index'); + } + + var continuationByte = byteArray[byteIndex] & 0xFF; + byteIndex++; + + if ((continuationByte & 0xC0) == 0x80) { + return continuationByte & 0x3F; + } + + // If we end up here, it’s not a continuation byte + throw Error('Invalid continuation byte'); + } + + function decodeSymbol() { + var byte1; + var byte2; + var byte3; + var byte4; + var codePoint; + + if (byteIndex > byteCount) { + throw Error('Invalid byte index'); + } + + if (byteIndex == byteCount) { + return false; + } + + // Read first byte + byte1 = byteArray[byteIndex] & 0xFF; + byteIndex++; + + // 1-byte sequence (no continuation bytes) + if ((byte1 & 0x80) == 0) { + return byte1; + } + + // 2-byte sequence + if ((byte1 & 0xE0) == 0xC0) { + byte2 = readContinuationByte(); + codePoint = ((byte1 & 0x1F) << 6) | byte2; + if (codePoint >= 0x80) { + return codePoint; + } else { + throw Error('Invalid continuation byte'); + } + } + + // 3-byte sequence (may include unpaired surrogates) + if ((byte1 & 0xF0) == 0xE0) { + byte2 = readContinuationByte(); + byte3 = readContinuationByte(); + codePoint = ((byte1 & 0x0F) << 12) | (byte2 << 6) | byte3; + if (codePoint >= 0x0800) { + checkScalarValue(codePoint); + return codePoint; + } else { + throw Error('Invalid continuation byte'); + } + } + + // 4-byte sequence + if ((byte1 & 0xF8) == 0xF0) { + byte2 = readContinuationByte(); + byte3 = readContinuationByte(); + byte4 = readContinuationByte(); + codePoint = ((byte1 & 0x07) << 0x12) | (byte2 << 0x0C) | + (byte3 << 0x06) | byte4; + if (codePoint >= 0x010000 && codePoint <= 0x10FFFF) { + return codePoint; + } + } + + throw Error('Invalid UTF-8 detected'); + } + + var byteArray; + var byteCount; + var byteIndex; + function utf8decode(byteString) { + byteArray = ucs2decode(byteString); + byteCount = byteArray.length; + byteIndex = 0; + var codePoints = []; + var tmp; + while ((tmp = decodeSymbol()) !== false) { + codePoints.push(tmp); + } + return ucs2encode(codePoints); + } + + /*--------------------------------------------------------------------------*/ + + var utf8 = { + 'version': '2.1.2', + 'encode': utf8encode, + 'decode': utf8decode + }; + + // Some AMD build optimizers, like r.js, check for specific condition patterns + // like the following: + if ( + typeof define == 'function' && + typeof define.amd == 'object' && + define.amd + ) { + define(function() { + return utf8; + }); + } else if (freeExports && !freeExports.nodeType) { + if (freeModule) { // in Node.js or RingoJS v0.8.0+ + freeModule.exports = utf8; + } else { // in Narwhal or RingoJS v0.7.0- + var object = {}; + var hasOwnProperty = object.hasOwnProperty; + for (var key in utf8) { + hasOwnProperty.call(utf8, key) && (freeExports[key] = utf8[key]); + } + } + } else { // in Rhino or a web browser + root.utf8 = utf8; + } + +}(this)); diff --git a/node_modules/vcard-js/.npmignore b/node_modules/vcard-js/.npmignore new file mode 100644 index 00000000..c74b4de7 --- /dev/null +++ b/node_modules/vcard-js/.npmignore @@ -0,0 +1,6 @@ +/out +node_modules +npm-debug.log +.*.swp +.direcotry +*~ diff --git a/node_modules/vcard-js/README.md b/node_modules/vcard-js/README.md new file mode 100644 index 00000000..2fd7f92b --- /dev/null +++ b/node_modules/vcard-js/README.md @@ -0,0 +1,89 @@ +# vcard-js + +## Description + +`vcard-js` is a library to deal with the vCard format. +It can parse from vCard format to jCard format, +and transform current version to other version. + +## Installation + +``` +npm install vcard-js +``` + +## Usage + +```js +var VCard = require('vcard-js'); + +var str = [ + 'BEGIN:VCARD', + 'VERSION:2.1', + 'N:Einstein', + 'FN:Albert Einstein', + 'TEL:(111) 555-6666', + 'END:VCARD' +].join(VCard.EOL); + +var arr = VCard.parse(str); + +console.log(VCard.serialize(arr)); +console.log(VCard.serialize(arr, '3.0')); + +arr.forEach(function(vCard){ + console.log(vCard.toString('4.0')); + console.log(vCard.toJSON()); +}); +``` + +## API + +### Classes + +#### VCard + +* `VCard.EOL` +* `VCard.MAX_WIDTH` +* `VCard.parse(data[,opt])` +* `VCard.serialize(vCards,version)` +* `VCard.serialize(vCards[,opt])` +* `VCard.readFile(filename[,opt],callback)` +* `VCard.identifyType(item)` +* `VCard#items` +* `VCard#toJSON()` +* `VCard#toString(version)` +* `VCard#toString([opt])` +* `VCard#find(name)` +* `VCard#find(filter)` +* `VCard#add(line)` +* `VCard#remove(name)` + +#### VCard.Item + +* `VCard.Item#name` +* `VCard.Item#params` +* `VCard.Item#dataType` +* `VCard.Item#value` +* `VCard.Item#encode(value)` +* `VCard.Item#decode()` +* `VCard.Item#quotedPrintable([value])` +* `VCard.Item#base64([value])` +* `VCard.Item#toJSON()` +* `VCard.Item#toString(version)` +* `VCard.Item#toString([opt])` + +You can output the detail documentation by [jsdoc](https://github.com/jsdoc3/jsdoc) command. + +``` +jsdoc -R README.md lib/ +``` + +## References + +* [Wikipedia](https://en.wikipedia.org/wiki/VCard) +* [IANA](http://www.iana.org/assignments/vcard-elements/vcard-elements.xhtml) + +## License + +ISC diff --git a/node_modules/vcard-js/index.js b/node_modules/vcard-js/index.js new file mode 100644 index 00000000..0f8de37a --- /dev/null +++ b/node_modules/vcard-js/index.js @@ -0,0 +1 @@ +module.exports = require('./lib/VCard'); diff --git a/node_modules/vcard-js/lib/VCard.js b/node_modules/vcard-js/lib/VCard.js new file mode 100644 index 00000000..04fa5580 --- /dev/null +++ b/node_modules/vcard-js/lib/VCard.js @@ -0,0 +1,713 @@ +var util = require('util'); +var fs = require('fs'); +var quoted_printable = require('quoted-printable'); +var utf8 = require('utf8'); + +/** + * + * @class VCard + * @version 1.2.1 + * @param {array} lines + * @param {object} [opt] + * @param {string} [opt.filename] + * @param {VCard~filterCb} [opt.filter] + * @param {VCard~debugCb} [opt.debug] + * + */ +function VCard(lines, opt){ + opt = opt || {}; + var self = opt.vCard = this; + + /** + * @member {array} VCard#_origin + * @protected + */ + this._origin = lines; + + /** + * + * @member {string} VCard#_filename + * @protected + */ + this._filename = opt.filename; + + /** + * @member {string} VCard#version + */ + this.version = getVersion(lines); + /** + * @member {VCard.Item[]} VCard#items + */ + this.items = []; + + lines.forEach(function(line){ + var item = new VCard.Item(line, opt); + if(opt.filter){ + if(!opt.filter(item)) + return; + } + self.items.push(item); + }); + + (opt.debug || VCard.debug)(self); + + function getVersion(lines){ + var line = find(lines, function(line){ + if(util.isArray(line)) + return /^version$/i.test(line[0]); + else + return /^version:/i.test(line); + }); + if(util.isString(line)) + return line.substr(8); + else if(util.isArray(line)) + return line[3]; + else + return null; + }; +} + +/** + * + * @function VCard#toString + * @param {object} [opt] + * @param {string} [opt.version] + * @param {VCard~filterCb} [opt.filter] + * @returns {string} + */ +/** + * @function VCard#toString + * @param {string} version + * @returns {string} + */ +VCard.prototype.toString = function(opt){ + opt = opt || {}; + var self = this; + var list = []; + + if(util.isString(opt)){ + opt = { + version : opt + }; + } + + self.items.forEach(function(item){ + if(/^(begin|end):/i.test(item.name)) + return; + if(opt.filter){ + if(!opt.filter(item)) + return; + } + var line = item.toString(opt); + line && list.push(line); + }); + if(list.length){ + list.unshift('BEGIN:VCARD'); + list.push('END:VCARD'); + return list.join(VCard.EOL); + } + return ''; +}; + +/** + * @function VCard#toJSON + * @returns {array} + */ +VCard.prototype.toJSON = function(){ + var self = this; + var list = []; + self.items.forEach(function(item){ + if(/^(begin|end):/i.test(item.name)) + return; + list.push(item.toJSON()); + }); + return ['vcard', list]; +}; + +/** + * @function VCard#find + * @param {string} name + * @since 1.2.0 + * @returns {VCard.Item[]} + */ +/** + * @function VCard#find + * @param {VCard~filterCb} filter + * @since 1.2.0 + * @returns {VCard.Item[]} + */ +VCard.prototype.find = function(filter){ + var self = this; + if(!util.isFunction(filter)){ + var name = filter.toLowerCase(); + filter = function(item){ + return item.name.toLowerCase() === name; + }; + } + return self.items.filter(filter); +}; + +/** + * @function VCard#add + * @param {(string|array)} line + * @since 1.2.0 + * @returns {VCard.Item} + */ +VCard.prototype.add = function(line){ + var self = this; + var item = new VCard.Item(line, { + vCard : self + }); + self.items.push(item); + return item; +}; + +/** + * @function VCard#remove + * @param {string} name + * @since 1.2.0 + * @returns {number} + */ +VCard.prototype.remove = function(name){ + var self = this; + var i = 0, j = 0, item; + name = name.toLowerCase(); + while(i < self.items.length){ + item = self.items[i]; + if(item.name.toLowerCase() === name){ + self.items.splice(i, 1); + j++; + }else{ + i++; + } + } + return j; +}; + +VCard.VCard = VCard; + +/** + * @constant {string} VCard.EOL + * @default \r\n + */ +VCard.EOL = '\r\n'; + +/** + * @constant {number} VCard.MAX_WIDTH + * @default 1024 + */ +VCard.MAX_WIDTH = 1024; + +/** + * @function VCard.identifyType + * @param {VCard.Item} item + * @since 1.2.0 + */ +VCard.identifyType = function(item){ + if(item.name === 'LANG'){ + item.type = 'language-tag'; + }else if(item.name === 'REV'){ + item.type = 'timestamp'; + }else if(/^(http|https)\:\/\//i.test(item.value)){ + item.type = 'uri'; + } +}; + +/** + * @function VCard.debug + * @param {*} any + * @abstract + */ +VCard.debug = function(any){ +}; + +/** + * @class VCard.Item + * @param {(string|array)} line + * @param {object} [opt] + * @param {VCard} [opt.vCard] + * @param {VCard~debugCb} [opt.debug] + */ +VCard.Item = function(line, opt){ + opt = opt || {}; + var self = this; + + /** + * @member {VCard} VCard.Item#_vCard + * @protected + */ + this._vCard = opt.vCard; + /** + * @member {(string|array)} VCard.Item#_origin + * @protected + */ + this._origin = line; + + /** + * @member {string} VCard.Item#name + */ + this.name = ''; + /** + * @member {string} VCard.Item#value + */ + this.value = ''; + /** + * @member {object} VCard.Item#params + */ + this.params = {}; + /** + * @member {string} VCard.Item#type + * @default text + * @since 1.2.0 + */ + this.type = 'text'; + + if(!line) + return; + else if(util.isString(line)) + parse(); + else if(util.isArray(line)) + init(); + + (opt.debug || VCard.debug)(self); + + function parse(){ + var frag = line, results, k, v, i; + while(frag){ + if(!self.name){ + results = frag.match(/^([\w\-]+)(?=:|;|\r\n|\r|\n)/g); + if(results){ + self.name = v = results[0]; + frag = frag.substr(v.length); + continue; + } + } + + if(frag.indexOf(':') !== 0){ + frag = frag.replace(/^;/, ''); + + results = frag.match(/^([\w\-]+)="/); + if(results){ + k = results[1]; + frag = frag.substr(k.length + 2); + i = frag.search(/[^\\]"/); + if(i !== -1) + v = frag.substr(0, i + 1); + else + v = frag; + setParams(k, v); + frag = frag.substr(v.length + 1); + continue; + } + + results = frag.match(/^([\w\-]+)=/) + if(results){ + k = results[1]; + frag = frag.substr(k.length + 1); + i = frag.search(/[^\\](;|:)/); + if(i !== -1) + v = frag.substr(0, i + 1); + else + v = frag; + setParams(k, v); + frag = frag.substr(v.length); + continue; + } + + i = frag.search(/[^\\](;|:)/); + if(i !== -1){ + v = frag.substr(0, i + 1); + setParams('type', v); + frag = frag.substr(v.length); + continue; + } + } + + self.value = frag.replace(/^:/, ''); + frag = null; + } + VCard.identifyType(self); + } + + function init(){ + self.name = line[0]; + self.type = line[2]; + self.value = line[3]; + var k, o = line[1]; + for(k in o){ + self.params[k] = o[k]; + } + self.encode(self.value); + } + + function setParams(k, v){ + var obj = self.params; + k = k.toUpperCase(); + + if(/^(type)$/i.test(k)) + v = v.split(/\s*,\s*/); + + if(obj.hasOwnProperty(k)){ + if(!util.isArray(obj[k])) + obj[k] = [obj[k]]; + + if(util.isArray(v)) + obj[k] = obj[k].concat(v); + else + obj[k].push(v); + + return; + } + obj[k] = v; + } +}; + +/** + * @function VCard.Item#toString + * @param {object} [opt] + * @param {string} [opt.version] + * @param {VCard~replaceCb} [opt.replace] + * @returns {string} + */ +/** + * @function VCard.Item#toString + * @param {string} version + * @returns {string} + */ +VCard.Item.prototype.toString = function(opt){ + opt = opt || {}; + var self = this; + var line = self.name.toUpperCase(), k, v; + var value = self.value.toString(); + + if(util.isString(opt)){ + opt = { + version : opt + }; + } + var version = opt.version; + if(!version){ + version = self._vCard ? self._vCard.version : '4.0'; + } + + for(k in self.params){ + v = self.params[k]; + k = k.toUpperCase(); + + if(version === '2.1' && /^(type)$/i.test(k)){ + line += ';'; + if(/^(photo)$/i.test(self.name)) + line += k + '='; + + if(util.isArray(v)) + line += v.join(';'); + else + line += v; + continue; + } + + if(/^(impp)$/i.test(self.name)){ + if(util.isArray(v)){ + v.forEach(function(v){ + line += ';' + k + '=' + v; + }); + continue; + } + } + + if(k === 'ENCODING'){ + if(/^(BASE64|b)$/.test(v) || /^data\:.+\;base64\,/.test(value)){ + if(version === '2.1') + v = 'BASE64'; + else if(version === '3.0') + v = 'b'; + else + v = ''; + + if(v) + value = value.replace(/^data\:.+\;base64\,/, ''); + } + } + + v = v.toString(); + if(!v) + continue; + + if(version >= 4 && /\s/.test(v)){ + line += ';' + k + '="' + v + '"'; + continue; + } + + line += ';' + k + '=' + v; + } + + if(version < 4) + value = value.replace(/^geo\:/i, ''); + + if(/^version$/i.test(self.name)) + line += ':' + version; + else if(value) + line += ':' + value; + else + line = ''; + + if(line.length > VCard.MAX_WIDTH){ + line = line.replace(/(^.{75})|(.{74})/g, function(v){ + return v + VCard.EOL + ' '; + }); + line = line.replace(/\s$/, ''); + if(version === '2.1') + line += VCard.EOL; + } + + if(opt.replace){ + line = opt.replace(line, self); + } + return line; +}; + +/** + * @function VCard.Item#toJSON + * @returns {array} + */ +VCard.Item.prototype.toJSON = function(){ + var self = this; + var name = self.name.toLowerCase(); + var type = self.type; + var value = self.decode(); + var params = {}; + var k, v; + for(k in self.params){ + v = self.params[k]; + if(/^value$/i.test(k)){ + type = v; + continue; + }else if(/^label$/i.test(k) && !value){ + value = v; + } + params[k.toLowerCase()] = v; + } + switch(type){ + case 'boolean': + value = value === 'TRUE'; + break; + case 'integer': + value = parseInt(value); + break; + case 'float': + value = parseFloat(value); + break; + } + return [name, params, type, value]; +}; + +/** + * @function VCard.Item#encode + * @param {string} value + * @since 1.1.0 + */ +VCard.Item.prototype.encode = function(value){ + switch(this.params.ENCODING){ + case 'QUOTED-PRINTABLE': + this.quotedPrintable(value); + break; + case 'BASE64': + case 'b': + this.base64(value); + break; + } +}; + +/** + * @function VCard.Item#decode + * @returns {string} + * @since 1.1.0 + */ +VCard.Item.prototype.decode = function(){ + switch(this.params.ENCODING){ + case 'QUOTED-PRINTABLE': + return this.quotedPrintable(); + case 'BASE64': + case 'b': + return this.base64(); + default: + return this.value; + } +}; + +/** + * @function VCard.Item#quotedPrintable + * @param {string} value + * @since 1.2.0 + */ +/** + * @function VCard.Item#quotedPrintable + * @returns {string} + * @since 1.2.0 + */ +VCard.Item.prototype.quotedPrintable = function(value){ + if(value){ + value = utf8.encode(value); + this.value = quoted_printable.encode(value); + this.params.CHARSET = 'UTF-8'; + this.params.ENCODING = 'QUOTED-PRINTABLE'; + }else{ + value = quoted_printable.decode(this.value); + value = utf8.decode(value); + return value; + } +}; + +/** + * @function VCard.Item#base64 + * @param {string} value + * @since 1.2.0 + */ +/** + * @function VCard.Item#base64 + * @returns {string} + * @since 1.2.0 + */ +VCard.Item.prototype.base64 = function(value){ + if(value){ + this.value = value; + this.param.ENCODING = this.param.ENCODING || 'BASE64'; + }else{ + return this.value; + } +}; + +/** + * @function VCard.parse + * @param {(string|array)} data + * @param {object} [opt] + * @param {VCard~filterCb} [opt.filter] + * @returns {array} + */ +VCard.parse = function(data, opt){ + opt = opt || {}; + var vCards = [], lines, vCard; + if(util.isString(data)){ + lines = []; + data = data.replace(/^=/mg, ' '); + data.split(/(\r\n|\r|\n)(?=\S)/).forEach(function(line){ + line = line.replace(/^item\d+\.|\r\n\s*|\r\s*|\n\s*/g, ''); + if(line){ + switch(line){ + case 'BEGIN:VCARD': + lines = []; + break; + case 'END:VCARD': + vCard = new VCard(lines, opt); + if(opt.filter){ + if(!opt.filter(vCard)) + break; + } + vCards.push(vCard); + break; + default: + lines.push(line); + } + } + }); + }else if(util.isArray(data)){ + data.forEach(function(arr){ + vCards.push(new VCard(arr[1])); + }); + } + return vCards; +}; + +/** + * @function VCard.serialize + * @param {(VCard[]|array)} vCards + * @param {string} version + * @returns {string} + */ +/** + * @function VCard.serialize + * @param {(VCard[]|array)} vCards + * @param {object} [opt] + * @param {VCard~filterCb} [opt.filter] + * @returns {string} + */ +VCard.serialize = function(vCards, opt){ + opt = opt || {}; + var list = []; + vCards.forEach(function(vCard){ + if(util.isArray(vCard)){ + vCard = new VCard(vCard[1]); + } + if(opt.filter){ + if(!opt.filter(vCard)) + return; + } + list.push(vCard.toString(opt)); + }); + return list.join(VCard.EOL); +}; + +/** + * @function VCard.readFile + * @param {string} filename + * @param {object} [opt] + * @param {string} [opt.encoding=utf8] + * @param {VCard~readFileCb} callback + */ +/** + * + * @callback VCard~readFileCb + * @param {?Error} err + * @param {array} vCards + * @param {string} data + */ +VCard.readFile = function (filename, opt, callback){ + opt = opt || {}; + if(util.isFunction(opt)){ + callback = opt; + opt = {}; + } + + fs.readFile(opt.filename = filename, { + encoding : opt.encoding || 'utf8' + }, function(err, data){ + var vCards = null; + if(!err) + try{ + vCards = VCard.parse(data, opt); + }catch(e){ + err = e; + } + callback(err, vCards, data); + }); +}; + +function find(arr, predicate){ + if(util.isArray(arr)) + for(var i=0,len=arr.length,o; i=1.0.2: dependencies: semver "~5.3.0" +utf8@^2.1.0, utf8@^2.1.1: + version "2.1.2" + resolved "https://registry.npmjs.org/utf8/-/utf8-2.1.2.tgz" + integrity sha512-QXo+O/QkLP/x1nyi54uQiG0XrODxdysuQvE5dtVqv7F5K2Qb6FsN+qbr6KhF5wQ20tfcV3VQp0/2x1e1MRSPWg== + util-deprecate@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" @@ -2965,6 +2977,14 @@ vaul@^0.9.6: dependencies: "@radix-ui/react-dialog" "^1.1.1" +vcard-js@^1.2.2: + version "1.2.2" + resolved "https://registry.npmjs.org/vcard-js/-/vcard-js-1.2.2.tgz" + integrity sha512-YShz0/tIXJ3gOg2o/OCNzuPCb7KTnhjn81cUaTZg5+elb99yVr+MRMkbD+vljTny6YuocPI0Dtb1ttG2Yf8oMg== + dependencies: + quoted-printable "^1.0.0" + utf8 "^2.1.1" + victory-vendor@^36.6.8: version "36.9.2" resolved "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz"