diff --git a/packages/viewer/src/latex.ts b/packages/viewer/src/latex.ts index 7b7f4b875..ecb98086b 100644 --- a/packages/viewer/src/latex.ts +++ b/packages/viewer/src/latex.ts @@ -44,9 +44,16 @@ async function replaceLatexInTextNode(properties: Properties, node: Node) { // Get LaTeX text. const latex = textContent.substring(nextLatexPosition.start + "$$".length, nextLatexPosition.end); // Convert LaTeX to mathml. - const response = await latexToMathml(latex, properties.editorServicesRoot, properties.editorServicesExtension); + let responseText; + try { + const response = await latexToMathml(latex, properties.editorServicesRoot, properties.editorServicesExtension); + responseText = response.text; + } catch (e) { + responseText = "Error converting LaTeX to MathML"; + console.error(responseText + " " + e); + } // Insert mathml node. - const fragment = document.createRange().createContextualFragment(response.text); + const fragment = document.createRange().createContextualFragment(responseText); node.parentNode?.insertBefore(fragment, node); node.nodeValue = node.nodeValue.substring(nextLatexPosition.start, nextLatexPosition.end); diff --git a/packages/viewer/src/mathml.ts b/packages/viewer/src/mathml.ts index 7f5344df7..6fe1dd066 100644 --- a/packages/viewer/src/mathml.ts +++ b/packages/viewer/src/mathml.ts @@ -86,8 +86,8 @@ export async function renderMathML(properties: Properties, root: HTMLElement): P const img = await setImageProperties(properties, imgSource, mml); // Replace the MathML for the generated formula image. mathElement.parentNode?.replaceChild(img, mathElement); - } catch { - console.error(`Cannot render ${mml}: invalid MathML format.`); + } catch (e) { + console.error(`Cannot render ${mml}: ${e}`); continue; } } diff --git a/packages/viewer/src/services.ts b/packages/viewer/src/services.ts index f0f2a6e05..8513f13cd 100644 --- a/packages/viewer/src/services.ts +++ b/packages/viewer/src/services.ts @@ -20,23 +20,19 @@ export class StatusError extends Error { * Helper function to process responses from the editor services. * These usually come wrapped in a JSON with a status field that can be either "ok" or "warning". * If status is "ok", return the result value along it. Otherwise, throw a StatusError. - * @param {Promise} response - The response given by the service. + * @param {Response} response - The response given by the service. * @returns {Promise} The unwrapped result of the response, if valid. * @throws {StatusError} Service responded with a non-ok status. + * If the response.json() fails, it'll return a generic exception. */ -export async function processJsonResponse(response: Promise): Promise { - try { - const { status, result } = await (await response).json(); - - if (status !== "ok") { - throw new StatusError("Service responded with a non-ok status"); - } +async function processJsonResponse(response: Response): Promise { + const { status, result } = await response.json(); - return result; - } catch (e) { - // TODO manage network and status non-ok errors - throw e; + if (status !== "ok") { + throw new StatusError("Service responded with a non-ok status"); } + + return result; } /** @@ -54,36 +50,31 @@ export async function callService( serverURL: string, extension: string, ): Promise { - try { - const url = new URL(serviceName + extension, serverURL); - - // Getting the configuration is asynchronous since we make some requests. - const properties = await Properties.getInstance(); - const headers = { - "Content-type": "application/x-www-form-urlencoded; charset=utf-8", - ...properties.config.backendConfig.wiriscustomheaders, - }; - - const init: RequestInit = { - method, - headers, - }; - - if (method === MethodType.Get) { - // Add the query as search params - for (const [key, value] of Object.entries(query)) { - url.searchParams.set(key, value); - } - } else { - // Add the query as the body of the request - init.body = new URLSearchParams({ ...query }); - } + const url = new URL(serviceName + extension, serverURL); - return fetch(url.toString(), init); - } catch (e) { - // TODO manage network and status non-ok errors - throw e; + // Getting the configuration is asynchronous since we make some requests. + const properties = await Properties.getInstance(); + const headers = { + "Content-type": "application/x-www-form-urlencoded; charset=utf-8", + ...properties.config.backendConfig.wiriscustomheaders, + }; + + const init: RequestInit = { + method, + headers, + }; + + if (method === MethodType.Get) { + // Add the query as search params + for (const [key, value] of Object.entries(query)) { + url.searchParams.set(key, value); + } + } else { + // Add the query as the body of the request + init.body = new URLSearchParams({ ...query }); } + + return await fetch(url.toString(), init); } /** @@ -105,7 +96,7 @@ export async function mathml2accessible(mml: string, lang: string, url: string, ignoreStyles: "true", }; - const response = callService(params, "service", MethodType.Post, url, extension); + const response = await callService(params, "service", MethodType.Post, url, extension); return processJsonResponse(response); } @@ -127,20 +118,17 @@ export async function showImage(mml: string, lang: string, url: string, extensio // Try to obtain the image via GET const getParams = Parser.createShowImageSrcData(params, params.lang); - const getResponse = callService(getParams, "showimage", MethodType.Get, url, extension); + const showImageResponse = await callService(getParams, "showimage", MethodType.Get, url, extension); try { - return await processJsonResponse(getResponse); + return await processJsonResponse(showImageResponse); } catch (e) { if (e instanceof StatusError) { - // Formula was not in cache; proceed with calling showimage via POST + // If GET request fails, it means that the formula was not in cache. Proceed to create the image: + return createImage(mml, lang, url, extension); } else { throw e; } } - - // If GET request fails, it means that the formula was not in cache. Proceed with POST: - const response = callService(params, "showimage", MethodType.Post, url, extension); - return processJsonResponse(response); } /** @@ -160,7 +148,7 @@ export async function createImage(mml: string, lang: string, url: string, extens }; // POST request to retrieve the corresponding image. - const response = callService(params, "showimage", MethodType.Post, url, extension); + const response = await callService(params, "showimage", MethodType.Post, url, extension); return processJsonResponse(response); } @@ -177,7 +165,7 @@ export async function latexToMathml(latex: string, url: string, extension: strin latex: latex, }; - const response = callService(params, "service", MethodType.Post, url, extension); + const response = await callService(params, "service", MethodType.Post, url, extension); return processJsonResponse(response); } @@ -192,6 +180,10 @@ export async function configurationJson(variablekeys: string[], url: string, ext variablekeys: variablekeys.join(","), }; - const response = callService(params, "configurationjson", MethodType.Get, url, extension); - return processJsonResponse(response); + try { + const response = await callService(params, "configurationjson", MethodType.Get, url, extension); + return processJsonResponse(response); + } catch (e) { + return e; + } }