1014 lines
35 KiB
JavaScript
Executable File
1014 lines
35 KiB
JavaScript
Executable File
/*
|
|
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
|
|
if you want to view the source, please visit the github repository of this plugin
|
|
*/
|
|
|
|
var __defProp = Object.defineProperty;
|
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
var __commonJS = (cb, mod) => function __require() {
|
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
};
|
|
var __export = (target, all) => {
|
|
for (var name in all)
|
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
};
|
|
var __copyProps = (to, from, except, desc) => {
|
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
for (let key of __getOwnPropNames(from))
|
|
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
}
|
|
return to;
|
|
};
|
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
|
|
// node_modules/foldline/foldline.js
|
|
var require_foldline = __commonJS({
|
|
"node_modules/foldline/foldline.js"(exports, module2) {
|
|
var CRLF = "\r\n";
|
|
var SP = " ";
|
|
var MAX_LINE_LENGTH = 998;
|
|
var DEFAULT_LINE_LENGTH = 78;
|
|
var MIN_LINE_LENGTH = 2;
|
|
function foldLine(input, maxLength, hardWrap) {
|
|
if (maxLength != null && maxLength < MIN_LINE_LENGTH) {
|
|
throw new Error("Maximum length must not be less than " + MIN_LINE_LENGTH);
|
|
}
|
|
if (maxLength != null && maxLength > MAX_LINE_LENGTH) {
|
|
throw new Error("Maximum length must not exceed " + MAX_LINE_LENGTH);
|
|
}
|
|
maxLength = maxLength || DEFAULT_LINE_LENGTH;
|
|
input = input.replace(/[\r\n]+/g, "");
|
|
if (input.length <= maxLength) {
|
|
return input;
|
|
}
|
|
var output = "";
|
|
var index = 0;
|
|
var nextIndex = 0;
|
|
var length = input.length;
|
|
var line = 0;
|
|
var trim = 0;
|
|
while (index < length) {
|
|
if (!hardWrap && ~(nextIndex = input.lastIndexOf(SP, index + maxLength))) {
|
|
if (nextIndex > index) {
|
|
output += input.slice(index, nextIndex) + CRLF + SP;
|
|
index = nextIndex;
|
|
} else {
|
|
output += input.slice(index, index + maxLength - trim) + CRLF + SP;
|
|
index = index + maxLength - trim;
|
|
hardWrap = true;
|
|
}
|
|
} else {
|
|
output += input.slice(index, index + maxLength - trim) + CRLF + SP;
|
|
index = index + maxLength - trim;
|
|
}
|
|
if (length - index < maxLength) {
|
|
output += input.slice(index);
|
|
break;
|
|
}
|
|
if (line === 0) {
|
|
trim = 1;
|
|
}
|
|
line++;
|
|
}
|
|
return output;
|
|
}
|
|
foldLine.unfold = function unfold(input) {
|
|
return input.replace(/\r\n\s/gm, "");
|
|
};
|
|
module2.exports = foldLine;
|
|
}
|
|
});
|
|
|
|
// node_modules/camelcase/index.js
|
|
var require_camelcase = __commonJS({
|
|
"node_modules/camelcase/index.js"(exports, module2) {
|
|
"use strict";
|
|
var preserveCamelCase = (string) => {
|
|
let isLastCharLower = false;
|
|
let isLastCharUpper = false;
|
|
let isLastLastCharUpper = false;
|
|
for (let i = 0; i < string.length; i++) {
|
|
const character = string[i];
|
|
if (isLastCharLower && /[a-zA-Z]/.test(character) && character.toUpperCase() === character) {
|
|
string = string.slice(0, i) + "-" + string.slice(i);
|
|
isLastCharLower = false;
|
|
isLastLastCharUpper = isLastCharUpper;
|
|
isLastCharUpper = true;
|
|
i++;
|
|
} else if (isLastCharUpper && isLastLastCharUpper && /[a-zA-Z]/.test(character) && character.toLowerCase() === character) {
|
|
string = string.slice(0, i - 1) + "-" + string.slice(i - 1);
|
|
isLastLastCharUpper = isLastCharUpper;
|
|
isLastCharUpper = false;
|
|
isLastCharLower = true;
|
|
} else {
|
|
isLastCharLower = character.toLowerCase() === character && character.toUpperCase() !== character;
|
|
isLastLastCharUpper = isLastCharUpper;
|
|
isLastCharUpper = character.toUpperCase() === character && character.toLowerCase() !== character;
|
|
}
|
|
}
|
|
return string;
|
|
};
|
|
var camelCase = (input, options) => {
|
|
if (!(typeof input === "string" || Array.isArray(input))) {
|
|
throw new TypeError("Expected the input to be `string | string[]`");
|
|
}
|
|
options = Object.assign({
|
|
pascalCase: false
|
|
}, options);
|
|
const postProcess = (x) => options.pascalCase ? x.charAt(0).toUpperCase() + x.slice(1) : x;
|
|
if (Array.isArray(input)) {
|
|
input = input.map((x) => x.trim()).filter((x) => x.length).join("-");
|
|
} else {
|
|
input = input.trim();
|
|
}
|
|
if (input.length === 0) {
|
|
return "";
|
|
}
|
|
if (input.length === 1) {
|
|
return options.pascalCase ? input.toUpperCase() : input.toLowerCase();
|
|
}
|
|
const hasUpperCase = input !== input.toLowerCase();
|
|
if (hasUpperCase) {
|
|
input = preserveCamelCase(input);
|
|
}
|
|
input = input.replace(/^[_.\- ]+/, "").toLowerCase().replace(/[_.\- ]+(\w|$)/g, (_, p1) => p1.toUpperCase()).replace(/\d+(\w|$)/g, (m) => m.toUpperCase());
|
|
return postProcess(input);
|
|
};
|
|
module2.exports = camelCase;
|
|
module2.exports.default = camelCase;
|
|
}
|
|
});
|
|
|
|
// node_modules/vcf/lib/property.js
|
|
var require_property = __commonJS({
|
|
"node_modules/vcf/lib/property.js"(exports, module2) {
|
|
function Property(field, value, params) {
|
|
if (!(this instanceof Property))
|
|
return new Property(value);
|
|
if (params != null)
|
|
Object.assign(this, params);
|
|
this._field = field;
|
|
this._data = value;
|
|
Object.defineProperty(this, "_field", { enumerable: false });
|
|
Object.defineProperty(this, "_data", { enumerable: false });
|
|
}
|
|
Property.fromJSON = function(data) {
|
|
var field = data[0];
|
|
var params = data[1];
|
|
if (!/text/i.test(data[2]))
|
|
params.value = data[2];
|
|
var value = Array.isArray(data[3]) ? data[3].join(";") : data[3];
|
|
return new Property(field, value, params);
|
|
};
|
|
function capitalDashCase(value) {
|
|
return value.replace(/([A-Z])/g, "-$1").toUpperCase();
|
|
}
|
|
Property.prototype = {
|
|
constructor: Property,
|
|
/**
|
|
* Check whether the property is of a given type
|
|
* @param {String} type
|
|
* @return {Boolean}
|
|
*/
|
|
is: function(type) {
|
|
type = (type + "").toLowerCase();
|
|
return Array.isArray(this.type) ? this.type.indexOf(type) >= 0 : this.type === type;
|
|
},
|
|
/**
|
|
* Check whether the property is empty
|
|
* @return {Boolean}
|
|
*/
|
|
isEmpty: function() {
|
|
return this._data == null && Object.keys(this).length === 0;
|
|
},
|
|
/**
|
|
* Clone the property
|
|
* @return {Property}
|
|
*/
|
|
clone: function() {
|
|
return new Property(this._field, this._data, this);
|
|
},
|
|
/**
|
|
* Format the property as vcf with given version
|
|
* @param {String} version
|
|
* @return {String}
|
|
*/
|
|
toString: function(version) {
|
|
var propName = (this.group ? this.group + "." : "") + capitalDashCase(this._field);
|
|
var keys = Object.keys(this);
|
|
var params = [];
|
|
for (var i = 0; i < keys.length; i++) {
|
|
if (keys[i] === "group")
|
|
continue;
|
|
switch (propName) {
|
|
case "TEL":
|
|
case "ADR":
|
|
case "EMAIL":
|
|
if (version === "2.1") {
|
|
if (Array.isArray(this[keys[i]]))
|
|
params.push(this[keys[i]].join(";"));
|
|
else
|
|
params.push(this[keys[i]]);
|
|
} else
|
|
params.push(capitalDashCase(keys[i]) + "=" + this[keys[i]]);
|
|
break;
|
|
default:
|
|
params.push(capitalDashCase(keys[i]) + "=" + this[keys[i]]);
|
|
}
|
|
}
|
|
if (version === "2.1" || version === "3.0")
|
|
return propName + (params.length ? ";" + params.join(";").toUpperCase() : params.toString().toUpperCase()) + ":" + (Array.isArray(this._data) ? this._data.join(";") : this._data);
|
|
else
|
|
return propName + (params.length ? ";" + params.join(";") : params) + ":" + (Array.isArray(this._data) ? this._data.join(";") : this._data);
|
|
},
|
|
/**
|
|
* Get the property's value
|
|
* @return {String}
|
|
*/
|
|
valueOf: function() {
|
|
return this._data;
|
|
},
|
|
/**
|
|
* Format the property as jCard data
|
|
* @return {Array}
|
|
*/
|
|
toJSON: function() {
|
|
var params = Object.assign({}, this);
|
|
if (params.value === "text") {
|
|
params.value = void 0;
|
|
delete params.value;
|
|
}
|
|
var data = [this._field, params, this.value || "text"];
|
|
switch (this._field) {
|
|
default:
|
|
data.push(this._data);
|
|
break;
|
|
case "adr":
|
|
case "n":
|
|
data.push(this._data.split(";"));
|
|
}
|
|
return data;
|
|
}
|
|
};
|
|
module2.exports = Property;
|
|
}
|
|
});
|
|
|
|
// node_modules/vcf/lib/parse-lines.js
|
|
var require_parse_lines = __commonJS({
|
|
"node_modules/vcf/lib/parse-lines.js"(exports, module2) {
|
|
var camelCase = require_camelcase();
|
|
var Property = require_property();
|
|
function set(object, key, value) {
|
|
if (Array.isArray(object[key])) {
|
|
object[key].push(value);
|
|
} else if (object[key] != null) {
|
|
object[key] = [object[key], value];
|
|
} else {
|
|
object[key] = value;
|
|
}
|
|
}
|
|
function createParams(params, param) {
|
|
var parts = param.split("=");
|
|
var k = camelCase(parts[0]);
|
|
var value = parts[1];
|
|
if (value == null || value === "") {
|
|
value = parts[0];
|
|
k = "type";
|
|
}
|
|
if (k === "type") {
|
|
if (value[0] === '"' && value[value.length - 1] === '"' && value.indexOf(",") !== -1)
|
|
value = value.slice(1, -1);
|
|
value.toLowerCase().split(",").forEach(function(value2) {
|
|
set(params, k, value2);
|
|
});
|
|
return params;
|
|
}
|
|
set(params, k, value);
|
|
return params;
|
|
}
|
|
function parseLines(lines) {
|
|
var data = {};
|
|
var line = null;
|
|
var pattern = /^([^;:]+)((?:;(?:[^;:]+))*)(?:\:([\s\S]+))?$/i;
|
|
var len = lines.length - 1;
|
|
for (var i = 1; i < len; i++) {
|
|
line = lines[i];
|
|
var match = pattern.exec(line);
|
|
if (!match)
|
|
continue;
|
|
var name = match[1].split(".");
|
|
var property = name.pop();
|
|
var group = name.pop();
|
|
var value = match[3];
|
|
var params = match[2] ? match[2].replace(/^;|;$/g, "").split(";") : [];
|
|
var propParams = params.reduce(createParams, group ? { group } : {});
|
|
var propName = camelCase(property);
|
|
var propVal = new Property(propName, value, propParams);
|
|
set(data, propName, propVal);
|
|
}
|
|
return data;
|
|
}
|
|
module2.exports = parseLines;
|
|
}
|
|
});
|
|
|
|
// node_modules/vcf/lib/vcard.js
|
|
var require_vcard = __commonJS({
|
|
"node_modules/vcf/lib/vcard.js"(exports, module2) {
|
|
function vCard() {
|
|
if (!(this instanceof vCard))
|
|
return new vCard();
|
|
this.version = vCard.versions[vCard.versions.length - 1];
|
|
this.data = {};
|
|
}
|
|
vCard.mimeType = "text/vcard";
|
|
vCard.extension = ".vcf";
|
|
vCard.versions = ["2.1", "3.0", "4.0"];
|
|
vCard.EOL = "\r\n";
|
|
vCard.foldLine = require_foldline();
|
|
vCard.normalize = function(input) {
|
|
return (input + "").replace(/^[\s\r\n]+|[\s\r\n]+$/g, "").replace(/(\r\n)[\x09\x20]?(\r\n)|$/g, "$1").replace(/\r\n[\x20\x09]/g, "");
|
|
};
|
|
vCard.isSupported = function(version) {
|
|
return /^\d\.\d$/.test(version) && vCard.versions.indexOf(version) !== -1;
|
|
};
|
|
vCard.parse = function(value) {
|
|
var objects = (value + "").split(/(?=BEGIN\:VCARD)/gi);
|
|
var cards = [];
|
|
for (var i = 0; i < objects.length; i++) {
|
|
cards.push(new vCard().parse(objects[i]));
|
|
}
|
|
return cards;
|
|
};
|
|
vCard.parseLines = require_parse_lines();
|
|
vCard.fromJSON = function(jcard) {
|
|
jcard = typeof jcard === "string" ? JSON.parse(jcard) : jcard;
|
|
if (jcard == null || !Array.isArray(jcard))
|
|
return new vCard();
|
|
if (!/vcard/i.test(jcard[0]))
|
|
throw new Error("Object not in jCard format");
|
|
var card = new vCard();
|
|
jcard[1].forEach(function(prop) {
|
|
card.addProperty(vCard.Property.fromJSON(prop));
|
|
});
|
|
return card;
|
|
};
|
|
vCard.format = function(card, version) {
|
|
version = version || card.version || vCard.versions[vCard.versions.length - 1];
|
|
if (!vCard.isSupported(version))
|
|
throw new Error('Unsupported vCard version "' + version + '"');
|
|
var vcf3 = [];
|
|
vcf3.push("BEGIN:VCARD");
|
|
vcf3.push("VERSION:" + version);
|
|
var props = Object.keys(card.data);
|
|
var prop = "";
|
|
for (var i = 0; i < props.length; i++) {
|
|
if (props[i] === "version")
|
|
continue;
|
|
prop = card.data[props[i]];
|
|
if (Array.isArray(prop)) {
|
|
for (var k = 0; k < prop.length; k++) {
|
|
if (prop[k].isEmpty())
|
|
continue;
|
|
vcf3.push(vCard.foldLine(prop[k].toString(version), 75));
|
|
}
|
|
} else if (!prop.isEmpty()) {
|
|
vcf3.push(vCard.foldLine(prop.toString(version), 75));
|
|
}
|
|
}
|
|
vcf3.push("END:VCARD");
|
|
return vcf3.join(vCard.EOL);
|
|
};
|
|
vCard.Property = require_property();
|
|
vCard.prototype = {
|
|
constructor: vCard,
|
|
/**
|
|
* Get a vCard property
|
|
* @param {String} key
|
|
* @return {Object|Array}
|
|
*/
|
|
get: function(key) {
|
|
if (this.data[key] == null) {
|
|
return this.data[key];
|
|
}
|
|
if (Array.isArray(this.data[key])) {
|
|
return this.data[key].map(function(prop) {
|
|
return prop.clone();
|
|
});
|
|
} else {
|
|
return this.data[key].clone();
|
|
}
|
|
},
|
|
/**
|
|
* Set a vCard property
|
|
* @param {String} key
|
|
* @param {String} value
|
|
* @param {Object} params
|
|
*/
|
|
set: function(key, value, params) {
|
|
return this.setProperty(new vCard.Property(key, value, params));
|
|
},
|
|
/**
|
|
* Add a vCard property
|
|
* @param {String} key
|
|
* @param {String} value
|
|
* @param {Object} params
|
|
*/
|
|
add: function(key, value, params) {
|
|
var prop = new vCard.Property(key, value, params);
|
|
this.addProperty(prop);
|
|
return this;
|
|
},
|
|
/**
|
|
* Set a vCard property from an already
|
|
* constructed vCard.Property
|
|
* @param {vCard.Property} prop
|
|
*/
|
|
setProperty: function(prop) {
|
|
this.data[prop._field] = prop;
|
|
return this;
|
|
},
|
|
/**
|
|
* Add a vCard property from an already
|
|
* constructed vCard.Property
|
|
* @param {vCard.Property} prop
|
|
*/
|
|
addProperty: function(prop) {
|
|
var key = prop._field;
|
|
if (Array.isArray(this.data[key])) {
|
|
this.data[key].push(prop);
|
|
} else if (this.data[key] != null) {
|
|
this.data[key] = [this.data[key], prop];
|
|
} else {
|
|
this.data[key] = prop;
|
|
}
|
|
return this;
|
|
},
|
|
/**
|
|
* Parse a vcf formatted vCard
|
|
* @param {String} value
|
|
* @return {vCard}
|
|
*/
|
|
parse: function(value) {
|
|
var lines = vCard.normalize(value).split(/\r\n/g);
|
|
var begin = lines[0];
|
|
var version = lines[1];
|
|
var end = lines[lines.length - 1];
|
|
const regexp_version = /VERSION:\d\.\d/i;
|
|
if (!/BEGIN:VCARD/i.test(begin))
|
|
throw new SyntaxError('Invalid vCard: Expected "BEGIN:VCARD" but found "' + begin + '"');
|
|
if (!/END:VCARD/i.test(end))
|
|
throw new SyntaxError('Invalid vCard: Expected "END:VCARD" but found "' + end + '"');
|
|
if (!regexp_version.test(version)) {
|
|
if (!(version = lines.find((line) => regexp_version.test(line))))
|
|
throw new SyntaxError('Invalid vCard: Expected "VERSION:\\d.\\d" but none found');
|
|
}
|
|
this.version = version.substring(8, 11);
|
|
if (!vCard.isSupported(this.version))
|
|
throw new Error('Unsupported version "' + this.version + '"');
|
|
this.data = vCard.parseLines(lines);
|
|
return this;
|
|
},
|
|
/**
|
|
* Format the vCard as vcf with given version
|
|
* @param {String} version
|
|
* @param {String} charset
|
|
* @return {String}
|
|
*/
|
|
toString: function(version, charset) {
|
|
version = version || this.version;
|
|
return vCard.format(this, version);
|
|
},
|
|
/**
|
|
* Format the card as jCard
|
|
* @param {String} version='4.0'
|
|
* @return {Array} jCard
|
|
*/
|
|
toJCard: function(version) {
|
|
version = version || "4.0";
|
|
var keys = Object.keys(this.data);
|
|
var data = [["version", {}, "text", version]];
|
|
var prop = null;
|
|
for (var i = 0; i < keys.length; i++) {
|
|
if (keys[i] === "version")
|
|
continue;
|
|
prop = this.data[keys[i]];
|
|
if (Array.isArray(prop)) {
|
|
for (var k = 0; k < prop.length; k++) {
|
|
data.push(prop[k].toJSON());
|
|
}
|
|
} else {
|
|
data.push(prop.toJSON());
|
|
}
|
|
}
|
|
return ["vcard", data];
|
|
},
|
|
/**
|
|
* Format the card as jCard
|
|
* @return {Array} jCard
|
|
*/
|
|
toJSON: function() {
|
|
return this.toJCard(this.version);
|
|
}
|
|
};
|
|
module2.exports = vCard;
|
|
}
|
|
});
|
|
|
|
// src/main.ts
|
|
var main_exports = {};
|
|
__export(main_exports, {
|
|
default: () => ContactsPlugin
|
|
});
|
|
module.exports = __toCommonJS(main_exports);
|
|
var import_obsidian2 = require("obsidian");
|
|
|
|
// src/vcard.ts
|
|
var vcf = require_vcard();
|
|
var VCard = class {
|
|
constructor(card) {
|
|
var _a, _b, _c, _d, _e;
|
|
this.version = card.version;
|
|
this.fn = (_a = card.get("fn")) == null ? void 0 : _a.valueOf().replace(/\\,/g, ",");
|
|
this.nickname = (_c = (_b = card.get("nickname")) == null ? void 0 : _b.valueOf()) != null ? _c : void 0;
|
|
this.organization = this.parseOrganization(card.get("org"));
|
|
this.title = (_e = (_d = card.get("title")) == null ? void 0 : _d.valueOf()) != null ? _e : void 0;
|
|
this.telephones = this.parseTelephones(card.get("tel"));
|
|
this.addresses = this.parseAddresses(card.get("adr"));
|
|
this.emails = this.parseEmails(card.get("email"));
|
|
this.birthdate = this.parseBirthdate(card.get("bday"));
|
|
this.URLs = this.parseURLs(card.get("url"));
|
|
this.notes = this.parseNotes(card.get("note"));
|
|
this.xabuid = this.parseXAbuid(card.get("xAbuid"));
|
|
if (this.fn == void 0) {
|
|
console.debug(`Found vCard without name:
|
|
${JSON.stringify(this, null, 2)}`);
|
|
}
|
|
}
|
|
getFilename() {
|
|
var _a;
|
|
return (_a = this.fn) != null ? _a : "NO_NAME";
|
|
}
|
|
toMarkdown(enabledFields) {
|
|
var _a;
|
|
let markdown = `## \u{1F464} ${this.fn}
|
|
|
|
[Open in Contacts](addressbook://${(_a = this.xabuid) == null ? void 0 : _a.replace(":", "%3A")})
|
|
|
|
`;
|
|
this.getVCardFields().forEach((field) => {
|
|
var _a2;
|
|
if (!enabledFields.includes(field))
|
|
return;
|
|
markdown += (_a2 = this.fieldToMarkdown(field)) != null ? _a2 : "";
|
|
});
|
|
return markdown.trim();
|
|
}
|
|
fieldToMarkdown(field) {
|
|
var _a, _b, _c, _d, _e;
|
|
if (typeof this[field] === "undefined")
|
|
return void 0;
|
|
switch (field) {
|
|
case "nickname":
|
|
return `- Nickname: ${this.nickname}
|
|
`;
|
|
case "notes":
|
|
return `- \u{1F4DD} Notes:
|
|
${this.notes}
|
|
`;
|
|
case "birthdate":
|
|
return `- \u{1F382} Birthday: ${(_a = this.birthdate) == null ? void 0 : _a.toLocaleDateString()}
|
|
`;
|
|
case "organization":
|
|
return `- \u{1F3E2} Organization: ${this.organization}
|
|
`;
|
|
case "title":
|
|
return `- \u{1F454} Title: ${this.title}
|
|
`;
|
|
case "emails":
|
|
return (_b = this.emails) == null ? void 0 : _b.map((mail) => {
|
|
return `- \u{1F4E7} [${mail}](mailto:${mail})
|
|
`;
|
|
}).join("");
|
|
case "URLs":
|
|
return (_c = this.URLs) == null ? void 0 : _c.map((URL) => {
|
|
return `- \u{1F310} Website: [${URL}](${URL})
|
|
`;
|
|
}).join("");
|
|
case "telephones":
|
|
return (_d = this.telephones) == null ? void 0 : _d.map(([type, tel]) => {
|
|
let emotes = ``;
|
|
const types = type.split(",");
|
|
switch (types[0]) {
|
|
case "cell":
|
|
emotes += "\u{1F4F1}";
|
|
break;
|
|
case "home":
|
|
emotes += "\u{1F3E0}";
|
|
break;
|
|
case "work":
|
|
emotes += "\u{1F3E2}";
|
|
break;
|
|
default:
|
|
emotes += "\u260E\uFE0F";
|
|
}
|
|
switch (types[1]) {
|
|
case "voice":
|
|
emotes += "\u{1F4DE}";
|
|
break;
|
|
case "fax":
|
|
emotes += "\u{1F4E0}";
|
|
break;
|
|
default:
|
|
}
|
|
return `- ${emotes} [${tel}](tel:${tel.replace(" ", "")})
|
|
`;
|
|
}).join("");
|
|
case "addresses":
|
|
return (_e = this.addresses) == null ? void 0 : _e.map(([type, adr]) => {
|
|
let emotes = ``;
|
|
switch (type) {
|
|
case "home":
|
|
emotes += "\u{1F3E0}";
|
|
break;
|
|
case "work":
|
|
emotes += "\u{1F3E2}";
|
|
break;
|
|
default:
|
|
emotes += "\u260E\uFE0F";
|
|
}
|
|
let [_, __, street, city, ___, postcode, country] = adr.valueOf().split(";");
|
|
return `- ${emotes} ${type} address:
|
|
${street}
|
|
${postcode} ${city}
|
|
${country}
|
|
`;
|
|
}).join("");
|
|
default:
|
|
console.error(`Error: Unknown field or markdown convertion: ${field}`);
|
|
return void 0;
|
|
}
|
|
}
|
|
parseBirthdate(bday) {
|
|
let birthdate = void 0;
|
|
if (bday) {
|
|
birthdate = new Date(Date.parse(bday.valueOf()));
|
|
}
|
|
return birthdate;
|
|
}
|
|
parseURLs(URLs) {
|
|
let links = void 0;
|
|
if (URLs) {
|
|
links = new Array();
|
|
if (!Array.isArray(URLs)) {
|
|
URLs = [URLs];
|
|
}
|
|
for (let url of URLs) {
|
|
links.push(url.valueOf());
|
|
}
|
|
}
|
|
return links;
|
|
}
|
|
parseNotes(notes) {
|
|
var _a;
|
|
let notesString = void 0;
|
|
if (notes) {
|
|
notesString = (_a = notes.valueOf()) == null ? void 0 : _a.split("\\n").map((line) => line.endsWith("\\") ? line.slice(0, line.length - 2) : line).join("\n ");
|
|
}
|
|
return notesString;
|
|
}
|
|
parseXAbuid(xabuid) {
|
|
let xabuidString = void 0;
|
|
if (xabuid) {
|
|
xabuidString = xabuid.valueOf();
|
|
}
|
|
return xabuidString;
|
|
}
|
|
parseEmails(email) {
|
|
let emails = void 0;
|
|
if (email) {
|
|
emails = new Array();
|
|
if (!Array.isArray(email)) {
|
|
email = [email];
|
|
}
|
|
for (let mail of email) {
|
|
emails.push(mail.valueOf());
|
|
}
|
|
}
|
|
return emails;
|
|
}
|
|
parseAddresses(adr) {
|
|
var _a, _b, _c, _d, _e;
|
|
let addresses = void 0;
|
|
if (adr) {
|
|
addresses = new Array();
|
|
if (!Array.isArray(adr)) {
|
|
adr = [adr];
|
|
}
|
|
for (let address of adr) {
|
|
let type;
|
|
if (typeof address.type == "string")
|
|
type = (_b = (_a = address.type) == null ? void 0 : _a.toLowerCase()) != null ? _b : "home";
|
|
else if (Array.isArray(address.type))
|
|
type = (_e = (_d = (_c = address.type) == null ? void 0 : _c[0]) == null ? void 0 : _d.toLowerCase()) != null ? _e : "home";
|
|
addresses.push([type, address.valueOf()]);
|
|
}
|
|
}
|
|
return addresses;
|
|
}
|
|
parseTelephones(tel) {
|
|
var _a, _b, _c, _d, _e, _f;
|
|
let telephones = void 0;
|
|
if (tel) {
|
|
telephones = new Array();
|
|
if (!Array.isArray(tel)) {
|
|
tel = [tel];
|
|
}
|
|
for (let telephone of tel) {
|
|
let type = ((_c = (_b = (_a = telephone.type) == null ? void 0 : _a[0]) == null ? void 0 : _b.toLowerCase()) != null ? _c : "phone") + "," + ((_f = (_e = (_d = telephone.type) == null ? void 0 : _d[1]) == null ? void 0 : _e.toLowerCase()) != null ? _f : "voice");
|
|
telephones.push([type, telephone.valueOf()]);
|
|
}
|
|
}
|
|
return telephones;
|
|
}
|
|
parseOrganization(org) {
|
|
let organization = void 0;
|
|
if (org) {
|
|
organization = org.valueOf().replace(";", ", ").replace(/\\,/g, ",");
|
|
}
|
|
return organization;
|
|
}
|
|
static getVCardFields() {
|
|
const JSON_VCARD = ["vcard", [["version", {}, "text", "4.0"], ["fn", {}, "text", "name"]]];
|
|
const inst = new VCard(vcf.fromJSON(JSON_VCARD));
|
|
return inst.getVCardFields();
|
|
}
|
|
getVCardFields() {
|
|
return Object.getOwnPropertyNames(this).filter((prop) => prop !== "constructor" && typeof this[prop] !== "function" && prop !== "version" && prop !== "fn");
|
|
}
|
|
};
|
|
|
|
// src/osascriptService.ts
|
|
var { spawn } = require("child_process");
|
|
var OsaScriptService = class {
|
|
async executeScript(JXA_script) {
|
|
return new Promise((resolve, reject) => {
|
|
const osascript = spawn("osascript", ["-l", "JavaScript", "-e", JXA_script]);
|
|
let output = "";
|
|
osascript.stdout.on("data", (data) => {
|
|
output += data.toString("utf-8");
|
|
});
|
|
osascript.stderr.on("data", (data) => {
|
|
const errorMsg = data.toString("utf-8");
|
|
reject(new Error(`Error executing JXA script:
|
|
${errorMsg}`));
|
|
});
|
|
osascript.on("close", (code) => {
|
|
if (code === 0) {
|
|
resolve(output);
|
|
} else {
|
|
reject(new Error(`Error executing JXA script:
|
|
${output}`));
|
|
}
|
|
});
|
|
});
|
|
}
|
|
};
|
|
|
|
// src/contactsService.ts
|
|
var vcf2 = require_vcard();
|
|
var ContactsService = class {
|
|
constructor(groupName, enabledContactFields, osaScriptService = new OsaScriptService()) {
|
|
this.GROUP_NOT_DEFINED_ERROR = "GROUP NOT DEFINED";
|
|
this.groupName = groupName;
|
|
this.enabledContactFields = enabledContactFields;
|
|
this.osaScriptService = osaScriptService;
|
|
}
|
|
async loadContacts() {
|
|
let vCards = await this.getVCards();
|
|
vCards = vCards.filter((vcard) => {
|
|
return vcard.fn != void 0;
|
|
});
|
|
const filenameToMarkdown = /* @__PURE__ */ new Map();
|
|
for (let vcard of vCards) {
|
|
filenameToMarkdown.set(vcard.getFilename(), vcard.toMarkdown(this.enabledContactFields));
|
|
}
|
|
return filenameToMarkdown;
|
|
}
|
|
async getNumberOfContacts() {
|
|
const JXA_SCRIPT = `
|
|
let Contacts = Application('Contacts');
|
|
Contacts.includeStandardAdditions = true;
|
|
|
|
let groups = Contacts.groups.whose({ name: '${this.groupName}'});
|
|
if (groups.length === 0 || groups === undefined || groups === null)
|
|
throw new Error('${this.GROUP_NOT_DEFINED_ERROR}');
|
|
|
|
groups[0].people.length;
|
|
`;
|
|
let resultPromise = this.osaScriptService.executeScript(JXA_SCRIPT).then((resultStr) => {
|
|
let resultInt = parseInt(resultStr);
|
|
if (isNaN(resultInt)) {
|
|
throw new Error(`Non-numeric result from JXA script: ${resultStr}`);
|
|
}
|
|
return resultInt;
|
|
});
|
|
return await resultPromise;
|
|
}
|
|
async getVCards() {
|
|
const JXA_SCRIPT = `
|
|
ObjC.import('Foundation');
|
|
const stdout = $.NSFileHandle.fileHandleWithStandardOutput;
|
|
|
|
let Contacts = Application('Contacts');
|
|
Contacts.includeStandardAdditions = true;
|
|
|
|
let groups = Contacts.groups.whose({ name: '${this.groupName}'});
|
|
if (groups.length === 0 || groups === undefined || groups === null)
|
|
throw new Error('${this.GROUP_NOT_DEFINED_ERROR}');
|
|
|
|
for (let vcard of groups[0].people.vcard()) {
|
|
// Write to stdout
|
|
const nsString = $.NSString.alloc.initWithUTF8String(vcard);
|
|
const data = nsString.dataUsingEncoding($.NSUTF8StringEncoding);
|
|
stdout.writeData(data);
|
|
}
|
|
`;
|
|
const vCardRegex = /BEGIN:VCARD[\s\S]*?END:VCARD/g;
|
|
let resultPromise = this.osaScriptService.executeScript(JXA_SCRIPT).then((vCardStr) => {
|
|
let matches = vCardStr.match(vCardRegex);
|
|
let vCards = [];
|
|
for (let match of matches != null ? matches : []) {
|
|
const card = new vcf2().parse(match);
|
|
const vCardObj = new VCard(card);
|
|
vCards.push(vCardObj);
|
|
}
|
|
return vCards;
|
|
});
|
|
return await resultPromise;
|
|
}
|
|
};
|
|
|
|
// src/fileService.ts
|
|
var import_obsidian = require("obsidian");
|
|
var ContentSeperator = class {
|
|
constructor(startTag, startText, stopTag, stopText, commented = true) {
|
|
this.startTag = startTag;
|
|
this.startText = startText;
|
|
this.endTag = stopTag;
|
|
this.endText = stopText;
|
|
this.commented = commented;
|
|
this.commentStart = commented ? "<!--" : "";
|
|
this.commentEnd = commented ? "-->" : "";
|
|
}
|
|
buildContentString(content) {
|
|
return `${this.commentStart} ${this.startTag} ${this.startText} ${this.commentEnd}
|
|
${content}
|
|
${this.commentStart} ${this.endTag} ${this.endText} ${this.commentEnd}`;
|
|
}
|
|
};
|
|
var FileService = class {
|
|
async createFolder(folderPath, app) {
|
|
if (await app.vault.adapter.exists((0, import_obsidian.normalizePath)(folderPath)) == false)
|
|
await app.vault.createFolder(folderPath);
|
|
}
|
|
async saveFile(filePath, content, app) {
|
|
return app.vault.create(filePath, content);
|
|
}
|
|
async updateFile(file, updatedContent, seperator, app) {
|
|
return app.vault.process(file, (oldContent) => {
|
|
let oldLines = oldContent.split("\n");
|
|
let startIndex = oldLines.findIndex((line) => line.contains(seperator.startTag));
|
|
let stopIndex = oldLines.findIndex((line) => line.contains(seperator.endTag));
|
|
let newLines = Array();
|
|
for (let i = Math.min(0, startIndex); i < oldLines.length; i++) {
|
|
if (i == startIndex) {
|
|
newLines.push(updatedContent);
|
|
i = stopIndex;
|
|
continue;
|
|
}
|
|
newLines.push(oldLines[i]);
|
|
}
|
|
return newLines.join("\n");
|
|
});
|
|
}
|
|
};
|
|
|
|
// src/main.ts
|
|
var SettingTab = class extends import_obsidian2.PluginSettingTab {
|
|
constructor(app, plugin) {
|
|
super(app, plugin);
|
|
this.plugin = plugin;
|
|
}
|
|
display() {
|
|
const { containerEl } = this;
|
|
containerEl.empty();
|
|
new import_obsidian2.Setting(containerEl).setName("Contacts folder").setDesc("Select the folder in which your contacts will stored").addText((text) => text.setPlaceholder("Contacts").setValue(this.plugin.settings.contactsFolder).onChange(async (value) => {
|
|
this.plugin.settings.contactsFolder = value;
|
|
await this.plugin.saveSettings();
|
|
}));
|
|
new import_obsidian2.Setting(containerEl).setName("Contacts group").setDesc('Enter the name of the group ("Smart List") in which your contacts are stored in the MacOS Contacts app').addText((text) => text.setPlaceholder("Obsidian").setValue(this.plugin.settings.contactsGroup).onChange(async (value) => {
|
|
this.plugin.settings.contactsGroup = value;
|
|
await this.plugin.saveSettings();
|
|
}));
|
|
new import_obsidian2.Setting(containerEl).setName("Configure the shown contact fields below").setDesc("To update the shown contact fields, re-sync your contacts");
|
|
for (let attribute of VCard.getVCardFields()) {
|
|
new import_obsidian2.Setting(containerEl).setName(`${attribute}`).addToggle((toggle) => {
|
|
toggle.setValue(this.plugin.settings.enabledContactFields.includes(attribute));
|
|
toggle.onChange(async (value) => {
|
|
this.plugin.settings.enabledContactFields = this.toggleEnabledField(attribute, value);
|
|
await this.plugin.saveSettings();
|
|
});
|
|
});
|
|
}
|
|
}
|
|
toggleEnabledField(field, value) {
|
|
let enabledFields = this.plugin.settings.enabledContactFields.split(",");
|
|
if (value) {
|
|
enabledFields.push(field);
|
|
} else {
|
|
enabledFields = enabledFields.filter((enabledField) => enabledField != field);
|
|
}
|
|
return enabledFields.join(",");
|
|
}
|
|
};
|
|
var DEFAULT_SETTINGS = {
|
|
contactsGroup: "Obsidian",
|
|
contactsFolder: "Contacts",
|
|
autogenerationStartTag: "START",
|
|
autogenerationStartText: "Content BELOW this line is AUTOGENERATED and will be REPLACED.",
|
|
autogenerationEndTag: "END",
|
|
autogenerationEndText: "Content ABOVE this line is AUTOGENERATED and will be REPLACED.",
|
|
enabledContactFields: "nickname,emails,title,organization,telephones,addresses,birthdate,URLs,notes"
|
|
};
|
|
var ContactsPlugin = class extends import_obsidian2.Plugin {
|
|
async onload() {
|
|
await this.loadSettings();
|
|
this.addCommand({
|
|
id: "sync-contacts",
|
|
name: "Sync contacts",
|
|
callback: async () => {
|
|
if (!import_obsidian2.Platform.isMacOS)
|
|
return new import_obsidian2.Notice("Error: This plugin only works on MacOS");
|
|
new import_obsidian2.Notice("Syncing...");
|
|
let loadContactsLogic = new ContactsService(this.settings.contactsGroup, this.settings.enabledContactFields);
|
|
let numContactsPromise = loadContactsLogic.getNumberOfContacts().then((numContacts2) => {
|
|
new import_obsidian2.Notice(`Found ${numContacts2} Contacts in group ${this.settings.contactsGroup}`);
|
|
return numContacts2;
|
|
});
|
|
let fileService = new FileService();
|
|
let createFolderPromise = fileService.createFolder(this.settings.contactsFolder, this.app);
|
|
let contentSeperator = new ContentSeperator(
|
|
this.settings.autogenerationStartTag,
|
|
this.settings.autogenerationStartText,
|
|
this.settings.autogenerationEndTag,
|
|
this.settings.autogenerationEndText
|
|
);
|
|
let [numContacts, _] = await Promise.all([numContactsPromise, createFolderPromise]);
|
|
let markdownResults = await loadContactsLogic.loadContacts();
|
|
let successfulContacts = 0;
|
|
let promises = [];
|
|
for (let [filename, markdown] of markdownResults) {
|
|
let filePath = (0, import_obsidian2.normalizePath)(`${this.settings.contactsFolder}/${filename}.md`);
|
|
let file = this.app.vault.getAbstractFileByPath(filePath);
|
|
let newContactInfo = contentSeperator.buildContentString(markdown);
|
|
if (file instanceof import_obsidian2.TFolder) {
|
|
console.error(`Error: ${filePath} is a folder`);
|
|
new import_obsidian2.Notice(`Error: ${filePath} is a folder`);
|
|
} else if (file === null) {
|
|
promises.push(
|
|
fileService.saveFile(filePath, newContactInfo, this.app).then((_2) => successfulContacts++).catch((error) => console.error(`Error syncing ${filename}
|
|
${error}`))
|
|
);
|
|
} else if (file instanceof import_obsidian2.TFile) {
|
|
promises.push(
|
|
// extract the old contact info and replace it with the new data
|
|
fileService.updateFile(file, newContactInfo, new ContentSeperator(this.settings.autogenerationStartTag, this.settings.autogenerationStartText, this.settings.autogenerationEndTag, this.settings.autogenerationEndText), this.app).then((_2) => successfulContacts++).catch((error) => console.error(`Error syncing ${filename}
|
|
${error}`))
|
|
);
|
|
}
|
|
}
|
|
await Promise.all(promises).catch((error) => {
|
|
new import_obsidian2.Notice("Error syncing contacts!");
|
|
console.error(error);
|
|
}).finally(() => {
|
|
new import_obsidian2.Notice(`Successfully synced ${successfulContacts} of ${numContacts} Contacts`);
|
|
console.info(`Successfully synced ${successfulContacts} of ${numContacts} Contacts`);
|
|
});
|
|
}
|
|
});
|
|
this.addSettingTab(new SettingTab(this.app, this));
|
|
}
|
|
async loadSettings() {
|
|
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
|
|
}
|
|
async saveSettings() {
|
|
await this.saveData(this.settings);
|
|
}
|
|
};
|
|
|
|
/* nosourcemap */ |