summaryrefslogtreecommitdiffstats
path: root/unused/vcard/vcard.js
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 07:58:57 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 07:58:57 +0000
commit592d2180f5d3984853bf55f91be87ac5d1dc0c1a (patch)
tree74ebbedcb80b9eca5e9773fb1b558e75f69cad8d /unused/vcard/vcard.js
parentInitial commit. (diff)
downloaddav4tbsync-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.js305
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);