From 0e6cc0e27a90a0759fd8932dde416e377acb3ffe Mon Sep 17 00:00:00 2001 From: mnhnam-axonivy Date: Thu, 11 Jan 2024 14:23:26 +0700 Subject: [PATCH 001/189] IVYPORTAL-16295: SPIKE UI-PoC for Portal Chatbot - Implemented --- .../Start Processes/PortalStart.p.json | 32 ++ .../portal/generic/bean/UserMenuBean.java | 22 +- .../generic/navigation/PortalNavigator.java | 5 + .../portal/response/ChatBotResponse.java | 14 + .../portal/rest/ChatBotRestService.java | 99 ++++ .../ChatDashboard/ChatDashboard.rddescriptor | 7 + .../ChatDashboard/ChatDashboard.xhtml | 11 + .../ChatDashboard/ChatDashboardData.ivyClass | 2 + .../ChatDashboard/ChatDashboardProcess.p.json | 48 ++ .../includes/ChatDashboardTemplate.xhtml | 88 ++++ .../webContent/layouts/includes/topbar.xhtml | 6 + .../resources/css/atom-one-dark.min.css | 1 + .../webContent/resources/css/chatbot.css | 131 +++++ .../webContent/resources/js/chatbot-utils.js | 158 ++++++ .../portal/webContent/resources/js/chatbot.js | 158 ++++++ .../resources/js/highlightJS/highlight.min.js | 491 ++++++++++++++++++ .../js/highlightJS/languages/css.min.js | 31 ++ .../js/highlightJS/languages/java.min.js | 38 ++ .../highlightJS/languages/javascript.min.js | 80 +++ .../js/highlightJS/languages/json.min.js | 7 + .../js/highlightJS/languages/markdown.min.js | 31 ++ 21 files changed, 1458 insertions(+), 2 deletions(-) create mode 100644 AxonIvyPortal/portal/src/com/axonivy/portal/response/ChatBotResponse.java create mode 100644 AxonIvyPortal/portal/src/com/axonivy/portal/rest/ChatBotRestService.java create mode 100644 AxonIvyPortal/portal/src_hd/com/axonivy/portal/component/ChatDashboard/ChatDashboard.rddescriptor create mode 100644 AxonIvyPortal/portal/src_hd/com/axonivy/portal/component/ChatDashboard/ChatDashboard.xhtml create mode 100644 AxonIvyPortal/portal/src_hd/com/axonivy/portal/component/ChatDashboard/ChatDashboardData.ivyClass create mode 100644 AxonIvyPortal/portal/src_hd/com/axonivy/portal/component/ChatDashboard/ChatDashboardProcess.p.json create mode 100644 AxonIvyPortal/portal/webContent/layouts/includes/ChatDashboardTemplate.xhtml create mode 100644 AxonIvyPortal/portal/webContent/resources/css/atom-one-dark.min.css create mode 100644 AxonIvyPortal/portal/webContent/resources/css/chatbot.css create mode 100644 AxonIvyPortal/portal/webContent/resources/js/chatbot-utils.js create mode 100644 AxonIvyPortal/portal/webContent/resources/js/chatbot.js create mode 100644 AxonIvyPortal/portal/webContent/resources/js/highlightJS/highlight.min.js create mode 100644 AxonIvyPortal/portal/webContent/resources/js/highlightJS/languages/css.min.js create mode 100644 AxonIvyPortal/portal/webContent/resources/js/highlightJS/languages/java.min.js create mode 100644 AxonIvyPortal/portal/webContent/resources/js/highlightJS/languages/javascript.min.js create mode 100644 AxonIvyPortal/portal/webContent/resources/js/highlightJS/languages/json.min.js create mode 100644 AxonIvyPortal/portal/webContent/resources/js/highlightJS/languages/markdown.min.js diff --git a/AxonIvyPortal/portal/processes/Start Processes/PortalStart.p.json b/AxonIvyPortal/portal/processes/Start Processes/PortalStart.p.json index fb22e76dd32..d98d38bcc86 100644 --- a/AxonIvyPortal/portal/processes/Start Processes/PortalStart.p.json +++ b/AxonIvyPortal/portal/processes/Start Processes/PortalStart.p.json @@ -4650,5 +4650,37 @@ "visual" : { "at" : { "x" : 376, "y" : 3848 } } + }, { + "id" : "f269", + "type" : "RequestStart", + "name" : "PortalChatDashboard", + "config" : { + "signature" : "PortalChatDashboard" + }, + "visual" : { + "at" : { "x" : 88, "y" : 3976 } + }, + "connect" : [ + { "id" : "f273", "to" : "f271" } + ] + }, { + "id" : "f270", + "type" : "TaskEnd", + "visual" : { + "at" : { "x" : 376, "y" : 3976 } + } + }, { + "id" : "f271", + "type" : "DialogCall", + "name" : "ChatDashboard", + "config" : { + "dialog" : "com.axonivy.portal.component.ChatDashboard:start()" + }, + "visual" : { + "at" : { "x" : 232, "y" : 3976 } + }, + "connect" : [ + { "id" : "f277", "to" : "f270" } + ] } ] } \ No newline at end of file diff --git a/AxonIvyPortal/portal/src/ch/ivy/addon/portal/generic/bean/UserMenuBean.java b/AxonIvyPortal/portal/src/ch/ivy/addon/portal/generic/bean/UserMenuBean.java index 3c20dd9a0d7..9f1f437a94b 100644 --- a/AxonIvyPortal/portal/src/ch/ivy/addon/portal/generic/bean/UserMenuBean.java +++ b/AxonIvyPortal/portal/src/ch/ivy/addon/portal/generic/bean/UserMenuBean.java @@ -212,7 +212,7 @@ public void resetTaskAndNavigateWithGrowl(ITask task) throws IOException { private void executeJSResetPortalMenuState() { PrimeFaces.current().executeScript("resetPortalLeftMenuState()"); } - + private void openTaskLosingConfirmationDialog() { PrimeFaces.current().executeScript("PF('logo-task-losing-confirmation-dialog').show()"); } @@ -260,7 +260,11 @@ public void navigateToHomePage() throws IOException { public void navigateToUserProfile() throws IOException { getExternalContext().redirect(getUserProfileUrl()); } - + + public void navigateToChatbotDashboard() throws IOException { + getExternalContext().redirect(getChatbotUrl()); + } + private void navigateToTargetPage() throws IOException { getExternalContext().redirect(targetPage); } @@ -269,6 +273,10 @@ private String getUserProfileUrl() { return PortalNavigator.buildUserProfileUrl(); } + private String getChatbotUrl() { + return PortalNavigator.buildChatbotUrl(); + } + private void navigateToPortalManagement() throws IOException { getExternalContext().redirect(getPortalManagementUrl()); } @@ -355,4 +363,14 @@ public String getQRcodeData() { return new Gson().toJson(data); } + + public void navigateToChatBotOrDisplayWorkingTaskWarning(boolean isWorkingOnATask, ITask task) throws IOException { + if (isWorkingOnATask && task.getState() != TaskState.DONE) { + openTaskLosingConfirmationDialog(); + targetPage = getChatbotUrl(); + } else { + executeJSResetPortalMenuState(); + navigateToChatbotDashboard(); + } + } } diff --git a/AxonIvyPortal/portal/src/ch/ivy/addon/portal/generic/navigation/PortalNavigator.java b/AxonIvyPortal/portal/src/ch/ivy/addon/portal/generic/navigation/PortalNavigator.java index 230f77efb2f..96be93a568a 100644 --- a/AxonIvyPortal/portal/src/ch/ivy/addon/portal/generic/navigation/PortalNavigator.java +++ b/AxonIvyPortal/portal/src/ch/ivy/addon/portal/generic/navigation/PortalNavigator.java @@ -40,6 +40,7 @@ public final class PortalNavigator extends BaseNavigator{ private static final String PORTAL_NEW_DASHBOARD_CONFIGURATION = "Start Processes/PortalStart/PortalDashboardConfiguration.ivp"; private static final String PORTAL_PROCESS_INFO = "Start Processes/PortalStart/ProcessInformation.ivp"; private static final String PORTAL_DASHBOARD_DETAILS = "Start Processes/PortalStart/DashboardDetails.ivp"; + private static final String PORTAL_CHATBOT = "Start Processes/PortalStart/PortalChatDashboard.ivp"; public static final String PORTAL_DASHBOARD_START = "/DefaultDashboardPage.ivp"; public static final String PORTAL_PROCESS_START = "/DefaultProcessStartListPage.ivp"; @@ -277,4 +278,8 @@ public static void navigateToNotificationFullPage() { public static String buildNotificationFullPageUrl() { return buildUrlByKeyword(PORTAL_NOTIFICATION_FULLPAGE_START, PORTAL_NOTIFICATION_FULLPAGE, new HashMap<>()); } + + public static String buildChatbotUrl() { + return buildUrlByKeyword("PortalChatDashboard.ivp", PORTAL_CHATBOT, new HashMap<>()); + } } diff --git a/AxonIvyPortal/portal/src/com/axonivy/portal/response/ChatBotResponse.java b/AxonIvyPortal/portal/src/com/axonivy/portal/response/ChatBotResponse.java new file mode 100644 index 00000000000..4e6d0a9f293 --- /dev/null +++ b/AxonIvyPortal/portal/src/com/axonivy/portal/response/ChatBotResponse.java @@ -0,0 +1,14 @@ +package com.axonivy.portal.response; + +public class ChatBotResponse { + private String chatResult; + + public String getChatResult() { + return chatResult; + } + + public void setChatResult(String chatResult) { + this.chatResult = chatResult; + } + +} diff --git a/AxonIvyPortal/portal/src/com/axonivy/portal/rest/ChatBotRestService.java b/AxonIvyPortal/portal/src/com/axonivy/portal/rest/ChatBotRestService.java new file mode 100644 index 00000000000..456a2b55774 --- /dev/null +++ b/AxonIvyPortal/portal/src/com/axonivy/portal/rest/ChatBotRestService.java @@ -0,0 +1,99 @@ +package com.axonivy.portal.rest; + +import java.util.Date; + +import javax.annotation.security.RolesAllowed; +import javax.ws.rs.NotFoundException; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; + +import com.google.gson.GsonBuilder; + +import ch.ivy.addon.portal.chat.ChatMessage; +import ch.ivy.addon.portal.chat.GsonUTCDateAdapter; +import ch.ivy.addon.portalkit.bean.SecurityMemberDisplayNameFormatBean; +import ch.ivyteam.ivy.environment.Ivy; +import ch.ivyteam.ivy.security.ISecurityConstants; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; + +@Path(value = "chatbot") +@RolesAllowed(value = { ISecurityConstants.TOP_LEVEL_ROLE_NAME }) +public class ChatBotRestService { + + private String userDisplayName; + + @POST + @Path(value = "{clientId}") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", content = { @Content(mediaType = MediaType.APPLICATION_JSON) }) }) + public Response processes(@PathParam("clientId") String clientId, String content) { + if (userDisplayName == null) { + SecurityMemberDisplayNameFormatBean nameBean = new SecurityMemberDisplayNameFormatBean(); + userDisplayName = nameBean.generateFullDisplayNameForSecurityMember(Ivy.session().getSessionUser(), + Ivy.session().getSessionUserName()); + } + try { + ChatMessage response = new ChatMessage(); + if (content.contentEquals("link")) { + response.setMessage("www.google.com"); + } else if (content.contentEquals("json")) { + response.setMessage(""" + + [ + { + "id" : "axon-ivy", + "title" : "Axon Ivy", + "permissions": ["#demo"], + "url" : "https://www.axonivy.com/" + }, + { + "id" : "express", + "title" : "Portal Express process", + "permissions": ["Everybody"], + "url" : "Portal Express process" + }, + { + "id" : "re-order-dashboard", + "titles": [ + { + "locale": "en", + "value": "Reorder your dashboards" + }, + { + "locale": "de", + "value": "Dashboards neu anordnen" + } + ], + "permissions": ["Employee", "AXONIVY_PORTAL_ADMIN", "#daniel"], + "url": "Start Processes/ExamplePortalStart/DashboardReorder.ivp", + "params": { + "isPublicDashboard":"false" + } + } + ]"""); + } else if (content.contentEquals("image")) { + response.setMessage("https://market.axonivy.com/market-cache/portal/portal-guide/11.1.0/_images/my-profile.png"); + } else { + response.setMessage("Please type 'link', 'json', or 'image'"); + } + + response.setSentDate(new Date()); + response.setSender(userDisplayName); + + return Response.ok(toJson(response)).build(); + } catch (NotFoundException e) { + return Response.status(Status.NOT_FOUND).entity(e.getMessage()).build(); + } + } + + private String toJson(Object object) { + return new GsonBuilder().registerTypeAdapter(Date.class, new GsonUTCDateAdapter()).setPrettyPrinting() + .disableHtmlEscaping().create().toJson(object); + } +} diff --git a/AxonIvyPortal/portal/src_hd/com/axonivy/portal/component/ChatDashboard/ChatDashboard.rddescriptor b/AxonIvyPortal/portal/src_hd/com/axonivy/portal/component/ChatDashboard/ChatDashboard.rddescriptor new file mode 100644 index 00000000000..ae605f0d9ad --- /dev/null +++ b/AxonIvyPortal/portal/src_hd/com/axonivy/portal/component/ChatDashboard/ChatDashboard.rddescriptor @@ -0,0 +1,7 @@ + + + + viewTechnology + JSF + + diff --git a/AxonIvyPortal/portal/src_hd/com/axonivy/portal/component/ChatDashboard/ChatDashboard.xhtml b/AxonIvyPortal/portal/src_hd/com/axonivy/portal/component/ChatDashboard/ChatDashboard.xhtml new file mode 100644 index 00000000000..9f86d01318b --- /dev/null +++ b/AxonIvyPortal/portal/src_hd/com/axonivy/portal/component/ChatDashboard/ChatDashboard.xhtml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/AxonIvyPortal/portal/src_hd/com/axonivy/portal/component/ChatDashboard/ChatDashboardData.ivyClass b/AxonIvyPortal/portal/src_hd/com/axonivy/portal/component/ChatDashboard/ChatDashboardData.ivyClass new file mode 100644 index 00000000000..5844577d66f --- /dev/null +++ b/AxonIvyPortal/portal/src_hd/com/axonivy/portal/component/ChatDashboard/ChatDashboardData.ivyClass @@ -0,0 +1,2 @@ +ChatDashboardData #class +com.axonivy.portal.component.ChatDashboard #namespace diff --git a/AxonIvyPortal/portal/src_hd/com/axonivy/portal/component/ChatDashboard/ChatDashboardProcess.p.json b/AxonIvyPortal/portal/src_hd/com/axonivy/portal/component/ChatDashboard/ChatDashboardProcess.p.json new file mode 100644 index 00000000000..80c5cd7503f --- /dev/null +++ b/AxonIvyPortal/portal/src_hd/com/axonivy/portal/component/ChatDashboard/ChatDashboardProcess.p.json @@ -0,0 +1,48 @@ +{ + "$schema" : "https://json-schema.axonivy.com/process/11.2.2/process.json", + "id" : "18CE74C696F6F830", + "kind" : "HTML_DIALOG", + "config" : { + "data" : "com.axonivy.portal.component.ChatDashboard.ChatDashboardData" + }, + "elements" : [ { + "id" : "f0", + "type" : "HtmlDialogStart", + "name" : "start()", + "config" : { + "signature" : "start", + "guid" : "18CE74C69708A7D2" + }, + "visual" : { + "at" : { "x" : 96, "y" : 64 } + }, + "connect" : [ + { "id" : "f2", "to" : "f1" } + ] + }, { + "id" : "f1", + "type" : "HtmlDialogEnd", + "visual" : { + "at" : { "x" : 224, "y" : 64 } + } + }, { + "id" : "f3", + "type" : "HtmlDialogEventStart", + "name" : "close", + "config" : { + "guid" : "18CE74C6971D6A38" + }, + "visual" : { + "at" : { "x" : 96, "y" : 160 } + }, + "connect" : [ + { "id" : "f5", "to" : "f4" } + ] + }, { + "id" : "f4", + "type" : "HtmlDialogExit", + "visual" : { + "at" : { "x" : 224, "y" : 160 } + } + } ] +} \ No newline at end of file diff --git a/AxonIvyPortal/portal/webContent/layouts/includes/ChatDashboardTemplate.xhtml b/AxonIvyPortal/portal/webContent/layouts/includes/ChatDashboardTemplate.xhtml new file mode 100644 index 00000000000..4927b3555db --- /dev/null +++ b/AxonIvyPortal/portal/webContent/layouts/includes/ChatDashboardTemplate.xhtml @@ -0,0 +1,88 @@ + + + + + Portal Chat + + + + + + + + + + + + +
+
+ + + +
+
+ + +
+ + + + +
+ +
+
+
+
+ +
+ +
+
+ +
+
+
+ +
+
+ +
+ +
+
+ + + + + \ No newline at end of file diff --git a/AxonIvyPortal/portal/webContent/layouts/includes/topbar.xhtml b/AxonIvyPortal/portal/webContent/layouts/includes/topbar.xhtml index 25f76ba411e..419e7d43daa 100644 --- a/AxonIvyPortal/portal/webContent/layouts/includes/topbar.xhtml +++ b/AxonIvyPortal/portal/webContent/layouts/includes/topbar.xhtml @@ -61,6 +61,12 @@ +
  • + + + +
  • diff --git a/AxonIvyPortal/portal/webContent/resources/css/atom-one-dark.min.css b/AxonIvyPortal/portal/webContent/resources/css/atom-one-dark.min.css new file mode 100644 index 00000000000..5344ee38193 --- /dev/null +++ b/AxonIvyPortal/portal/webContent/resources/css/atom-one-dark.min.css @@ -0,0 +1 @@ +pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#abb2bf;background:#282c34}.hljs-comment,.hljs-quote{color:#5c6370;font-style:italic}.hljs-doctag,.hljs-formula,.hljs-keyword{color:#c678dd}.hljs-deletion,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-subst{color:#e06c75}.hljs-literal{color:#56b6c2}.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#98c379}.hljs-attr,.hljs-number,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-pseudo,.hljs-template-variable,.hljs-type,.hljs-variable{color:#d19a66}.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-symbol,.hljs-title{color:#61aeee}.hljs-built_in,.hljs-class .hljs-title,.hljs-title.class_{color:#e6c07b}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline} \ No newline at end of file diff --git a/AxonIvyPortal/portal/webContent/resources/css/chatbot.css b/AxonIvyPortal/portal/webContent/resources/css/chatbot.css new file mode 100644 index 00000000000..717e994bc0b --- /dev/null +++ b/AxonIvyPortal/portal/webContent/resources/css/chatbot.css @@ -0,0 +1,131 @@ +.chatbot-panel { + display: flex; + flex-direction: column; + flex-wrap: nowrap; + justify-content: space-evenly; + align-items: stretch; +} + +.chatbot-panel .message-list { + display: block; + height: 100%; + width: 100%; + padding: 0; + overflow-x: hidden; + overflow-y: auto; +} + +.chatbot-panel .message-list .chat-message-list { + height: 100%; + width: 100%; +} + +.chatbot-panel .message-list-content { + display: flex; + flex-direction: column; + flex-wrap: nowrap; + justify-content: space-evenly; + align-items: stretch; +} + +.chatbot-panel .message-list { + background: transparent; + box-shadow: none; +} + +.chatbot-panel .chat-message-list { + height: calc(100% - 235px); + width: 100%; + padding: 0 30px; + overflow-y: auto; + overflow-x: hidden; + display: flex; + flex-direction: row; +} + +.chatbot-panel .my-message .chat-meta { + text-align: left; +} + +.chatbot-panel .avatar-block { + display: inline; + margin-right: 0.5rem; +} + +.chatbot-panel .message-block { + display: flex; + flex-direction: column; + flex-wrap: nowrap; + justify-content: flex-start; + align-items: flex-start; +} + +.chatbot-panel .message-block .chat-message { + cursor: default; + border-radius: 10px; + margin: 0; + word-break: break-all; + box-sizing: border-box; + border-top-left-radius: 0px; + float: left; + font-size: 1.1rem; +} + +.chatbot-panel .message-block .chat-sender, +.chatbot-panel .message-block .chat-message { + align-self: flex-start; +} + +.chatbot-panel .chatbot-logo, +.chatbot-panel .gravatar { + height: 2rem; + width: 2rem; + border-radius: 50%; +} + +.chatbot-panel .chat-meta { + display: flex; +} + +.chatbot-panel .chat-sender { + font-size: 1.2rem; + font-weight: 500; + line-height: 2rem; +} + +.chatbot-panel .code-block { + border-radius: 5px; + flex-direction: column; + overflow-x: auto; +} + +.chatbot-panel .code-block .code-block-header { + border-radius: 5px 5px 0 0 ; + display: block; + width: 100%; + background: var(--ivy-primary-color-grey-dark); + line-height: 2rem; + padding: 0 0.5rem; +} + +.chatbot-panel .code-block .code-block-header span { + color: var(--ivy-primary-text-color); + font-weight: bold; +} + + +.chatbot-panel .code-block .code-wrapper { + padding: 5px; +} + +.chatbot-panel .image-container { + width: 50%; + border-radius: 5px; + background: transparent; + position: relative; +} + +.chatbot-panel .image-container .image { + width: 100%; + border-radius: 5px; +} \ No newline at end of file diff --git a/AxonIvyPortal/portal/webContent/resources/js/chatbot-utils.js b/AxonIvyPortal/portal/webContent/resources/js/chatbot-utils.js new file mode 100644 index 00000000000..9664986a78f --- /dev/null +++ b/AxonIvyPortal/portal/webContent/resources/js/chatbot-utils.js @@ -0,0 +1,158 @@ +// Constants +const URL_PATTERN = /^(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$/gm; +const IMAGE_PATTERN = /\.(jpg|jpeg|png|webp|avif|gif|svg)$/; +const HAS_HTTP_PATTERN = /^https?:\/\//i; +const CODE_TAG_START = ''; +const CODE_TAG_END = ''; +const HLJS_LANGUAGE_PREFIX = 'language-'; + +// Helper Functions + +// Converts an HTML element to its string representation. +const elemToString = el => el.outerHTML; + +// Checks if a paragraph starts with a code tag. +const isCode = paragraph => paragraph.startsWith(CODE_TAG_START); + +// Checks if a URL is an image URL. +const isImageUrl = url => isUrl(url) && url.match(IMAGE_PATTERN); + +// Adds a link component to a word if it's a URL, otherwise returns the word unchanged. +const addLink = word => (isUrl(word) ? generateLinkComponent(word) : word); + +// Code Generation Functions + +// Converts a code block paragraph to a formatted code component. +const convertCode = paragraph => { + paragraph = paragraph.replace(CODE_TAG_START, ''); + paragraph = generateCodeComponent(paragraph); + return paragraph.endsWith(CODE_TAG_END) ? paragraph.slice(0, -CODE_TAG_END.length) : paragraph; +}; + +// Generates an HTML link element with an updated URL. +const generateLinkComponent = url => { + const updatedUrl = correctUrl(url); + const link = $('.js-message-components').find('.js-external-link').get(0); + const clone = link.cloneNode(true); + clone.setAttribute('href', updatedUrl); + clone.setAttribute('title', url); + clone.innerHTML = url; + clone.classList.remove('js-external-link'); + return elemToString(clone); +}; + +// Generates a formatted code component with syntax highlighting. +const generateCodeComponent = codeBlock => { + // Adds a header with the code block language to the code component. + const addCodeHeaderComponent = codeComponent => { + const headerElem = document.createElement('div'); + headerElem.className = 'code-block-header'; + + const headerTextElem = document.createElement('span'); + headerTextElem.textContent = getCodeClass(codeComponent); + headerElem.appendChild(headerTextElem); + + codeComponent.innerHTML = headerElem.outerHTML + codeComponent.innerHTML; + }; + + // Retrieves the code block language from the code component. + const getCodeClass = codeComponent => { + const classList = codeComponent.className.split(' '); + const languageClass = classList.find(className => className.startsWith(HLJS_LANGUAGE_PREFIX)); + return languageClass ? languageClass.substring(HLJS_LANGUAGE_PREFIX.length) : ''; + }; + + // Wraps the content of the code component with a wrapper element. + const wrapCodeContent = codeComponent => { + const clonedChildNodes = []; + + while (codeComponent.firstChild) { + const childNode = codeComponent.firstChild; + const clonedNode = childNode.cloneNode(true); + clonedChildNodes.push(clonedNode); + codeComponent.removeChild(childNode); + } + + const wrapperElem = document.createElement('div'); + wrapperElem.className = 'code-wrapper'; + + for (const node of clonedChildNodes) { + wrapperElem.appendChild(node); + } + + codeComponent.appendChild(wrapperElem); + }; + + // Creates the code element, pre element, and adds syntax highlighting and wrapping. + const codeElem = document.createElement('code'); + codeElem.innerHTML = codeBlock; + + const preElem = document.createElement('pre'); + preElem.appendChild(codeElem); + preElem.className = 'code-block'; + + // Highlights code + hljs.highlightElement(preElem); + + // Wraps highlighted code + wrapCodeContent(preElem); + + // Adds header + addCodeHeaderComponent(preElem); + + return elemToString(preElem); +}; + +// Converts an image URL to a formatted image component. +const convertImage = paragraph => { + const createImageContainerElement = () => { + const imageContainer = document.createElement('div'); + imageContainer.className = 'image-container js-image-container'; + return imageContainer; + }; + + const image = document.createElement('img'); + image.src = paragraph; + image.alt = paragraph; + image.className = 'image js-image'; + + const imageContainer = createImageContainerElement(); + imageContainer.appendChild(image); + + return elemToString(imageContainer); +}; + +// Other Functions + +// Corrects a URL by adding 'http://' if missing. +const correctUrl = url => (HAS_HTTP_PATTERN.test(url) ? url : 'http://' + url); + +// Checks if a word matches the URL pattern. +const isUrl = word => word.match(URL_PATTERN); + +// Parse Functions + +// Parses a paragraph based on its content type (code, image, or regular text). +const parseParagraph = paragraph => { + paragraph = paragraph.trim(); + + if (isCode(paragraph)) { + paragraph = convertCode(paragraph); + } else if (isImageUrl(paragraph)) { + paragraph = convertImage(paragraph); + } else { + const words = paragraph.split(' '); + const formattedWords = words.map(w => addLink(w)); + paragraph = formattedWords.join(' '); + } + + return paragraph; +}; + +// Parses a message by splitting it into paragraphs and formatting each paragraph. +const parseMessage = message => { + const paragraphs = message.split(/\r?\n\n/); + const formattedParagraphs = paragraphs.map(p => parseParagraph(p)); + message = formattedParagraphs.join('\n'); + return message; +}; \ No newline at end of file diff --git a/AxonIvyPortal/portal/webContent/resources/js/chatbot.js b/AxonIvyPortal/portal/webContent/resources/js/chatbot.js new file mode 100644 index 00000000000..c03e4a174a4 --- /dev/null +++ b/AxonIvyPortal/portal/webContent/resources/js/chatbot.js @@ -0,0 +1,158 @@ +// Detecting browser type +const isEdge = window.navigator.userAgent.includes('Edge'); +const isFireFox = navigator.userAgent.toLowerCase().includes('firefox'); +const isIphone = navigator.userAgent.match(/iPhone|iPod/i); + +// Global variables +let messages = []; +let currentIndex; +const numberOfLoad = 20; +const numberToApplyLazyLoad = 50; +let recipientName; +let originalMessageTemplate; +let jsMessageList; +const clientId = new Date().getTime(); +let isChatDeactivated = false; +let hasPendingRequestForSendersOfUnreadMessages = false; +let hidden, visibilityChange; + +// Handling browser visibility change +if (typeof document.hidden !== 'undefined') { + hidden = 'hidden'; + visibilityChange = 'visibilitychange'; +} else if (typeof document.msHidden !== 'undefined') { + hidden = 'msHidden'; + visibilityChange = 'msvisibilitychange'; +} else if (typeof document.webkitHidden !== 'undefined') { + hidden = 'webkitHidden'; + visibilityChange = 'webkitvisibilitychange'; +} + +// Helper class for constructing RESTful URIs +function IvyUri() { + this.rest = function () { + const baseUri = window.location.origin; + return `${baseUri}${contextPath}/api`; + }; +} + +// ChatBot class for handling chat interactions +function ChatBot(uri, view) { + this.uri = uri; + + // Sending a message + this.sendMessage = async function () { + const inputMessage = $('.js-chatbot-input-message').val().trim(); + if (!inputMessage) { + return; + } + + const message = { 'message': inputMessage }; + sendChatMessage(uri, view, message); + }; + + // Handling key events, e.g., Enter key + this.onKeyEvent = function (e) { + if (e.ctrlKey && e.keyCode == 13) { + adjustMessageInputForNewline(); + } else if (e.keyCode === 13) { + e.preventDefault(); + this.sendMessage(); + } + }; +} + +// View class for rendering chat messages +function View(uri) { + // Initializing message components + function setValueForMessageComponent() { + originalMessageTemplate = document.getElementsByClassName('js-message-template')[0]; + jsMessageList = document.getElementsByClassName('js-chatbot-message-list')[0]; + } + + // Rendering received messages + this.renderMessage = function (message) { + setValueForMessageComponent(); + renderMessageFunc(message, false); + }; + + // Rendering user's own messages + this.renderMyMessage = function (message) { + setValueForMessageComponent(); + renderMessageFunc(message, true); + }; + + // Helper function for rendering messages + function renderMessageFunc(messageWrapper, isMyMessage) { + const cloneTemplate = originalMessageTemplate.cloneNode(true); + const message = isMyMessage ? messageWrapper.message : parseMessage(messageWrapper.message); + + cloneTemplate.getElementsByClassName('js-message')[0].innerHTML = message; + $(cloneTemplate).removeClass('u-hidden').removeClass('js-message-template'); + + const senderElem = cloneTemplate.getElementsByClassName('js-sender')[0]; + + if (isMyMessage) { + $(cloneTemplate).addClass('my-message'); + senderElem.innerText = 'You'; + $(cloneTemplate).find('.js-chatbot-avatar').get(0).remove(); + } else { + senderElem.innerText = 'Portal'; + $(cloneTemplate).find('.js-user-avatar').get(0).remove(); + } + + appendMessageToList(cloneTemplate); + return cloneTemplate; + } + + // Helper function for appending messages to the message list + function appendMessageToList(cloneTemplate) { + const $messageList = $(jsMessageList); + $messageList.append(cloneTemplate); + + if ($(cloneTemplate).hasClass('my-message')) { + const $chatMessages = $('.js-chatbot-message-list').find('.chat-message'); + setTimeout(function () { + $messageList.animate({ + scrollTop: $messageList.scrollTop() + $chatMessages.last().offset().top + }, 0); + }, 0); + } + } +} + +// Function for toggling full-screen mode +function toggleFullScreen(e) { + const container = $(e).parent('.js-image-container'); + container.hasClass('expand-fullscreen') ? + container.removeClass('expand-fullscreen') : + container.addClass('expand-fullscreen'); +} + +// Helper function to send chat messages +async function sendChatMessage(uri, view, message) { + jQuery.ajax({ + type: 'POST', + contentType: 'application/json', + url: `${uri}/${clientId}`, + crossDomain: true, + cache: false, + headers: { 'X-Requested-By': 'ivy' }, + data: message.message, + complete: function (response) { + view.renderMyMessage(message); + view.renderMessage(JSON.parse(response.responseText)); + $('.js-chatbot-input-message').val(''); + } + }); +} + +// Helper function to adjust message input for a new line +function adjustMessageInputForNewline() { + const messageInputField = document.getElementById('message-input-field'); + const position = messageInputField.selectionEnd; + messageInputField.value = messageInputField.value.substring(0, position) + '\n' + messageInputField.value.substring(position); + messageInputField.selectionEnd = position + 1; + messageInputField.seletionStart = position + 1; + messageInputField.scrollTop = messageInputField.scrollHeight; +} diff --git a/AxonIvyPortal/portal/webContent/resources/js/highlightJS/highlight.min.js b/AxonIvyPortal/portal/webContent/resources/js/highlightJS/highlight.min.js new file mode 100644 index 00000000000..6c559c2f70f --- /dev/null +++ b/AxonIvyPortal/portal/webContent/resources/js/highlightJS/highlight.min.js @@ -0,0 +1,491 @@ +/*! + Highlight.js v11.9.0 (git: b7ec4bfafc) + (c) 2006-2023 undefined and other contributors + License: BSD-3-Clause + */ +var hljs=function(){"use strict";function e(t){ +return t instanceof Map?t.clear=t.delete=t.set=()=>{ +throw Error("map is read-only")}:t instanceof Set&&(t.add=t.clear=t.delete=()=>{ +throw Error("set is read-only") +}),Object.freeze(t),Object.getOwnPropertyNames(t).forEach((n=>{ +const i=t[n],s=typeof i;"object"!==s&&"function"!==s||Object.isFrozen(i)||e(i) +})),t}class t{constructor(e){ +void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1} +ignoreMatch(){this.isMatchIgnored=!0}}function n(e){ +return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'") +}function i(e,...t){const n=Object.create(null);for(const t in e)n[t]=e[t] +;return t.forEach((e=>{for(const t in e)n[t]=e[t]})),n}const s=e=>!!e.scope +;class o{constructor(e,t){ +this.buffer="",this.classPrefix=t.classPrefix,e.walk(this)}addText(e){ +this.buffer+=n(e)}openNode(e){if(!s(e))return;const t=((e,{prefix:t})=>{ +if(e.startsWith("language:"))return e.replace("language:","language-") +;if(e.includes(".")){const n=e.split(".") +;return[`${t}${n.shift()}`,...n.map(((e,t)=>`${e}${"_".repeat(t+1)}`))].join(" ") +}return`${t}${e}`})(e.scope,{prefix:this.classPrefix});this.span(t)} +closeNode(e){s(e)&&(this.buffer+="")}value(){return this.buffer}span(e){ +this.buffer+=``}}const r=(e={})=>{const t={children:[]} +;return Object.assign(t,e),t};class a{constructor(){ +this.rootNode=r(),this.stack=[this.rootNode]}get top(){ +return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){ +this.top.children.push(e)}openNode(e){const t=r({scope:e}) +;this.add(t),this.stack.push(t)}closeNode(){ +if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){ +for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)} +walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,t){ +return"string"==typeof t?e.addText(t):t.children&&(e.openNode(t), +t.children.forEach((t=>this._walk(e,t))),e.closeNode(t)),e}static _collapse(e){ +"string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{ +a._collapse(e)})))}}class c extends a{constructor(e){super(),this.options=e} +addText(e){""!==e&&this.add(e)}startScope(e){this.openNode(e)}endScope(){ +this.closeNode()}__addSublanguage(e,t){const n=e.root +;t&&(n.scope="language:"+t),this.add(n)}toHTML(){ +return new o(this,this.options).value()}finalize(){ +return this.closeAllNodes(),!0}}function l(e){ +return e?"string"==typeof e?e:e.source:null}function g(e){return h("(?=",e,")")} +function u(e){return h("(?:",e,")*")}function d(e){return h("(?:",e,")?")} +function h(...e){return e.map((e=>l(e))).join("")}function f(...e){const t=(e=>{ +const t=e[e.length-1] +;return"object"==typeof t&&t.constructor===Object?(e.splice(e.length-1,1),t):{} +})(e);return"("+(t.capture?"":"?:")+e.map((e=>l(e))).join("|")+")"} +function p(e){return RegExp(e.toString()+"|").exec("").length-1} +const b=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./ +;function m(e,{joinWith:t}){let n=0;return e.map((e=>{n+=1;const t=n +;let i=l(e),s="";for(;i.length>0;){const e=b.exec(i);if(!e){s+=i;break} +s+=i.substring(0,e.index), +i=i.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?s+="\\"+(Number(e[1])+t):(s+=e[0], +"("===e[0]&&n++)}return s})).map((e=>`(${e})`)).join(t)} +const E="[a-zA-Z]\\w*",x="[a-zA-Z_]\\w*",w="\\b\\d+(\\.\\d+)?",y="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",_="\\b(0b[01]+)",O={ +begin:"\\\\[\\s\\S]",relevance:0},v={scope:"string",begin:"'",end:"'", +illegal:"\\n",contains:[O]},k={scope:"string",begin:'"',end:'"',illegal:"\\n", +contains:[O]},N=(e,t,n={})=>{const s=i({scope:"comment",begin:e,end:t, +contains:[]},n);s.contains.push({scope:"doctag", +begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)", +end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0}) +;const o=f("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/) +;return s.contains.push({begin:h(/[ ]+/,"(",o,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),s +},S=N("//","$"),M=N("/\\*","\\*/"),R=N("#","$");var j=Object.freeze({ +__proto__:null,APOS_STRING_MODE:v,BACKSLASH_ESCAPE:O,BINARY_NUMBER_MODE:{ +scope:"number",begin:_,relevance:0},BINARY_NUMBER_RE:_,COMMENT:N, +C_BLOCK_COMMENT_MODE:M,C_LINE_COMMENT_MODE:S,C_NUMBER_MODE:{scope:"number", +begin:y,relevance:0},C_NUMBER_RE:y,END_SAME_AS_BEGIN:e=>Object.assign(e,{ +"on:begin":(e,t)=>{t.data._beginMatch=e[1]},"on:end":(e,t)=>{ +t.data._beginMatch!==e[1]&&t.ignoreMatch()}}),HASH_COMMENT_MODE:R,IDENT_RE:E, +MATCH_NOTHING_RE:/\b\B/,METHOD_GUARD:{begin:"\\.\\s*"+x,relevance:0}, +NUMBER_MODE:{scope:"number",begin:w,relevance:0},NUMBER_RE:w, +PHRASAL_WORDS_MODE:{ +begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/ +},QUOTE_STRING_MODE:k,REGEXP_MODE:{scope:"regexp",begin:/\/(?=[^/\n]*\/)/, +end:/\/[gimuy]*/,contains:[O,{begin:/\[/,end:/\]/,relevance:0,contains:[O]}]}, +RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~", +SHEBANG:(e={})=>{const t=/^#![ ]*\// +;return e.binary&&(e.begin=h(t,/.*\b/,e.binary,/\b.*/)),i({scope:"meta",begin:t, +end:/$/,relevance:0,"on:begin":(e,t)=>{0!==e.index&&t.ignoreMatch()}},e)}, +TITLE_MODE:{scope:"title",begin:E,relevance:0},UNDERSCORE_IDENT_RE:x, +UNDERSCORE_TITLE_MODE:{scope:"title",begin:x,relevance:0}});function A(e,t){ +"."===e.input[e.index-1]&&t.ignoreMatch()}function I(e,t){ +void 0!==e.className&&(e.scope=e.className,delete e.className)}function T(e,t){ +t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)", +e.__beforeBegin=A,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords, +void 0===e.relevance&&(e.relevance=0))}function L(e,t){ +Array.isArray(e.illegal)&&(e.illegal=f(...e.illegal))}function B(e,t){ +if(e.match){ +if(e.begin||e.end)throw Error("begin & end are not supported with match") +;e.begin=e.match,delete e.match}}function P(e,t){ +void 0===e.relevance&&(e.relevance=1)}const D=(e,t)=>{if(!e.beforeMatch)return +;if(e.starts)throw Error("beforeMatch cannot be used with starts") +;const n=Object.assign({},e);Object.keys(e).forEach((t=>{delete e[t] +})),e.keywords=n.keywords,e.begin=h(n.beforeMatch,g(n.begin)),e.starts={ +relevance:0,contains:[Object.assign(n,{endsParent:!0})] +},e.relevance=0,delete n.beforeMatch +},H=["of","and","for","in","not","or","if","then","parent","list","value"],C="keyword" +;function $(e,t,n=C){const i=Object.create(null) +;return"string"==typeof e?s(n,e.split(" ")):Array.isArray(e)?s(n,e):Object.keys(e).forEach((n=>{ +Object.assign(i,$(e[n],t,n))})),i;function s(e,n){ +t&&(n=n.map((e=>e.toLowerCase()))),n.forEach((t=>{const n=t.split("|") +;i[n[0]]=[e,U(n[0],n[1])]}))}}function U(e,t){ +return t?Number(t):(e=>H.includes(e.toLowerCase()))(e)?0:1}const z={},W=e=>{ +console.error(e)},X=(e,...t)=>{console.log("WARN: "+e,...t)},G=(e,t)=>{ +z[`${e}/${t}`]||(console.log(`Deprecated as of ${e}. ${t}`),z[`${e}/${t}`]=!0) +},K=Error();function F(e,t,{key:n}){let i=0;const s=e[n],o={},r={} +;for(let e=1;e<=t.length;e++)r[e+i]=s[e],o[e+i]=!0,i+=p(t[e-1]) +;e[n]=r,e[n]._emit=o,e[n]._multi=!0}function Z(e){(e=>{ +e.scope&&"object"==typeof e.scope&&null!==e.scope&&(e.beginScope=e.scope, +delete e.scope)})(e),"string"==typeof e.beginScope&&(e.beginScope={ +_wrap:e.beginScope}),"string"==typeof e.endScope&&(e.endScope={_wrap:e.endScope +}),(e=>{if(Array.isArray(e.begin)){ +if(e.skip||e.excludeBegin||e.returnBegin)throw W("skip, excludeBegin, returnBegin not compatible with beginScope: {}"), +K +;if("object"!=typeof e.beginScope||null===e.beginScope)throw W("beginScope must be object"), +K;F(e,e.begin,{key:"beginScope"}),e.begin=m(e.begin,{joinWith:""})}})(e),(e=>{ +if(Array.isArray(e.end)){ +if(e.skip||e.excludeEnd||e.returnEnd)throw W("skip, excludeEnd, returnEnd not compatible with endScope: {}"), +K +;if("object"!=typeof e.endScope||null===e.endScope)throw W("endScope must be object"), +K;F(e,e.end,{key:"endScope"}),e.end=m(e.end,{joinWith:""})}})(e)}function V(e){ +function t(t,n){ +return RegExp(l(t),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(n?"g":"")) +}class n{constructor(){ +this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0} +addRule(e,t){ +t.position=this.position++,this.matchIndexes[this.matchAt]=t,this.regexes.push([t,e]), +this.matchAt+=p(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null) +;const e=this.regexes.map((e=>e[1]));this.matcherRe=t(m(e,{joinWith:"|" +}),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex +;const t=this.matcherRe.exec(e);if(!t)return null +;const n=t.findIndex(((e,t)=>t>0&&void 0!==e)),i=this.matchIndexes[n] +;return t.splice(0,n),Object.assign(t,i)}}class s{constructor(){ +this.rules=[],this.multiRegexes=[], +this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){ +if(this.multiRegexes[e])return this.multiRegexes[e];const t=new n +;return this.rules.slice(e).forEach((([e,n])=>t.addRule(e,n))), +t.compile(),this.multiRegexes[e]=t,t}resumingScanAtSamePosition(){ +return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,t){ +this.rules.push([e,t]),"begin"===t.type&&this.count++}exec(e){ +const t=this.getMatcher(this.regexIndex);t.lastIndex=this.lastIndex +;let n=t.exec(e) +;if(this.resumingScanAtSamePosition())if(n&&n.index===this.lastIndex);else{ +const t=this.getMatcher(0);t.lastIndex=this.lastIndex+1,n=t.exec(e)} +return n&&(this.regexIndex+=n.position+1, +this.regexIndex===this.count&&this.considerAll()),n}} +if(e.compilerExtensions||(e.compilerExtensions=[]), +e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.") +;return e.classNameAliases=i(e.classNameAliases||{}),function n(o,r){const a=o +;if(o.isCompiled)return a +;[I,B,Z,D].forEach((e=>e(o,r))),e.compilerExtensions.forEach((e=>e(o,r))), +o.__beforeBegin=null,[T,L,P].forEach((e=>e(o,r))),o.isCompiled=!0;let c=null +;return"object"==typeof o.keywords&&o.keywords.$pattern&&(o.keywords=Object.assign({},o.keywords), +c=o.keywords.$pattern, +delete o.keywords.$pattern),c=c||/\w+/,o.keywords&&(o.keywords=$(o.keywords,e.case_insensitive)), +a.keywordPatternRe=t(c,!0), +r&&(o.begin||(o.begin=/\B|\b/),a.beginRe=t(a.begin),o.end||o.endsWithParent||(o.end=/\B|\b/), +o.end&&(a.endRe=t(a.end)), +a.terminatorEnd=l(a.end)||"",o.endsWithParent&&r.terminatorEnd&&(a.terminatorEnd+=(o.end?"|":"")+r.terminatorEnd)), +o.illegal&&(a.illegalRe=t(o.illegal)), +o.contains||(o.contains=[]),o.contains=[].concat(...o.contains.map((e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((t=>i(e,{ +variants:null},t)))),e.cachedVariants?e.cachedVariants:q(e)?i(e,{ +starts:e.starts?i(e.starts):null +}):Object.isFrozen(e)?i(e):e))("self"===e?o:e)))),o.contains.forEach((e=>{n(e,a) +})),o.starts&&n(o.starts,r),a.matcher=(e=>{const t=new s +;return e.contains.forEach((e=>t.addRule(e.begin,{rule:e,type:"begin" +}))),e.terminatorEnd&&t.addRule(e.terminatorEnd,{type:"end" +}),e.illegal&&t.addRule(e.illegal,{type:"illegal"}),t})(a),a}(e)}function q(e){ +return!!e&&(e.endsWithParent||q(e.starts))}class J extends Error{ +constructor(e,t){super(e),this.name="HTMLInjectionError",this.html=t}} +const Y=n,Q=i,ee=Symbol("nomatch"),te=n=>{ +const i=Object.create(null),s=Object.create(null),o=[];let r=!0 +;const a="Could not find the language '{}', did you forget to load/include a language module?",l={ +disableAutodetect:!0,name:"Plain text",contains:[]};let p={ +ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i, +languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-", +cssSelector:"pre code",languages:null,__emitter:c};function b(e){ +return p.noHighlightRe.test(e)}function m(e,t,n){let i="",s="" +;"object"==typeof t?(i=e, +n=t.ignoreIllegals,s=t.language):(G("10.7.0","highlight(lang, code, ...args) has been deprecated."), +G("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"), +s=e,i=t),void 0===n&&(n=!0);const o={code:i,language:s};N("before:highlight",o) +;const r=o.result?o.result:E(o.language,o.code,n) +;return r.code=o.code,N("after:highlight",r),r}function E(e,n,s,o){ +const c=Object.create(null);function l(){if(!N.keywords)return void M.addText(R) +;let e=0;N.keywordPatternRe.lastIndex=0;let t=N.keywordPatternRe.exec(R),n="" +;for(;t;){n+=R.substring(e,t.index) +;const s=_.case_insensitive?t[0].toLowerCase():t[0],o=(i=s,N.keywords[i]);if(o){ +const[e,i]=o +;if(M.addText(n),n="",c[s]=(c[s]||0)+1,c[s]<=7&&(j+=i),e.startsWith("_"))n+=t[0];else{ +const n=_.classNameAliases[e]||e;u(t[0],n)}}else n+=t[0] +;e=N.keywordPatternRe.lastIndex,t=N.keywordPatternRe.exec(R)}var i +;n+=R.substring(e),M.addText(n)}function g(){null!=N.subLanguage?(()=>{ +if(""===R)return;let e=null;if("string"==typeof N.subLanguage){ +if(!i[N.subLanguage])return void M.addText(R) +;e=E(N.subLanguage,R,!0,S[N.subLanguage]),S[N.subLanguage]=e._top +}else e=x(R,N.subLanguage.length?N.subLanguage:null) +;N.relevance>0&&(j+=e.relevance),M.__addSublanguage(e._emitter,e.language) +})():l(),R=""}function u(e,t){ +""!==e&&(M.startScope(t),M.addText(e),M.endScope())}function d(e,t){let n=1 +;const i=t.length-1;for(;n<=i;){if(!e._emit[n]){n++;continue} +const i=_.classNameAliases[e[n]]||e[n],s=t[n];i?u(s,i):(R=s,l(),R=""),n++}} +function h(e,t){ +return e.scope&&"string"==typeof e.scope&&M.openNode(_.classNameAliases[e.scope]||e.scope), +e.beginScope&&(e.beginScope._wrap?(u(R,_.classNameAliases[e.beginScope._wrap]||e.beginScope._wrap), +R=""):e.beginScope._multi&&(d(e.beginScope,t),R="")),N=Object.create(e,{parent:{ +value:N}}),N}function f(e,n,i){let s=((e,t)=>{const n=e&&e.exec(t) +;return n&&0===n.index})(e.endRe,i);if(s){if(e["on:end"]){const i=new t(e) +;e["on:end"](n,i),i.isMatchIgnored&&(s=!1)}if(s){ +for(;e.endsParent&&e.parent;)e=e.parent;return e}} +if(e.endsWithParent)return f(e.parent,n,i)}function b(e){ +return 0===N.matcher.regexIndex?(R+=e[0],1):(T=!0,0)}function m(e){ +const t=e[0],i=n.substring(e.index),s=f(N,e,i);if(!s)return ee;const o=N +;N.endScope&&N.endScope._wrap?(g(), +u(t,N.endScope._wrap)):N.endScope&&N.endScope._multi?(g(), +d(N.endScope,e)):o.skip?R+=t:(o.returnEnd||o.excludeEnd||(R+=t), +g(),o.excludeEnd&&(R=t));do{ +N.scope&&M.closeNode(),N.skip||N.subLanguage||(j+=N.relevance),N=N.parent +}while(N!==s.parent);return s.starts&&h(s.starts,e),o.returnEnd?0:t.length} +let w={};function y(i,o){const a=o&&o[0];if(R+=i,null==a)return g(),0 +;if("begin"===w.type&&"end"===o.type&&w.index===o.index&&""===a){ +if(R+=n.slice(o.index,o.index+1),!r){const t=Error(`0 width match regex (${e})`) +;throw t.languageName=e,t.badRule=w.rule,t}return 1} +if(w=o,"begin"===o.type)return(e=>{ +const n=e[0],i=e.rule,s=new t(i),o=[i.__beforeBegin,i["on:begin"]] +;for(const t of o)if(t&&(t(e,s),s.isMatchIgnored))return b(n) +;return i.skip?R+=n:(i.excludeBegin&&(R+=n), +g(),i.returnBegin||i.excludeBegin||(R=n)),h(i,e),i.returnBegin?0:n.length})(o) +;if("illegal"===o.type&&!s){ +const e=Error('Illegal lexeme "'+a+'" for mode "'+(N.scope||"")+'"') +;throw e.mode=N,e}if("end"===o.type){const e=m(o);if(e!==ee)return e} +if("illegal"===o.type&&""===a)return 1 +;if(I>1e5&&I>3*o.index)throw Error("potential infinite loop, way more iterations than matches") +;return R+=a,a.length}const _=O(e) +;if(!_)throw W(a.replace("{}",e)),Error('Unknown language: "'+e+'"') +;const v=V(_);let k="",N=o||v;const S={},M=new p.__emitter(p);(()=>{const e=[] +;for(let t=N;t!==_;t=t.parent)t.scope&&e.unshift(t.scope) +;e.forEach((e=>M.openNode(e)))})();let R="",j=0,A=0,I=0,T=!1;try{ +if(_.__emitTokens)_.__emitTokens(n,M);else{for(N.matcher.considerAll();;){ +I++,T?T=!1:N.matcher.considerAll(),N.matcher.lastIndex=A +;const e=N.matcher.exec(n);if(!e)break;const t=y(n.substring(A,e.index),e) +;A=e.index+t}y(n.substring(A))}return M.finalize(),k=M.toHTML(),{language:e, +value:k,relevance:j,illegal:!1,_emitter:M,_top:N}}catch(t){ +if(t.message&&t.message.includes("Illegal"))return{language:e,value:Y(n), +illegal:!0,relevance:0,_illegalBy:{message:t.message,index:A, +context:n.slice(A-100,A+100),mode:t.mode,resultSoFar:k},_emitter:M};if(r)return{ +language:e,value:Y(n),illegal:!1,relevance:0,errorRaised:t,_emitter:M,_top:N} +;throw t}}function x(e,t){t=t||p.languages||Object.keys(i);const n=(e=>{ +const t={value:Y(e),illegal:!1,relevance:0,_top:l,_emitter:new p.__emitter(p)} +;return t._emitter.addText(e),t})(e),s=t.filter(O).filter(k).map((t=>E(t,e,!1))) +;s.unshift(n);const o=s.sort(((e,t)=>{ +if(e.relevance!==t.relevance)return t.relevance-e.relevance +;if(e.language&&t.language){if(O(e.language).supersetOf===t.language)return 1 +;if(O(t.language).supersetOf===e.language)return-1}return 0})),[r,a]=o,c=r +;return c.secondBest=a,c}function w(e){let t=null;const n=(e=>{ +let t=e.className+" ";t+=e.parentNode?e.parentNode.className:"" +;const n=p.languageDetectRe.exec(t);if(n){const t=O(n[1]) +;return t||(X(a.replace("{}",n[1])), +X("Falling back to no-highlight mode for this block.",e)),t?n[1]:"no-highlight"} +return t.split(/\s+/).find((e=>b(e)||O(e)))})(e);if(b(n))return +;if(N("before:highlightElement",{el:e,language:n +}),e.dataset.highlighted)return void console.log("Element previously highlighted. To highlight again, first unset `dataset.highlighted`.",e) +;if(e.children.length>0&&(p.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."), +console.warn("https://github.com/highlightjs/highlight.js/wiki/security"), +console.warn("The element with unescaped HTML:"), +console.warn(e)),p.throwUnescapedHTML))throw new J("One of your code blocks includes unescaped HTML.",e.innerHTML) +;t=e;const i=t.textContent,o=n?m(i,{language:n,ignoreIllegals:!0}):x(i) +;e.innerHTML=o.value,e.dataset.highlighted="yes",((e,t,n)=>{const i=t&&s[t]||n +;e.classList.add("hljs"),e.classList.add("language-"+i) +})(e,n,o.language),e.result={language:o.language,re:o.relevance, +relevance:o.relevance},o.secondBest&&(e.secondBest={ +language:o.secondBest.language,relevance:o.secondBest.relevance +}),N("after:highlightElement",{el:e,result:o,text:i})}let y=!1;function _(){ +"loading"!==document.readyState?document.querySelectorAll(p.cssSelector).forEach(w):y=!0 +}function O(e){return e=(e||"").toLowerCase(),i[e]||i[s[e]]} +function v(e,{languageName:t}){"string"==typeof e&&(e=[e]),e.forEach((e=>{ +s[e.toLowerCase()]=t}))}function k(e){const t=O(e) +;return t&&!t.disableAutodetect}function N(e,t){const n=e;o.forEach((e=>{ +e[n]&&e[n](t)}))} +"undefined"!=typeof window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",(()=>{ +y&&_()}),!1),Object.assign(n,{highlight:m,highlightAuto:x,highlightAll:_, +highlightElement:w, +highlightBlock:e=>(G("10.7.0","highlightBlock will be removed entirely in v12.0"), +G("10.7.0","Please use highlightElement now."),w(e)),configure:e=>{p=Q(p,e)}, +initHighlighting:()=>{ +_(),G("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")}, +initHighlightingOnLoad:()=>{ +_(),G("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.") +},registerLanguage:(e,t)=>{let s=null;try{s=t(n)}catch(t){ +if(W("Language definition for '{}' could not be registered.".replace("{}",e)), +!r)throw t;W(t),s=l} +s.name||(s.name=e),i[e]=s,s.rawDefinition=t.bind(null,n),s.aliases&&v(s.aliases,{ +languageName:e})},unregisterLanguage:e=>{delete i[e] +;for(const t of Object.keys(s))s[t]===e&&delete s[t]}, +listLanguages:()=>Object.keys(i),getLanguage:O,registerAliases:v, +autoDetection:k,inherit:Q,addPlugin:e=>{(e=>{ +e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=t=>{ +e["before:highlightBlock"](Object.assign({block:t.el},t)) +}),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=t=>{ +e["after:highlightBlock"](Object.assign({block:t.el},t))})})(e),o.push(e)}, +removePlugin:e=>{const t=o.indexOf(e);-1!==t&&o.splice(t,1)}}),n.debugMode=()=>{ +r=!1},n.safeMode=()=>{r=!0},n.versionString="11.9.0",n.regex={concat:h, +lookahead:g,either:f,optional:d,anyNumberOfTimes:u} +;for(const t in j)"object"==typeof j[t]&&e(j[t]);return Object.assign(n,j),n +},ne=te({});return ne.newInstance=()=>te({}),ne}() +;"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs);/*! `css` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict" +;const e=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","main","mark","menu","nav","object","ol","p","q","quote","samp","section","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video","defs","g","marker","mask","pattern","svg","switch","symbol","feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feFlood","feGaussianBlur","feImage","feMerge","feMorphology","feOffset","feSpecularLighting","feTile","feTurbulence","linearGradient","radialGradient","stop","circle","ellipse","image","line","path","polygon","polyline","rect","text","use","textPath","tspan","foreignObject","clipPath"],r=["any-hover","any-pointer","aspect-ratio","color","color-gamut","color-index","device-aspect-ratio","device-height","device-width","display-mode","forced-colors","grid","height","hover","inverted-colors","monochrome","orientation","overflow-block","overflow-inline","pointer","prefers-color-scheme","prefers-contrast","prefers-reduced-motion","prefers-reduced-transparency","resolution","scan","scripting","update","width","min-width","max-width","min-height","max-height"].sort().reverse(),o=["active","any-link","blank","checked","current","default","defined","dir","disabled","drop","empty","enabled","first","first-child","first-of-type","fullscreen","future","focus","focus-visible","focus-within","has","host","host-context","hover","indeterminate","in-range","invalid","is","lang","last-child","last-of-type","left","link","local-link","not","nth-child","nth-col","nth-last-child","nth-last-col","nth-last-of-type","nth-of-type","only-child","only-of-type","optional","out-of-range","past","placeholder-shown","read-only","read-write","required","right","root","scope","target","target-within","user-invalid","valid","visited","where"].sort().reverse(),t=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"].sort().reverse(),i=["align-content","align-items","align-self","alignment-baseline","all","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","backface-visibility","background","background-attachment","background-blend-mode","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","baseline-shift","block-size","border","border-block","border-block-color","border-block-end","border-block-end-color","border-block-end-style","border-block-end-width","border-block-start","border-block-start-color","border-block-start-style","border-block-start-width","border-block-style","border-block-width","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-inline","border-inline-color","border-inline-end","border-inline-end-color","border-inline-end-style","border-inline-end-width","border-inline-start","border-inline-start-color","border-inline-start-style","border-inline-start-width","border-inline-style","border-inline-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","cx","cy","caption-side","caret-color","clear","clip","clip-path","clip-rule","color","color-interpolation","color-interpolation-filters","color-profile","color-rendering","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","contain","content","content-visibility","counter-increment","counter-reset","cue","cue-after","cue-before","cursor","direction","display","dominant-baseline","empty-cells","enable-background","fill","fill-opacity","fill-rule","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","flow","flood-color","flood-opacity","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-size","font-size-adjust","font-smoothing","font-stretch","font-style","font-synthesis","font-variant","font-variant-caps","font-variant-east-asian","font-variant-ligatures","font-variant-numeric","font-variant-position","font-variation-settings","font-weight","gap","glyph-orientation-horizontal","glyph-orientation-vertical","grid","grid-area","grid-auto-columns","grid-auto-flow","grid-auto-rows","grid-column","grid-column-end","grid-column-start","grid-gap","grid-row","grid-row-end","grid-row-start","grid-template","grid-template-areas","grid-template-columns","grid-template-rows","hanging-punctuation","height","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","inline-size","isolation","kerning","justify-content","left","letter-spacing","lighting-color","line-break","line-height","list-style","list-style-image","list-style-position","list-style-type","marker","marker-end","marker-mid","marker-start","mask","margin","margin-block","margin-block-end","margin-block-start","margin-bottom","margin-inline","margin-inline-end","margin-inline-start","margin-left","margin-right","margin-top","marks","mask","mask-border","mask-border-mode","mask-border-outset","mask-border-repeat","mask-border-slice","mask-border-source","mask-border-width","mask-clip","mask-composite","mask-image","mask-mode","mask-origin","mask-position","mask-repeat","mask-size","mask-type","max-block-size","max-height","max-inline-size","max-width","min-block-size","min-height","min-inline-size","min-width","mix-blend-mode","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-wrap","overflow-x","overflow-y","padding","padding-block","padding-block-end","padding-block-start","padding-bottom","padding-inline","padding-inline-end","padding-inline-start","padding-left","padding-right","padding-top","page-break-after","page-break-before","page-break-inside","pause","pause-after","pause-before","perspective","perspective-origin","pointer-events","position","quotes","r","resize","rest","rest-after","rest-before","right","row-gap","scroll-margin","scroll-margin-block","scroll-margin-block-end","scroll-margin-block-start","scroll-margin-bottom","scroll-margin-inline","scroll-margin-inline-end","scroll-margin-inline-start","scroll-margin-left","scroll-margin-right","scroll-margin-top","scroll-padding","scroll-padding-block","scroll-padding-block-end","scroll-padding-block-start","scroll-padding-bottom","scroll-padding-inline","scroll-padding-inline-end","scroll-padding-inline-start","scroll-padding-left","scroll-padding-right","scroll-padding-top","scroll-snap-align","scroll-snap-stop","scroll-snap-type","scrollbar-color","scrollbar-gutter","scrollbar-width","shape-image-threshold","shape-margin","shape-outside","shape-rendering","stop-color","stop-opacity","stroke","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke-width","speak","speak-as","src","tab-size","table-layout","text-anchor","text-align","text-align-all","text-align-last","text-combine-upright","text-decoration","text-decoration-color","text-decoration-line","text-decoration-style","text-emphasis","text-emphasis-color","text-emphasis-position","text-emphasis-style","text-indent","text-justify","text-orientation","text-overflow","text-rendering","text-shadow","text-transform","text-underline-position","top","transform","transform-box","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vector-effect","vertical-align","visibility","voice-balance","voice-duration","voice-family","voice-pitch","voice-range","voice-rate","voice-stress","voice-volume","white-space","widows","width","will-change","word-break","word-spacing","word-wrap","writing-mode","x","y","z-index"].sort().reverse() +;return n=>{const a=n.regex,l=(e=>({IMPORTANT:{scope:"meta",begin:"!important"}, +BLOCK_COMMENT:e.C_BLOCK_COMMENT_MODE,HEXCOLOR:{scope:"number", +begin:/#(([0-9a-fA-F]{3,4})|(([0-9a-fA-F]{2}){3,4}))\b/},FUNCTION_DISPATCH:{ +className:"built_in",begin:/[\w-]+(?=\()/},ATTRIBUTE_SELECTOR_MODE:{ +scope:"selector-attr",begin:/\[/,end:/\]/,illegal:"$", +contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},CSS_NUMBER_MODE:{ +scope:"number", +begin:e.NUMBER_RE+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?", +relevance:0},CSS_VARIABLE:{className:"attr",begin:/--[A-Za-z_][A-Za-z0-9_-]*/} +}))(n),s=[n.APOS_STRING_MODE,n.QUOTE_STRING_MODE];return{name:"CSS", +case_insensitive:!0,illegal:/[=|'\$]/,keywords:{keyframePosition:"from to"}, +classNameAliases:{keyframePosition:"selector-tag"},contains:[l.BLOCK_COMMENT,{ +begin:/-(webkit|moz|ms|o)-(?=[a-z])/},l.CSS_NUMBER_MODE,{ +className:"selector-id",begin:/#[A-Za-z0-9_-]+/,relevance:0},{ +className:"selector-class",begin:"\\.[a-zA-Z-][a-zA-Z0-9_-]*",relevance:0 +},l.ATTRIBUTE_SELECTOR_MODE,{className:"selector-pseudo",variants:[{ +begin:":("+o.join("|")+")"},{begin:":(:)?("+t.join("|")+")"}]},l.CSS_VARIABLE,{ +className:"attribute",begin:"\\b("+i.join("|")+")\\b"},{begin:/:/,end:/[;}{]/, +contains:[l.BLOCK_COMMENT,l.HEXCOLOR,l.IMPORTANT,l.CSS_NUMBER_MODE,...s,{ +begin:/(url|data-uri)\(/,end:/\)/,relevance:0,keywords:{built_in:"url data-uri" +},contains:[...s,{className:"string",begin:/[^)]/,endsWithParent:!0, +excludeEnd:!0}]},l.FUNCTION_DISPATCH]},{begin:a.lookahead(/@/),end:"[{;]", +relevance:0,illegal:/:/,contains:[{className:"keyword",begin:/@-?\w[\w]*(-\w+)*/ +},{begin:/\s/,endsWithParent:!0,excludeEnd:!0,relevance:0,keywords:{ +$pattern:/[a-z-]+/,keyword:"and or not only",attribute:r.join(" ")},contains:[{ +begin:/[a-z-]+(?=:)/,className:"attribute"},...s,l.CSS_NUMBER_MODE]}]},{ +className:"selector-tag",begin:"\\b("+e.join("|")+")\\b"}]}}})() +;hljs.registerLanguage("css",e)})();/*! `java` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict" +;var e="[0-9](_*[0-9])*",a=`\\.(${e})`,n="[0-9a-fA-F](_*[0-9a-fA-F])*",s={ +className:"number",variants:[{ +begin:`(\\b(${e})((${a})|\\.)?|(${a}))[eE][+-]?(${e})[fFdD]?\\b`},{ +begin:`\\b(${e})((${a})[fFdD]?\\b|\\.([fFdD]\\b)?)`},{begin:`(${a})[fFdD]?\\b` +},{begin:`\\b(${e})[fFdD]\\b`},{ +begin:`\\b0[xX]((${n})\\.?|(${n})?\\.(${n}))[pP][+-]?(${e})[fFdD]?\\b`},{ +begin:"\\b(0|[1-9](_*[0-9])*)[lL]?\\b"},{begin:`\\b0[xX](${n})[lL]?\\b`},{ +begin:"\\b0(_*[0-7])*[lL]?\\b"},{begin:"\\b0[bB][01](_*[01])*[lL]?\\b"}], +relevance:0};function t(e,a,n){return-1===n?"":e.replace(a,(s=>t(e,a,n-1)))} +return e=>{ +const a=e.regex,n="[\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*",i=n+t("(?:<"+n+"~~~(?:\\s*,\\s*"+n+"~~~)*>)?",/~~~/g,2),r={ +keyword:["synchronized","abstract","private","var","static","if","const ","for","while","strictfp","finally","protected","import","native","final","void","enum","else","break","transient","catch","instanceof","volatile","case","assert","package","default","public","try","switch","continue","throws","protected","public","private","module","requires","exports","do","sealed","yield","permits"], +literal:["false","true","null"], +type:["char","boolean","long","float","int","byte","short","double"], +built_in:["super","this"]},l={className:"meta",begin:"@"+n,contains:[{ +begin:/\(/,end:/\)/,contains:["self"]}]},c={className:"params",begin:/\(/, +end:/\)/,keywords:r,relevance:0,contains:[e.C_BLOCK_COMMENT_MODE],endsParent:!0} +;return{name:"Java",aliases:["jsp"],keywords:r,illegal:/<\/|#/, +contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{begin:/\w+@/, +relevance:0},{className:"doctag",begin:"@[A-Za-z]+"}]}),{ +begin:/import java\.[a-z]+\./,keywords:"import",relevance:2 +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{begin:/"""/,end:/"""/, +className:"string",contains:[e.BACKSLASH_ESCAPE] +},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{ +match:[/\b(?:class|interface|enum|extends|implements|new)/,/\s+/,n],className:{ +1:"keyword",3:"title.class"}},{match:/non-sealed/,scope:"keyword"},{ +begin:[a.concat(/(?!else)/,n),/\s+/,n,/\s+/,/=(?!=)/],className:{1:"type", +3:"variable",5:"operator"}},{begin:[/record/,/\s+/,n],className:{1:"keyword", +3:"title.class"},contains:[c,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{ +beginKeywords:"new throw return else",relevance:0},{ +begin:["(?:"+i+"\\s+)",e.UNDERSCORE_IDENT_RE,/\s*(?=\()/],className:{ +2:"title.function"},keywords:r,contains:[{className:"params",begin:/\(/, +end:/\)/,keywords:r,relevance:0, +contains:[l,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,s,e.C_BLOCK_COMMENT_MODE] +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},s,l]}}})() +;hljs.registerLanguage("java",e)})();/*! `javascript` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict" +;const e="[A-Za-z$_][0-9A-Za-z$_]*",n=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],a=["true","false","null","undefined","NaN","Infinity"],t=["Object","Function","Boolean","Symbol","Math","Date","Number","BigInt","String","RegExp","Array","Float32Array","Float64Array","Int8Array","Uint8Array","Uint8ClampedArray","Int16Array","Int32Array","Uint16Array","Uint32Array","BigInt64Array","BigUint64Array","Set","Map","WeakSet","WeakMap","ArrayBuffer","SharedArrayBuffer","Atomics","DataView","JSON","Promise","Generator","GeneratorFunction","AsyncFunction","Reflect","Proxy","Intl","WebAssembly"],s=["Error","EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"],r=["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],c=["arguments","this","super","console","window","document","localStorage","sessionStorage","module","global"],i=[].concat(r,t,s) +;return o=>{const l=o.regex,b=e,d={begin:/<[A-Za-z0-9\\._:-]+/, +end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(e,n)=>{ +const a=e[0].length+e.index,t=e.input[a] +;if("<"===t||","===t)return void n.ignoreMatch();let s +;">"===t&&(((e,{after:n})=>{const a="",$={ +match:[/const|var|let/,/\s+/,b,/\s*/,/=\s*/,/(async\s*)?/,l.lookahead(B)], +keywords:"async",className:{1:"keyword",3:"title.function"},contains:[R]} +;return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:g,exports:{ +PARAMS_CONTAINS:w,CLASS_REFERENCE:k},illegal:/#(?![$_A-z])/, +contains:[o.SHEBANG({label:"shebang",binary:"node",relevance:5}),{ +label:"use_strict",className:"meta",relevance:10, +begin:/^\s*['"]use (strict|asm)['"]/ +},o.APOS_STRING_MODE,o.QUOTE_STRING_MODE,h,N,_,f,v,{match:/\$\d+/},A,k,{ +className:"attr",begin:b+l.lookahead(":"),relevance:0},$,{ +begin:"("+o.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*", +keywords:"return throw case",relevance:0,contains:[v,o.REGEXP_MODE,{ +className:"function",begin:B,returnBegin:!0,end:"\\s*=>",contains:[{ +className:"params",variants:[{begin:o.UNDERSCORE_IDENT_RE,relevance:0},{ +className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0, +excludeEnd:!0,keywords:g,contains:w}]}]},{begin:/,/,relevance:0},{match:/\s+/, +relevance:0},{variants:[{begin:"<>",end:""},{ +match:/<[A-Za-z0-9\\._:-]+\s*\/>/},{begin:d.begin, +"on:begin":d.isTrulyOpeningTag,end:d.end}],subLanguage:"xml",contains:[{ +begin:d.begin,end:d.end,skip:!0,contains:["self"]}]}]},I,{ +beginKeywords:"while if switch catch for"},{ +begin:"\\b(?!function)"+o.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{", +returnBegin:!0,label:"func.def",contains:[R,o.inherit(o.TITLE_MODE,{begin:b, +className:"title.function"})]},{match:/\.\.\./,relevance:0},C,{match:"\\$"+b, +relevance:0},{match:[/\bconstructor(?=\s*\()/],className:{1:"title.function"}, +contains:[R]},x,{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/, +className:"variable.constant"},O,M,{match:/\$[(.]/}]}}})() +;hljs.registerLanguage("javascript",e)})();/*! `json` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const a=["true","false","null"],n={ +scope:"literal",beginKeywords:a.join(" ")};return{name:"JSON",keywords:{ +literal:a},contains:[{className:"attr",begin:/"(\\.|[^\\"\r\n])*"(?=\s*:)/, +relevance:1.01},{match:/[{}[\],:]/,className:"punctuation",relevance:0 +},e.QUOTE_STRING_MODE,n,e.C_NUMBER_MODE,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE], +illegal:"\\S"}}})();hljs.registerLanguage("json",e)})();/*! `markdown` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const n={begin:/<\/?[A-Za-z_]/, +end:">",subLanguage:"xml",relevance:0},a={variants:[{begin:/\[.+?\]\[.*?\]/, +relevance:0},{ +begin:/\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/, +relevance:2},{ +begin:e.regex.concat(/\[.+?\]\(/,/[A-Za-z][A-Za-z0-9+.-]*/,/:\/\/.*?\)/), +relevance:2},{begin:/\[.+?\]\([./?&#].*?\)/,relevance:1},{ +begin:/\[.*?\]\(.*?\)/,relevance:0}],returnBegin:!0,contains:[{match:/\[(?=\])/ +},{className:"string",relevance:0,begin:"\\[",end:"\\]",excludeBegin:!0, +returnEnd:!0},{className:"link",relevance:0,begin:"\\]\\(",end:"\\)", +excludeBegin:!0,excludeEnd:!0},{className:"symbol",relevance:0,begin:"\\]\\[", +end:"\\]",excludeBegin:!0,excludeEnd:!0}]},i={className:"strong",contains:[], +variants:[{begin:/_{2}(?!\s)/,end:/_{2}/},{begin:/\*{2}(?!\s)/,end:/\*{2}/}] +},s={className:"emphasis",contains:[],variants:[{begin:/\*(?![*\s])/,end:/\*/},{ +begin:/_(?![_\s])/,end:/_/,relevance:0}]},c=e.inherit(i,{contains:[] +}),t=e.inherit(s,{contains:[]});i.contains.push(t),s.contains.push(c) +;let g=[n,a];return[i,s,c,t].forEach((e=>{e.contains=e.contains.concat(g) +})),g=g.concat(i,s),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{ +className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:g},{ +begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n", +contains:g}]}]},n,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)", +end:"\\s+",excludeEnd:!0},i,s,{className:"quote",begin:"^>\\s+",contains:g, +end:"$"},{className:"code",variants:[{begin:"(`{3,})[^`](.|\\n)*?\\1`*[ ]*"},{ +begin:"(~{3,})[^~](.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{ +begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))", +contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{ +begin:"^[-\\*]{3,}",end:"$"},a,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{ +className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{ +className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]}]}}})() +;hljs.registerLanguage("markdown",e)})(); \ No newline at end of file diff --git a/AxonIvyPortal/portal/webContent/resources/js/highlightJS/languages/css.min.js b/AxonIvyPortal/portal/webContent/resources/js/highlightJS/languages/css.min.js new file mode 100644 index 00000000000..d25a7951b36 --- /dev/null +++ b/AxonIvyPortal/portal/webContent/resources/js/highlightJS/languages/css.min.js @@ -0,0 +1,31 @@ +/*! `css` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict" +;const e=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","main","mark","menu","nav","object","ol","p","q","quote","samp","section","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video","defs","g","marker","mask","pattern","svg","switch","symbol","feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feFlood","feGaussianBlur","feImage","feMerge","feMorphology","feOffset","feSpecularLighting","feTile","feTurbulence","linearGradient","radialGradient","stop","circle","ellipse","image","line","path","polygon","polyline","rect","text","use","textPath","tspan","foreignObject","clipPath"],r=["any-hover","any-pointer","aspect-ratio","color","color-gamut","color-index","device-aspect-ratio","device-height","device-width","display-mode","forced-colors","grid","height","hover","inverted-colors","monochrome","orientation","overflow-block","overflow-inline","pointer","prefers-color-scheme","prefers-contrast","prefers-reduced-motion","prefers-reduced-transparency","resolution","scan","scripting","update","width","min-width","max-width","min-height","max-height"].sort().reverse(),o=["active","any-link","blank","checked","current","default","defined","dir","disabled","drop","empty","enabled","first","first-child","first-of-type","fullscreen","future","focus","focus-visible","focus-within","has","host","host-context","hover","indeterminate","in-range","invalid","is","lang","last-child","last-of-type","left","link","local-link","not","nth-child","nth-col","nth-last-child","nth-last-col","nth-last-of-type","nth-of-type","only-child","only-of-type","optional","out-of-range","past","placeholder-shown","read-only","read-write","required","right","root","scope","target","target-within","user-invalid","valid","visited","where"].sort().reverse(),t=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"].sort().reverse(),i=["align-content","align-items","align-self","alignment-baseline","all","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","backface-visibility","background","background-attachment","background-blend-mode","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","baseline-shift","block-size","border","border-block","border-block-color","border-block-end","border-block-end-color","border-block-end-style","border-block-end-width","border-block-start","border-block-start-color","border-block-start-style","border-block-start-width","border-block-style","border-block-width","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-inline","border-inline-color","border-inline-end","border-inline-end-color","border-inline-end-style","border-inline-end-width","border-inline-start","border-inline-start-color","border-inline-start-style","border-inline-start-width","border-inline-style","border-inline-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","cx","cy","caption-side","caret-color","clear","clip","clip-path","clip-rule","color","color-interpolation","color-interpolation-filters","color-profile","color-rendering","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","contain","content","content-visibility","counter-increment","counter-reset","cue","cue-after","cue-before","cursor","direction","display","dominant-baseline","empty-cells","enable-background","fill","fill-opacity","fill-rule","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","flow","flood-color","flood-opacity","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-size","font-size-adjust","font-smoothing","font-stretch","font-style","font-synthesis","font-variant","font-variant-caps","font-variant-east-asian","font-variant-ligatures","font-variant-numeric","font-variant-position","font-variation-settings","font-weight","gap","glyph-orientation-horizontal","glyph-orientation-vertical","grid","grid-area","grid-auto-columns","grid-auto-flow","grid-auto-rows","grid-column","grid-column-end","grid-column-start","grid-gap","grid-row","grid-row-end","grid-row-start","grid-template","grid-template-areas","grid-template-columns","grid-template-rows","hanging-punctuation","height","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","inline-size","isolation","kerning","justify-content","left","letter-spacing","lighting-color","line-break","line-height","list-style","list-style-image","list-style-position","list-style-type","marker","marker-end","marker-mid","marker-start","mask","margin","margin-block","margin-block-end","margin-block-start","margin-bottom","margin-inline","margin-inline-end","margin-inline-start","margin-left","margin-right","margin-top","marks","mask","mask-border","mask-border-mode","mask-border-outset","mask-border-repeat","mask-border-slice","mask-border-source","mask-border-width","mask-clip","mask-composite","mask-image","mask-mode","mask-origin","mask-position","mask-repeat","mask-size","mask-type","max-block-size","max-height","max-inline-size","max-width","min-block-size","min-height","min-inline-size","min-width","mix-blend-mode","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-wrap","overflow-x","overflow-y","padding","padding-block","padding-block-end","padding-block-start","padding-bottom","padding-inline","padding-inline-end","padding-inline-start","padding-left","padding-right","padding-top","page-break-after","page-break-before","page-break-inside","pause","pause-after","pause-before","perspective","perspective-origin","pointer-events","position","quotes","r","resize","rest","rest-after","rest-before","right","row-gap","scroll-margin","scroll-margin-block","scroll-margin-block-end","scroll-margin-block-start","scroll-margin-bottom","scroll-margin-inline","scroll-margin-inline-end","scroll-margin-inline-start","scroll-margin-left","scroll-margin-right","scroll-margin-top","scroll-padding","scroll-padding-block","scroll-padding-block-end","scroll-padding-block-start","scroll-padding-bottom","scroll-padding-inline","scroll-padding-inline-end","scroll-padding-inline-start","scroll-padding-left","scroll-padding-right","scroll-padding-top","scroll-snap-align","scroll-snap-stop","scroll-snap-type","scrollbar-color","scrollbar-gutter","scrollbar-width","shape-image-threshold","shape-margin","shape-outside","shape-rendering","stop-color","stop-opacity","stroke","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke-width","speak","speak-as","src","tab-size","table-layout","text-anchor","text-align","text-align-all","text-align-last","text-combine-upright","text-decoration","text-decoration-color","text-decoration-line","text-decoration-style","text-emphasis","text-emphasis-color","text-emphasis-position","text-emphasis-style","text-indent","text-justify","text-orientation","text-overflow","text-rendering","text-shadow","text-transform","text-underline-position","top","transform","transform-box","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vector-effect","vertical-align","visibility","voice-balance","voice-duration","voice-family","voice-pitch","voice-range","voice-rate","voice-stress","voice-volume","white-space","widows","width","will-change","word-break","word-spacing","word-wrap","writing-mode","x","y","z-index"].sort().reverse() +;return n=>{const a=n.regex,l=(e=>({IMPORTANT:{scope:"meta",begin:"!important"}, +BLOCK_COMMENT:e.C_BLOCK_COMMENT_MODE,HEXCOLOR:{scope:"number", +begin:/#(([0-9a-fA-F]{3,4})|(([0-9a-fA-F]{2}){3,4}))\b/},FUNCTION_DISPATCH:{ +className:"built_in",begin:/[\w-]+(?=\()/},ATTRIBUTE_SELECTOR_MODE:{ +scope:"selector-attr",begin:/\[/,end:/\]/,illegal:"$", +contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},CSS_NUMBER_MODE:{ +scope:"number", +begin:e.NUMBER_RE+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?", +relevance:0},CSS_VARIABLE:{className:"attr",begin:/--[A-Za-z_][A-Za-z0-9_-]*/} +}))(n),s=[n.APOS_STRING_MODE,n.QUOTE_STRING_MODE];return{name:"CSS", +case_insensitive:!0,illegal:/[=|'\$]/,keywords:{keyframePosition:"from to"}, +classNameAliases:{keyframePosition:"selector-tag"},contains:[l.BLOCK_COMMENT,{ +begin:/-(webkit|moz|ms|o)-(?=[a-z])/},l.CSS_NUMBER_MODE,{ +className:"selector-id",begin:/#[A-Za-z0-9_-]+/,relevance:0},{ +className:"selector-class",begin:"\\.[a-zA-Z-][a-zA-Z0-9_-]*",relevance:0 +},l.ATTRIBUTE_SELECTOR_MODE,{className:"selector-pseudo",variants:[{ +begin:":("+o.join("|")+")"},{begin:":(:)?("+t.join("|")+")"}]},l.CSS_VARIABLE,{ +className:"attribute",begin:"\\b("+i.join("|")+")\\b"},{begin:/:/,end:/[;}{]/, +contains:[l.BLOCK_COMMENT,l.HEXCOLOR,l.IMPORTANT,l.CSS_NUMBER_MODE,...s,{ +begin:/(url|data-uri)\(/,end:/\)/,relevance:0,keywords:{built_in:"url data-uri" +},contains:[...s,{className:"string",begin:/[^)]/,endsWithParent:!0, +excludeEnd:!0}]},l.FUNCTION_DISPATCH]},{begin:a.lookahead(/@/),end:"[{;]", +relevance:0,illegal:/:/,contains:[{className:"keyword",begin:/@-?\w[\w]*(-\w+)*/ +},{begin:/\s/,endsWithParent:!0,excludeEnd:!0,relevance:0,keywords:{ +$pattern:/[a-z-]+/,keyword:"and or not only",attribute:r.join(" ")},contains:[{ +begin:/[a-z-]+(?=:)/,className:"attribute"},...s,l.CSS_NUMBER_MODE]}]},{ +className:"selector-tag",begin:"\\b("+e.join("|")+")\\b"}]}}})() +;hljs.registerLanguage("css",e)})(); \ No newline at end of file diff --git a/AxonIvyPortal/portal/webContent/resources/js/highlightJS/languages/java.min.js b/AxonIvyPortal/portal/webContent/resources/js/highlightJS/languages/java.min.js new file mode 100644 index 00000000000..90d6ebc27e0 --- /dev/null +++ b/AxonIvyPortal/portal/webContent/resources/js/highlightJS/languages/java.min.js @@ -0,0 +1,38 @@ +/*! `java` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict" +;var e="[0-9](_*[0-9])*",a=`\\.(${e})`,n="[0-9a-fA-F](_*[0-9a-fA-F])*",s={ +className:"number",variants:[{ +begin:`(\\b(${e})((${a})|\\.)?|(${a}))[eE][+-]?(${e})[fFdD]?\\b`},{ +begin:`\\b(${e})((${a})[fFdD]?\\b|\\.([fFdD]\\b)?)`},{begin:`(${a})[fFdD]?\\b` +},{begin:`\\b(${e})[fFdD]\\b`},{ +begin:`\\b0[xX]((${n})\\.?|(${n})?\\.(${n}))[pP][+-]?(${e})[fFdD]?\\b`},{ +begin:"\\b(0|[1-9](_*[0-9])*)[lL]?\\b"},{begin:`\\b0[xX](${n})[lL]?\\b`},{ +begin:"\\b0(_*[0-7])*[lL]?\\b"},{begin:"\\b0[bB][01](_*[01])*[lL]?\\b"}], +relevance:0};function t(e,a,n){return-1===n?"":e.replace(a,(s=>t(e,a,n-1)))} +return e=>{ +const a=e.regex,n="[\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*",i=n+t("(?:<"+n+"~~~(?:\\s*,\\s*"+n+"~~~)*>)?",/~~~/g,2),r={ +keyword:["synchronized","abstract","private","var","static","if","const ","for","while","strictfp","finally","protected","import","native","final","void","enum","else","break","transient","catch","instanceof","volatile","case","assert","package","default","public","try","switch","continue","throws","protected","public","private","module","requires","exports","do","sealed","yield","permits"], +literal:["false","true","null"], +type:["char","boolean","long","float","int","byte","short","double"], +built_in:["super","this"]},l={className:"meta",begin:"@"+n,contains:[{ +begin:/\(/,end:/\)/,contains:["self"]}]},c={className:"params",begin:/\(/, +end:/\)/,keywords:r,relevance:0,contains:[e.C_BLOCK_COMMENT_MODE],endsParent:!0} +;return{name:"Java",aliases:["jsp"],keywords:r,illegal:/<\/|#/, +contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{begin:/\w+@/, +relevance:0},{className:"doctag",begin:"@[A-Za-z]+"}]}),{ +begin:/import java\.[a-z]+\./,keywords:"import",relevance:2 +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{begin:/"""/,end:/"""/, +className:"string",contains:[e.BACKSLASH_ESCAPE] +},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{ +match:[/\b(?:class|interface|enum|extends|implements|new)/,/\s+/,n],className:{ +1:"keyword",3:"title.class"}},{match:/non-sealed/,scope:"keyword"},{ +begin:[a.concat(/(?!else)/,n),/\s+/,n,/\s+/,/=(?!=)/],className:{1:"type", +3:"variable",5:"operator"}},{begin:[/record/,/\s+/,n],className:{1:"keyword", +3:"title.class"},contains:[c,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{ +beginKeywords:"new throw return else",relevance:0},{ +begin:["(?:"+i+"\\s+)",e.UNDERSCORE_IDENT_RE,/\s*(?=\()/],className:{ +2:"title.function"},keywords:r,contains:[{className:"params",begin:/\(/, +end:/\)/,keywords:r,relevance:0, +contains:[l,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,s,e.C_BLOCK_COMMENT_MODE] +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},s,l]}}})() +;hljs.registerLanguage("java",e)})(); \ No newline at end of file diff --git a/AxonIvyPortal/portal/webContent/resources/js/highlightJS/languages/javascript.min.js b/AxonIvyPortal/portal/webContent/resources/js/highlightJS/languages/javascript.min.js new file mode 100644 index 00000000000..3fa119329a1 --- /dev/null +++ b/AxonIvyPortal/portal/webContent/resources/js/highlightJS/languages/javascript.min.js @@ -0,0 +1,80 @@ +/*! `javascript` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict" +;const e="[A-Za-z$_][0-9A-Za-z$_]*",n=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],a=["true","false","null","undefined","NaN","Infinity"],t=["Object","Function","Boolean","Symbol","Math","Date","Number","BigInt","String","RegExp","Array","Float32Array","Float64Array","Int8Array","Uint8Array","Uint8ClampedArray","Int16Array","Int32Array","Uint16Array","Uint32Array","BigInt64Array","BigUint64Array","Set","Map","WeakSet","WeakMap","ArrayBuffer","SharedArrayBuffer","Atomics","DataView","JSON","Promise","Generator","GeneratorFunction","AsyncFunction","Reflect","Proxy","Intl","WebAssembly"],s=["Error","EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"],r=["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],c=["arguments","this","super","console","window","document","localStorage","sessionStorage","module","global"],i=[].concat(r,t,s) +;return o=>{const l=o.regex,b=e,d={begin:/<[A-Za-z0-9\\._:-]+/, +end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(e,n)=>{ +const a=e[0].length+e.index,t=e.input[a] +;if("<"===t||","===t)return void n.ignoreMatch();let s +;">"===t&&(((e,{after:n})=>{const a="",$={ +match:[/const|var|let/,/\s+/,b,/\s*/,/=\s*/,/(async\s*)?/,l.lookahead(B)], +keywords:"async",className:{1:"keyword",3:"title.function"},contains:[R]} +;return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:g,exports:{ +PARAMS_CONTAINS:w,CLASS_REFERENCE:k},illegal:/#(?![$_A-z])/, +contains:[o.SHEBANG({label:"shebang",binary:"node",relevance:5}),{ +label:"use_strict",className:"meta",relevance:10, +begin:/^\s*['"]use (strict|asm)['"]/ +},o.APOS_STRING_MODE,o.QUOTE_STRING_MODE,h,N,_,f,v,{match:/\$\d+/},A,k,{ +className:"attr",begin:b+l.lookahead(":"),relevance:0},$,{ +begin:"("+o.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*", +keywords:"return throw case",relevance:0,contains:[v,o.REGEXP_MODE,{ +className:"function",begin:B,returnBegin:!0,end:"\\s*=>",contains:[{ +className:"params",variants:[{begin:o.UNDERSCORE_IDENT_RE,relevance:0},{ +className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0, +excludeEnd:!0,keywords:g,contains:w}]}]},{begin:/,/,relevance:0},{match:/\s+/, +relevance:0},{variants:[{begin:"<>",end:""},{ +match:/<[A-Za-z0-9\\._:-]+\s*\/>/},{begin:d.begin, +"on:begin":d.isTrulyOpeningTag,end:d.end}],subLanguage:"xml",contains:[{ +begin:d.begin,end:d.end,skip:!0,contains:["self"]}]}]},I,{ +beginKeywords:"while if switch catch for"},{ +begin:"\\b(?!function)"+o.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{", +returnBegin:!0,label:"func.def",contains:[R,o.inherit(o.TITLE_MODE,{begin:b, +className:"title.function"})]},{match:/\.\.\./,relevance:0},C,{match:"\\$"+b, +relevance:0},{match:[/\bconstructor(?=\s*\()/],className:{1:"title.function"}, +contains:[R]},x,{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/, +className:"variable.constant"},O,M,{match:/\$[(.]/}]}}})() +;hljs.registerLanguage("javascript",e)})(); \ No newline at end of file diff --git a/AxonIvyPortal/portal/webContent/resources/js/highlightJS/languages/json.min.js b/AxonIvyPortal/portal/webContent/resources/js/highlightJS/languages/json.min.js new file mode 100644 index 00000000000..43e6328fe59 --- /dev/null +++ b/AxonIvyPortal/portal/webContent/resources/js/highlightJS/languages/json.min.js @@ -0,0 +1,7 @@ +/*! `json` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const a=["true","false","null"],n={ +scope:"literal",beginKeywords:a.join(" ")};return{name:"JSON",keywords:{ +literal:a},contains:[{className:"attr",begin:/"(\\.|[^\\"\r\n])*"(?=\s*:)/, +relevance:1.01},{match:/[{}[\],:]/,className:"punctuation",relevance:0 +},e.QUOTE_STRING_MODE,n,e.C_NUMBER_MODE,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE], +illegal:"\\S"}}})();hljs.registerLanguage("json",e)})(); \ No newline at end of file diff --git a/AxonIvyPortal/portal/webContent/resources/js/highlightJS/languages/markdown.min.js b/AxonIvyPortal/portal/webContent/resources/js/highlightJS/languages/markdown.min.js new file mode 100644 index 00000000000..01a1c28dfc0 --- /dev/null +++ b/AxonIvyPortal/portal/webContent/resources/js/highlightJS/languages/markdown.min.js @@ -0,0 +1,31 @@ +/*! `markdown` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const n={begin:/<\/?[A-Za-z_]/, +end:">",subLanguage:"xml",relevance:0},a={variants:[{begin:/\[.+?\]\[.*?\]/, +relevance:0},{ +begin:/\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/, +relevance:2},{ +begin:e.regex.concat(/\[.+?\]\(/,/[A-Za-z][A-Za-z0-9+.-]*/,/:\/\/.*?\)/), +relevance:2},{begin:/\[.+?\]\([./?&#].*?\)/,relevance:1},{ +begin:/\[.*?\]\(.*?\)/,relevance:0}],returnBegin:!0,contains:[{match:/\[(?=\])/ +},{className:"string",relevance:0,begin:"\\[",end:"\\]",excludeBegin:!0, +returnEnd:!0},{className:"link",relevance:0,begin:"\\]\\(",end:"\\)", +excludeBegin:!0,excludeEnd:!0},{className:"symbol",relevance:0,begin:"\\]\\[", +end:"\\]",excludeBegin:!0,excludeEnd:!0}]},i={className:"strong",contains:[], +variants:[{begin:/_{2}(?!\s)/,end:/_{2}/},{begin:/\*{2}(?!\s)/,end:/\*{2}/}] +},s={className:"emphasis",contains:[],variants:[{begin:/\*(?![*\s])/,end:/\*/},{ +begin:/_(?![_\s])/,end:/_/,relevance:0}]},c=e.inherit(i,{contains:[] +}),t=e.inherit(s,{contains:[]});i.contains.push(t),s.contains.push(c) +;let g=[n,a];return[i,s,c,t].forEach((e=>{e.contains=e.contains.concat(g) +})),g=g.concat(i,s),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{ +className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:g},{ +begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n", +contains:g}]}]},n,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)", +end:"\\s+",excludeEnd:!0},i,s,{className:"quote",begin:"^>\\s+",contains:g, +end:"$"},{className:"code",variants:[{begin:"(`{3,})[^`](.|\\n)*?\\1`*[ ]*"},{ +begin:"(~{3,})[^~](.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{ +begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))", +contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{ +begin:"^[-\\*]{3,}",end:"$"},a,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{ +className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{ +className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]}]}}})() +;hljs.registerLanguage("markdown",e)})(); \ No newline at end of file From d494c03f88ad32f0fb02e41a226843fc4e6b2fb7 Mon Sep 17 00:00:00 2001 From: mnhnam-axonivy Date: Fri, 12 Jan 2024 15:23:13 +0700 Subject: [PATCH 002/189] IVYPORTAL-16295: SPIKE UI-PoC for Portal Chatbot - Implement render normal paragraph --- .../axonivy/portal/rest/ChatBotRestService.java | 14 +++++++++++++- .../layouts/includes/ChatDashboardTemplate.xhtml | 3 +-- .../webContent/resources/js/chatbot-utils.js | 7 +++++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/AxonIvyPortal/portal/src/com/axonivy/portal/rest/ChatBotRestService.java b/AxonIvyPortal/portal/src/com/axonivy/portal/rest/ChatBotRestService.java index 456a2b55774..29f4dcc4c5c 100644 --- a/AxonIvyPortal/portal/src/com/axonivy/portal/rest/ChatBotRestService.java +++ b/AxonIvyPortal/portal/src/com/axonivy/portal/rest/ChatBotRestService.java @@ -80,7 +80,19 @@ public Response processes(@PathParam("clientId") String clientId, String content } else if (content.contentEquals("image")) { response.setMessage("https://market.axonivy.com/market-cache/portal/portal-guide/11.1.0/_images/my-profile.png"); } else { - response.setMessage("Please type 'link', 'json', or 'image'"); + response.setMessage(""" +Please type 'link', 'json', or 'image' to see special elements + +Below is long text example: + +Axon Ivy is a Process Automation Platform that simplifies and automates the interaction of humans with their digital systems. The platform is typically responsible for the most precious business cases where companies produce value. Here is how we do it: + +Visualize +Our platform allows you to document business processes fast and intuitive. A shared view of users, roles, departments and technical systems involved in a business process improves your work. HR recruitment profiles become clearer, bottlenecks become obvious, ideas for effective improvements arise from anyone involved in the process. + +Automate +Documented processes are good. But what you really want is to drive your highly valuable processes automatically. Employees are often interrupted by searching and filtering data from various tools and feeding this data into other technical systems. Even though value is produced in a well-known business case, there is a lack of a straightforward interface that guides the involved users through the process. Valuable data is often segregated and stored in various dedicated technical systems. With Axon Ivy, you can drive your process automatically. Our platform can easily orchestrate people, data and technical systems. You can generate an initial application that leads users through the process without hiring a software engineer. People can contribute to the process by using their favorite devices such as smartphones or workstations. + """); } response.setSentDate(new Date()); diff --git a/AxonIvyPortal/portal/webContent/layouts/includes/ChatDashboardTemplate.xhtml b/AxonIvyPortal/portal/webContent/layouts/includes/ChatDashboardTemplate.xhtml index 4927b3555db..5dbecaf81a8 100644 --- a/AxonIvyPortal/portal/webContent/layouts/includes/ChatDashboardTemplate.xhtml +++ b/AxonIvyPortal/portal/webContent/layouts/includes/ChatDashboardTemplate.xhtml @@ -35,10 +35,9 @@ var userName = "#{ivy.session.getSessionUserName()}"; var contextPath = "#{ivy.task.getApplication().getContextPath()}"; var resource = new IvyUri().rest() + "/chatbot"; - + var view = new View(resource); var chatbot = new ChatBot(resource, view); -
    diff --git a/AxonIvyPortal/portal/webContent/resources/js/chatbot-utils.js b/AxonIvyPortal/portal/webContent/resources/js/chatbot-utils.js index 9664986a78f..77dcbab43e6 100644 --- a/AxonIvyPortal/portal/webContent/resources/js/chatbot-utils.js +++ b/AxonIvyPortal/portal/webContent/resources/js/chatbot-utils.js @@ -122,6 +122,12 @@ const convertImage = paragraph => { return elemToString(imageContainer); }; +const convertParagraph = paragraph => { + const result = document.createElement('p'); + result.innerHTML = paragraph; + return elemToString(result); +} + // Other Functions // Corrects a URL by adding 'http://' if missing. @@ -144,6 +150,7 @@ const parseParagraph = paragraph => { const words = paragraph.split(' '); const formattedWords = words.map(w => addLink(w)); paragraph = formattedWords.join(' '); + paragraph = convertParagraph(paragraph); } return paragraph; From 124baaba9ad994ffc3b0d79f68562fc5ac62a65c Mon Sep 17 00:00:00 2001 From: mnhnam-axonivy Date: Tue, 16 Jan 2024 15:12:23 +0700 Subject: [PATCH 003/189] IVYPORTAL-16295: SPIKE UI-PoC for Portal Chatbot - Updated UI --- .../includes/ChatDashboardTemplate.xhtml | 11 +++++----- .../webContent/resources/css/chatbot.css | 21 +++++++++++++++++++ .../webContent/resources/js/chatbot-utils.js | 11 +++++++++- 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/AxonIvyPortal/portal/webContent/layouts/includes/ChatDashboardTemplate.xhtml b/AxonIvyPortal/portal/webContent/layouts/includes/ChatDashboardTemplate.xhtml index 5dbecaf81a8..5c4b0ec78b0 100644 --- a/AxonIvyPortal/portal/webContent/layouts/includes/ChatDashboardTemplate.xhtml +++ b/AxonIvyPortal/portal/webContent/layouts/includes/ChatDashboardTemplate.xhtml @@ -20,12 +20,11 @@
    -
    +
    -
    +
    diff --git a/AxonIvyPortal/portal/webContent/resources/css/chatbot.css b/AxonIvyPortal/portal/webContent/resources/css/chatbot.css index ff2faa4c42b..b8c52db04aa 100644 --- a/AxonIvyPortal/portal/webContent/resources/css/chatbot.css +++ b/AxonIvyPortal/portal/webContent/resources/css/chatbot.css @@ -57,6 +57,14 @@ text-align: left; } +.chatbot-panel .my-message .chat-message { + padding: 10px; + border-radius: 10px; + background: var(--my-message-color); + color: var(--my-message-text); + box-shadow: 0px 3px 5px rgba(41, 50, 65, 0.2); +} + .chatbot-panel .avatar-block { display: inline; margin-right: 0.5rem; @@ -74,11 +82,12 @@ cursor: default; border-radius: 10px; margin: 0; - word-break: break-all; + word-break: break-word; box-sizing: border-box; border-top-left-radius: 0px; float: left; font-size: 1.1rem; + white-space: pre-wrap; } .chatbot-panel .message-block .chat-sender, diff --git a/AxonIvyPortal/portal/webContent/resources/css/portal-variables-dark.css b/AxonIvyPortal/portal/webContent/resources/css/portal-variables-dark.css index 6f458d64d66..4120f0a8999 100644 --- a/AxonIvyPortal/portal/webContent/resources/css/portal-variables-dark.css +++ b/AxonIvyPortal/portal/webContent/resources/css/portal-variables-dark.css @@ -568,5 +568,9 @@ /*Notification channels*/ --state-active: var(--case-state-done-color); --state-inactive: var(--task-state-failed-color); + + /* Chatbot */ + --my-message-color: var(--ivy-portal-blue-light); + --my-message-text: var(--ivy-primary-color-black); } } \ No newline at end of file diff --git a/AxonIvyPortal/portal/webContent/resources/css/portal-variables-light.css b/AxonIvyPortal/portal/webContent/resources/css/portal-variables-light.css index d367f94ff9e..90355c090a3 100644 --- a/AxonIvyPortal/portal/webContent/resources/css/portal-variables-light.css +++ b/AxonIvyPortal/portal/webContent/resources/css/portal-variables-light.css @@ -579,4 +579,8 @@ /*Notification channels*/ --state-active: var(--case-state-done-color); --state-inactive: var(--task-state-failed-color); + + /* Chatbot */ + --my-message-color: var(--ivy-portal-blue-light); + --my-message-text: var(--ivy-primary-color-black); } \ No newline at end of file diff --git a/AxonIvyPortal/portal/webContent/resources/js/chatbot.js b/AxonIvyPortal/portal/webContent/resources/js/chatbot.js index c03e4a174a4..a0b32be2cc2 100644 --- a/AxonIvyPortal/portal/webContent/resources/js/chatbot.js +++ b/AxonIvyPortal/portal/webContent/resources/js/chatbot.js @@ -143,6 +143,7 @@ async function sendChatMessage(uri, view, message) { view.renderMyMessage(message); view.renderMessage(JSON.parse(response.responseText)); $('.js-chatbot-input-message').val(''); + scrollToLatestUserMessage(); } }); } @@ -156,3 +157,14 @@ function adjustMessageInputForNewline() { messageInputField.seletionStart = position + 1; messageInputField.scrollTop = messageInputField.scrollHeight; } + +function scrollToLatestUserMessage() { + var $messageList = $('.js-message-list'); + var $latestMessage = $('.my-message').last(); + + if ($latestMessage.length > 0) { + $messageList.animate({ + scrollTop: $latestMessage.offset().top - $messageList.offset().top + $messageList.scrollTop() + }, 300); + } +} From 967b6d596a264c075bd888682212db5db1b0218c Mon Sep 17 00:00:00 2001 From: mnhnam-axonivy Date: Wed, 17 Jan 2024 17:05:44 +0700 Subject: [PATCH 005/189] IVYPORTAL-16295: SPIKE UI-PoC for Portal Chatbot - Improved UX --- .../includes/ChatDashboardTemplate.xhtml | 44 ++-- .../webContent/resources/css/chatbot.css | 34 ++- .../webContent/resources/js/chatbot-utils.js | 11 +- .../portal/webContent/resources/js/chatbot.js | 233 +++++++++++++----- 4 files changed, 224 insertions(+), 98 deletions(-) diff --git a/AxonIvyPortal/portal/webContent/layouts/includes/ChatDashboardTemplate.xhtml b/AxonIvyPortal/portal/webContent/layouts/includes/ChatDashboardTemplate.xhtml index 918752d1462..45398a1b555 100644 --- a/AxonIvyPortal/portal/webContent/layouts/includes/ChatDashboardTemplate.xhtml +++ b/AxonIvyPortal/portal/webContent/layouts/includes/ChatDashboardTemplate.xhtml @@ -20,23 +20,26 @@
    -
    +
    @@ -45,14 +48,17 @@ styleClass="chatbot-message-list js-chatbot-message-list ui-g" />
    - - +
    +