UNPKG

custom-x

Version:

A library to facilitate the storage of javascript objects in Custom XML Parts.

203 lines (202 loc) 9.39 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.setCustomXmlPartByValue = setCustomXmlPartByValue; exports.getCustomXmlPart = getCustomXmlPart; exports.getCustomXmlPartValue = getCustomXmlPartValue; exports.removeCustomXmlPart = removeCustomXmlPart; /// <reference types="office-js" /> const fast_xml_parser_1 = require("fast-xml-parser"); const xmlBuilder = new fast_xml_parser_1.XMLBuilder(); const xmlParser = new fast_xml_parser_1.XMLParser(); /** * Sets a custom XML part for the specified key and value. * * This asynchronous function converts the provided value into an XML string and associates it * with a namespace derived from the given key. If a custom XML part with the same namespace already * exists, it is deleted; otherwise, a new custom XML part is added. * * @param key - A string used to determine the namespace for the custom XML part. * @param value - The value to be converted into an XML format and stored as a custom XML part. * * @returns A promise that resolves when the operation is complete. */ async function setCustomXmlPartByValue(key, value) { const xmlValue = xmlBuilder.build(value); const namespace = getNameSpace(key); const customXmlPart = await getCustomXmlPartByNameSpace(namespace); if (customXmlPart != null) { await deleteCustomXmlPartByNameSpace(namespace); } await addCustomXmlPartByNameSpace(namespace, xmlValue); } /** * Retrieves a custom XML part associated with the specified key. * * @param key - The key used to derive a namespace for locating the custom XML part. * @returns A promise that resolves to the matching Office.CustomXmlPart if found, or null if not found. * * @remarks * This asynchronous function converts the provided key to a namespace using getNameSpace and then * obtains the related custom XML part by invoking getCustomXmlPartByNameSpace. It is primarily used * for accessing custom XML parts based on a key-to-namespace transformation. */ async function getCustomXmlPart(key) { const namespace = getNameSpace(key); return await getCustomXmlPartByNameSpace(namespace); } /** * Retrieves and parses the value of a custom XML part by its key. * * This asynchronous function first attempts to locate the custom XML part identified by the provided key. If the XML part exists, * it fetches its XML content asynchronously using the Office API. The retrieved XML string is then parsed into an object. * * @param key - The unique key that identifies the custom XML part. * @returns A promise that resolves to the parsed XML value, or null if the XML part is not found. * @throws An error if the asynchronous retrieval of the XML content fails. */ async function getCustomXmlPartValue(key) { const xmlPart = await getCustomXmlPart(key); if (xmlPart == null) { return null; } return new Promise((resolve, reject) => { xmlPart.getXmlAsync((result) => { if (result.status != Office.AsyncResultStatus.Succeeded) { reject(new Error(`Error getting XML: ${result.error.message}`)); } else { const xmlValue = result.value; const parsedValue = xmlParser.parse(xmlValue); // Return the contents of customData if it exists if (parsedValue.customData) { resolve(parsedValue.customData); } else { resolve(parsedValue); } } }); }); } /** * Removes a custom XML part associated with the provided key. * * This asynchronous function retrieves the namespace corresponding to the given key, * then fetches the custom XML part using that namespace. If the XML part exists, * it attempts to delete it using Office's asynchronous API. * * @param key - The identifier used to determine the namespace of the custom XML part. * @returns A promise that resolves if the XML part is successfully removed, or rejects with an error if deletion fails. */ async function removeCustomXmlPart(key) { const namespace = getNameSpace(key); let xmlPart = await getCustomXmlPartByNameSpace(namespace); if (xmlPart == null) { return; } await new Promise((resolve, reject) => { xmlPart.deleteAsync((result) => { if (result.status != Office.AsyncResultStatus.Succeeded) { reject(new Error(`Error deleting custom XML part: ${result.error.message}`)); } else { resolve(); } }); }); } /** * Retrieves the custom XML part for the specified namespace from the current Office document. * * This asynchronous function returns a Promise that: * - Resolves with the single matching custom XML part if one is found. * - Resolves with null if no matching custom XML parts are found. * - Rejects with an error if more than one matching custom XML part is found or if an * error occurs during retrieval. * * @async * @param namespace - The XML namespace to search for within the custom XML parts. * @returns A Promise that resolves with the matching Office.CustomXmlPart, null, or rejects with an error. */ async function getCustomXmlPartByNameSpace(namespace) { return new Promise((resolve, reject) => { Office.context.document.customXmlParts.getByNamespaceAsync(namespace, (result) => { if (result.status === Office.AsyncResultStatus.Succeeded) { if (result.value.length === 0) { resolve(null); } else if (result.value.length > 1) { reject(new Error("More than one custom XML part found for the namespace: " + namespace)); } else { resolve(result.value[0]); } } else { reject(new Error(`Error getting custom XML parts: ${result.error.message}`)); } }); }); } /** * Adds a custom XML part with the specified namespace and XML content to the document. * * This function creates an XML string by wrapping the provided content inside a <customData> element * with the given namespace. It then asynchronously adds this XML part to the Office document. * * @param namespace - The XML namespace associated with the custom data. * @param xmlValue - The XML content to be encapsulated within the custom data element. * @returns A promise that resolves when the custom XML part is successfully added. * * @throws Error if the addition of the custom XML part fails. */ async function addCustomXmlPartByNameSpace(namespace, xmlValue) { const xmlWithNamespace = `<customData xmlns="${namespace}">${xmlValue}</customData>`; return await Office.context.document.customXmlParts.addAsync(xmlWithNamespace, (result) => { if (result.status != Office.AsyncResultStatus.Succeeded) { throw new Error(`Error adding custom XML part: ${result.error.message}`); } }); } /** * Retrieves the custom XML parts for the specified namespace. * * This asynchronous function queries the document for custom XML parts that match the given namespace. * By convention, only one custom XML part is stored per namespace. * * @param namespace - The XML namespace used to locate the custom XML part. * @returns A Promise that resolves with an array of Office.CustomXmlPart instances if the operation succeeds. * If the operation fails, the Promise is rejected with the encountered error. */ async function deleteCustomXmlPartByNameSpace(namespace) { // Get the first custom XML part in the document for the specified namespace // (By convention, we only ever store one part per namespace.) return new Promise((resolve, reject) => { Office.context.document.customXmlParts.getByNamespaceAsync(namespace, (result) => { if (result.status === Office.AsyncResultStatus.Succeeded) { resolve(result.value); } else { reject(new Error(`Error getting custom XML parts: ${result.error.message}`)); } }); }); } /** * Sanitizes the provided key by replacing all non-alphanumeric characters with underscores and converting the result to lowercase. * * This function ensures that the resulting key is neither null, undefined, nor an empty string after sanitization. If the sanitized key is empty or only whitespace, * an error is thrown with details about the original and sanitized keys. * * @param key - The original key string to be sanitized. It is expected to contain only alphanumeric characters or underscores. * @returns The sanitized key string in lowercase. * @throws {Error} If the provided key is null, undefined, or empty after sanitization. */ function getNameSpace(key) { const sanitizedKey = key.replace(/[^a-zA-Z0-9]/g, "_").toLowerCase(); // make sure key is not null or undefined or empty string if (sanitizedKey == null || sanitizedKey == undefined || sanitizedKey == "" || sanitizedKey.trim() == "") { throw new Error(`Key is case-insensitive and cannot be null, undefined, or empty string. Keys can only contain alphanumeric characters or underscores.\nOriginal key: ${key} Sanitized key: ${sanitizedKey}`); } return sanitizedKey; }