diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 07:58:57 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 07:58:57 +0000 |
commit | 592d2180f5d3984853bf55f91be87ac5d1dc0c1a (patch) | |
tree | 74ebbedcb80b9eca5e9773fb1b558e75f69cad8d /unused/vcard/vcard.js | |
parent | Initial commit. (diff) | |
download | dav4tbsync-upstream/4.7.tar.xz dav4tbsync-upstream/4.7.zip |
Adding upstream version 4.7.upstream/4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'unused/vcard/vcard.js')
-rw-r--r-- | unused/vcard/vcard.js | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/unused/vcard/vcard.js b/unused/vcard/vcard.js new file mode 100644 index 0000000..d1b9757 --- /dev/null +++ b/unused/vcard/vcard.js @@ -0,0 +1,305 @@ +(function (namespace) { + var PREFIX = 'BEGIN:VCARD', + POSTFIX = 'END:VCARD'; + + /** + * Return json representation of vCard + * @param {string} string raw vCard + * @returns {*} + */ + function parse(string) { + var result = {}, + lines = string.split(/\r\n|\r|\n/), + count = lines.length, + pieces, + key, + value, + meta, + namespace; + + for (var i = 0; i < count; i++) { + if (lines[i] == '') { + continue; + } + if (lines[i].toUpperCase() == PREFIX || lines[i].toUpperCase() == POSTFIX) { + continue; + } + var data = lines[i]; + + /** + * Check that next line continues current + * @param {number} i + * @returns {boolean} + */ + var isValueContinued = function (i) { + return i + 1 < count && (lines[i + 1][0] == ' ' || lines[i + 1][0] == '\t'); + }; + // handle multiline properties (i.e. photo). + // next line should start with space or tab character + if (isValueContinued(i)) { + while (isValueContinued(i)) { + data += lines[i + 1].trim(); + i++; + } + } + + pieces = data.split(':'); + key = pieces.shift(); + value = pieces.join(':'); + namespace = false; + meta = {}; + + // meta fields in property + if (key.match(/;/)) { + key = key + .replace(/\\;/g, 'ΩΩΩ') + .replace(/\\,/, ','); + var metaArr = key.split(';').map(function (item) { + return item.replace(/ΩΩΩ/g, ';'); + }); + key = metaArr.shift(); + metaArr.forEach(function (item) { + var arr = item.split('='); + arr[0] = arr[0].toLowerCase(); + if (arr[0].length === 0) { + return; + } + if (arr.length>1) { + //removing boundary quotes and splitting up values, if send as list - upperCase for hitory reasons + let metavalue = arr[1].replace (/(^")|("$)/g, '').toUpperCase().split(","); + if (meta[arr[0]]) { + meta[arr[0]].push(...metavalue); + } else { + meta[arr[0]] = metavalue; + } + } + }); + } + + // values with \n + value = value + .replace(/\\r/g, '') + .replace(/\\n/g, '\n'); + + value = tryToSplit(value); + + // Grouped properties + if (key.match(/\./)) { + var arr = key.split('.'); + key = arr[1]; + namespace = arr[0]; + } + + var newValue = { + value: value + }; + if (Object.keys(meta).length) { + newValue.meta = meta; + } + if (namespace) { + newValue.namespace = namespace; + } + + if (key.indexOf('X-') !== 0) { + key = key.toLowerCase(); + } + + if (typeof result[key] === 'undefined') { + result[key] = [newValue]; + } else { + result[key].push(newValue); + } + + } + + return result; + } + + var HAS_SEMICOLON_SEPARATOR = /[^\\];|^;/, + HAS_COMMA_SEPARATOR = /[^\\],|^,/; + /** + * Split value by "," or ";" and remove escape sequences for this separators + * @param {string} value + * @returns {string|string[] + */ + function tryToSplit(value) { + if (value.match(HAS_SEMICOLON_SEPARATOR)) { + value = value.replace(/\\,/g, ','); + return splitValue(value, ';'); + } else if (value.match(HAS_COMMA_SEPARATOR)) { + value = value.replace(/\\;/g, ';'); + return splitValue(value, ','); + } else { + return value + .replace(/\\,/g, ',') + .replace(/\\;/g, ';'); + } + } + /** + * Split vcard field value by separator + * @param {string|string[]} value + * @param {string} separator + * @returns {string|string[]} + */ + function splitValue(value, separator) { + var separatorRegexp = new RegExp(separator); + var escapedSeparatorRegexp = new RegExp('\\\\' + separator, 'g'); + // easiest way, replace it with really rare character sequence + value = value.replace(escapedSeparatorRegexp, 'ΩΩΩ'); + if (value.match(separatorRegexp)) { + value = value.split(separator); + + value = value.map(function (item) { + return item.replace(/ΩΩΩ/g, separator); + }); + } else { + value = value.replace(/ΩΩΩ/g, separator); + } + return value; + } + + var guid = (function() { + function s4() { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); + } + return function() { + return s4() + s4() + '-' + s4() + '-' + s4() + '-' + + s4() + '-' + s4() + s4() + s4(); + }; + })(); + + var COMMA_SEPARATED_FIELDS = ['nickname', 'related', 'categories', 'pid']; + + /** + * Generate vCard representation af object + * @param {*} data + * @param {obj} + * member "simpleType" returns all types joined by , instead of multiple TYPE= entries + * member "addRequired" determine if generator should add required properties (version and uid) + * @returns {string} + */ + function generate(data, options = {simpleType: true}) { + var lines = [PREFIX], + line = ''; + + if (options.addRequired && !data.version) { + data.version = [{value: '3.0'}]; + } + if (options.addRequired && !data.uid) { + data.uid = [{value: guid()}]; + } + + var escapeCharacters = function (v) { + if (typeof v === 'undefined') { + return ''; + } + return v + .replace(/\r\n|\r|\n/g, '\\n') + .replace(/;/g, '\\;') + .replace(/,/g, '\\,') + }; + + var escapeTypeCharacters = function(v) { + if (typeof v === 'undefined') { + return ''; + } + return v + .replace(/\r\n|\r|\n/g, '\\n') + .replace(/;/g, '\\;') + }; + + Object.keys(data).forEach(function (key) { + if (!data[key] || typeof data[key].forEach !== 'function') { + return; + } + data[key].forEach(function (value) { + // ignore empty values + if (typeof value.value === 'undefined' || value.value == '') { + return; + } + // ignore empty array values + if (value.value instanceof Array) { + var empty = true; + for (var i = 0; i < value.value.length; i++) { + if (typeof value.value[i] !== 'undefined' && value.value[i] != '') { + empty = false; + break; + } + } + if (empty) { + return; + } + } + line = ''; + + // add namespace if exists + if (value.namespace) { + line += value.namespace + '.'; + } + line += key.indexOf('X-') === 0 ? key : key.toUpperCase(); + + // add meta properties + if (typeof value.meta === 'object') { + Object.keys(value.meta).forEach(function (metaKey) { + // values of meta tags must be an array + if (typeof value.meta[metaKey].forEach !== 'function') { + return; + } + //join meta types so we get TYPE=a,b,c instead of TYPE=a;TYPE=b;TYPE=c + let metaArr = (options.simpleType && metaKey.toUpperCase() === 'TYPE') ? [value.meta[metaKey].join(",")] : value.meta[metaKey]; + metaArr.forEach(function (metaValue) { + if (metaKey.length > 0) { + if (metaKey.toUpperCase() === 'TYPE') { + // Do not escape the comma when it is the type property. This breaks a lot. + line += ';' + escapeCharacters(metaKey.toUpperCase()) + '=' + escapeTypeCharacters(metaValue); + } else { + line += ';' + escapeCharacters(metaKey.toUpperCase()) + '=' + escapeCharacters(metaValue); + } + } + }); + }); + } + + line += ':'; + + + + if (typeof value.value === 'string') { + line += escapeCharacters(value.value); + } else { + // list-values + var separator = COMMA_SEPARATED_FIELDS.indexOf(key) !== -1 + ? ',' + : ';'; + line += value.value.map(function (item) { + return escapeCharacters(item); + }).join(separator); + } + + // line-length limit. Content lines + // SHOULD be folded to a maximum width of 75 octets, excluding the line break. + if (line.length > 75) { + var firstChunk = line.substr(0, 75), + least = line.substr(75); + var splitted = least.match(/.{1,74}/g); + lines.push(firstChunk); + splitted.forEach(function (chunk) { + lines.push(' ' + chunk); + }); + } else { + lines.push(line); + } + }); + }); + + lines.push(POSTFIX); + return lines.join('\r\n'); + } + + namespace.vCard = { + parse: parse, + generate: generate + }; +})(this); |